From 2be5fb29ae793a06a4adc140e7ddc51cc8948245 Mon Sep 17 00:00:00 2001 From: imx-sync-bot Date: Fri, 15 Nov 2024 13:01:46 +0000 Subject: [PATCH] Automated sync from source branch master --- .commit | 1 + imxweb/.eslintignore | 1 + imxweb/.eslintrc.json | 64 + imxweb/.gitignore | 4 +- imxweb/.npmrc | 4 +- imxweb/.prettierignore | 5 + imxweb/.prettierrc.json | 7 + imxweb/.vscode/launch.json | 9 +- imxweb/.vscode/settings.json | 30 +- imxweb/angular.json | 720 +- imxweb/build-docs.js | 40 + imxweb/build/imx-prepare-workspace/Build.proj | 10 +- imxweb/build/nx/Build.proj | 41 + imxweb/changes/changesFrom9.1.1To9.2.0.md | 1575 - imxweb/compodoc/samples/menu.md | 2 +- imxweb/custom-theme/custom-theme.scss | 6 +- imxweb/custom-theme/package.json | 4 +- .../imx-modules/elemental-ui_cadence-icon.tgz | Bin 323247 -> 0 bytes imxweb/imx-modules/elemental-ui_core.tgz | Bin 1322765 -> 0 bytes imxweb/imx-modules/imx-api-APC.tgz-hash | 8 + imxweb/imx-modules/imx-api-APC.tgz-version | 1 + imxweb/imx-modules/imx-api-aad.tgz | Bin 43355 -> 0 bytes imxweb/imx-modules/imx-api-aad.tgz-hash | 8 + imxweb/imx-modules/imx-api-aad.tgz-version | 1 + imxweb/imx-modules/imx-api-aob.tgz | Bin 57653 -> 0 bytes imxweb/imx-modules/imx-api-aob.tgz-hash | 8 + imxweb/imx-modules/imx-api-aob.tgz-version | 1 + imxweb/imx-modules/imx-api-apc.tgz | Bin 16821 -> 0 bytes imxweb/imx-modules/imx-api-att.tgz | Bin 64094 -> 0 bytes imxweb/imx-modules/imx-api-att.tgz-hash | 8 + imxweb/imx-modules/imx-api-att.tgz-version | 1 + imxweb/imx-modules/imx-api-cpl.tgz | Bin 26565 -> 0 bytes imxweb/imx-modules/imx-api-cpl.tgz-hash | 8 + imxweb/imx-modules/imx-api-cpl.tgz-version | 1 + imxweb/imx-modules/imx-api-dpr.tgz | Bin 16302 -> 0 bytes imxweb/imx-modules/imx-api-dpr.tgz-hash | 8 + imxweb/imx-modules/imx-api-dpr.tgz-version | 1 + imxweb/imx-modules/imx-api-hds.tgz | Bin 12160 -> 0 bytes imxweb/imx-modules/imx-api-hds.tgz-hash | 8 + imxweb/imx-modules/imx-api-hds.tgz-version | 1 + imxweb/imx-modules/imx-api-o3e.tgz | Bin 26180 -> 0 bytes imxweb/imx-modules/imx-api-o3t.tgz | Bin 11090 -> 0 bytes imxweb/imx-modules/imx-api-olg.tgz | Bin 21648 -> 0 bytes imxweb/imx-modules/imx-api-olg.tgz-hash | 8 + imxweb/imx-modules/imx-api-olg.tgz-version | 1 + imxweb/imx-modules/imx-api-pol.tgz | Bin 17128 -> 0 bytes imxweb/imx-modules/imx-api-pol.tgz-hash | 8 + imxweb/imx-modules/imx-api-pol.tgz-version | 1 + imxweb/imx-modules/imx-api-qbm.tgz | Bin 70381 -> 0 bytes imxweb/imx-modules/imx-api-qbm.tgz-hash | 8 + imxweb/imx-modules/imx-api-qbm.tgz-version | 1 + imxweb/imx-modules/imx-api-qer.tgz | Bin 276994 -> 0 bytes imxweb/imx-modules/imx-api-qer.tgz-hash | 8 + imxweb/imx-modules/imx-api-qer.tgz-version | 1 + imxweb/imx-modules/imx-api-rmb.tgz | Bin 22563 -> 0 bytes imxweb/imx-modules/imx-api-rmb.tgz-hash | 8 + imxweb/imx-modules/imx-api-rmb.tgz-version | 1 + imxweb/imx-modules/imx-api-rms.tgz | Bin 22920 -> 0 bytes imxweb/imx-modules/imx-api-rms.tgz-hash | 8 + imxweb/imx-modules/imx-api-rms.tgz-version | 1 + imxweb/imx-modules/imx-api-rps.tgz | Bin 27001 -> 0 bytes imxweb/imx-modules/imx-api-rps.tgz-hash | 8 + imxweb/imx-modules/imx-api-rps.tgz-version | 1 + imxweb/imx-modules/imx-api-sac.tgz | Bin 23970 -> 0 bytes imxweb/imx-modules/imx-api-sac.tgz-hash | 8 + imxweb/imx-modules/imx-api-sac.tgz-version | 1 + imxweb/imx-modules/imx-api-tsb.tgz | Bin 66352 -> 0 bytes imxweb/imx-modules/imx-api-tsb.tgz-hash | 8 + imxweb/imx-modules/imx-api-tsb.tgz-version | 1 + imxweb/imx-modules/imx-api-uci.tgz | Bin 7724 -> 0 bytes imxweb/imx-modules/imx-api-uci.tgz-hash | 8 + imxweb/imx-modules/imx-api-uci.tgz-version | 1 + imxweb/imx-modules/imx-api.tgz | Bin 3499 -> 0 bytes imxweb/imx-modules/imx-qbm-dbts.tgz | Bin 150441 -> 0 bytes imxweb/install-local-packages.js | 68 + imxweb/nx.json | 48 + imxweb/package-lock.json | 41311 ++++++---------- imxweb/package.json | 219 +- imxweb/prebuild.js | 48 + imxweb/projects/aad/.compodocrc.json | 2 +- imxweb/projects/aad/.eslintrc.json | 35 + imxweb/projects/aad/imx-plugin-config.json | 14 +- imxweb/projects/aad/karma.conf.js | 13 +- imxweb/projects/aad/ng-package.dynamic.json | 5 +- imxweb/projects/aad/ng-package.json | 2 +- imxweb/projects/aad/package.json | 2 +- imxweb/projects/aad/project.json | 65 + .../projects/aad/src/lib/aad-config.module.ts | 22 +- .../aad-extension/aad-extension.service.ts | 10 +- .../licence-overview-button.component.html | 12 +- .../licence-overview-button.component.scss | 7 - .../licence-overview-button.component.ts | 30 +- .../src/lib/admin/aad-permissions.service.ts | 8 +- .../aad/src/lib/admin/permissions-helper.ts | 6 +- .../projects/aad/src/lib/api.service.spec.ts | 4 +- imxweb/projects/aad/src/lib/api.service.ts | 11 +- .../aad-group-denied-plans.component.html | 30 +- .../aad-group-denied-plans.component.ts | 23 +- .../aad-group-subscriptions.component.html | 31 +- .../aad-group-subscriptions.component.ts | 26 +- .../aad-user-create-dialog.component.html | 21 +- .../aad-user-create-dialog.component.scss | 8 - .../aad-user-create-dialog.component.ts | 15 +- .../aad-user-denied-plans.component.html | 50 +- .../aad-user-denied-plans.component.ts | 34 +- .../aad-user-subscriptions.component.html | 53 +- .../aad-user-subscriptions.component.ts | 37 +- .../aad/src/lib/azure-ad/azure-ad-common.scss | 26 +- .../aad/src/lib/azure-ad/azure-ad.module.ts | 11 +- .../aad/src/lib/azure-ad/azure-ad.service.ts | 53 +- imxweb/projects/aad/src/lib/init.service.ts | 45 +- imxweb/projects/aad/src/public_api.ts | 4 +- imxweb/projects/aad/src/test.ts | 26 +- .../aad/src/test/aad-common-test-mocks.ts | 77 +- imxweb/projects/aad/tsconfig.lib.json | 9 +- imxweb/projects/aad/tsconfig.lib.prod.json | 3 - imxweb/projects/aad/tsconfig.spec.json | 15 +- imxweb/projects/aob/.compodocrc.json | 2 +- imxweb/projects/aob/.eslintrc.json | 35 + imxweb/projects/aob/imx-plugin-config.json | 8 +- imxweb/projects/aob/karma.conf.js | 17 +- imxweb/projects/aob/ng-package.dynamic.json | 5 +- imxweb/projects/aob/ng-package.json | 2 +- imxweb/projects/aob/package.json | 3 +- imxweb/projects/aob/project.json | 64 + .../aob/src/lib/accounts/accounts.module.ts | 12 +- .../aob/src/lib/accounts/accounts.service.ts | 137 +- .../src/lib/accounts/aob-account-container.ts | 24 +- .../aob/src/lib/aob-api-client.service.ts | 11 +- .../projects/aob/src/lib/aob-config.module.ts | 55 +- imxweb/projects/aob/src/lib/aob.service.ts | 36 +- .../application-property.component.html | 4 +- .../application-property.component.scss | 30 - .../application-property.component.ts | 4 +- .../application-property.module.ts | 17 +- .../application-content.interface.ts | 12 +- .../application-create.component.html | 52 +- .../application-create.component.scss | 39 +- .../application-create.component.ts | 55 +- .../application-detail.component.html | 38 +- .../application-detail.component.scss | 86 +- .../application-detail.component.ts | 32 +- .../account-details.interface.ts | 6 +- .../application-details.component.html | 145 +- .../application-details.component.scss | 198 +- .../application-details.component.ts | 137 +- .../application-hyperview.component.html | 3 - .../application-hyperview.component.scss | 23 - .../application-hyperview.component.ts | 65 - .../application-hyperview.module.ts | 44 - .../application-image-select.component.html | 6 +- .../application-image-select.component.scss | 2 +- .../application-image-select.component.ts | 43 +- ...age-selector-dialog-parameter.interface.ts | 4 +- .../image-selector-dialog.component.html | 61 +- .../image-selector-dialog.component.scss | 53 +- .../image-selector-dialog.component.ts | 35 +- .../application-navigation.component.html | 42 +- .../application-navigation.component.scss | 43 +- .../application-navigation.component.ts | 46 +- .../applications-routing.module.ts | 18 +- .../applications/applications.component.html | 2 +- .../applications/applications.component.scss | 12 - .../applications/applications.component.ts | 71 +- .../lib/applications/applications.module.ts | 82 +- .../lib/applications/applications.service.ts | 40 +- .../authentication-root.component.html | 15 +- .../authentication-root.component.scss | 2 +- .../authentication-root.component.ts | 12 +- .../edit-application.component.html | 57 +- .../edit-application.component.scss | 26 - .../edit-application.component.ts | 108 +- .../selection-container.spec.ts | 45 +- .../edit-application/selection-container.ts | 39 +- ...ervice-category-information.component.html | 10 +- ...-service-category-information.component.ts | 18 +- .../service-category-tree-database.ts | 23 +- .../service-category.component.html | 19 +- .../service-category.component.scss | 11 - .../service-category.component.ts | 53 +- .../service-category.service.ts | 12 +- .../identities/identities.component.html | 89 +- .../identities/identities.component.scss | 21 - .../identities/identities.component.ts | 20 +- .../identities/identity-detail-data.ts | 10 +- .../identity-detail.component.html | 39 +- .../identity-detail.component.ts | 58 +- .../identities/identity.service.ts | 23 +- .../column-info/column-info.component.html | 6 +- .../column-info/column-info.component.scss | 6 +- .../lib/column-info/column-info.component.ts | 6 +- .../src/lib/column-info/column-info.module.ts | 8 +- .../entitlements-add.component.html | 138 +- .../entitlements-add.component.scss | 39 +- .../entitlements-add.component.ts | 150 +- .../system-role-config.component.html | 62 +- .../system-role-config.component.scss | 29 +- .../system-role-config.component.ts | 82 +- .../system-role-config.service.ts | 20 +- .../entitlement-detail.component.html | 3 +- .../entitlement-detail.component.scss | 2 +- .../entitlement-detail.component.ts | 54 +- .../entitlement-edit-auto-add.component.html | 39 +- .../entitlement-edit-auto-add.component.scss | 24 - .../entitlement-edit-auto-add.component.ts | 74 +- .../entitlement-edit-auto-add.service.ts | 18 +- ...entitlement-to-add-data-wrapper.service.ts | 32 +- .../entitlement-to-add-typed.ts | 17 +- ...mapped-entitlements-preview.component.html | 93 +- ...mapped-entitlements-preview.component.scss | 34 +- .../mapped-entitlements-preview.component.ts | 48 +- .../entitlement-edit.component.html | 40 +- .../entitlement-edit.component.scss | 12 +- .../entitlement-edit.component.ts | 83 +- .../entitlement-edit.module.ts | 16 +- .../entitlements/entitlement-filter.spec.ts | 18 +- .../lib/entitlements/entitlement-filter.ts | 4 +- .../entitlement-wrapper.interface.ts | 8 +- .../entitlements/entitlements.component.html | 241 +- .../entitlements/entitlements.component.scss | 72 +- .../entitlements/entitlements.component.ts | 252 +- .../lib/entitlements/entitlements.model.ts | 6 +- .../lib/entitlements/entitlements.module.ts | 52 +- .../lib/entitlements/entitlements.service.ts | 118 +- .../entitlements/publish-data.interface.ts | 6 +- .../lock-info-alert-extension.ts | 2 +- .../lock-info-alert.component.html | 9 +- .../lock-info-alert.component.scss | 10 +- .../lock-info-alert.component.spec.ts | 7 +- .../lock-info-alert.component.ts | 10 +- .../lib/global-kpi/global-kpi.component.html | 52 +- .../lib/global-kpi/global-kpi.component.scss | 44 +- .../lib/global-kpi/global-kpi.component.ts | 163 +- .../src/lib/global-kpi/global-kpi.module.ts | 16 +- .../src/lib/global-kpi/global-kpi.service.ts | 12 +- .../src/lib/global-kpi/kpi-data.interface.ts | 18 +- .../kpi-tile/kpi-tile.component.html | 27 +- .../kpi-tile/kpi-tile.component.scss | 9 - .../global-kpi/kpi-tile/kpi-tile.component.ts | 10 +- .../guards/aob-applications-guard.service.ts | 12 +- .../src/lib/guards/aob-kpi-guard.service.ts | 12 +- .../aob/src/lib/images/image.service.ts | 9 +- .../kpi-overview/kpi-overview.component.html | 90 +- .../kpi-overview/kpi-overview.component.scss | 50 +- .../kpi-overview/kpi-overview.component.ts | 66 +- .../kpi/kpi-overview/kpi-overview.service.ts | 12 +- imxweb/projects/aob/src/lib/kpi/kpi.module.ts | 2 +- .../lifecycle-action-parameter.interface.ts | 5 +- .../lifecycle-action.component.html | 142 +- .../lifecycle-action.component.scss | 71 +- .../lifecycle-action.component.spec.ts | 156 +- .../lifecycle-action.component.ts | 112 +- .../lifecycle-action.enum.ts | 4 +- .../lifecycle-actions.module.ts | 17 +- .../lib/permissions/aob-permissions-helper.ts | 6 +- .../permissions/aob-permissions.service.ts | 15 +- .../lib/service-items/service-items.module.ts | 8 +- .../service-items/service-items.service.ts | 27 +- .../aob/src/lib/shops/shops.service.ts | 69 +- .../lib/start-page/start-page.component.html | 32 +- .../lib/start-page/start-page.component.scss | 25 +- .../lib/start-page/start-page.component.ts | 24 +- .../src/lib/start-page/start-page.module.ts | 11 +- .../aob/src/lib/user/user.component.html | 4 +- .../aob/src/lib/user/user.component.scss | 16 +- .../aob/src/lib/user/user.component.ts | 12 +- .../projects/aob/src/lib/user/user.module.ts | 18 +- imxweb/projects/aob/src/public-api.ts | 4 +- imxweb/projects/aob/src/test.ts | 26 +- imxweb/projects/aob/tsconfig.lib.json | 11 +- imxweb/projects/aob/tsconfig.lib.prod.json | 3 - imxweb/projects/aob/tsconfig.spec.json | 15 +- imxweb/projects/aob/tslint.json | 17 - imxweb/projects/apc/.compodocrc.json | 2 +- imxweb/projects/apc/.eslintrc.json | 35 + imxweb/projects/apc/imx-plugin-config.json | 2 +- imxweb/projects/apc/karma.conf.js | 11 +- imxweb/projects/apc/ng-package.dynamic.json | 5 +- imxweb/projects/apc/ng-package.json | 2 +- imxweb/projects/apc/package.json | 2 +- imxweb/projects/apc/project.json | 58 + .../apc/src/lib/apc-api-client.service.ts | 9 +- .../projects/apc/src/lib/apc-config.module.ts | 20 +- imxweb/projects/apc/src/lib/app-info.model.ts | 6 +- imxweb/projects/apc/src/lib/init.service.ts | 11 +- .../software-memberships.component.html | 150 +- .../software-memberships.component.scss | 57 +- .../software-memberships.component.ts | 93 +- .../software-sidesheet.component.html | 43 +- .../software-sidesheet.component.scss | 113 +- .../software-sidesheet.component.ts | 30 +- .../src/lib/software/software.component.html | 53 +- .../src/lib/software/software.component.scss | 51 +- .../src/lib/software/software.component.ts | 65 +- .../apc/src/lib/software/software.module.ts | 31 +- .../apc/src/lib/software/software.service.ts | 64 +- imxweb/projects/apc/src/public_api.ts | 2 +- imxweb/projects/apc/src/test.ts | 26 +- imxweb/projects/apc/tsconfig.lib.json | 9 +- imxweb/projects/apc/tsconfig.lib.prod.json | 3 - imxweb/projects/apc/tsconfig.spec.json | 15 +- imxweb/projects/apc/tslint.json | 17 - imxweb/projects/att/.compodocrc.json | 2 +- imxweb/projects/att/.eslintrc.json | 35 + imxweb/projects/att/imx-plugin-config.json | 26 +- imxweb/projects/att/karma.conf.js | 13 +- .../ng-package.dynamic-pwd.json} | 5 +- imxweb/projects/att/ng-package.dynamic.json | 5 +- imxweb/projects/att/ng-package.json | 2 +- imxweb/projects/att/package.json | 2 +- imxweb/projects/att/project.json | 78 + imxweb/projects/att/src/default-mocks.spec.ts | 4 +- .../att/src/lib/admin/permissions-helper.ts | 14 +- .../att/src/lib/admin/permissions.service.ts | 10 +- imxweb/projects/att/src/lib/api.service.ts | 11 +- .../projects/att/src/lib/att-config.module.ts | 12 +- .../attestation-action.component.html | 10 +- .../attestation-action.component.scss | 21 +- .../attestation-action.component.spec.ts | 6 +- .../attestation-action.component.ts | 24 +- .../attestation-action.service.ts | 183 +- .../attestation-case-action.interface.ts | 10 +- .../attestation-workflow.service.ts | 12 +- .../attestation-display.component.html | 2 +- .../attestation-display.component.scss | 1 - .../attestation-display.component.ts | 11 +- .../attestation-display.module.ts | 17 +- .../attestation-feature-guard.service.spec.ts | 14 +- .../lib/attestation-feature-guard.service.ts | 15 +- ...estation-case-load-parameters.interface.ts | 6 +- .../attestation-history-action.service.ts | 90 +- .../attestation-history-case.ts | 29 +- ...attestation-history-details.component.html | 67 +- ...attestation-history-details.component.scss | 54 +- .../attestation-history-details.component.ts | 62 +- .../attestation-history-filter.component.html | 4 +- .../attestation-history-filter.component.scss | 2 +- .../attestation-history-filter.component.ts | 31 +- ...attestation-history-wrapper.component.html | 52 +- ...attestation-history-wrapper.component.scss | 18 +- .../attestation-history-wrapper.component.ts | 46 +- .../attestation-history.component.html | 119 +- .../attestation-history.component.scss | 90 +- .../attestation-history.component.ts | 232 +- .../attestation-history.module.ts | 35 +- .../attestation-history.service.ts | 46 +- .../my-attestation-cases.component.html | 15 +- .../my-attestation-cases.component.scss | 7 - .../my-attestation-cases.component.ts | 4 +- .../attestation-snapshot.component.html | 14 +- .../attestation-snapshot.component.scss | 17 +- .../attestation-snapshot.component.ts | 28 +- .../attestation-snapshot.module.ts | 41 +- .../claim-device/claim-device.component.html | 58 +- .../claim-device/claim-device.component.scss | 5 - .../claim-device.component.spec.ts | 56 +- .../claim-device/claim-device.component.ts | 72 +- .../lib/claim-device/claim-device.module.ts | 11 +- .../lib/claim-device/claim-device.service.ts | 47 +- .../dashboard-plugin.component.html | 8 +- .../dashboard-plugin.component.ts | 12 +- .../att/src/lib/datamodel/datamodel-helper.ts | 40 +- .../src/lib/decision/approvers.interface.ts | 6 +- .../approvers/approvers.component.html | 26 +- .../approvers/approvers.component.scss | 23 +- .../decision/approvers/approvers.component.ts | 4 +- .../decision/attestation-case.component.html | 94 +- .../decision/attestation-case.component.scss | 90 +- .../decision/attestation-case.component.ts | 45 +- .../src/lib/decision/attestation-case.spec.ts | 47 +- .../att/src/lib/decision/attestation-case.ts | 30 +- .../lib/decision/attestation-cases.service.ts | 114 +- .../attestation-decision-load-parameters.ts | 6 +- .../attestation-decision.component.html | 302 +- .../attestation-decision.component.scss | 167 +- .../attestation-decision.component.ts | 383 +- .../decision/attestation-decision.module.ts | 52 +- .../attestation-inquiries.component.html | 100 +- .../attestation-inquiries.component.scss | 29 +- .../attestation-inquiries.component.ts | 153 +- .../attestation-inquiry.model.ts | 6 +- ...cision-compliance-violation.component.html | 162 +- ...cision-compliance-violation.component.scss | 46 +- ...decision-compliance-violation.component.ts | 6 +- .../decision-history-item.component.html | 71 +- .../decision-history-item.component.scss | 41 +- .../decision-history-item.component.ts | 6 +- .../decision-policy-violation.component.html | 136 +- .../decision-policy-violation.component.scss | 46 +- .../decision-policy-violation.component.ts | 15 +- .../loss-preview-dialog.component.html | 4 +- .../loss-preview-dialog.component.scss | 12 +- .../loss-preview-dialog.component.ts | 9 +- .../loss-preview-table.component.html | 37 +- .../loss-preview-table.component.scss | 26 - .../loss-preview-table.component.ts | 10 +- .../lib/decision/loss-preview.interface.ts | 4 +- .../mitigating-controls.component.html | 29 +- .../mitigating-controls.component.scss | 2 +- .../mitigating-controls.component.ts | 15 +- .../entity-property-editor.component.html | 2 +- .../entity-property-editor.component.scss | 2 +- .../entity-property-editor.component.ts | 14 +- .../guards/attestation-admin-guard.service.ts | 12 +- .../attestation-policies-guard.service.ts | 12 +- .../att/src/lib/hardware-guard.service.ts | 13 +- .../src/lib/identity-attestation.service.ts | 34 +- imxweb/projects/att/src/lib/init.service.ts | 61 +- .../new-user/confirm-dialog.component.html | 6 +- .../lib/new-user/confirm-dialog.component.ts | 9 +- .../new-user/new-user-metadata.service.ts} | 24 +- .../src/lib/new-user/new-user.component.html | 34 +- .../src/lib/new-user/new-user.component.scss | 19 - .../src/lib/new-user/new-user.component.ts | 76 +- .../att/src/lib/new-user/new-user.module.ts | 9 +- .../new-user/open-sidesheet.component.scss | 5 - .../lib/new-user/open-sidesheet.component.ts | 31 +- .../new-user/user-activation.component.html | 29 +- .../new-user/user-activation.component.scss | 8 - .../lib/new-user/user-activation.component.ts | 67 +- .../pick-category/pick-category-common.scss | 27 +- .../pick-category-create.component.html | 50 +- .../pick-category-create.component.scss | 91 +- .../pick-category-create.component.spec.ts | 56 +- .../pick-category-create.component.ts | 78 +- ...-category-select-identities.component.html | 55 +- ...-category-select-identities.component.scss | 38 +- ...tegory-select-identities.component.spec.ts | 57 +- ...ck-category-select-identities.component.ts | 67 +- .../pick-category-sidesheet.component.html | 75 +- .../pick-category-sidesheet.component.scss | 31 +- .../pick-category-sidesheet.component.spec.ts | 59 +- .../pick-category-sidesheet.component.ts | 108 +- .../pick-category.component.html | 66 +- .../pick-category.component.scss | 41 +- .../pick-category.component.spec.ts | 66 +- .../pick-category/pick-category.component.ts | 161 +- .../lib/pick-category/pick-category.module.ts | 29 +- .../pick-category.service.spec.ts | 52 +- .../pick-category/pick-category.service.ts | 136 +- ...ion-cases-component-parameter.interface.ts | 18 +- ...attestation-cases-tree-database.service.ts | 122 - .../attestation-cases.component.html | 95 +- .../attestation-cases.component.scss | 149 +- .../attestation-cases.component.ts | 125 +- .../confirm-deactivation.component.html | 26 +- .../confirm-deactivation.component.scss | 15 +- .../confirm-deactivation.component.ts | 5 +- .../edit-master-data.component.html | 133 +- .../edit-master-data.component.scss | 70 +- .../edit-master-data.component.ts | 153 +- .../editors/edit-generic.component.html | 3 +- .../editors/edit-generic.component.ts | 10 +- .../policies/editors/edit-name.component.html | 3 +- .../policies/editors/edit-name.component.ts | 9 +- .../editors/edit-origin.component.html | 21 +- .../editors/edit-origin.component.scss | 6 +- .../editors/edit-origin.component.spec.ts | 141 +- .../policies/editors/edit-origin.component.ts | 32 +- .../editors/edit-threshold.component.html | 40 +- .../editors/edit-threshold.component.scss | 12 +- .../editors/edit-threshold.component.ts | 16 +- .../policies/editors/edit-uint.component.html | 7 +- .../policies/editors/edit-uint.component.ts | 9 +- .../filter-changed-argument.interface.ts | 10 +- .../editors/filter-editor.component.html | 50 +- .../editors/filter-editor.component.scss | 2 +- .../editors/filter-editor.component.ts | 15 +- .../editors/filter-element-column.service.ts | 75 +- .../policies/editors/filter-element-model.ts | 47 +- .../policy-details.component.html | 21 +- .../policy-details.component.scss | 37 +- .../policy-details.component.ts | 24 +- .../policies/policy-editor/filter-model.ts | 79 +- .../policy-editor.component.html | 53 +- .../policy-editor.component.scss | 63 +- .../policy-editor/policy-editor.component.ts | 199 +- .../policy-filter-element.component.html | 92 +- .../policy-filter-element.component.scss | 57 +- .../policy-filter-element.component.ts | 48 +- .../policy-list/attestation-policy.ts | 4 +- .../policy-list/policy-list.component.html | 161 +- .../policy-list/policy-list.component.scss | 81 +- .../policy-list/policy-list.component.ts | 297 +- .../policy-load-parameters.interface.ts | 4 +- .../att/src/lib/policies/policy.interface.ts | 4 +- .../att/src/lib/policies/policy.module.ts | 149 +- .../att/src/lib/policies/policy.service.ts | 27 +- .../selected-objects-info.interface.ts | 10 +- .../selected-objects.component.html | 23 +- .../selected-objects.component.scss | 13 +- .../selected-objects.component.ts | 37 +- ...edit-policy-group-sidesheet.component.html | 18 +- ...edit-policy-group-sidesheet.component.scss | 13 +- .../edit-policy-group-sidesheet.component.ts | 45 +- .../policy-group-list.component.html | 87 +- .../policy-group-list.component.scss | 34 +- .../policy-group-list.component.ts | 185 +- .../policy-group/policy-group.interface.ts | 6 +- .../lib/policy-group/policy-group.module.ts | 44 +- .../lib/policy-group/policy-group.service.ts | 116 +- .../src/lib/runs/attestation-runs.module.ts | 34 +- .../attestation-wrapper.component.html | 3 +- .../attestation-wrapper.component.scss | 6 +- .../attestation-wrapper.component.ts | 28 +- .../attestation/attestation.component.html | 47 +- .../attestation/attestation.component.scss | 82 +- .../runs/attestation/attestation.component.ts | 23 +- .../runs/case-chart/case-chart.component.html | 4 +- .../runs/case-chart/case-chart.component.scss | 6 - .../runs/case-chart/case-chart.component.ts | 12 +- imxweb/projects/att/src/lib/runs/helpers.ts | 2 +- .../lib/runs/pending-approvers.component.html | 72 +- .../lib/runs/pending-approvers.component.scss | 56 +- .../lib/runs/pending-approvers.component.ts | 45 +- .../lib/runs/progress/progress.component.html | 12 +- .../lib/runs/progress/progress.component.scss | 10 - .../lib/runs/progress/progress.component.ts | 7 +- .../runs/run-extend/run-extend.component.html | 35 +- .../runs/run-extend/run-extend.component.scss | 26 +- .../runs/run-extend/run-extend.component.ts | 24 +- .../src/lib/runs/run-sidesheet.component.html | 313 +- .../src/lib/runs/run-sidesheet.component.scss | 94 +- .../src/lib/runs/run-sidesheet.component.ts | 135 +- .../runs/runs-grid/runs-grid.component.html | 166 +- .../runs/runs-grid/runs-grid.component.scss | 23 +- .../lib/runs/runs-grid/runs-grid.component.ts | 247 +- .../runs/runs-load-parameters.interface.ts | 4 +- .../att/src/lib/runs/runs.component.html | 17 +- .../att/src/lib/runs/runs.component.scss | 15 - .../att/src/lib/runs/runs.component.ts | 17 +- .../projects/att/src/lib/runs/runs.service.ts | 31 +- .../runs/send-reminder-mail.component.html | 20 +- .../runs/send-reminder-mail.component.scss | 30 +- .../lib/runs/send-reminder-mail.component.ts | 13 +- imxweb/projects/att/src/public_api.ts | 20 +- imxweb/projects/att/src/test.ts | 29 +- imxweb/projects/att/tsconfig.lib.json | 12 +- imxweb/projects/att/tsconfig.lib.prod.json | 3 - imxweb/projects/att/tsconfig.spec.json | 15 +- imxweb/projects/att/tslint.json | 17 - imxweb/projects/cpl/.compodocrc.json | 2 +- imxweb/projects/cpl/.eslintrc.json | 35 + imxweb/projects/cpl/imx-plugin-config.json | 15 +- imxweb/projects/cpl/karma.conf.js | 13 +- imxweb/projects/cpl/ng-package.dynamic.json | 5 +- imxweb/projects/cpl/ng-package.json | 2 +- imxweb/projects/cpl/package.json | 2 +- imxweb/projects/cpl/project.json | 64 + imxweb/projects/cpl/src/lib/api.service.ts | 11 +- .../projects/cpl/src/lib/cpl-config.module.ts | 28 +- .../dashboard-plugin.component.html | 14 +- .../dashboard-plugin.component.ts | 11 +- .../guards/compliance-rules-guard.service.ts | 12 +- .../guards/rule-violations-guard.service.ts | 12 +- ...olations-mitigation-control.component.html | 14 +- ...olations-mitigation-control.component.scss | 2 +- ...violations-mitigation-control.component.ts | 42 +- .../identity-rule-violations.component.html | 31 +- .../identity-rule-violations.component.scss | 48 +- .../identity-rule-violations.component.ts | 23 +- .../identity-rule-violations.module.ts | 14 +- .../identity-rule-violations.service.ts | 37 +- imxweb/projects/cpl/src/lib/init.service.ts | 32 +- .../cart-item-compliance-check.component.html | 4 +- .../cart-item-compliance-check.component.ts | 12 +- .../item-validator/item-validator.service.ts | 12 +- ...itigating-control-container.component.html | 11 +- .../mitigating-control-container.component.ts | 42 +- .../mitigating-control-container.module.ts | 4 +- .../src/lib/mitigating-controls-common.scss | 155 +- ...ompliance-violation-details.component.html | 39 +- ...ompliance-violation-details.component.scss | 18 +- .../compliance-violation-details.component.ts | 60 +- .../compliance-violation.service.ts | 11 +- .../edit-mitigating-controls.component.scss | 4 +- .../edit-mitigating-controls.component.ts | 10 +- .../extended-deferred-operations-data.ts | 30 +- .../mitigating-control-data.interface.ts | 10 +- ...mitigating-controls-request.component.html | 32 +- ...mitigating-controls-request.component.scss | 2 +- .../mitigating-controls-request.component.ts | 44 +- .../mitigating-controls-request.service.ts | 20 +- .../request-mitigating-control-filter.pipe.ts | 2 +- .../request-mitigating-controls.ts | 13 +- .../lib/request/compliance-violation-model.ts | 4 +- .../request/request-rule-violation-detail.ts | 8 +- .../request/request-rule-violation.spec.ts | 2 +- .../src/lib/request/request-rule-violation.ts | 14 +- .../cpl/src/lib/request/request.module.ts | 8 +- .../workflow-violation-details.component.html | 12 +- .../workflow-violation-details.component.scss | 17 - .../workflow-violation-details.component.ts | 13 +- .../role-compliance-violation-typed-entity.ts | 26 +- .../role-compliance-violations-wrapper.ts | 12 +- .../role-compliance-violations.component.html | 36 +- .../role-compliance-violations.component.scss | 8 +- .../role-compliance-violations.component.ts | 10 +- .../role-compliance-violations.module.ts | 14 +- .../role-compliance-violations.service.ts | 24 +- .../mitigating-controls-person.component.html | 39 +- .../mitigating-controls-person.component.scss | 34 +- .../mitigating-controls-person.component.ts | 22 +- .../mitigating-controls-person.service.ts | 20 +- .../person-mitigating-controls.ts | 22 +- .../resolve/resolve.component.html | 43 +- .../resolve/resolve.component.scss | 15 +- .../resolve/resolve.component.ts | 300 +- ...-violations-action-parameters.interface.ts | 2 +- .../rules-violations-action.component.html | 18 +- .../rules-violations-action.component.scss | 26 +- .../rules-violations-action.component.ts | 9 +- .../rules-violations-action.interface.ts | 2 +- .../rules-violations-action.service.ts | 95 +- ...les-violations-multi-action.component.html | 23 +- ...les-violations-multi-action.component.scss | 40 +- ...rules-violations-multi-action.component.ts | 10 +- ...es-violations-single-action.component.html | 11 +- ...es-violations-single-action.component.scss | 10 - ...ules-violations-single-action.component.ts | 10 +- .../rules-violations-approval.ts | 6 +- .../rules-violations-details.component.html | 35 +- .../rules-violations-details.component.scss | 57 +- .../rules-violations-details.component.ts | 22 +- ...es-violations-load-parameters.interface.ts | 4 +- .../rules-violations.component.html | 180 +- .../rules-violations.component.scss | 66 +- .../rules-violations.component.ts | 168 +- .../rules-violations.module.ts | 94 +- .../rules-violations.service.ts | 35 +- .../rules/admin/cpl-permissions.service.ts | 10 +- .../src/lib/rules/admin/permissions-helper.ts | 7 +- .../mitigating-controls-rules.component.html | 30 +- .../mitigating-controls-rules.component.scss | 28 +- ...itigating-controls-rules.component.spec.ts | 28 +- .../mitigating-controls-rules.component.ts | 11 +- .../mitigating-controls-rules.service.spec.ts | 7 +- .../mitigating-controls-rules.service.ts | 21 +- .../rules-mitigating-controls.ts | 21 +- .../cpl/src/lib/rules/rule-parameter.ts | 6 +- .../rules-sidesheet.component.html | 19 +- .../rules-sidesheet.component.scss | 47 +- .../rules-sidesheet.component.ts | 24 +- .../violations-per-rule.component.html | 8 +- .../violations-per-rule.component.scss | 29 +- .../violations-per-rule.component.ts | 25 +- .../cpl/src/lib/rules/rules.component.html | 127 +- .../cpl/src/lib/rules/rules.component.scss | 33 +- .../cpl/src/lib/rules/rules.component.ts | 97 +- .../cpl/src/lib/rules/rules.module.ts | 30 +- .../cpl/src/lib/rules/rules.service.ts | 52 +- imxweb/projects/cpl/src/public_api.ts | 4 +- imxweb/projects/cpl/src/test.ts | 26 +- imxweb/projects/cpl/tsconfig.lib.json | 11 +- imxweb/projects/cpl/tsconfig.lib.prod.json | 3 - imxweb/projects/cpl/tsconfig.spec.json | 15 +- imxweb/projects/cpl/tslint.json | 17 - imxweb/projects/custom-app/.eslintrc.json | 35 + imxweb/projects/custom-app/karma.conf.js | 13 +- imxweb/projects/custom-app/package.json | 4 +- imxweb/projects/custom-app/project.json | 197 + imxweb/projects/custom-app/readme.md | 2 +- .../custom-app/src/app/app-routing.module.ts | 17 +- .../custom-app/src/app/app.component.html | 17 +- .../custom-app/src/app/app.component.scss | 2 +- .../custom-app/src/app/app.component.ts | 19 +- .../projects/custom-app/src/app/app.module.ts | 143 +- .../custom-app/src/app/app.service.ts | 25 +- .../custom-app/src/app/start.component.html | 6 +- .../custom-app/src/app/start.component.ts | 17 +- .../src/environments/environment.prod.ts | 4 +- .../src/environments/environment.ts | 6 +- imxweb/projects/custom-app/src/index.html | 20 +- imxweb/projects/custom-app/src/main.ts | 7 +- imxweb/projects/custom-app/src/polyfills.ts | 20 +- imxweb/projects/custom-app/src/styles.scss | 9 +- .../projects/{o3t => custom-app}/src/test.ts | 26 +- .../projects/custom-app/tsconfig-es5.app.json | 8 +- imxweb/projects/custom-app/tsconfig.app.json | 13 +- imxweb/projects/custom-app/tsconfig.spec.json | 15 +- imxweb/projects/custom-app/tslint.json | 17 - imxweb/projects/dpr/.compodocrc.json | 2 +- imxweb/projects/dpr/.eslintrc.json | 35 + imxweb/projects/dpr/karma.conf.js | 13 +- imxweb/projects/dpr/ng-package.dynamic.json | 3 +- imxweb/projects/dpr/ng-package.json | 2 +- imxweb/projects/dpr/package.json | 2 +- imxweb/projects/dpr/project.json | 64 + imxweb/projects/dpr/src/lib/api.service.ts | 11 +- .../outstanding/dependencies.component.html | 10 +- .../outstanding/dependencies.component.scss | 13 +- .../lib/outstanding/dependencies.component.ts | 19 +- .../outstanding/outstanding-object-entity.ts | 10 +- .../outstanding/outstanding.component.html | 81 +- .../outstanding/outstanding.component.scss | 55 +- .../lib/outstanding/outstanding.component.ts | 82 +- .../src/lib/outstanding/outstanding.module.ts | 18 +- .../lib/outstanding/outstanding.service.ts | 31 +- .../selected-items.component.html | 25 +- .../selected-items.component.scss | 31 +- .../selected-items.component.ts | 36 +- imxweb/projects/dpr/src/public_api.ts | 4 +- imxweb/projects/dpr/src/test.ts | 26 +- imxweb/projects/dpr/tsconfig.lib.json | 9 +- imxweb/projects/dpr/tsconfig.lib.prod.json | 3 - imxweb/projects/dpr/tsconfig.spec.json | 15 +- imxweb/projects/dpr/tslint.json | 17 - imxweb/projects/hds/.compodocrc.json | 2 +- imxweb/projects/hds/.eslintrc.json | 35 + imxweb/projects/hds/imx-plugin-config.json | 2 +- imxweb/projects/hds/karma.conf.js | 11 +- imxweb/projects/hds/ng-package.dynamic.json | 5 +- imxweb/projects/hds/ng-package.json | 2 +- imxweb/projects/hds/package.json | 2 +- imxweb/projects/hds/project.json | 65 + .../calls-attachment-dialog.component.html | 14 +- .../calls-attachment-dialog.component.scss | 25 - .../calls-attachment-dialog.component.ts | 14 +- .../calls-attachment-service.service.ts | 26 +- .../calls-attachment.component.html | 59 +- .../calls-attachment.component.scss | 89 +- .../calls-attachment.component.ts | 171 +- .../calls-attachment.model.ts | 2 +- .../calls-history-sidesheet.component.html | 23 +- .../calls-history-sidesheet.component.scss | 117 - .../calls-history-sidesheet.component.ts | 20 +- .../calls-history.component.html | 21 +- .../calls-history.component.scss | 12 +- .../calls-history/calls-history.component.ts | 85 +- .../calls-sidesheet.component.html | 56 +- .../calls-sidesheet.component.scss | 81 +- .../calls-sidesheet.component.ts | 73 +- .../hds/src/lib/calls/calls.component.html | 187 +- .../hds/src/lib/calls/calls.component.scss | 26 - .../hds/src/lib/calls/calls.component.ts | 125 +- .../hds/src/lib/calls/calls.module.ts | 27 +- .../hds/src/lib/hds-api-client.service.ts | 12 +- .../projects/hds/src/lib/hds-config.module.ts | 17 +- imxweb/projects/hds/src/lib/init.service.ts | 21 +- imxweb/projects/hds/src/public_api.ts | 2 +- imxweb/projects/hds/src/test.ts | 26 +- imxweb/projects/hds/tsconfig.lib.json | 9 +- imxweb/projects/hds/tsconfig.lib.prod.json | 3 - imxweb/projects/hds/tsconfig.spec.json | 15 +- imxweb/projects/hds/tslint.json | 17 - imxweb/projects/o3t/imx-plugin-config.json | 8 - imxweb/projects/o3t/package.json | 8 - .../team-channel-details.component.html | 38 - .../team-channel-details.component.ts | 83 - .../team-channels.component.html | 34 - .../team-channels/team-channels.component.ts | 123 - .../team-details/team-details.component.html | 60 - .../team-details/team-details.component.ts | 140 - .../o3t/src/lib/teams/teams.component.html | 42 - .../o3t/src/lib/teams/teams.component.ts | 123 - .../o3t/src/lib/teams/teams.service.ts | 75 - .../o3t/src/test/o3t-common-test-mocks.ts | 165 - imxweb/projects/o3t/tslint.json | 17 - imxweb/projects/olg/.compodocrc.json | 2 +- imxweb/projects/olg/.eslintrc.json | 35 + imxweb/projects/olg/imx-plugin-config.json | 2 +- imxweb/projects/olg/karma.conf.js | 11 +- imxweb/projects/olg/ng-package.dynamic.json | 5 +- imxweb/projects/olg/ng-package.json | 2 +- imxweb/projects/olg/package.json | 2 +- imxweb/projects/olg/project.json | 65 + .../projects/olg/src/lib/init.service.spec.ts | 2 +- imxweb/projects/olg/src/lib/init.service.ts | 9 +- .../mfa-form-control.component.html | 20 +- .../mfa-form-control.component.scss | 12 +- .../mfa-form-control.component.ts | 72 +- .../olg/src/lib/mfa/mfa.component.html | 7 +- .../olg/src/lib/mfa/mfa.component.scss | 7 +- .../projects/olg/src/lib/mfa/mfa.component.ts | 18 +- .../projects/olg/src/lib/mfa/mfa.service.ts | 4 +- .../olg/src/lib/mfa/portal-mfa.service.ts | 4 +- .../olg/src/lib/olg-api-client.service.ts | 12 +- .../projects/olg/src/lib/olg-config.module.ts | 25 +- imxweb/projects/olg/src/public_api.ts | 3 +- imxweb/projects/olg/src/test.ts | 26 +- imxweb/projects/olg/tsconfig.lib.json | 9 +- imxweb/projects/olg/tsconfig.lib.prod.json | 3 - imxweb/projects/olg/tsconfig.spec.json | 15 +- imxweb/projects/olg/tslint.json | 17 - imxweb/projects/pol/.compodocrc.json | 2 +- imxweb/projects/pol/.eslintrc.json | 35 + imxweb/projects/pol/imx-plugin-config.json | 14 +- imxweb/projects/pol/karma.conf.js | 13 +- imxweb/projects/pol/ng-package.dynamic.json | 5 +- imxweb/projects/pol/ng-package.json | 2 +- imxweb/projects/pol/package.json | 2 +- imxweb/projects/pol/project.json | 64 + .../pol/src/lib/admin/permissions-helper.ts | 10 +- .../pol/src/lib/admin/permissions.service.ts | 7 +- imxweb/projects/pol/src/lib/api.service.ts | 11 +- .../dashboard-plugin.component.html | 14 +- .../dashboard-plugin.component.ts | 14 +- .../lib/guards/policy-admin-guard.service.ts | 19 +- .../policy-owner-or-admin-guard.service.ts} | 42 +- imxweb/projects/pol/src/lib/init.service.ts | 51 +- .../projects/pol/src/lib/pol-config.module.ts | 43 +- .../mitigating-controls-policy.component.html | 15 +- .../mitigating-controls-policy.component.scss | 31 +- .../mitigating-controls-policy.component.ts | 12 +- .../policies-sidesheet.component.html | 25 +- .../policies-sidesheet.component.scss | 48 - .../policies-sidesheet.component.ts | 18 +- .../src/lib/policies/policies.component.html | 85 +- .../src/lib/policies/policies.component.scss | 29 +- .../src/lib/policies/policies.component.ts | 85 +- .../pol/src/lib/policies/policies.module.ts | 29 +- .../pol/src/lib/policies/policies.service.ts | 18 +- .../pol/src/lib/policies/policy-parameter.ts | 6 +- .../mitigating-controls.component.html | 66 +- .../mitigating-controls.component.scss | 154 +- .../mitigating-controls.component.ts | 34 +- .../policy-violation-extended.ts | 11 +- .../lib/policy-violations/policy-violation.ts | 32 +- ...lations-action-multi-action.component.html | 33 +- ...lations-action-multi-action.component.scss | 18 - ...iolations-action-multi-action.component.ts | 7 +- ...-violations-action-parameters.interface.ts | 10 +- ...ations-action-single-action.component.html | 13 +- ...ations-action-single-action.component.scss | 12 +- ...olations-action-single-action.component.ts | 10 +- .../policy-violations-action.component.html | 13 +- .../policy-violations-action.component.scss | 14 +- .../policy-violations-action.component.ts | 9 +- .../policy-violations-action.interface.ts | 2 +- ...policy-violations-sidesheet.component.html | 48 +- ...policy-violations-sidesheet.component.scss | 74 +- .../policy-violations-sidesheet.component.ts | 69 +- .../policy-violations.component.html | 176 +- .../policy-violations.component.scss | 83 +- .../policy-violations.component.ts | 180 +- .../policy-violations.module.ts | 40 +- .../policy-violations.service.ts | 125 +- imxweb/projects/pol/src/public_api.ts | 3 +- imxweb/projects/pol/src/test.ts | 26 +- imxweb/projects/pol/tsconfig.lib.json | 9 +- imxweb/projects/pol/tsconfig.lib.prod.json | 3 - imxweb/projects/pol/tsconfig.spec.json | 15 +- imxweb/projects/pol/tslint.json | 17 - imxweb/projects/{o3t => qam}/.compodocrc.json | 11 +- imxweb/projects/{o3t => qam}/Build.proj | 11 +- imxweb/projects/qam/imx-plugin-config.json | 8 + imxweb/projects/{o3t => qam}/karma.conf.js | 16 +- .../{uci => qam}/ng-package.dynamic.json | 4 +- imxweb/projects/{o3t => qam}/ng-package.json | 4 +- imxweb/projects/qam/package.json | 8 + imxweb/projects/qam/project.json | 65 + imxweb/projects/qam/src/lib/TypedClient.ts | 5497 ++ .../access-request-data.service.ts | 64 + ...access-request-sidesheet-data.interface.ts | 36 + .../access-request-sidesheet.component.html | 53 + .../access-request-sidesheet.component.scss | 3 + .../access-request-sidesheet.component.ts | 209 + .../access-request-tree-database.ts | 89 + .../access-request/access-request.module.ts} | 33 +- .../access-request/access-request.service.ts | 120 + .../lib/access-request/qam-resourcetree.ts | 124 + .../qam/src/lib/access/access.component.html | 13 + .../qam/src/lib/access/access.component.scss | 21 + .../qam/src/lib/access/access.component.ts | 69 + .../lib/access/trustee-view.component.html | 61 + .../lib/access/trustee-view.component.scss | 73 + .../src/lib/access/trustee-view.component.ts | 97 + .../src/lib/access/user-access.component.html | 1 + .../src/lib/access/user-access.component.ts} | 20 +- .../dug-activities.component.html | 61 + .../dug-activities.component.scss | 3 + .../dug-activities.component.ts | 116 + .../dug-activities/dug-activities.service.ts} | 34 +- .../lib/dug-activities/dug-activity-entity.ts | 91 + .../dug-dashboards.component.html | 12 + .../dug-dashboards.component.scss | 41 + .../dug-dashboards.component.ts | 57 + .../dug-dashboards/dug-dashboards.service.ts} | 21 +- .../dug-overview/dug-overview.component.html | 47 + .../dug-overview/dug-overview.component.scss | 11 + .../dug-overview/dug-overview.component.ts | 128 + .../lib/dug-overview/dug-overview.service.ts | 67 + .../lib/dug/access-comparison.component.html | 13 + .../lib/dug/access-comparison.component.ts | 120 + .../dug/dug-access-analysis.component.html | 17 + .../lib/dug/dug-access-analysis.component.ts | 58 + .../lib/dug/dug-access-analysis.service.ts | 46 + .../lib/dug/dug-access-detail.component.html | 27 + .../lib/dug/dug-access-detail.component.scss | 46 + .../lib/dug/dug-access-detail.component.ts | 151 + .../assigned-permissions.component.html | 30 + .../assigned-permissions.component.scss | 5 + .../assigned-permissions.component.ts | 71 + .../assigned-permissions/trustee-entity.ts | 142 + .../box-icon/box-icon.component.html | 8 + .../box-icon/box-icon.component.scss | 10 + .../dug-access/box-icon/box-icon.component.ts | 37 + .../dug/dug-access/dug-access.component.html | 21 + .../dug/dug-access/dug-access.component.scss | 12 + .../dug/dug-access/dug-access.component.ts | 86 + .../lib/dug/dug-access/dug-access.module.ts} | 62 +- .../effective-permission-tree-database.ts | 52 + .../effective-permission.component.html | 50 + .../effective-permission.component.scss | 153 + .../effective-permission.component.ts | 89 + .../permission-member.component.html | 46 + .../permission-member.component.scss | 85 + .../permission-member.component.ts | 91 + .../trustee-entity-hierarchy.ts | 190 + .../dug-activity/dug-activity.component.html | 14 + .../dug-activity/dug-activity.component.scss | 9 + .../dug-activity/dug-activity.component.ts | 64 + .../dug/dug-activity/dug-activity.module.ts} | 25 +- .../lib/dug/dug-reports/dug-report-entity.ts | 96 + .../dug-reports/dug-reports.component.html | 15 + .../dug-reports/dug-reports.component.scss | 3 + .../dug/dug-reports/dug-reports.component.ts | 73 + .../dug/dug-reports/dug-reports.service.ts | 49 + .../src/lib/dug/dug-sidesheet.component.html | 150 + .../src/lib/dug/dug-sidesheet.component.scss | 30 + .../src/lib/dug/dug-sidesheet.component.ts | 256 + .../src/lib/identity/identity.component.html | 73 + .../src/lib/identity/identity.component.scss | 42 + .../src/lib/identity/identity.component.ts | 156 + imxweb/projects/qam/src/lib/init.service.ts | 118 + .../src/lib/qam-api-client.service.ts} | 39 +- .../projects/qam/src/lib/qam-config.module.ts | 131 + imxweb/projects/qam/src/lib/qam.scss | 50 + .../src/public_api.ts} | 10 +- imxweb/projects/qam/src/test.ts | 40 + .../projects/{o3t => qam}/tsconfig.lib.json | 9 +- .../{o3t => qam}/tsconfig.lib.prod.json | 3 - .../projects/{o3t => qam}/tsconfig.spec.json | 0 imxweb/projects/{aad => qam}/tslint.json | 0 .../qbm-app-landingpage/.compodocrc.json | 2 +- .../qbm-app-landingpage/.eslintrc.json | 35 + imxweb/projects/qbm-app-landingpage/README.md | 1 + .../qbm-app-landingpage/karma.conf.js | 13 +- .../projects/qbm-app-landingpage/package.json | 6 +- .../projects/qbm-app-landingpage/project.json | 209 + .../src/app/app-routing.module.ts | 9 +- .../src/app/app.component.html | 5 +- .../src/app/app.component.scss | 25 +- .../src/app/app.component.ts | 83 +- .../qbm-app-landingpage/src/app/app.module.ts | 46 +- .../src/app/app.service.ts | 40 +- .../src/app/appcontainer.service.spec.ts | 24 +- .../src/app/appcontainer.service.ts | 10 +- .../src/app/start/start.component.html | 6 +- .../src/app/start/start.component.scss | 9 +- .../src/app/start/start.component.ts | 6 +- .../src/environments/environment.prod.ts | 4 +- .../src/environments/environment.ts | 4 +- .../qbm-app-landingpage/src/index.html | 20 +- .../projects/qbm-app-landingpage/src/main.ts | 7 +- .../qbm-app-landingpage/src/polyfills.ts | 22 +- .../qbm-app-landingpage/src/styles.scss | 55 +- .../projects/qbm-app-landingpage/src/test.ts | 13 +- .../qbm-app-landingpage/tsconfig-es5.app.json | 8 +- .../qbm-app-landingpage/tsconfig.app.json | 13 +- .../qbm-app-landingpage/tsconfig.spec.json | 16 +- .../projects/qbm-app-landingpage/tslint.json | 17 - imxweb/projects/qbm/.compodocrc.json | 2 +- imxweb/projects/qbm/.eslintrc.json | 35 + imxweb/projects/qbm/karma.conf.js | 39 +- imxweb/projects/qbm/ng-package.json | 2 +- imxweb/projects/qbm/package.json | 10 +- imxweb/projects/qbm/project.json | 60 + imxweb/projects/qbm/src/default-mocks.spec.ts | 12 +- .../qbm/src/lib/about/About.component.html | 21 +- .../qbm/src/lib/about/About.component.scss | 122 +- .../qbm/src/lib/about/About.component.ts | 40 +- .../qbm/src/lib/about/About.service.ts | 54 +- .../lib/admin/about/admin-about.service.ts | 56 + .../admin/add-config-sidesheet.component.html | 26 +- .../admin/add-config-sidesheet.component.scss | 9 - .../admin/add-config-sidesheet.component.ts | 32 +- .../lib/admin/admin-component.interface.ts | 6 +- .../qbm/src/lib/admin/admin-routes.ts | 28 +- .../qbm/src/lib/admin/admin.module.ts | 23 +- .../apply-config-sidesheet.component.html | 28 +- .../apply-config-sidesheet.component.scss | 53 +- .../admin/apply-config-sidesheet.component.ts | 26 +- .../qbm/src/lib/admin/cache.component.html | 24 +- .../qbm/src/lib/admin/cache.component.ts | 66 +- .../lib/admin/config-key-path.component.html | 2 +- .../lib/admin/config-key-path.component.scss | 4 +- .../lib/admin/config-key-path.component.ts | 11 +- .../qbm/src/lib/admin/config-section.ts | 17 +- .../qbm/src/lib/admin/config.component.html | 71 +- .../qbm/src/lib/admin/config.component.scss | 51 +- .../qbm/src/lib/admin/config.component.ts | 72 +- .../qbm/src/lib/admin/config.service.ts | 225 +- .../convert-config-sidesheet.component.html | 15 +- .../convert-config-sidesheet.component.scss | 2 +- .../convert-config-sidesheet.component.ts | 21 +- .../src/lib/admin/dashboard.component.scss | 162 - .../qbm/src/lib/admin/dashboard.component.ts | 12 +- .../delete-config-sidesheet.component.html | 10 +- .../delete-config-sidesheet.component.ts | 27 +- .../src/lib/admin/list-setting.component.html | 27 +- .../src/lib/admin/list-setting.component.scss | 23 +- .../src/lib/admin/list-setting.component.ts | 19 +- .../log-details-sidesheet.component.html | 37 +- .../log-details-sidesheet.component.scss | 9 +- .../admin/log-details-sidesheet.component.ts | 17 +- .../qbm/src/lib/admin/logs.component.html | 70 +- .../qbm/src/lib/admin/logs.component.scss | 65 +- .../qbm/src/lib/admin/logs.component.ts | 127 +- .../qbm/src/lib/admin/packages.component.html | 2 +- .../qbm/src/lib/admin/packages.component.scss | 5 +- .../qbm/src/lib/admin/packages.component.ts | 29 +- .../qbm/src/lib/admin/plugins.component.html | 8 +- .../qbm/src/lib/admin/plugins.component.ts | 4 +- .../src/lib/admin/select-value.component.html | 6 +- .../src/lib/admin/select-value.component.ts | 23 +- imxweb/projects/qbm/src/lib/admin/shared.scss | 42 +- .../qbm/src/lib/admin/status.component.html | 28 +- .../qbm/src/lib/admin/status.component.ts | 219 +- .../qbm/src/lib/admin/status.service.ts | 18 +- .../lib/admin/swagger/swagger.component.html | 3 +- .../lib/admin/swagger/swagger.component.scss | 4 +- .../lib/admin/swagger/swagger.component.ts | 6 +- .../api-client/api-client-angular.service.ts | 17 +- .../lib/api-client/api-client-fetch.spec.ts | 102 +- .../src/lib/api-client/api-client-fetch.ts | 240 +- .../src/lib/api-client/api-client.service.ts | 12 +- .../qbm/src/lib/api-client/api-client.spec.ts | 56 +- .../qbm/src/lib/api-client/dynamic-method.ts | 37 +- ...ic-collection-load-parameters.interface.ts | 4 +- .../dynamic-method-type-wrapper.interface.ts | 4 +- .../dynamic-method/dynamic-method.service.ts | 32 +- .../interactive-parameter.interface.ts | 2 +- .../method-descriptor.service.ts | 79 +- .../typed-entity-builder.service.ts | 46 +- .../src/lib/appConfig/appConfig.service.ts | 69 +- .../src/lib/appConfig/appconfig.interface.ts | 2 +- .../lib/appConfig/route-config.interface.ts | 2 +- .../translationConfiguration.interface.ts | 4 +- .../auth-config-provider.interface.ts | 9 +- .../authentication-guard.service.ts | 29 +- .../authentication/authentication.module.ts | 13 +- .../authentication/authentication.service.ts | 75 +- .../custom-auth-flow.interface.ts | 8 +- .../src/lib/authentication/oauth.service.ts | 34 +- .../lib/authentication/redirect.service.ts | 4 +- .../auto-complete.component.html | 19 +- .../auto-complete.component.scss | 17 +- .../auto-complete/auto-complete.component.ts | 9 +- .../lib/auto-complete/auto-complete.module.ts | 16 +- imxweb/projects/qbm/src/lib/base/Guid.ts | 2 +- .../projects/qbm/src/lib/base/busy.service.ts | 22 +- .../elemental-defaults.ts} | 35 +- .../qbm/src/lib/base/error.service.ts | 15 +- .../qbm/src/lib/base/global-error-handler.ts | 31 +- .../qbm/src/lib/base/ie-warning.service.ts | 8 +- .../qbm/src/lib/base/metadata.service.ts | 86 +- .../lib/base/opsupport-db-object.service.ts | 4 +- .../qbm/src/lib/base/paginator.spec.ts | 54 +- imxweb/projects/qbm/src/lib/base/paginator.ts | 63 +- .../qbm/src/lib/base/qbm-sqlwizard.service.ts | 4 +- .../lib/base/query-parameters-handler.spec.ts | 46 +- .../src/lib/base/query-parameters-handler.ts | 20 +- .../qbm/src/lib/base/registry.service.ts | 10 +- .../projects/qbm/src/lib/base/server-error.ts | 16 +- .../src/lib/base/server-exception-error.ts | 30 +- .../qbm/src/lib/base/sidesheet-helper.ts | 43 + .../qbm/src/lib/base/timezone-info.spec.ts | 5 +- .../qbm/src/lib/base/timezone-info.ts | 69 +- .../qbm/src/lib/base/user-action.service.ts | 2 +- .../qbm/src/lib/base/user-agent-helper.ts | 2 +- .../bulk-item/bulk-item-icon.ts | 2 +- .../bulk-item/bulk-item.component.html | 45 +- .../bulk-item/bulk-item.component.scss | 33 +- .../bulk-item/bulk-item.component.ts | 8 +- .../bulk-item/bulk-item.ts | 6 +- .../bulk-property-editor.component.html | 6 +- .../bulk-property-editor.component.scss | 1 - .../bulk-property-editor.component.ts | 12 +- .../bulk-property-editor.module.ts | 8 +- .../busy-indicator.component.html | 2 +- .../busy-indicator.component.scss | 6 +- .../busy-indicator.component.ts | 8 +- .../busy-indicator/busy-indicator.module.ts | 27 +- .../qbm/src/lib/cache/cache.service.ts | 27 +- .../src/lib/captcha/captcha.component.html | 38 +- .../src/lib/captcha/captcha.component.scss | 18 + .../qbm/src/lib/captcha/captcha.component.ts | 65 +- .../qbm/src/lib/captcha/captcha.module.ts | 23 +- .../qbm/src/lib/captcha/captcha.service.ts | 71 +- imxweb/projects/qbm/src/lib/cdr/Readme.md | 45 +- .../src/lib/cdr/base-cdr-editor-provider.ts | 8 +- .../projects/qbm/src/lib/cdr/base-cdr.spec.ts | 23 +- imxweb/projects/qbm/src/lib/cdr/base-cdr.ts | 18 +- .../qbm/src/lib/cdr/base-readonly-cdr.ts | 3 +- .../cdr-editor-provider-registry.interface.ts | 13 +- .../lib/cdr/cdr-editor-provider.interface.ts | 25 +- .../qbm/src/lib/cdr/cdr-editor.interface.ts | 25 +- .../cdr/cdr-editor/cdr-editor.component.html | 2 +- .../cdr/cdr-editor/cdr-editor.component.scss | 4 +- .../cdr/cdr-editor/cdr-editor.component.ts | 38 +- .../qbm/src/lib/cdr/cdr-factory.service.ts | 29 +- .../qbm/src/lib/cdr/cdr-registry.service.ts | 51 +- .../cdr/cdr-sidesheet/cdr-sidesheet-config.ts | 2 +- .../cdr-sidesheet.component.html | 15 +- .../cdr-sidesheet/cdr-sidesheet.component.ts | 15 +- imxweb/projects/qbm/src/lib/cdr/cdr.module.ts | 59 +- .../column-dependent-reference.interface.ts | 57 +- .../cdr/date-range/date-range.component.html | 33 +- .../cdr/date-range/date-range.component.scss | 9 +- .../cdr/date-range/date-range.component.ts | 31 +- .../cdr/default-cdr-editor-provider.spec.ts | 115 +- .../lib/cdr/default-cdr-editor-provider.ts | 29 +- .../edit-binary/edit-binary.component.html | 13 +- .../edit-binary/edit-binary.component.scss | 4 - .../cdr/edit-binary/edit-binary.component.ts | 2 +- .../edit-bitmask/edit-bitmask.component.html | 10 + .../edit-bitmask/edit-bitmask.component.scss | 3 + .../edit-bitmask/edit-bitmask.component.ts | 179 + .../edit-boolean/edit-boolean.component.html | 16 +- .../edit-boolean/edit-boolean.component.scss | 6 - .../edit-boolean/edit-boolean.component.ts | 2 +- .../cdr/edit-date/edit-date.component.html | 4 +- .../cdr/edit-date/edit-date.component.scss | 2 +- .../lib/cdr/edit-date/edit-date.component.ts | 34 +- .../edit-default/edit-default.component.html | 45 +- .../edit-default/edit-default.component.scss | 6 +- .../edit-default/edit-default.component.ts | 4 +- .../cdr/edit-fk/edit-fk-multi.component.html | 30 +- .../cdr/edit-fk/edit-fk-multi.component.scss | 40 +- .../edit-fk/edit-fk-multi.component.spec.ts | 26 +- .../cdr/edit-fk/edit-fk-multi.component.ts | 45 +- .../lib/cdr/edit-fk/edit-fk.component.html | 90 +- .../lib/cdr/edit-fk/edit-fk.component.scss | 59 +- .../lib/cdr/edit-fk/edit-fk.component.spec.ts | 26 +- .../src/lib/cdr/edit-fk/edit-fk.component.ts | 155 +- .../cdr/edit-image/edit-image.component.html | 10 +- .../cdr/edit-image/edit-image.component.scss | 2 +- .../cdr/edit-image/edit-image.component.ts | 30 +- .../edit-limited-value.component.html | 25 +- .../edit-limited-value.component.scss | 20 - .../edit-limited-value.component.ts | 8 +- .../edit-multi-limited-value.component.html | 43 +- .../edit-multi-limited-value.component.scss | 12 +- .../edit-multi-limited-value.component.ts | 88 +- .../edit-multi-value.component.html | 20 +- .../edit-multi-value.component.scss | 6 +- .../edit-multi-value.component.ts | 25 +- .../edit-multiline.component.html | 36 +- .../edit-multiline.component.scss | 19 +- .../edit-multiline.component.ts | 2 +- .../edit-number/edit-number.component.html | 57 +- .../edit-number/edit-number.component.scss | 10 +- .../cdr/edit-number/edit-number.component.ts | 4 +- .../cdr/edit-number/number-error.interface.ts | 2 +- .../edit-number/number-validator.service.ts | 6 +- .../edit-risk-index.component.html | 41 +- .../edit-risk-index.component.scss | 73 +- .../edit-risk-index.component.ts | 17 +- .../lib/cdr/edit-url/edit-url.component.html | 21 +- .../lib/cdr/edit-url/edit-url.component.scss | 4 - .../cdr/edit-url/edit-url.component.spec.ts | 11 +- .../lib/cdr/edit-url/edit-url.component.ts | 6 +- .../edit-url/url-validator.service.spec.ts | 2 +- .../lib/cdr/edit-url/url-validator.service.ts | 6 +- .../projects/qbm/src/lib/cdr/editor-base.ts | 70 +- .../lib/cdr/entity-column-container.spec.ts | 23 +- .../src/lib/cdr/entity-column-container.ts | 30 +- .../entity-column-editor.component.html | 4 +- .../entity-column-editor.component.scss | 2 +- .../entity-column-editor.component.ts | 11 +- .../lib/cdr/fk-cdr-editor-provider.spec.ts | 95 +- .../qbm/src/lib/cdr/fk-cdr-editor-provider.ts | 14 +- .../lib/cdr/limited-values-container.spec.ts | 63 +- .../src/lib/cdr/limited-values-container.ts | 17 +- .../property-viewer.component.ts | 40 +- .../view-property-default.component.html | 3 +- .../view-property-default.component.scss | 2 +- .../view-property-default.component.ts | 4 +- .../view-property.component.html | 2 +- .../view-property/view-property.component.ts | 4 +- .../chart-options/line-chart-options.spec.ts | 10 +- .../lib/chart-options/line-chart-options.ts | 24 +- .../chart-options/series-information.spec.ts | 3 +- .../lib/chart-options/series-information.ts | 7 +- .../chart-options/x-axis-information.spec.ts | 4 +- .../lib/chart-options/x-axis-information.ts | 4 +- .../chart-options/y-axis-information.spec.ts | 3 +- .../lib/chart-options/y-axis-information.ts | 16 +- .../lib/chart-tile/chart-tile.component.html | 22 +- .../lib/chart-tile/chart-tile.component.ts | 7 +- .../src/lib/classlogger/classlogger.module.ts | 13 +- .../lib/classlogger/classlogger.service.ts | 5 +- .../elemental-ui-config.interface.ts | 4 +- .../elemental-ui-config.service.ts | 35 +- .../lib/confirmation/confirmation.module.ts | 8 +- .../lib/confirmation/confirmation.service.ts | 68 +- .../lib/connection/connection.component.html | 33 +- .../lib/connection/connection.component.scss | 44 +- .../lib/connection/connection.component.ts | 94 +- .../qbm/src/lib/connection/connection.ts | 4 +- .../lib/custom-theme/custom-theme.module.ts | 10 +- .../lib/custom-theme/custom-theme.service.ts | 18 +- .../data-export/data-export.component.html | 108 +- .../data-export/data-export.component.scss | 32 +- .../lib/data-export/data-export.component.ts | 118 +- .../src/lib/data-export/data-export.module.ts | 5 +- .../export-columns.service.spec.ts | 2 +- .../lib/data-export/export-columns.service.ts | 78 +- .../additional-infos.component.html | 55 +- .../additional-infos.component.scss | 36 +- .../additional-infos.component.ts | 16 +- .../client-property-for-table-columns.ts | 8 +- .../lib/data-source-toolbar/column-options.ts | 94 +- .../data-model/data-model-helper.spec.ts | 51 +- .../data-model/data-model-helper.ts | 52 +- .../data-model-wrapper.interface.ts | 6 +- .../data-model/filter-tree-parameter.ts | 4 +- .../group-info-load-parameters.interface.ts | 4 +- .../data-source-item-status.interface.ts | 26 +- .../data-source-paginator.component.html | 8 +- .../data-source-paginator.component.scss | 8 - .../data-source-paginator.component.ts | 45 +- .../data-source-toolbar-custom.component.html | 2 +- .../data-source-toolbar-custom.component.ts | 6 +- ...-source-toolbar-export-method.interface.ts | 7 +- .../data-source-toolbar-filters.interface.ts | 8 +- .../data-source-toolbar-groups.interface.ts | 14 +- ...data-source-toolbar-menu-item.interface.ts | 4 +- .../data-source-toolbar-settings.ts | 11 +- .../data-source-toolbar-view-config-helper.ts | 6 +- ...ta-source-toolbar-view-config.interface.ts | 7 +- .../data-source-toolbar.component.html | 284 +- .../data-source-toolbar.component.scss | 239 +- .../data-source-toolbar.component.ts | 599 +- .../data-source-toolbar.module.ts | 98 +- .../data-source-wrapper.spec.ts | 30 +- .../data-source-wrapper.ts | 67 +- .../data-tile-badge.interface.ts | 3 +- .../filter-tree/filter-tree-database.ts | 36 +- .../filter-tree-entity-wrapper.service.ts | 41 +- .../filter-tree-selection-arg.interface.ts | 16 +- .../filter-tree/filter-tree.component.html | 68 +- .../filter-tree/filter-tree.component.scss | 22 +- .../filter-tree/filter-tree.component.ts | 18 +- .../filter-tree-sidesheet.component.html | 36 + .../filter-tree-sidesheet.component.scss | 90 + .../filter-tree-sidesheet.component.ts | 124 + .../filter-tree-sidesheet.model.ts | 109 + .../filter-wizard.component.html | 63 +- .../filter-wizard.component.scss | 21 +- .../filter-wizard/filter-wizard.component.ts | 90 +- .../filter-wizard/filter-wizard.interfaces.ts | 11 +- .../filter-wizard/filter-wizard.module.ts | 33 +- .../filter-wizard/filter-wizard.service.ts | 14 +- .../predefined-filter-tree.component.scss | 1 - .../predefined-filter-tree.component.ts | 9 +- .../predefined-filter.component.html | 114 +- .../predefined-filter.component.scss | 26 +- .../predefined-filter.component.ts | 243 +- .../save-config-dialog.component.html | 27 +- .../save-config-dialog.component.scss | 2 +- .../save-config-dialog.component.ts | 24 +- .../selection-model-wrapper.spec.ts | 23 +- .../selection-model-wrapper.ts | 40 +- .../data-table-additional-info.model.ts | 8 +- .../data-table-cell.component.html | 5 +- .../data-table-cell.component.scss | 2 +- .../data-table-cell.component.ts | 16 +- .../data-table-column.component.html | 2 +- .../data-table-column.component.scss | 44 +- .../data-table/data-table-column.component.ts | 33 +- .../data-table-display-cell.component.html | 15 +- .../data-table-display-cell.component.ts | 7 +- .../data-table-generic-column.component.html | 8 +- .../data-table-generic-column.component.scss | 4 - .../data-table-generic-column.component.ts | 5 +- .../data-table/data-table-groups.interface.ts | 8 +- .../data-table-row-highlight.interface.ts | 4 +- .../lib/data-table/data-table.component.html | 51 +- .../lib/data-table/data-table.component.scss | 171 +- .../lib/data-table/data-table.component.ts | 95 +- .../src/lib/data-table/data-table.module.ts | 26 +- .../lib/data-table/excluded-columns.pipe.ts | 9 +- .../group-paginator.component.html | 8 +- .../group-paginator.component.ts | 14 +- .../table-accessibility.directive.ts | 23 +- .../data-tile-menu-item.interface.ts | 4 +- .../lib/data-tiles/data-tile.component.html | 71 +- .../lib/data-tiles/data-tile.component.scss | 20 +- .../src/lib/data-tiles/data-tile.component.ts | 68 +- .../lib/data-tiles/data-tiles.component.html | 18 +- .../lib/data-tiles/data-tiles.component.scss | 11 +- .../lib/data-tiles/data-tiles.component.ts | 41 +- .../src/lib/data-tiles/data-tiles.module.ts | 13 +- .../data-tree-wrapper.component.html | 4 + .../data-tree-wrapper.component.scss | 2 +- .../data-tree-wrapper.component.ts | 9 +- .../data-tree-wrapper.module.ts | 21 +- .../checkable-tree.component.html | 35 +- .../checkable-tree.component.scss | 8 +- .../checkable-tree.component.ts | 59 +- .../lib/data-tree/data-tree-no-results.scss | 9 +- .../data-tree-search-results.component.html | 58 +- .../data-tree-search-results.component.scss | 49 +- .../data-tree-search-results.component.ts | 60 +- .../search-result-action.interface.ts | 4 +- .../lib/data-tree/data-tree.component.html | 10 +- .../lib/data-tree/data-tree.component.scss | 28 +- .../src/lib/data-tree/data-tree.component.ts | 26 +- .../qbm/src/lib/data-tree/data-tree.module.ts | 13 +- .../src/lib/data-tree/entity-tree-database.ts | 11 +- .../mat-selection-list-multiple-directive.ts | 33 +- .../node-checked-change.interface.ts | 4 +- .../qbm/src/lib/data-tree/tree-database.ts | 32 +- .../src/lib/data-tree/tree-datasource.spec.ts | 142 +- .../qbm/src/lib/data-tree/tree-datasource.ts | 28 +- .../tree-node-result-parameter.interface.ts | 4 +- .../qbm/src/lib/data-tree/tree-node.ts | 28 +- .../tree-selection-list.component.scss | 4 +- .../tree-selection-list.component.ts | 23 +- .../data-view-auto-table.component.html | 181 + .../data-view-auto-table.component.scss | 95 + .../data-view-auto-table.component.ts | 210 + .../data-view-chipbar.component.html | 67 + .../data-view-chipbar.component.scss | 22 + .../data-view-chipbar.component.ts | 187 + .../data-view-filter.component.html | 10 + .../data-view-filter.component.scss | 0 .../data-view-filter.component.ts | 177 + .../data-view-group.component.html | 8 + .../data-view-group.component.ts | 69 + .../data-view-nested-table.component.html | 90 + .../data-view-nested-table.component.ts | 204 + .../data-view-paginator.component.html | 13 + .../data-view-paginator.component.scss | 8 + .../data-view-paginator.component.ts | 73 + .../data-view-search.component.html | 2 + .../data-view-search.component.scss | 3 + .../data-view-search.component.ts | 125 + .../data-view-selection.component.html | 25 + .../data-view-selection.component.scss | 58 + .../data-view-selection.component.ts | 76 + .../data-view-settings.component.html | 85 + .../data-view-settings.component.ts | 262 + .../data-view-source-factory.service.ts | 51 + .../lib/data-view/data-view-source.spec.ts | 138 + .../qbm/src/lib/data-view/data-view-source.ts | 622 + .../data-view-status.component.html | 34 + .../data-view-status.component.scss | 4 + .../data-view-status.component.ts} | 20 +- .../data-view-toolbar.component.html | 19 + .../data-view-toolbar.component.scss | 9 + .../data-view-toolbar.component.ts | 67 + .../src/lib/data-view/data-view.interface.ts | 100 + .../qbm/src/lib/data-view/data-view.module.ts | 109 + .../projects/qbm/src/lib/date/date.module.ts | 30 +- .../date/calendar/calendar.component.html | 12 +- .../date/calendar/calendar.component.scss | 2 +- .../date/date/calendar/calendar.component.ts | 8 +- .../qbm/src/lib/date/date/date-parser.spec.ts | 86 +- .../qbm/src/lib/date/date/date-parser.ts | 65 +- .../qbm/src/lib/date/date/date.component.html | 50 +- .../qbm/src/lib/date/date/date.component.scss | 19 - .../qbm/src/lib/date/date/date.component.ts | 55 +- .../time-picker/time-picker.component.html | 12 +- .../date/time-picker/time-picker.component.ts | 12 +- .../qbm/src/lib/date/localized-date.pipe.ts | 10 +- .../qbm/src/lib/date/short-date.pipe.ts | 10 +- .../disable-control.directive.ts | 10 +- .../disable-control/disable-control.module.ts | 16 +- .../dynamic-tab-data-provider.directive.ts | 6 +- .../lib/dynamic-tabs/dynamic-tabs.model.ts | 2 +- .../lib/dynamic-tabs/dynamic-tabs.module.ts | 18 +- .../entity-select.component.html | 9 +- .../entity-select/entity-select.component.ts | 42 +- .../entity-select/entity-select.interface.ts | 6 +- .../qbm/src/lib/entity/entity.module.ts | 34 +- .../qbm/src/lib/entity/entity.service.ts | 20 +- .../fk-table-select.component.html | 12 +- .../fk-table-select.component.scss | 2 +- .../fk-table-select.component.ts | 16 +- ...candidate-sidesheet-parameter.interface.ts | 6 +- ...-entity-candidate-sidesheet.component.html | 31 +- ...-entity-candidate-sidesheet.component.scss | 26 +- ...ed-entity-candidate-sidesheet.component.ts | 31 +- .../typed-entity-table-filter.interface.ts | 4 +- .../typed-entity-fk-data.interface.ts | 18 +- .../typed-entity-select.component.html | 23 +- .../typed-entity-select.component.scss | 41 +- .../typed-entity-select.component.ts | 44 +- .../typed-entity-selection-data.interface.ts | 12 +- .../typed-entity-selector.component.html | 46 +- .../typed-entity-selector.component.scss | 39 +- .../typed-entity-selector.component.ts | 31 +- .../projects/qbm/src/lib/ext/ext.component.ts | 25 +- .../projects/qbm/src/lib/ext/ext.directive.ts | 4 +- imxweb/projects/qbm/src/lib/ext/ext.module.ts | 4 +- .../projects/qbm/src/lib/ext/ext.service.ts | 21 +- imxweb/projects/qbm/src/lib/ext/extension.ts | 2 +- .../file-selector.service.spec.ts | 12 +- .../file-selector/file-selector.service.ts | 10 +- .../filter-tile/filter-tile.component.html | 2 +- .../filter-tile/filter-tile.component.scss | 28 +- .../lib/filter-tile/filter-tile.component.ts | 6 +- .../fk-advanced-picker/candidate-entity.ts | 31 +- .../fk-advanced-picker/candidate.interface.ts | 6 +- .../fk-advanced-picker.component.html | 33 +- .../fk-advanced-picker.component.scss | 49 +- .../fk-advanced-picker.component.ts | 34 +- .../fk-advanced-picker.module.ts | 2 +- .../fk-candidate-entity-builder.service.ts | 15 +- .../fk-candidates-data.interface.ts | 21 +- .../fk-candidates.component.html | 6 +- .../fk-candidates.component.scss | 9 - .../fk-candidates/fk-candidates.component.ts | 72 +- .../fk-selector.component.html | 17 +- .../fk-selector.component.scss | 39 - .../fk-selector.component.ts | 42 +- .../foreign-key-picker-data.interface.ts | 16 +- .../foreign-key-selection.interface.ts | 8 +- .../src/lib/fk-container/dyn-fk-container.ts | 4 +- .../qbm/src/lib/fk-container/fk-container.ts | 21 +- .../fk-hierarchical-dialog.component.html | 65 +- .../fk-hierarchical-dialog.component.scss | 47 +- .../fk-hierarchical-dialog.component.spec.ts | 11 +- .../fk-hierarchical-dialog.component.ts | 32 +- .../fk-hierarchical-dialog.module.ts | 14 +- .../hierarchical-candidate.ts | 10 +- .../hierarchical-fk-database.ts | 61 +- .../help-contextual-dialog.component.html | 6 +- .../help-contextual-dialog.component.scss | 8 +- .../help-contextual-dialog.component.ts | 12 +- .../help-contextual.component.html | 2 +- .../help-contextual.component.scss | 27 +- .../help-contextual.component.ts | 37 +- .../help-contextual/help-contextual.module.ts | 21 +- .../help-contextual.service.ts | 18 +- .../lib/hyperview/connector-provider.spec.ts | 36 +- .../src/lib/hyperview/connector-provider.ts | 29 +- .../qbm/src/lib/hyperview/connector.spec.ts | 6 +- .../qbm/src/lib/hyperview/connector.ts | 15 +- .../qbm/src/lib/hyperview/connectors.spec.ts | 38 +- .../qbm/src/lib/hyperview/connectors.ts | 14 +- .../hyperview-layout-hierarchical.ts | 65 +- .../hyperview-layout-horizontal.spec.ts | 7 +- .../hyperview/hyperview-layout-horizontal.ts | 12 +- .../hyperview-layout-vertical.spec.ts | 9 +- .../hyperview/hyperview-layout-vertical.ts | 15 +- .../qbm/src/lib/hyperview/hyperview-types.ts | 2 +- .../lib/hyperview/hyperview.component.html | 36 +- .../lib/hyperview/hyperview.component.scss | 21 +- .../src/lib/hyperview/hyperview.component.ts | 53 +- .../qbm/src/lib/hyperview/hyperview.module.ts | 27 +- .../lib/hyperview/listshape.component.scss | 13 +- .../src/lib/hyperview/listshape.component.ts | 7 +- .../hyperview/propertyshape.component.html | 10 +- .../hyperview/propertyshape.component.scss | 21 +- .../lib/hyperview/propertyshape.component.ts | 26 +- .../src/lib/hyperview/shape.component.html | 20 +- .../src/lib/hyperview/shape.component.scss | 20 +- .../qbm/src/lib/hyperview/shape.component.ts | 32 +- .../lib/hyperview/simpleshape.component.html | 4 +- .../lib/hyperview/simpleshape.component.ts | 6 +- .../src/lib/hyperview/zoom-pan.directive.ts | 7 +- .../lib/icon-stack/icon-stack.component.html | 2 +- .../lib/icon-stack/icon-stack.component.scss | 9 +- .../lib/icon-stack/icon-stack.component.ts | 8 +- .../image-select/image-select.component.html | 73 +- .../image-select/image-select.component.scss | 40 - .../image-select/image-select.component.ts | 10 +- .../image-view/image-view.component.html | 8 +- .../image-view/image-view.component.scss | 1 - .../image/image-view/image-view.component.ts | 6 +- .../qbm/src/lib/image/image.module.ts | 18 +- .../lib/images/base64-image.service.spec.ts | 24 +- .../src/lib/images/base64-image.service.ts | 6 +- .../src/lib/indexbar/indexbar.component.html | 11 +- .../src/lib/indexbar/indexbar.component.ts | 149 +- .../info-badge/info-badge.component.html | 7 +- .../info-badge/info-badge.component.scss | 30 - .../info-badge/info-badge.component.ts | 5 +- .../info-button/info-button.component.html | 2 +- .../info-button/info-button.component.scss | 43 +- .../info-button/info-button.component.ts | 32 +- .../info-dialog/info-dialog.component.html | 4 +- .../info-dialog/info-dialog.component.scss | 6 +- .../info-dialog/info-dialog.component.ts | 16 +- .../info-modal-dialog.module.ts | 2 +- .../jobqueue-overview.component.html | 10 +- .../jobqueue-overview.component.scss | 21 +- .../jobqueue-overview.component.ts | 112 +- .../jobqueue-overview.module.ts | 2 +- .../jobqueue-overview.service.spec.ts | 6 +- .../jobqueue-overview.service.ts | 40 +- .../src/lib/lds-replace/lds-replace.module.ts | 16 +- .../src/lib/lds-replace/lds-replace.pipe.ts | 4 +- .../qbm/src/lib/login/login.component.html | 118 +- .../qbm/src/lib/login/login.component.scss | 109 +- .../qbm/src/lib/login/login.component.ts | 256 +- .../mast-head-menu-item.interface.ts | 2 +- .../lib/mast-head/mast-head-menu.interface.ts | 2 +- .../lib/mast-head/mast-head.component.html | 42 +- .../lib/mast-head/mast-head.component.scss | 66 +- .../src/lib/mast-head/mast-head.component.ts | 99 +- .../qbm/src/lib/mast-head/mast-head.module.ts | 26 +- .../src/lib/mast-head/mast-head.service.ts | 21 +- .../master-detail.component.html | 15 - .../master-detail.component.scss | 17 +- .../master-detail/master-detail.component.ts | 139 - .../lib/menu/menu-item/menu-item.interface.ts | 14 +- .../src/lib/menu/menu-item/menu-item.spec.ts | 96 +- ...navigation-commands-menu-item.interface.ts | 2 +- .../related-application-menu-item.ts | 58 +- .../related-application.interface.ts | 12 +- .../qbm/src/lib/menu/menu.component.html | 19 - .../qbm/src/lib/menu/menu.component.scss | 113 - .../qbm/src/lib/menu/menu.component.ts | 106 - .../projects/qbm/src/lib/menu/menu.module.ts | 43 - .../projects/qbm/src/lib/menu/menu.service.ts | 53 +- .../message-dialog-result.enum.ts | 4 +- .../message-dialog.component.html | 61 +- .../message-dialog.component.scss | 7 + .../message-dialog.component.ts | 22 +- .../message-dialog/message-dialog.service.ts} | 15 +- .../message-parameter.interface.ts | 3 +- .../src/lib/model-css/model-css.service.ts | 25 +- .../multi-select-formcontrol.component.html | 60 +- .../multi-select-formcontrol.component.scss | 41 +- .../multi-select-formcontrol.component.ts | 75 +- .../multi-select-formcontrol.module.ts | 8 +- .../src/lib/multi-value/multi-value.module.ts | 12 +- .../lib/multi-value/multi-value.service.ts | 14 +- .../object-history-api.service.ts | 11 +- .../object-history-gridview.component.html | 28 +- .../object-history-gridview.component.scss | 6 +- .../object-history-gridview.component.ts | 46 +- ...ct-history-state-comparison.component.html | 39 +- ...ct-history-state-comparison.component.scss | 16 +- ...ject-history-state-comparison.component.ts | 44 +- ...ject-history-state-overview.component.html | 39 +- ...ject-history-state-overview.component.scss | 17 +- ...object-history-state-overview.component.ts | 44 +- .../object-history.component.html | 101 +- .../object-history.component.scss | 107 +- .../object-history.component.ts | 137 +- .../object-history/object-history.module.ts | 39 +- .../object-history/object-history.service.ts | 27 +- .../ordered-list/ordered-list.component.html | 22 +- .../ordered-list/ordered-list.component.scss | 29 +- .../ordered-list/ordered-list.component.ts | 16 +- .../lib/ordered-list/ordered-list.module.ts | 37 +- .../parameter-replacement.interface.ts | 2 +- .../parameterized-text.component.html | 2 + .../parameterized-text.component.scss | 2 +- .../parameterized-text.component.ts | 7 +- .../parameterized-text.interface.ts | 4 +- .../parameterized-text.module.ts | 16 +- .../parameterized-text.service.ts | 14 +- .../text-token.interface.ts | 2 +- .../src/lib/plugins/plugin-loader.service.ts | 260 - .../processing-queue.interface.ts | 173 + .../processing-queue.service.ts | 136 + .../processing-queue/processing-queue.spec.ts | 132 + .../progressbar/progressbar.component.html | 12 +- .../progressbar/progressbar.component.scss | 22 - .../lib/progressbar/progressbar.component.ts | 11 +- imxweb/projects/qbm/src/lib/qbm.module.ts | 83 +- .../component-can-deactivate.interface.ts | 2 +- .../src/lib/route-guard/route-guard.module.ts | 12 +- .../lib/route-guard/route-guard.service.ts | 58 +- .../qbm/src/lib/search/db-object-info.ts | 8 +- .../qbm/src/lib/search/search.service.ts | 48 +- .../qbm/src/lib/searchbar/iSearchService.ts | 14 +- .../lib/searchbar/searchbar.component.html | 74 +- .../lib/searchbar/searchbar.component.scss | 100 +- .../src/lib/searchbar/searchbar.component.ts | 64 +- .../lib/select/autocomplete.component.html | 64 - .../lib/select/autocomplete.component.scss | 81 - .../src/lib/select/autocomplete.component.ts | 264 - .../data-navigation-parameters.interface.ts | 4 +- .../src/lib/select/fk-selection-container.ts | 38 +- .../qbm/src/lib/select/select-data-source.ts | 63 - .../qbm/src/lib/select/select.component.html | 40 - .../qbm/src/lib/select/select.component.scss | 40 - .../qbm/src/lib/select/select.component.ts | 222 - .../selected-elements-dialog.component.html | 14 +- .../selected-elements-dialog.component.scss | 35 +- .../selected-elements-dialog.component.ts | 44 +- .../selected-elements-dialog.model.ts | 5 +- .../selected-elements.component.html | 16 +- .../selected-elements.component.scss | 7 +- .../selected-elements.component.ts | 52 +- .../selected-elements.module.ts | 3 +- .../src/lib/services/device-state.service.ts | 80 - .../auth-prop-data-provider.interface.ts | 3 +- .../src/lib/session/imx-session.service.ts | 24 +- .../qbm/src/lib/session/session-state.spec.ts | 49 +- .../qbm/src/lib/session/session-state.ts | 104 +- .../qbm/src/lib/settings/settings-service.ts | 11 +- .../side-navigation-view-interfaces.ts | 11 +- .../side-navigation-view.component.html | 25 +- .../side-navigation-view.component.scss | 1 - .../side-navigation-view.component.ts | 59 +- .../side-navigation-view.module.ts | 2 +- .../custom-tree-control.component.html | 111 + .../custom-tree-control.component.scss | 48 + .../custom-tree-control.component.ts | 152 + .../sidenav-tree-dynamic-extension.ts | 68 +- .../sidenav-tree/sidenav-tree.component.html | 106 +- .../sidenav-tree/sidenav-tree.component.scss | 280 +- .../sidenav-tree/sidenav-tree.component.ts | 46 +- .../qbm/src/lib/snackbar/snack-bar.service.ts | 26 +- .../qbm/src/lib/splash/splash.service.ts | 16 +- .../qbm/src/lib/sqlwizard/SqlNodeView.ts | 213 +- .../sqlwizard/column-selection.component.html | 12 +- .../sqlwizard/column-selection.component.ts | 161 +- .../lib/sqlwizard/date-picker.component.html | 14 +- .../lib/sqlwizard/date-picker.component.ts | 29 +- .../simple-expression.component.html | 17 +- .../simple-expression.component.scss | 11 +- .../sqlwizard/simple-expression.component.ts | 82 +- .../single-expression.component.html | 47 +- .../sqlwizard/single-expression.component.ts | 60 +- .../lib/sqlwizard/single-value.component.html | 31 +- .../lib/sqlwizard/single-value.component.ts | 181 +- .../lib/sqlwizard/sqlwizard-api.service.ts | 5 +- .../lib/sqlwizard/sqlwizard.component.html | 15 +- .../src/lib/sqlwizard/sqlwizard.component.ts | 30 +- .../qbm/src/lib/sqlwizard/sqlwizard.module.ts | 34 +- .../qbm/src/lib/sqlwizard/sqlwizard.scss | 50 +- .../src/lib/sqlwizard/sqlwizard.service.ts | 14 +- .../sqlwizard/table-selection.component.html | 3 +- .../sqlwizard/table-selection.component.ts | 31 +- .../where-clause-expression.component.ts | 25 +- .../qbm/src/lib/storage/storage.module.ts | 12 +- .../src/lib/storage/storage.service.spec.ts | 3 +- .../qbm/src/lib/storage/storage.service.ts | 12 +- .../src/lib/styles/data-explorer-common.scss | 158 +- .../styles/data-explorer-details-common.scss | 13 +- .../qbm/src/lib/styles/imx-page-title.scss | 38 +- .../lib/system-info/system-info.service.ts | 13 +- .../lib/table-image/table-image.service.ts | 10 +- .../temp-billboard.component.ts | 139 - .../src/lib/testing/TestHelperModule.spec.ts | 13 +- .../src/lib/testing/base-imx-api-mock.spec.ts | 57 +- .../qbm/src/lib/testing/clear-styles.spec.ts | 8 +- .../qbm/src/lib/testing/dst-mock-help.spec.ts | 6 +- .../lib/testing/entity-column-stub.spec.ts | 27 +- .../lib/testing/entity-schema-stub.spec.ts | 15 +- .../qbm/src/lib/tile/tile-variables.scss | 1 - .../qbm/src/lib/tile/tile.component.html | 113 +- .../qbm/src/lib/tile/tile.component.scss | 63 +- .../qbm/src/lib/tile/tile.component.ts | 26 +- .../projects/qbm/src/lib/tile/tile.module.ts | 24 +- .../src/lib/timeline/timeline.component.html | 30 +- .../src/lib/timeline/timeline.component.scss | 63 +- .../src/lib/timeline/timeline.component.ts | 27 +- .../qbm/src/lib/timeline/timeline.model.ts | 53 + .../projects/qbm/src/lib/timeline/timeline.ts | 4 +- .../translation-editor.component.html | 25 +- .../translation-editor.component.scss | 20 +- .../translation-editor.component.ts | 90 +- .../imx-missing-translation-handler.spec.ts | 52 +- .../imx-missing-translation-handler.ts | 6 +- .../lib/translation/imx-translate-loader.ts | 6 +- .../imx-translation-provider.service.ts | 78 +- .../qbm/src/lib/translation/text-container.ts | 2 +- .../qbm/src/lib/treeTable/MatColumn.html | 35 +- .../qbm/src/lib/treeTable/MatColumn.scss | 2 +- .../qbm/src/lib/treeTable/MatColumn.ts | 41 +- .../qbm/src/lib/treeTable/imx-data-source.ts | 67 +- .../lib/treeTable/tableTestClasses.spec.ts | 13 +- .../lib/treeTable/treeTable.component.html | 48 +- .../src/lib/treeTable/treeTable.component.ts | 45 +- .../two-factor-authentication.component.ts | 28 +- .../two-factor-authentication.service.ts | 8 +- .../src/lib/user-message/message.interface.ts | 2 +- .../user-message/user-message.component.html | 11 +- .../user-message/user-message.component.scss | 6 +- .../user-message/user-message.component.ts | 45 +- .../lib/user-message/user-message.module.ts | 10 +- .../lib/user-message/user-message.service.ts | 4 +- .../src/lib/value-wrapper/value-wrapper.ts | 2 +- imxweb/projects/qbm/src/public_api.ts | 215 +- imxweb/projects/qbm/src/test.ts | 18 +- imxweb/projects/qbm/tsconfig.lib.json | 9 +- imxweb/projects/qbm/tsconfig.spec.json | 15 +- imxweb/projects/qbm/tslint.json | 17 - .../.compodocrc.json | 2 +- .../qer-app-operationssupport/.eslintrc.json | 38 + .../dpr-api-client.service.ts | 11 +- .../qer-app-operationssupport/karma.conf.js | 15 +- .../qer-app-operationssupport/package.json | 6 +- .../qer-app-operationssupport/prebuild.js | 6 + .../qer-app-operationssupport/project.json | 221 + .../src/app/app-routing.module.ts | 40 +- .../src/app/app.component.html | 5 +- .../src/app/app.component.scss | 25 +- .../src/app/app.component.ts | 85 +- .../src/app/app.module.ts | 68 +- .../src/app/app.service.ts | 101 +- .../app/base/ops-sql-wizard-api.service.ts | 8 +- .../src/app/base/subscription.service.ts | 6 +- .../app/dashboard/dashboard.component.html | 20 +- .../app/dashboard/dashboard.component.scss | 2 +- .../src/app/dashboard/dashboard.component.ts | 10 +- .../src/app/dashboard/dashboard.module.ts | 13 +- .../data-changes-sidesheet.component.html | 12 +- .../data-changes-sidesheet.component.scss | 1 - .../data-changes-sidesheet.component.ts | 28 +- .../data-changes/data-changes.component.html | 173 +- .../data-changes/data-changes.component.scss | 90 +- .../data-changes/data-changes.component.ts | 279 +- .../app/data-changes/data-changes.module.ts | 62 +- .../app/data-changes/data-changes.service.ts | 50 +- .../src/app/db-queue/db-queue.component.html | 23 +- .../src/app/db-queue/db-queue.component.scss | 17 - .../src/app/db-queue/db-queue.component.ts | 21 +- .../src/app/db-queue/db-queue.module.ts | 15 +- .../src/app/db-queue/db-queue.service.ts | 8 +- .../outstanding-manager-guard.service.ts | 12 +- .../system-status-route-guard.service.ts | 17 +- .../app/hyperview/ops-hyperview.service.ts | 10 +- .../src/app/information/issue-tiles.scss | 4 +- .../notifications/notification-issue-item.ts | 4 +- .../notifications.component.html | 18 +- .../notifications.component.scss | 32 +- .../notifications/notifications.component.ts | 7 +- .../notifications/notifications.module.ts | 8 +- .../notifications.service.spec.ts | 82 +- .../notifications/notifications.service.ts | 35 +- .../service-issues/service-issue-item.ts | 9 +- .../service-issue.component.html | 7 +- .../service-issue.component.scss | 12 +- .../service-issues/service-issue.component.ts | 4 +- .../service-issues.component.html | 16 +- .../service-issues.component.scss | 12 +- .../service-issues.component.ts | 10 +- .../service-issues/service-issues.models.ts | 2 +- .../service-issues/service-issues.module.ts | 18 +- .../service-issues.service.spec.ts | 395 +- .../service-issues/service-issues.service.ts | 95 +- .../system-overview.component.html | 29 +- .../system-overview.component.ts | 24 +- .../system-overview/system-overview.module.ts | 15 +- .../system-overview.service.ts | 6 +- .../system-tree/system-tree-database.spec.ts | 36 +- .../system-tree/system-tree-database.ts | 26 +- .../system-tree-datasource.spec.ts | 95 +- .../system-tree/system-tree-datasource.ts | 34 +- .../system-tree/system-tree-node.spec.ts | 22 +- .../system-tree/system-tree-node.ts | 8 +- .../system-status-information.interface.ts | 2 +- .../system-status.component.html | 55 +- .../system-status.component.scss | 17 +- .../system-status/system-status.component.ts | 79 +- .../system-status/system-status.module.ts | 15 +- .../system-status/system-status.service.ts | 15 +- .../src/app/journal/filter-tile-params.ts | 4 +- .../src/app/journal/journal.component.html | 46 +- .../src/app/journal/journal.component.scss | 25 +- .../src/app/journal/journal.component.ts | 25 +- .../src/app/journal/journal.module.ts | 8 +- .../src/app/journal/journal.service.ts | 15 +- .../object-overview.component.html | 108 +- .../object-overview.component.scss | 58 +- .../object-overview.component.ts | 114 +- .../object-overview/object-overview.module.ts | 27 +- .../object-overview.service.ts | 9 +- .../opsupport-history-api.service.ts | 13 +- .../object-overview/person-db-queue-info.ts | 8 +- .../object-overview/person-job-queue-info.ts | 7 +- .../object-search/object-search.component.ts | 19 +- .../app/object-search/object-search.module.ts | 14 +- .../src/app/permissions/permissions-helper.ts | 6 +- .../permissions/permissions.service.spec.ts | 8 +- .../app/permissions/permissions.service.ts | 8 +- .../error-message-sidesheet.component.html | 7 +- .../error-message-sidesheet.component.scss | 15 +- .../error-message-sidesheet.component.ts | 6 +- .../frozen-jobs/frozen-jobs.component.html | 37 +- .../frozen-jobs/frozen-jobs.component.scss | 39 - .../frozen-jobs/frozen-jobs.component.ts | 59 +- .../frozen-jobs/frozen-jobs.service.spec.ts | 10 +- .../frozen-jobs/frozen-jobs.service.ts | 8 +- .../frozen-jobs/queue-tree.service.ts | 113 +- .../single-frozen-job.component.html | 100 +- .../single-frozen-job.component.scss | 34 +- .../single-frozen-job.component.ts | 77 +- .../job-chains/job-chains.component.html | 26 +- .../job-chains/job-chains.component.scss | 15 - .../job-chains/job-chains.component.ts | 22 +- .../job-chains/job-chains.service.spec.ts | 12 +- .../job-chains/job-chains.service.ts | 10 +- .../job-history/job-history.component.html | 25 +- .../job-history/job-history.component.scss | 27 +- .../job-history/job-history.component.ts | 29 +- .../job-history/job-history.service.ts | 10 +- .../job-performance-queues.service.ts | 2 +- .../job-performance.component.html | 28 +- .../job-performance.component.scss | 19 +- .../job-performance.component.ts | 31 +- .../job-performance.service.spec.ts | 10 +- .../job-performance.service.ts | 10 +- .../jobs-gridview.component.html | 26 +- .../jobs-gridview.component.scss | 45 +- .../jobs-gridview/jobs-gridview.component.ts | 48 +- .../app/processes/jobs/jobs.component.html | 21 +- .../app/processes/jobs/jobs.component.scss | 15 +- .../src/app/processes/jobs/jobs.component.ts | 11 +- .../app/processes/jobs/queue-jobs.service.ts | 23 +- .../change-operation-sidesheet.component.html | 24 +- .../change-operation-sidesheet.component.scss | 2 +- .../change-operation-sidesheet.component.ts | 22 +- .../change-operation-table.component.html | 7 +- .../change-operation-table.component.scss | 3 +- .../change-operation-table.component.ts | 62 +- .../objects-by-id/object-by-id.service.ts | 10 +- .../objects-by-id.component.html | 23 +- .../objects-by-id.component.scss | 68 +- .../objects-by-id/objects-by-id.component.ts | 27 +- .../src/app/processes/processes.module.ts | 22 +- .../job-servers-details.component.html | 14 +- .../job-servers-details.component.scss | 11 +- .../job-servers-details.component.spec.ts | 21 +- .../job-servers-details.component.ts | 23 +- .../job-servers-edit.component.html | 20 +- .../job-servers-edit.component.scss | 2 +- .../job-servers-edit.component.spec.ts | 15 +- .../job-servers-edit.component.ts | 8 +- .../job-servers-gridview.component.html | 13 +- .../job-servers-gridview.component.scss | 23 - .../job-servers-gridview.component.ts | 43 +- .../app/service-report/job-servers.service.ts | 15 +- .../service-availability.component.html | 14 +- .../service-availability.component.scss | 4 +- .../service-availability.component.ts | 4 +- .../service-report.component.html | 13 +- .../service-report.component.scss | 22 +- .../service-report.component.ts | 7 +- .../service-report/service-report.module.ts | 10 +- .../services-inactive.component.html | 6 +- .../services-inactive.component.ts | 8 +- .../sync-information.component.html | 32 +- .../sync-information.component.scss | 17 - .../sync-information.component.ts | 22 +- .../sync-journal/sync-journal.component.html | 39 +- .../sync-journal/sync-journal.component.scss | 16 - .../sync-journal/sync-journal.component.ts | 43 +- .../sync/sync-journal/sync-summary.service.ts | 2 +- .../src/app/sync/sync.module.ts | 25 +- .../src/app/sync/sync.service.ts | 13 +- .../app/test-utilities/imx-api-mock.spec.ts | 276 +- .../imx-session.service.spy.spec.ts | 60 +- ...x-translation-provider.service.spy.spec.ts | 10 +- .../app/test-utilities/router-mock.spec.ts | 14 +- .../test-utilities/test-helper.module.spec.ts | 12 +- .../typed-entity-provider.spec.ts | 32 +- .../unresolved-refs.component.html | 18 +- .../unresolved-refs.component.scss | 16 - .../unresolved-refs.component.ts | 16 +- .../unresolved-refs/unresolved-refs.module.ts | 19 +- .../unresolved-refs.service.ts | 8 +- .../web-applications.component.html | 30 +- .../web-applications.component.scss | 26 +- .../web-applications.component.ts | 14 +- .../web-applications.module.ts | 22 +- .../web-applications.service.ts | 8 +- .../src/environments/environment.prod.ts | 4 +- .../src/environments/environment.ts | 4 +- .../qer-app-operationssupport/src/index.html | 20 +- .../qer-app-operationssupport/src/main.ts | 4 +- .../src/polyfills.ts | 21 +- .../qer-app-operationssupport/src/styles.scss | 280 +- .../qer-app-operationssupport/src/test.ts | 30 +- .../tsconfig-es5.app.json | 8 +- .../tsconfig.app.json | 14 +- .../tsconfig.spec.json | 16 +- .../qer-app-operationssupport/tslint.json | 17 - .../projects/qer-app-portal/.compodocrc.json | 2 +- imxweb/projects/qer-app-portal/.eslintrc.json | 35 + imxweb/projects/qer-app-portal/karma.conf.js | 13 +- imxweb/projects/qer-app-portal/package.json | 5 +- imxweb/projects/qer-app-portal/project.json | 214 + .../src/app/app-routing.module.ts | 28 +- .../qer-app-portal/src/app/app.component.html | 1 - .../qer-app-portal/src/app/app.component.scss | 20 +- .../qer-app-portal/src/app/app.component.ts | 96 +- .../qer-app-portal/src/app/app.module.ts | 41 +- .../qer-app-portal/src/app/app.service.ts | 101 +- .../app/hyperview/portal-hyperview.service.ts | 16 +- .../app/portal-doc-configuration.service.ts | 54 - .../src/app/portal-history.service.ts | 13 +- .../src/environments/environment.prod.ts | 4 +- .../src/environments/environment.ts | 7 +- imxweb/projects/qer-app-portal/src/index.html | 20 +- imxweb/projects/qer-app-portal/src/main.ts | 7 +- .../projects/qer-app-portal/src/polyfills.ts | 22 +- .../projects/qer-app-portal/src/styles.scss | 129 +- imxweb/projects/qer-app-portal/src/test.ts | 23 +- .../qer-app-portal/tsconfig-es5.app.json | 8 +- .../projects/qer-app-portal/tsconfig.app.json | 13 +- .../qer-app-portal/tsconfig.spec.json | 16 +- imxweb/projects/qer-app-portal/tslint.json | 17 - .../qer-app-pwdportal/.compodocrc.json | 2 +- .../projects/qer-app-pwdportal/.eslintrc.json | 35 + .../projects/qer-app-pwdportal/karma.conf.js | 13 +- .../projects/qer-app-pwdportal/package.json | 4 +- .../projects/qer-app-pwdportal/project.json | 211 + .../src/app/app-routing.module.ts | 27 +- .../src/app/app.component.html | 25 +- .../src/app/app.component.scss | 14 +- .../src/app/app.component.ts | 79 +- .../qer-app-pwdportal/src/app/app.module.ts | 25 +- .../qer-app-pwdportal/src/app/app.service.ts | 92 +- .../src/app/pwd-sql-wizard-api.service.ts | 6 +- .../src/environments/environment.prod.ts | 4 +- .../src/environments/environment.ts | 6 +- .../projects/qer-app-pwdportal/src/index.html | 28 +- imxweb/projects/qer-app-pwdportal/src/main.ts | 7 +- .../qer-app-pwdportal/src/polyfills.ts | 20 +- .../qer-app-pwdportal/src/styles.scss | 17 +- imxweb/projects/qer-app-pwdportal/src/test.ts | 27 +- .../qer-app-pwdportal/tsconfig-es5.app.json | 8 +- .../qer-app-pwdportal/tsconfig.app.json | 13 +- .../qer-app-pwdportal/tsconfig.spec.json | 15 +- imxweb/projects/qer-app-pwdportal/tslint.json | 17 - imxweb/projects/qer/.compodocrc.json | 2 +- imxweb/projects/qer/.eslintrc.json | 35 + imxweb/projects/qer/karma.conf.js | 11 +- imxweb/projects/qer/ng-package.json | 2 +- imxweb/projects/qer/package.json | 9 +- imxweb/projects/qer/project.json | 61 + imxweb/projects/qer/src/default-mocks.spec.ts | 4 +- .../qer/src/lib/about/portal-about.service.ts | 55 + .../addressbook-detail.component.html | 1 - .../addressbook-detail.component.scss | 8 - .../addressbook-detail.component.ts | 11 +- .../addressbook-detail.interface.ts | 4 +- .../addressbook/addressbook.component.html | 44 +- .../addressbook/addressbook.component.scss | 11 +- .../lib/addressbook/addressbook.component.ts | 135 +- .../src/lib/addressbook/addressbook.module.ts | 29 +- .../lib/addressbook/addressbook.service.ts | 40 +- .../admin/authentication-factors.interface.ts | 2 +- .../src/lib/admin/feature-config.service.ts | 14 +- .../src/lib/admin/qer-permissions-helper.ts | 11 +- .../src/lib/admin/qer-permissions.service.ts | 48 +- .../approval-level-form.component.html | 34 +- .../approval-level-form.component.scss | 2 +- .../approval-level-form.component.ts | 12 +- .../approval-step-form.component.html | 22 +- .../approval-step-form.component.scss | 3 +- .../approval-step-form.component.ts | 19 +- .../approval-workflow-constants.service.ts | 208 +- .../approval-workflow-data.service.ts | 37 +- ...approval-workflow-edit-info.component.html | 26 - ...approval-workflow-edit-info.component.scss | 61 - .../approval-workflow-edit.component.html | 150 +- .../approval-workflow-edit.component.scss | 59 +- .../approval-workflow-edit.component.ts | 390 +- .../approval-workflow.model.ts | 6 +- .../container-dom.component.scss | 2 +- .../container-dom/container-dom.component.ts | 7 +- .../dom-manager.service.ts | 22 +- .../edge-dom/edge-dom.component.html | 2 +- .../edge-dom/edge-dom.component.scss | 4 +- .../edge-dom/edge-dom.component.ts | 10 +- .../node-dom/node-dom.component.ts | 5 +- .../approval-workflow-form.component.html | 32 +- .../approval-workflow-form.component.scss | 2 +- .../approval-workflow-form.component.ts | 22 +- .../approval-workflow-home.component.html | 37 +- .../approval-workflow-home.component.scss | 31 +- .../approval-workflow-home.component.ts | 59 +- .../approval-workflow-styles.scss | 122 - .../approval-workflow.interface.ts | 18 +- .../approval-workflows.module.ts | 31 +- .../approval-workflows/awm-colors.service.ts | 8 +- .../approval-workflows/form-data.service.ts | 107 +- .../archived-requests.component.html | 20 +- .../archived-requests.component.scss | 34 +- .../archived-requests.component.ts | 15 +- .../archived-requests.module.ts | 84 +- .../archived-requests.service.ts | 34 +- .../businessowner-addon-tile.component.html | 12 +- .../businessowner-addon-tile.component.ts | 5 +- .../businessowner-addon-tile.module.ts | 14 +- ...businessowner-overview-tile.component.html | 2 +- .../businessowner-overview-tile.component.ts | 5 +- .../businessowner-overview-tile.module.ts | 13 +- .../data-explorer-registry.service.ts | 13 +- .../data-explorer-view.component.html | 2 +- .../data-explorer-view.component.scss | 154 - .../data-explorer-view.component.ts | 12 +- .../data-explorer-view.module.ts | 17 +- .../delegation/delegation-guard.service.ts | 56 + .../lib/delegation/delegation.component.html | 130 +- .../lib/delegation/delegation.component.scss | 139 +- .../lib/delegation/delegation.component.ts | 88 +- .../src/lib/delegation/delegation.module.ts | 103 +- .../src/lib/delegation/delegation.service.ts | 88 +- .../dynamic-exclusion-dialog.component.html | 20 +- .../dynamic-exclusion-dialog.component.scss | 3 - .../dynamic-exclusion-dialog.component.ts | 14 +- .../dynamic-exclusion-dialog.module.ts | 28 +- .../filter-sqlwizard.service.ts | 45 + .../lib/guards/application-guard.service.ts | 14 +- .../lib/guards/data-explorer-guard.service.ts | 87 - .../src/lib/guards/feature-guard.service.ts | 12 +- .../guards/itshop-pattern-guard.service.ts | 14 +- .../src/lib/guards/manager-guard.service.ts | 12 +- .../guards/person-admin-guard.service.spec.ts | 3 +- .../lib/guards/person-admin-guard.service.ts | 12 +- .../lib/guards/rule-admin-guard.service.ts | 14 +- .../lib/guards/shop-admin-guard.service.ts | 12 +- .../qer/src/lib/guards/shop-guard.service.ts | 12 +- .../guards/shop-statistics-guard.service.ts | 12 +- .../lib/guards/statistics-guard.service.ts | 12 +- .../lib/guards/struct-admin-guard.service.ts | 12 +- .../helper-alert-content.interface.ts | 4 +- .../create-new-identity.component.html | 97 +- .../create-new-identity.component.scss | 22 +- .../create-new-identity.component.ts | 196 +- .../duplicate-check-parameter.interface.ts | 4 +- .../duplicates-sidesheet.component.html | 28 +- .../duplicates-sidesheet.component.scss | 14 +- .../duplicates-sidesheet.component.ts | 61 +- .../identities/identities-reports.service.ts | 43 +- .../lib/identities/identities.component.html | 181 +- .../lib/identities/identities.component.scss | 15 +- .../lib/identities/identities.component.ts | 225 +- .../src/lib/identities/identities.module.ts | 106 +- .../src/lib/identities/identities.service.ts | 101 +- .../assignments/assignments.component.html | 30 +- .../assignments/assignments.component.scss | 156 +- .../assignments/assignments.component.ts | 94 +- ...ty-role-memberships-parameter.interface.ts | 4 +- .../identity-role-memberships.component.html | 17 +- .../identity-role-memberships.component.scss | 7 +- .../identity-role-memberships.component.ts | 39 +- .../identity-role-memberships.module.ts | 14 +- .../identity-role-memberships.service.ts | 106 +- .../identity-sidesheet.component.html | 181 +- .../identity-sidesheet.component.scss | 170 +- .../identity-sidesheet.component.ts | 154 +- .../identities/test/common-test-mocks.spec.ts | 55 +- .../irequestable-entitlement-type.ts | 9 +- .../member-selector.component.html | 10 +- .../member-selector.component.ts | 10 +- .../reason-sidesheet.component.ts | 13 +- .../request-config-members.component.html | 46 +- .../request-config-members.component.scss | 35 - .../request-config-members.component.ts | 102 +- .../request-config-sidesheet-common.scss | 109 +- .../request-config-sidesheet.component.html | 45 +- .../request-config-sidesheet.component.ts | 27 +- .../itshop-config/request-config.module.ts | 93 +- .../request-shelf-entitlements.component.html | 18 +- .../request-shelf-entitlements.component.ts | 56 +- .../request-shelf-sidesheet.component.html | 28 +- .../request-shelf-sidesheet.component.ts | 20 +- .../request-shelves/request-shelf-token.ts | 6 +- .../request-shelves.component.html | 8 +- .../request-shelves.component.ts | 33 +- .../requestable-entitlement-type.service.ts | 32 +- .../itshop-config/requestable-entl-type.ts | 54 +- .../requests-entity-selector.component.html | 5 +- .../requests-entity-selector.component.ts | 46 +- .../src/lib/itshop-config/requests.service.ts | 78 +- .../requests/requests.component.html | 65 +- .../requests/requests.component.scss | 54 +- .../requests/requests.component.ts | 97 +- .../resource-entitlement-type.ts | 33 +- .../test/requests-configuration-mocks.ts | 29 +- .../duplicate-pattern-item.ts | 5 +- .../duplicate-pattern-items.component.html | 16 +- .../duplicate-pattern-items.component.scss | 11 +- .../duplicate-pattern-items.component.ts | 21 +- ...itshop-pattern-add-products.component.html | 48 +- ...itshop-pattern-add-products.component.scss | 42 +- ...hop-pattern-add-products.component.spec.ts | 6 +- .../itshop-pattern-add-products.component.ts | 132 +- .../itshop-pattern-changed.enum.ts | 6 +- ...op-pattern-create-sidesheet.component.html | 22 +- ...op-pattern-create-sidesheet.component.scss | 44 +- ...shop-pattern-create-sidesheet.component.ts | 42 +- .../itshop-pattern-create.service.ts | 130 +- ...p-pattern-item-edit-parameter.interface.ts | 6 +- .../itshop-pattern-item-edit.component.html | 25 +- .../itshop-pattern-item-edit.component.scss | 29 +- ...itshop-pattern-item-edit.component.spec.ts | 16 +- .../itshop-pattern-item-edit.component.ts | 23 +- .../itshop-pattern-sidesheet.component.html | 164 +- .../itshop-pattern-sidesheet.component.scss | 105 +- ...itshop-pattern-sidesheet.component.spec.ts | 14 +- .../itshop-pattern-sidesheet.component.ts | 145 +- .../itshop-pattern.component.html | 105 +- .../itshop-pattern.component.scss | 52 +- .../itshop-pattern.component.ts | 200 +- .../itshop-pattern/itshop-pattern.module.ts | 85 +- .../itshop-pattern/itshop-pattern.service.ts | 145 +- .../pattern-item-candidate.interface.ts | 7 +- .../lib/itshop/decision-history.service.ts | 17 +- .../qer/src/lib/itshop/image.service.spec.ts | 12 +- .../qer/src/lib/itshop/image.service.ts | 32 +- .../src/lib/itshop/itshop-request.service.ts | 28 +- .../qer/src/lib/itshop/itshop.module.ts | 20 +- .../qer/src/lib/itshop/itshop.service.ts | 49 +- .../non-requestable-items.component.html | 15 +- .../non-requestable-items.component.scss | 2 +- .../non-requestable-items.component.spec.ts | 27 +- .../non-requestable-items.component.ts | 25 +- .../peer-group/peer-group.component.html | 7 +- .../itshop/peer-group/peer-group.component.ts | 8 +- .../request-info/approver-container.spec.ts | 191 +- .../itshop/request-info/approver-container.ts | 122 +- .../decision-history.component.html | 50 +- .../decision-history.component.scss | 43 +- .../decision-history.component.ts | 18 +- .../request-info/itshop-request-data.spec.ts | 24 +- .../request-info/itshop-request-data.ts | 12 +- ...itshop-request-entity-wrapper.interface.ts | 8 +- .../ordered-working-step.interface.ts | 2 +- .../request-info/request-info.component.html | 20 +- .../request-info/request-info.component.scss | 55 +- .../request-info/request-info.component.ts | 47 +- ...request-parameter-data-entity.interface.ts | 8 +- .../product-details.service.ts | 22 +- .../product-entitlements.component.html | 46 +- .../product-entitlements.component.scss | 28 - .../product-entitlements.component.ts | 34 +- .../service-item-detail.component.html | 6 +- .../service-item-detail.component.scss | 6 - .../service-item-detail.component.ts | 20 +- .../workflow-history-item-wrapper.spec.ts | 31 +- .../workflow-history-item-wrapper.ts | 54 +- .../itshop/shelf-selection-sidesheet.model.ts | 15 +- .../lib/itshop/shelf-selection.component.html | 66 +- .../lib/itshop/shelf-selection.component.scss | 15 +- .../lib/itshop/shelf-selection.component.ts | 54 +- .../qer/src/lib/itshop/shelf.service.ts | 66 +- .../lib/itshop/workflow-data-wrapper.spec.ts | 308 +- .../src/lib/itshop/workflow-data-wrapper.ts | 60 +- .../src/lib/itshopapprove/approval.spec.ts | 193 +- .../qer/src/lib/itshopapprove/approval.ts | 16 +- .../itshopapprove/approvals-decision.enum.ts | 4 +- .../approvals-load-parameters.ts | 4 +- .../approvals-sidesheet.component.html | 82 +- .../approvals-sidesheet.component.scss | 39 +- .../approvals-sidesheet.component.ts | 29 +- .../approvals-table.component.html | 273 +- .../approvals-table.component.scss | 127 +- .../approvals-table.component.spec.ts | 25 +- .../approvals-table.component.ts | 267 +- .../itshopapprove/approvals.component.html | 28 +- .../itshopapprove/approvals.component.scss | 56 +- .../itshopapprove/approvals.component.spec.ts | 10 +- .../lib/itshopapprove/approvals.component.ts | 31 +- .../src/lib/itshopapprove/approvals.module.ts | 100 +- .../lib/itshopapprove/approvals.service.ts | 77 +- .../itshopapprove/decision-step.service.ts | 21 +- .../inquiries/inquiries.component.html | 123 +- .../inquiries/inquiries.component.scss | 24 +- .../inquiries/inquiries.component.ts | 170 +- .../recommendation-sidesheet.component.html | 51 +- .../recommendation-sidesheet.component.scss | 26 +- .../recommendation-sidesheet.component.ts | 19 +- .../approval-history.component.html | 34 +- .../approval-history.component.scss | 27 +- .../approval-history.component.ts | 110 +- .../approval-history.service.ts | 28 +- .../history-filter.component.html | 8 +- .../history-filter.component.scss | 2 +- .../history-filter.component.ts | 32 +- .../workflow-action-edit-wrapper.interface.ts | 2 +- .../workflow-action-edit.interface.ts | 26 +- .../workflow-action-parameters.interface.ts | 2 +- .../workflow-action.component.html | 18 +- .../workflow-action.component.scss | 31 +- .../workflow-action.component.ts | 14 +- .../workflow-action.service.ts | 232 +- .../workflow-multi-action.component.html | 5 +- .../workflow-multi-action.component.ts | 38 +- .../workflow-single-action.component.html | 5 +- .../workflow-single-action.component.scss | 12 +- .../workflow-single-action.component.ts | 22 +- .../answer-questions.component.html | 70 +- .../answer-questions.component.ts | 57 +- .../query-person.component.html | 19 +- .../query-person.component.ts | 61 +- .../decision-reason.component.html | 15 +- .../decision-reason.component.scss | 2 +- .../decision-reason.component.ts | 47 +- .../justification/justification-type.enum.ts | 4 +- .../lib/justification/justification.module.ts | 23 +- .../justification/justification.service.ts | 27 +- .../lib/metadata/portal-metadata.service.ts} | 45 +- .../my-responsibilities-registry.service.ts | 10 +- .../my-responsibilities-view.component.html | 2 +- .../my-responsibilities-view.component.ts | 21 +- .../my-responsibilities-view.module.ts | 8 +- .../src/lib/new-request/constants.ts} | 8 +- .../lib/new-request/current-product-source.ts | 6 +- .../element-visibility.directive.ts | 7 +- .../new-request-add-to-cart.service.ts | 125 +- .../new-request-content.component.html | 37 +- .../new-request-content.component.scss | 51 +- .../new-request-content.component.ts | 131 +- .../lib/new-request/new-request-global.scss | 119 +- .../new-request-header-toolbar.component.html | 35 +- .../new-request-header-toolbar.component.scss | 8 +- .../new-request-header-toolbar.component.ts | 84 +- .../new-request-header.component.html | 8 +- .../new-request-header.component.scss | 30 +- .../new-request-header.component.ts | 5 +- .../new-request-recipients.component.html | 29 +- .../new-request-recipients.component.scss | 6 + .../new-request-recipients.component.ts | 65 +- .../recipients-api.service.ts | 14 +- ...request-reference-user-card.component.html | 13 +- ...request-reference-user-card.component.scss | 2 +- ...w-request-reference-user-card.component.ts | 12 +- .../new-request-orchestration.service.ts | 119 +- .../new-request-peer-group.component.html | 80 +- .../new-request-peer-group.component.scss | 7 +- .../new-request-peer-group.component.ts | 61 +- ...peer-group-discard-selected.component.html | 22 +- ...peer-group-discard-selected.component.scss | 15 - .../peer-group-discard-selected.component.ts | 24 +- .../new-request-product-bundle.component.scss | 4 +- .../new-request-product-bundle.component.ts | 2 +- .../product-bundle-items.component.html | 105 +- .../product-bundle-items.component.scss | 60 +- .../product-bundle-items.component.ts | 91 +- .../product-bundle-list.component.html | 6 +- .../product-bundle-list.component.scss | 136 +- .../product-bundle-list.component.ts | 46 +- .../fk-advanced-picker-response.ts | 16 +- .../new-request-category-api.service.ts | 15 +- .../new-request-product-api.service.ts | 13 +- .../new-request-product.component.html | 94 +- .../new-request-product.component.scss | 79 +- .../new-request-product.component.ts | 110 +- .../product-details-sidesheet.component.html | 39 +- .../product-details-sidesheet.component.scss | 64 +- .../product-details-sidesheet.component.ts | 52 +- .../product-details.service.ts | 43 +- .../product-entitlement-api.service.ts | 24 +- .../product-entitlements.component.html | 47 +- .../product-entitlements.component.scss | 2 +- .../product-entitlements.component.ts | 17 +- .../service-item-parameters.ts | 4 +- .../new-request-reference-user.component.html | 73 +- .../new-request-reference-user.component.scss | 3 +- .../new-request-reference-user.component.ts | 122 +- .../new-request/new-request-routing.module.ts | 15 +- ...w-request-selected-products.component.html | 8 +- ...w-request-selected-products.component.scss | 23 +- ...new-request-selected-products.component.ts | 6 +- ...selected-product-item-col-def.interface.ts | 2 +- .../selected-product-item.interface.ts | 6 +- .../new-request-selection.service.ts | 44 +- .../new-request-tab/new-request-tab-model.ts | 2 +- .../new-request/new-request.component.html | 1 - .../new-request/new-request.component.scss | 1 - .../lib/new-request/new-request.component.ts | 9 +- .../src/lib/new-request/new-request.module.ts | 109 +- .../notification-registry.service.ts | 96 +- .../notification-stream.service.ts | 25 +- ...object-attestation-statistics.interface.ts | 2 +- .../object-hyperview.component.scss | 17 +- .../object-hyperview.component.ts | 31 +- ...rface.ts => object-hyperview.interface.ts} | 2 +- .../object-hyperview.module.ts | 6 +- .../object-hyperview.service.ts | 8 +- .../src/lib/ops/about/ops-about.service.ts | 55 + .../lib/ops/metadata/ops-metadata.service.ts | 51 + .../src/lib/ops/objectOverviewContainer.ts | 4 +- .../ops/objectOverviewPerson.component.html | 2 +- .../lib/ops/objectOverviewPerson.component.ts | 27 +- imxweb/projects/qer/src/lib/ops/ops.module.ts | 58 +- .../src/lib/ops/ops.service.ts} | 24 +- .../qer/src/lib/ops/passcode.service.ts | 37 +- .../src/lib/ops/passcodeViewer.component.html | 25 +- .../src/lib/ops/passcodeViewer.component.scss | 2 +- .../src/lib/ops/passcodeViewer.component.ts | 12 +- .../ops/permissions/ops-permissions-helper.ts | 29 + .../permissions/ops-permissions.service.ts} | 26 +- .../projects/qer/src/lib/ops/user.service.ts | 27 +- .../src/lib/org-chart/identity.component.html | 11 +- .../src/lib/org-chart/identity.component.scss | 8 +- .../src/lib/org-chart/identity.component.ts | 27 +- .../lib/org-chart/org-chart.component.html | 11 +- .../lib/org-chart/org-chart.component.scss | 72 +- .../src/lib/org-chart/org-chart.component.ts | 9 +- .../qer/src/lib/org-chart/org-chart.module.ts | 26 +- .../src/lib/org-chart/org-chart.service.ts | 16 +- .../qer/src/lib/org-chart/variables.scss | 3 - .../owner-control.component.html | 11 +- .../owner-control.component.scss | 13 +- .../owner-control.component.spec.ts | 12 +- .../owner-control/owner-control.component.ts | 12 +- .../lib/owner-control/owner-control.module.ts | 21 +- .../owner-control/owner-control.service.ts | 35 +- .../qer/src/lib/owner-control/owner.model.ts | 4 +- .../extended-collection-data.interface.ts | 7 +- .../extended-entity-wrapper.interface.ts | 2 +- .../parameter-category-column.interface.ts | 4 +- .../parameter-category.interface.ts | 4 +- .../lib/parameter-data/parameter-container.ts | 40 +- .../parameter-data-container.ts | 4 +- .../parameter-data-fk-provider-item.ts | 30 +- ...arameter-data-load-parameters.interface.ts | 4 +- .../parameter-data-wrapper.interface.ts | 6 +- .../parameter-data/parameter-data.service.ts | 254 +- .../confirm-password-matcher.ts | 3 +- .../password-question.service.ts | 23 +- ...assword-questions-sidesheet.component.html | 87 + ...assword-questions-sidesheet.component.scss | 26 + .../password-questions-sidesheet.component.ts | 70 +- .../password-questions-validator.ts | 11 +- .../password-questions.component.html | 99 + .../password-questions.component.scss | 61 + .../password-questions.component.ts | 102 +- .../password-questions.module.ts} | 70 +- .../lib/password/about/pwd-about.service.ts | 55 + .../password/check-passwords.component.html | 120 +- .../password/check-passwords.component.scss | 10 +- .../lib/password/check-passwords.component.ts | 101 +- .../src/lib/password/dashboard.component.html | 64 +- .../src/lib/password/dashboard.component.ts | 17 +- .../qer/src/lib/password/helpers.model.ts | 4 +- .../password/metadata/pwd-metadata.service.ts | 51 + .../passcode-login/passcode-login-flow.ts | 9 +- .../passcode-login.component.html | 60 +- .../passcode-login.component.scss | 55 +- .../passcode-login.component.ts | 184 +- .../passcode-login/passcode-login.module.ts | 22 +- .../qer/src/lib/password/password-helper.ts | 16 +- .../qer/src/lib/password/password-item.ts | 13 +- .../password/password-query.component.html | 85 +- .../lib/password/password-query.component.ts | 46 +- .../password/password-reset.component.html | 108 +- .../password/password-reset.component.scss | 15 +- .../lib/password/password-reset.component.ts | 27 +- .../qer/src/lib/password/password.module.ts | 51 +- .../qer/src/lib/password/password.service.ts | 52 +- .../lib/password/qa-login/qa-login-flow.ts | 9 +- .../password/qa-login/qa-login.component.html | 91 +- .../password/qa-login/qa-login.component.scss | 56 +- .../password/qa-login/qa-login.component.ts | 241 +- .../lib/password/qa-login/qa-login.module.ts | 22 +- .../pattern-item-list-filter-type.enum.ts | 6 +- .../pattern-item-list.component.html | 25 +- .../pattern-item-list.component.scss | 12 - .../pattern-item-list.component.spec.ts | 4 +- .../pattern-item-list.component.ts | 82 +- .../pattern-item-list/pattern-item.service.ts | 61 +- .../pattern-item-list/pattern-items.module.ts | 16 +- .../person-all-load-parameters.interface.ts | 4 +- .../qer/src/lib/person/person.module.ts | 10 +- .../qer/src/lib/person/person.service.ts | 38 +- .../dependency.service.ts | 41 +- .../optional-items-sidesheet.component.html | 42 +- .../optional-items-sidesheet.component.scss | 38 +- .../optional-items-sidesheet.component.ts | 57 +- .../pattern-details-sidesheet.component.html | 101 +- .../pattern-details-sidesheet.component.scss | 48 +- .../pattern-details-sidesheet.component.ts | 47 +- .../product-details-sidesheet.component.html | 2 +- .../product-details-sidesheet.component.scss | 27 +- .../product-details-sidesheet.component.ts | 27 +- .../product-selection.component.html | 55 +- .../product-selection.component.scss | 51 +- .../product-selection.component.ts | 212 +- .../product-selection.module.ts | 179 +- .../product-selection.service.ts | 64 +- .../recipients-wrapper.spec.ts | 4 +- .../product-selection/recipients-wrapper.ts | 8 +- .../role-memberships.component.html | 44 +- .../role-memberships.component.scss | 10 - .../role-memberships.component.ts | 71 +- .../service-item-edit/item-edit.service.ts | 34 +- .../service-item-edit.component.html | 20 +- .../service-item-edit.component.scss | 16 +- .../service-item-edit.component.ts | 10 +- .../service-item-order.interface.ts | 9 +- .../category-tree.component.html | 5 +- .../category-tree.component.scss | 6 +- .../category-tree.component.ts | 18 +- .../servicecategory-list.component.html | 26 +- .../servicecategory-list.component.scss | 8 +- .../servicecategory-list.component.ts | 33 +- .../servicecategory-tree-database.ts | 29 +- .../identity-select.component.html | 10 +- .../identity-select.component.scss | 3 - .../identity-select.component.ts | 14 +- .../lib/profile/mailsubscription.service.ts | 46 +- .../profile/mailsubscriptions.component.html | 38 +- .../profile/mailsubscriptions.component.scss | 49 +- .../profile/mailsubscriptions.component.ts | 72 +- ...assword-questions-sidesheet.component.html | 60 - ...assword-questions-sidesheet.component.scss | 53 - .../password-questions.component.html | 91 - .../password-questions.component.scss | 107 - .../src/lib/profile/profile.component.html | 50 +- .../src/lib/profile/profile.component.scss | 55 +- .../qer/src/lib/profile/profile.component.ts | 138 +- .../qer/src/lib/profile/profile.module.ts | 68 +- .../security-keys-sidesheet.component.html | 35 +- .../security-keys-sidesheet.component.scss | 13 - .../security-keys-sidesheet.component.ts | 42 +- .../security-keys.component.html | 55 +- .../security-keys.component.scss | 14 - .../security-keys/security-keys.component.ts | 64 +- .../security-keys/security-keys.service.ts | 34 +- .../project-configuration.module.ts | 12 +- .../project-configuration.service.ts | 17 +- .../qer/src/lib/qer-api-client.service.ts | 35 +- imxweb/projects/qer/src/lib/qer.module.ts | 48 +- imxweb/projects/qer/src/lib/qer.service.ts | 26 +- .../qpm-integration.component.html | 32 +- .../qpm-integration.component.scss | 2 +- .../qpm-integration.component.ts | 46 +- .../qpm-integration/qpm-integration.module.ts | 22 +- .../queue-sidesheet.component.html | 186 + .../queue-sidesheet.component.scss | 191 + .../queue-sidesheet.component.ts | 104 + .../queue-status/queue-status.component.html | 40 + .../queue-status/queue-status.component.scss | 11 + .../queue-status/queue-status.component.ts | 118 + .../application-details.component.html | 4 +- .../application-details.component.scss | 22 +- .../application-details.component.ts | 21 +- .../application-dialog.component.html | 16 +- .../application-dialog.component.ts | 13 +- ...lated-application-menu-item.component.html | 2 +- ...related-application-menu-item.component.ts | 21 +- ...ated-applications-sidesheet.component.html | 16 +- ...elated-applications-sidesheet.component.ts | 36 +- .../related-applications.component.ts | 21 +- .../related-applications.module.ts | 28 +- .../related-applications.service.ts | 9 +- .../request-history/itshop-request.spec.ts | 109 +- .../src/lib/request-history/itshop-request.ts | 21 +- .../request-action.component.html | 59 +- .../request-action.component.ts | 71 +- .../request-action/request-action.service.ts | 103 +- .../request-detail-parameter.interface.ts | 6 +- .../request-detail.component.html | 129 +- .../request-detail.component.scss | 10 - .../request-detail.component.ts | 17 +- .../default-request-display.component.html | 18 +- .../default-request-display.component.scss | 21 +- .../default-request-display.component.ts | 18 +- .../request-display.component.ts | 32 +- .../request-display.interface.ts | 4 +- .../request-display.service.ts | 4 +- .../request-history-filter.component.html | 17 +- .../request-history-filter.component.scss | 2 +- .../request-history-filter.component.spec.ts | 9 +- .../request-history-filter.component.ts | 51 +- ...quest-history-load-parameters.interface.ts | 5 +- .../request-history.component.html | 15 +- .../request-history.component.scss | 11 - .../request-history.component.ts | 33 +- .../request-history/request-history.module.ts | 83 +- .../request-history.service.ts | 79 +- .../request-table.component.html | 139 +- .../request-table.component.scss | 9 +- .../request-table.component.ts | 298 +- .../requestfilter.component.html | 51 +- .../requestfilter.component.scss | 7 +- .../requestfilter.component.ts | 173 +- .../requests-feature-guard.service.spec.ts | 18 +- .../src/lib/requests-feature-guard.service.ts | 23 +- .../src/lib/resources/resource-info.model.ts | 6 +- .../resource-sidesheet.component.html | 67 +- .../resource-sidesheet.component.scss | 38 - .../resource-sidesheet.component.ts | 52 +- .../lib/resources/resources.component.html | 55 +- .../lib/resources/resources.component.scss | 41 +- .../src/lib/resources/resources.component.ts | 99 +- .../qer/src/lib/resources/resources.module.ts | 28 +- .../src/lib/resources/resources.service.ts | 411 +- .../risk-config-sidesheet.component.html | 16 +- .../risk-config-sidesheet.component.ts | 23 +- .../risk-config/risk-config.component.html | 117 +- .../risk-config/risk-config.component.scss | 44 +- .../lib/risk-config/risk-config.component.ts | 138 +- .../src/lib/risk-config/risk-config.module.ts | 93 +- .../lib/risk-config/risk-config.service.ts | 33 +- .../projects/qer/src/lib/risk/risk.module.ts | 14 +- .../riskanalysis-sidesheet.component.html | 2 +- .../riskanalysis-sidesheet.component.scss | 14 +- .../risk/riskanalysis-sidesheet.component.ts | 15 +- .../src/lib/risk/riskanalysis.component.html | 105 +- .../src/lib/risk/riskanalysis.component.scss | 7 +- .../src/lib/risk/riskanalysis.component.ts | 87 +- .../src/lib/role-management/api-wrapper.ts | 55 + .../compare/compare-item-builder.ts | 6 +- .../compare/compare-item.component.html | 2 +- .../compare/compare-item.component.ts | 11 +- .../role-management/compare/compare-item.ts | 4 +- .../compare/compare.component.html | 53 +- .../compare/compare.component.scss | 36 +- .../compare/compare.component.ts | 67 +- .../compare/compare.service.ts | 15 +- .../data-management.service.spec.ts | 8 +- .../data-management.service.ts | 16 +- .../dynamicrole-sqlwizard.service.ts | 12 +- .../identities.service.spec.ts | 2 +- .../identities.service.ts | 34 +- ...mberships-choose-identities.component.html | 83 +- ...mberships-choose-identities.component.scss | 4 +- ...rships-choose-identities.component.spec.ts | 11 +- ...memberships-choose-identities.component.ts | 96 +- .../not-requestable-memberships-entity.ts | 11 +- ...not-requestable-memberships.component.html | 26 +- ...not-requestable-memberships.component.scss | 19 +- .../not-requestable-memberships.component.ts | 28 +- .../new-role/new-role.component.html | 10 +- .../new-role/new-role.component.ts | 44 +- .../restore/restore-handler.ts | 30 +- .../restore/restore.component.html | 14 +- .../restore/restore.component.scss | 10 - .../restore/restore.component.ts | 33 +- .../role-data-model.interface.ts | 4 +- .../role-detail/role-detail.component.html | 6 +- .../role-detail/role-detail.component.scss | 99 +- .../role-detail/role-detail.component.spec.ts | 11 +- .../role-detail/role-detail.component.ts | 40 +- .../role-entitlements/entitlement-handlers.ts | 81 +- .../entitlement-selector.component.html | 11 +- .../entitlement-selector.component.scss | 40 +- .../entitlement-selector.component.ts | 70 +- .../role-entitlement-action.service.ts | 49 +- .../role-entitlements.component.html | 19 +- .../role-entitlements.component.scss | 48 - .../role-entitlements.component.ts | 126 +- ...role-recommendation-result-item-builder.ts | 10 +- .../role-recommendation-result-item.ts | 4 +- .../role-recommendations.component.html | 46 +- .../role-recommendations.component.scss | 25 +- .../role-recommendations.component.ts | 27 +- .../role-main-data.component.html | 62 +- .../role-main-data.component.scss | 48 +- .../role-main-data.component.spec.ts | 11 +- .../role-main-data.component.ts | 85 +- .../role-manangement.module.ts | 100 +- .../dynamic-role.component.html | 32 +- .../dynamic-role.component.scss | 32 +- .../dynamic-role.component.ts | 68 +- .../excluded-memberships.component.html | 113 +- .../excluded-memberships.component.scss | 24 +- .../excluded-memberships.component.ts | 40 +- .../role-memberships/membership-entity.ts | 4 +- .../role-memberships/membership-handlers.ts | 305 +- .../primary-memberships.component.html | 6 +- .../primary-memberships.component.scss | 10 +- .../primary-memberships.component.spec.ts | 11 +- .../primary-memberships.component.ts | 37 +- .../remove-membership.component.html | 76 +- .../remove-membership.component.scss | 56 +- .../remove-membership.component.ts | 26 +- .../role-memberships.component.html | 14 +- .../role-memberships.component.scss | 58 - .../role-memberships.component.spec.ts | 47 +- .../role-memberships.component.ts | 23 +- .../role-memberships.module.ts | 12 +- .../role-memberships/role-sidesheet-tabs.scss | 39 +- .../secondary-memberships.component.html | 37 +- .../secondary-memberships.component.scss | 55 +- .../secondary-memberships.component.ts | 124 +- .../lib/role-management/role-object-info.ts | 14 +- .../lib/role-management/role.service.spec.ts | 2 +- .../src/lib/role-management/role.service.ts | 752 +- .../roles-overview/role-adaptor.ts | 10 +- .../roles-overview.component.html | 74 +- .../roles-overview.component.scss | 76 +- .../roles-overview.component.spec.ts | 11 +- .../roles-overview.component.ts | 109 +- .../tree-database-adaptor.service.spec.ts | 2 +- .../tree-database-adaptor.service.ts | 46 +- .../rollback/rollback-item-builder.ts | 9 +- .../role-management/rollback/rollback-item.ts | 9 +- .../rollback/rollback.component.html | 47 +- .../rollback/rollback.component.scss | 39 +- .../rollback/rollback.component.ts | 36 +- .../rollback/rollback.service.ts | 21 +- .../src/lib/role-management/sidesheet.scss | 23 - .../split/split.component.html | 24 +- .../split/split.component.scss | 39 +- .../role-management/split/split.component.ts | 88 +- .../role-management/split/split.service.ts | 9 +- .../service-categories.component.html | 21 +- .../service-categories.component.scss | 22 +- .../service-categories.component.ts | 136 +- .../service-categories.module.ts | 74 +- .../service-categories.service.ts | 17 +- .../service-category-changed.enum.ts | 4 +- .../service-category-tree-database.ts | 32 +- .../service-category.component.html | 56 +- .../service-category.component.scss | 32 - .../service-category.component.ts | 69 +- .../service-item-tags.component.html | 24 +- .../service-item-tags.component.scss | 34 - .../service-item-tags.component.ts | 5 +- .../service-item-tags.module.ts | 18 +- .../service-item-tags.service.ts | 40 +- .../service-items-edit-form.component.html | 49 +- .../service-items-edit-form.component.scss | 9 +- .../service-items-edit-form.component.ts | 42 +- .../service-items-edit-form.module.ts | 15 +- ...ervice-items-edit-sidesheet.component.html | 53 +- ...ervice-items-edit-sidesheet.component.scss | 57 +- .../service-items-edit-sidesheet.component.ts | 25 +- .../service-items-edit.component.html | 47 +- .../service-items-edit.component.scss | 67 +- .../service-items-edit.component.ts | 111 +- .../service-items-edit.module.ts | 90 +- .../service-items-edit.service.ts | 53 +- .../service-item-info.component.html | 32 +- .../service-item-info.component.ts | 17 +- .../service-item-select.component.html | 23 +- .../service-item-select.component.scss | 41 +- .../service-item-select.component.ts | 44 +- .../typed-entity-selection-data.interface.ts | 4 +- .../service-items-selector.component.html | 27 +- .../service-items-selector.component.scss | 31 +- .../service-items-selector.component.ts | 11 +- .../lib/service-items/service-items.module.ts | 38 +- .../service-items/service-items.service.ts | 114 +- .../serviceitem-list.component.html | 85 +- .../serviceitem-list.component.scss | 15 +- .../serviceitem-list.component.spec.ts | 22 +- .../serviceitem-list.component.ts | 65 +- .../src/lib/settings/settings.component.html | 16 +- .../src/lib/settings/settings.component.scss | 7 +- .../src/lib/settings/settings.component.ts | 167 +- .../base-viewer/base-viewer.component.ts | 8 +- .../detail-viewer/detail-viewer.component.ts | 10 +- .../details-container.interface.ts | 2 +- .../details-view.interface.ts | 5 +- .../duplicate-check.component.html | 5 +- .../duplicate-check.component.ts | 7 +- .../exclusion-check.component.ts | 7 +- .../exclusion-result.interface.ts | 2 +- .../mandatory-acc-product-result.interface.ts | 2 +- .../product-dependency-check.component.html | 4 +- .../product-dependency-check.component.ts | 7 +- .../shopping-cart-validation-detail.module.ts | 22 +- ...ing-cart-validation-detail.service.spec.ts | 4 +- ...shopping-cart-validation-detail.service.ts | 8 +- .../cart-item-clone-parameters.interface.ts | 4 +- .../cart-item-edit/cart-item-clone.service.ts | 80 +- .../cart-item-edit-parameter.interface.ts | 12 +- .../cart-item-edit.component.html | 60 +- .../cart-item-edit.component.scss | 12 +- .../cart-item-edit.component.ts | 10 +- .../cart-item-edit/cart-item-fk.service.ts | 69 +- .../cart-item-interactive.service.ts | 33 +- .../order-for-additional-users.component.html | 26 +- .../order-for-additional-users.component.ts | 14 +- .../request-parameters.service.ts | 24 +- ...rt-item-validation-overview.component.html | 16 +- ...rt-item-validation-overview.component.scss | 4 +- ...cart-item-validation-overview.component.ts | 27 +- .../cart-item-validation-result.interface.ts | 4 +- .../cart-items-extension.service.ts | 46 + .../src/lib/shopping-cart/cart-items.model.ts | 47 + .../lib/shopping-cart/cart-items.service.ts | 161 +- .../cart-items/cart-item-check-status.enum.ts | 4 +- .../cart-items/cart-item-display.component.ts | 41 +- .../cart-items/cart-item-logic.interface.ts | 6 +- .../cart-items/cart-item-logic.service.ts | 20 +- .../cart-item-validation-status.enum.ts | 23 +- .../cart-items/cart-items.component.html | 150 +- .../cart-items/cart-items.component.scss | 33 +- .../cart-items/cart-items.component.spec.ts | 69 +- .../cart-items/cart-items.component.ts | 396 +- .../default-cart-item-display.component.ts | 34 +- .../confirm-cart-submit.dialog.html | 6 +- .../confirm-cart-submit.dialog.scss | 2 +- .../confirm-cart-submit.dialog.ts | 8 +- .../requestable-product.interface.ts | 9 +- .../shopping-cart-button.component.html | 9 + .../shopping-cart-button.component.scss | 8 + .../shopping-cart-button.component.ts | 68 + .../shopping-cart-empty.component.html | 31 +- .../shopping-cart-empty.component.scss | 27 +- .../shopping-cart-empty.component.ts | 6 +- .../shopping-cart-for-later.component.html | 96 +- .../shopping-cart-for-later.component.scss | 56 +- .../shopping-cart-for-later.component.ts | 39 +- .../shopping-cart-submit-warnings.dialog.html | 19 +- .../shopping-cart-submit-warnings.dialog.ts | 14 +- .../shopping-cart-validator.spec.ts | 109 +- .../shopping-cart/shopping-cart-validator.ts | 40 +- .../shopping-cart.component.html | 276 +- .../shopping-cart.component.scss | 27 +- .../shopping-cart/shopping-cart.component.ts | 175 +- .../lib/shopping-cart/shopping-cart.module.ts | 129 +- .../lib/shopping-cart/shopping-cart.spec.ts | 112 +- .../src/lib/shopping-cart/shopping-cart.ts | 110 +- .../sourcedetective-sidesheet.component.html | 9 +- .../sourcedetective-sidesheet.component.scss | 9 +- .../sourcedetective-sidesheet.component.ts | 17 +- .../sourcedetective-type.enum.ts | 4 +- .../sourcedetective.component.html | 103 +- .../sourcedetective.component.scss | 24 +- .../sourcedetective.component.ts | 75 +- .../sourcedetective/sourcedetective.module.ts | 16 +- .../lib/statistics/charts/chart-data-typed.ts | 54 +- .../chart-table-service.service.ts | 11 +- .../chart-table/chart-table.component.html | 33 +- .../chart-table/chart-table.component.scss | 38 +- .../chart-table/chart-table.component.ts | 18 +- .../charts/chart-tile/chart-details.ts | 4 +- .../chart-tile/chart-tile.component.html | 23 +- .../chart-tile/chart-tile.component.spec.ts | 20 +- .../charts/chart-tile/chart-tile.component.ts | 108 +- .../point-stat-visual/point-stat-typed.ts | 4 +- .../point-stat-visual.component.html | 8 +- .../point-stat-visual.component.scss | 8 +- .../point-stat-visual.component.ts | 4 +- .../point-stat-visual.service.ts | 28 +- .../table-stat-visual.component.html | 13 + .../table-stat-visual.component.scss | 20 + .../table-stat-visual.component.ts | 52 + .../charts-sidesheet.component.html | 117 +- .../charts-sidesheet.component.scss | 93 +- .../charts-sidesheet.component.spec.ts | 26 +- .../charts-sidesheet.component.ts | 49 +- .../statistics-chart-handler.service.ts | 99 +- .../block-detail-sidesheet.component.html | 17 +- .../block-detail-sidesheet.component.scss | 63 +- .../block-detail-sidesheet.component.ts | 50 +- .../heatmaps/block-properties.interface.ts | 68 +- .../discrete-legend.component.html | 10 +- .../discrete-legend.component.ts | 8 +- .../heatmap-chart.component.html | 4 +- .../heatmap-chart.component.scss | 4 - .../heatmap-chart.component.spec.ts | 8 +- .../heatmap-chart/heatmap-chart.component.ts | 10 +- .../heatmaps/heatmap-data-extended.ts | 4 +- .../statistics/heatmaps/heatmap-data-typed.ts | 40 +- .../statistics/heatmaps/heatmap-info-typed.ts | 4 +- .../heatmap-sidesheet.component.html | 115 +- .../heatmap-sidesheet.component.scss | 131 +- .../heatmap-sidesheet.component.spec.ts | 32 +- .../heatmap-sidesheet.component.ts | 84 +- .../heatmap-sidesheet.service.spec.ts | 7 +- .../heatmap-sidesheet.service.ts | 118 +- .../heatmap-tile/heatmap-tile.component.html | 4 +- .../heatmap-tile/heatmap-tile.component.scss | 32 +- .../heatmap-tile.component.spec.ts | 21 +- .../heatmap-tile/heatmap-tile.component.ts | 8 +- .../treemap-chart.component.html | 2 +- .../treemap-chart.component.scss | 4 - .../treemap-chart.component.spec.ts | 14 +- .../treemap-chart/treemap-chart.component.ts | 10 +- .../qer/src/lib/statistics/stat-mixins.scss | 150 +- .../lib/statistics/statistics-api.service.ts | 19 +- .../statistics-for-objects-api.service.ts | 32 +- .../statistics-for-objects.component.html | 26 +- .../statistics-for-objects.component.scss | 5 +- .../statistics-for-objects.component.spec.ts | 14 +- .../statistics-for-objects.component.ts | 82 +- .../statistics-home-page/chart-info-typed.ts | 15 +- .../favorites-tab.component.html | 11 +- .../favorites-tab.component.scss | 4 +- .../favorites-tab.component.spec.ts | 26 +- .../favorites-tab/favorites-tab.component.ts | 65 +- .../heatmap-info-typed.ts | 36 +- .../heatmap-visual.component.html | 4 +- .../heatmap-visual.component.scss | 32 +- .../heatmap-visual.component.ts | 8 +- .../statistics-cards-visuals.component.html | 44 +- .../statistics-cards-visuals.component.scss | 44 +- .../statistics-cards-visuals.component.ts | 65 +- .../statistics-cards.component.html | 112 +- .../statistics-cards.component.scss | 83 +- .../statistics-cards.component.spec.ts | 30 +- .../statistics-cards.component.ts | 33 +- .../statistics-constants.service.ts | 23 +- .../statistics-data.service.ts | 250 +- .../statistics-home-page.component.html | 60 +- .../statistics-home-page.component.scss | 67 +- .../statistics-home-page.component.spec.ts | 36 +- .../statistics-home-page.component.ts | 53 +- ...s-ordering-sidesheet-dialog.component.html | 12 - ...s-ordering-sidesheet-dialog.component.scss | 21 - ...atistics-ordering-sidesheet.component.html | 97 +- ...atistics-ordering-sidesheet.component.scss | 102 +- ...stics-ordering-sidesheet.component.spec.ts | 64 +- ...statistics-ordering-sidesheet.component.ts | 54 +- .../statistics-tree.component.html | 6 +- .../statistics-tree.component.scss | 39 +- .../statistics-tree.component.spec.ts | 26 +- .../statistics-tree.component.ts | 80 +- .../src/lib/statistics/statistics.module.ts | 83 +- .../team-responsibilities.component.html | 149 +- .../team-responsibilities.component.scss | 12 +- .../team-responsibilities.component.ts | 209 +- .../team-responsibilities.module.ts | 93 +- .../team-responsibilities.service.ts | 158 +- ...onsibility-assign-sidesheet.component.html | 48 + ...onsibility-assign-sidesheet.component.scss | 7 + ...sponsibility-assign-sidesheet.component.ts | 125 + .../team-responsibility-dialog.component.html | 22 + .../team-responsibility-dialog.component.scss | 3 + .../team-responsibility-dialog.component.ts | 53 + ...am-responsibility-sidesheet.component.html | 71 +- ...am-responsibility-sidesheet.component.scss | 48 + ...team-responsibility-sidesheet.component.ts | 172 +- .../team-responsibility-tile.component.html | 12 +- .../team-responsibility-tile.component.ts | 10 +- .../terms-of-use-accept.component.html | 19 +- .../terms-of-use-accept.component.scss | 11 - .../terms-of-use-accept.component.ts | 70 +- .../src/lib/terms-of-use/terms-of-use-item.ts | 8 +- .../terms-of-use-list.component.html | 20 +- .../terms-of-use-list.component.scss | 2 +- .../terms-of-use-list.component.ts | 49 +- .../terms-of-use-viewer.component.html | 10 +- .../terms-of-use-viewer.component.scss | 12 +- .../terms-of-use-viewer.component.ts | 21 +- .../lib/terms-of-use/terms-of-use.module.ts | 16 +- .../lib/terms-of-use/terms-of-use.service.ts | 46 +- .../badge-tile/badge-tile.component.html | 15 +- .../badge-tile/badge-tile.component.scss | 31 +- .../tiles/badge-tile/badge-tile.component.ts | 10 +- .../tiles/icon-tile/icon-tile.component.html | 28 +- .../tiles/icon-tile/icon-tile.component.scss | 32 +- .../tiles/icon-tile/icon-tile.component.ts | 5 +- .../notification-tile.component.html | 31 +- .../notification-tile.component.scss | 4 - .../notification-tile.component.ts | 27 +- .../qer/src/lib/tiles/tiles.module.ts | 28 +- .../user-process/user-process.component.html | 12 +- .../user-process/user-process.component.scss | 63 +- .../user-process/user-process.component.ts | 26 +- .../lib/user-process/user-process.module.ts | 34 +- .../user-process/user-processes.service.ts | 11 +- .../lib/user/pending-items-type.interface.ts | 4 +- .../qer/src/lib/user/user-model.service.ts | 22 +- .../projects/qer/src/lib/user/user.module.ts | 12 +- .../lib/view-config/view-config.service.ts | 103 +- .../create-new-device.component.html | 12 +- .../create-new-device.component.scss | 7 - .../create-new-device.component.ts | 30 +- .../view-devices.component.html | 95 +- .../view-devices.component.scss | 58 +- .../view-devices.component.ts | 238 +- .../view-devices-sidesheet.component.html | 18 +- .../view-devices-sidesheet.component.scss | 20 - .../view-devices-sidesheet.component.ts | 50 +- .../lib/view-devices/view-devices.module.ts | 67 +- .../lib/view-devices/view-devices.service.ts | 43 +- .../businessowner-chartsummary.component.html | 89 +- .../businessowner-chartsummary.component.scss | 35 +- .../businessowner-chartsummary.component.ts | 72 +- .../src/lib/wport/start/dashboard.service.ts | 10 +- .../src/lib/wport/start/start.component.html | 68 +- .../src/lib/wport/start/start.component.scss | 17 +- .../src/lib/wport/start/start.component.ts | 30 +- imxweb/projects/qer/src/public_api.ts | 72 +- imxweb/projects/qer/src/test.ts | 18 +- imxweb/projects/qer/tsconfig.lib.json | 11 +- imxweb/projects/qer/tsconfig.spec.json | 15 +- imxweb/projects/qer/tslint.json | 17 - imxweb/projects/rmb/.compodocrc.json | 2 +- imxweb/projects/rmb/.eslintrc.json | 35 + imxweb/projects/rmb/imx-plugin-config.json | 2 +- imxweb/projects/rmb/karma.conf.js | 11 +- imxweb/projects/rmb/ng-package.dynamic.json | 5 +- imxweb/projects/rmb/ng-package.json | 2 +- imxweb/projects/rmb/package.json | 2 +- imxweb/projects/rmb/project.json | 64 + imxweb/projects/rmb/src/lib/init.service.ts | 76 +- imxweb/projects/rmb/src/lib/org-data-model.ts | 13 +- imxweb/projects/rmb/src/lib/org-membership.ts | 84 +- .../rmb/src/lib/rmb-api-client.service.ts | 11 +- .../projects/rmb/src/lib/rmb-config.module.ts | 10 +- .../lib/team-role/team-role.component.html | 8 +- .../lib/team-role/team-role.component.scss | 3 - .../src/lib/team-role/team-role.component.ts | 76 +- .../rmb/src/lib/team-role/team-role.module.ts | 26 +- imxweb/projects/rmb/src/public_api.ts | 2 +- imxweb/projects/rmb/src/test.ts | 26 +- imxweb/projects/rmb/tsconfig.lib.json | 9 +- imxweb/projects/rmb/tsconfig.lib.prod.json | 3 - imxweb/projects/rmb/tsconfig.spec.json | 15 +- imxweb/projects/rmb/tslint.json | 17 - imxweb/projects/rms/.compodocrc.json | 2 +- imxweb/projects/rms/.eslintrc.json | 35 + imxweb/projects/rms/imx-plugin-config.json | 2 +- imxweb/projects/rms/karma.conf.js | 11 +- imxweb/projects/rms/ng-package.dynamic.json | 5 +- imxweb/projects/rms/ng-package.json | 2 +- imxweb/projects/rms/package.json | 2 +- imxweb/projects/rms/project.json | 64 + .../projects/rms/src/lib/eset-data-model.ts | 13 +- .../projects/rms/src/lib/eset-entitlements.ts | 65 +- .../projects/rms/src/lib/eset-membership.ts | 70 +- imxweb/projects/rms/src/lib/init.service.ts | 52 +- .../src/lib/requestable-systemrole-type.ts | 60 +- .../rms/src/lib/rms-api-client.service.ts | 13 +- .../projects/rms/src/lib/rms-config.module.ts | 9 +- imxweb/projects/rms/src/public_api.ts | 2 +- imxweb/projects/rms/src/test.ts | 26 +- imxweb/projects/rms/tsconfig.lib.json | 9 +- imxweb/projects/rms/tsconfig.lib.prod.json | 3 - imxweb/projects/rms/tsconfig.spec.json | 15 +- imxweb/projects/rms/tslint.json | 17 - imxweb/projects/rps/.compodocrc.json | 2 +- imxweb/projects/rps/.eslintrc.json | 35 + imxweb/projects/rps/imx-plugin-config.json | 8 +- imxweb/projects/rps/karma.conf.js | 11 +- imxweb/projects/rps/ng-package.dynamic.json | 5 +- imxweb/projects/rps/ng-package.json | 2 +- imxweb/projects/rps/package.json | 2 +- imxweb/projects/rps/project.json | 64 + .../rps/src/lib/admin/permissions-helper.ts | 6 +- .../src/lib/admin/rps-permissions.service.ts | 9 +- imxweb/projects/rps/src/lib/init.service.ts | 16 +- .../list-report-data-provider.interface.ts | 10 +- .../list-report-viewer.component.html | 4 +- .../list-report-viewer.component.scss | 21 +- .../list-report-viewer.component.ts | 46 +- .../list-report-viewer.module.ts | 6 +- .../list-report-viewer.service.ts | 6 +- .../parameter-sidesheet.component.html | 18 +- .../parameter-sidesheet.component.scss | 4 - .../parameter-sidesheet.component.ts | 29 +- .../report-button-mail.component.html | 3 + .../report-button-mail.component.scss | 1 + .../report-button-mail.component.ts | 119 + .../report-button/report-button-parameter.ts | 2 +- .../report-button.component.html | 4 +- .../report-button.component.scss | 2 +- .../report-button/report-button.component.ts | 59 +- .../lib/report-button/report-button.module.ts | 13 +- .../edit-report-sidesheet.component.html | 20 +- .../edit-report-sidesheet.component.scss | 25 - .../edit-report-sidesheet.component.ts | 67 +- .../lib/reports/edit-report.component.html | 62 +- .../lib/reports/edit-report.component.scss | 64 +- .../src/lib/reports/edit-report.component.ts | 113 +- .../rps/src/lib/reports/edit-report.module.ts | 82 +- .../src/lib/reports/edit-report.service.ts | 31 +- .../reports/editreport-sqlwizard.service.ts | 16 +- .../rps/src/lib/rps-api-client.service.ts | 11 +- .../projects/rps/src/lib/rps-config.module.ts | 21 +- .../statistic-report-button.component.html | 6 +- .../statistic-report-button.component.scss | 14 +- .../statistic-report-button.component.ts | 15 +- .../statistic-report-button.module.ts | 5 +- .../statistic-report-button.service.ts | 6 +- ...ist-report-viewer-sidesheet.component.html | 6 +- ...ist-report-viewer-sidesheet.component.scss | 4 +- .../list-report-viewer-sidesheet.component.ts | 12 +- .../report-parameter-wrapper.ts | 42 +- .../report-subscription.service.ts | 88 +- .../report-subscription.ts | 31 +- .../report-view-config.component.html | 10 +- .../report-view-config.component.scss | 32 +- .../report-view-config.component.ts | 55 +- .../subscription-details.component.html | 8 +- .../subscription-details.component.scss | 13 - .../subscription-details.component.ts | 20 +- .../subscription-properties.component.html | 40 +- .../subscription-properties.component.scss | 7 +- .../subscription-properties.component.ts | 12 +- .../report-selector.component.html | 32 +- .../report-selector.component.scss | 18 +- .../report-selector.component.ts | 24 +- .../subscription-overview.component.html | 40 +- .../subscription-overview.component.scss | 6 +- .../subscription-overview.component.ts | 51 +- .../subscription-wizard.component.html | 34 +- .../subscription-wizard.component.scss | 8 +- .../subscription-wizard.component.ts | 41 +- .../subscriptions.component.html | 40 +- .../subscriptions.component.scss | 49 +- .../subscriptions/subscriptions.component.ts | 48 +- .../lib/subscriptions/subscriptions.module.ts | 72 +- .../subscriptions/subscriptions.service.ts | 21 +- imxweb/projects/rps/src/public_api.ts | 6 +- imxweb/projects/rps/src/test.ts | 26 +- imxweb/projects/rps/tsconfig.lib.json | 9 +- imxweb/projects/rps/tsconfig.lib.prod.json | 3 - imxweb/projects/rps/tsconfig.spec.json | 15 +- imxweb/projects/rps/tslint.json | 17 - imxweb/projects/sac/.compodocrc.json | 2 +- imxweb/projects/sac/.eslintrc.json | 35 + imxweb/projects/sac/imx-plugin-config.json | 8 +- imxweb/projects/sac/karma.conf.js | 11 +- imxweb/projects/sac/ng-package.dynamic.json | 5 +- imxweb/projects/sac/ng-package.json | 3 +- imxweb/projects/sac/package.json | 24 +- imxweb/projects/sac/project.json | 49 + imxweb/projects/sac/src/lib/init.service.ts | 18 +- .../sac/src/lib/sac-api-client.service.ts | 11 +- .../projects/sac/src/lib/sac-config.module.ts | 7 +- ...ance-violation-views-by-ability-builder.ts | 6 +- ...iance-violation-views-by-ability-entity.ts | 4 +- ...-violation-views-by-ability.component.html | 29 +- ...-violation-views-by-ability.component.scss | 6 +- ...ce-violation-views-by-ability.component.ts | 51 +- ...nce-violation-views-by-role.component.html | 64 +- ...nce-violation-views-by-role.component.scss | 98 +- ...iance-violation-views-by-role.component.ts | 32 +- .../sap-compliance-violation.component.html | 38 +- .../sap-compliance-violation.component.scss | 45 +- .../sap-compliance-violation.component.ts | 10 +- .../sap-compliance-violation.model.ts | 13 +- imxweb/projects/sac/src/public_api.ts | 2 +- imxweb/projects/sac/src/test.ts | 26 +- imxweb/projects/sac/tsconfig.lib.json | 13 +- imxweb/projects/sac/tsconfig.lib.prod.json | 3 - imxweb/projects/sac/tsconfig.spec.json | 15 +- imxweb/projects/sac/tslint.json | 17 - imxweb/projects/tsb/.compodocrc.json | 2 +- imxweb/projects/tsb/.eslintrc.json | 35 + imxweb/projects/tsb/imx-plugin-config.json | 8 +- imxweb/projects/tsb/karma.conf.js | 11 +- imxweb/projects/tsb/ng-package.dynamic.json | 5 +- imxweb/projects/tsb/ng-package.json | 2 +- imxweb/projects/tsb/package.json | 2 +- imxweb/projects/tsb/project.json | 64 + .../account-ext/account-ext.service.ts | 10 +- .../account-ext/accounts-ext.component.html | 37 +- .../account-ext/accounts-ext.component.scss | 4 +- .../account-ext/accounts-ext.component.ts | 10 +- .../account-sidesheet.component.html | 84 +- .../account-sidesheet.component.scss | 90 +- .../account-sidesheet.component.ts | 47 +- .../src/lib/accounts/account-typed-entity.ts | 4 +- .../lib/accounts/accounts-reports.service.ts | 14 +- .../src/lib/accounts/accounts.component.html | 113 +- .../src/lib/accounts/accounts.component.scss | 21 +- .../src/lib/accounts/accounts.component.ts | 187 +- .../tsb/src/lib/accounts/accounts.models.ts | 8 +- .../tsb/src/lib/accounts/accounts.module.ts | 42 +- .../tsb/src/lib/accounts/accounts.service.ts | 46 +- .../target-system-report.component.html | 25 +- .../target-system-report.component.scss | 6 - .../target-system-report.component.ts | 47 +- .../src/lib/admin/tsb-permissions-helper.ts | 6 +- .../src/lib/admin/tsb-permissions.service.ts | 8 +- .../claim-group/claim-group.component.html | 67 +- .../claim-group/claim-group.component.scss | 19 +- .../lib/claim-group/claim-group.component.ts | 83 +- .../src/lib/claim-group/claim-group.module.ts | 11 +- .../lib/claim-group/claim-group.service.ts | 77 +- .../container-tree-database-wrapper.ts | 19 +- .../data-explorer-filters.component.html | 57 +- .../data-explorer-filters.component.scss | 16 +- .../data-explorer-filters.component.ts | 57 +- .../lib/data-filters/data-filters.module.ts | 17 +- .../projects/tsb/src/lib/de-helper.service.ts | 18 +- .../group-memberships-ext.component.html | 55 +- .../group-memberships-ext.component.scss | 14 +- .../group-memberships-ext.component.ts | 82 +- .../group-memberships-ext.service.ts | 14 +- .../child-system-entitlements.component.html | 14 +- ...hild-system-entitlements.component.spec.ts | 48 +- .../child-system-entitlements.component.ts | 36 +- .../group-members.component.html | 175 +- .../group-members.component.scss | 28 +- .../group-members/group-members.component.ts | 228 +- .../new-membership/new-membership.service.ts | 39 +- .../group-sidesheet.component.html | 173 +- .../group-sidesheet.component.scss | 105 +- .../group-sidesheet.component.ts | 81 +- .../tsb/src/lib/groups/group-typed-entity.ts | 12 +- .../src/lib/groups/groups-reports.service.ts | 12 +- .../tsb/src/lib/groups/groups.component.html | 117 +- .../tsb/src/lib/groups/groups.component.scss | 27 +- .../tsb/src/lib/groups/groups.component.ts | 245 +- .../tsb/src/lib/groups/groups.models.ts | 16 +- .../tsb/src/lib/groups/groups.module.ts | 32 +- .../tsb/src/lib/groups/groups.service.ts | 126 +- .../product-owner-sidesheet.component.html | 22 +- .../product-owner-sidesheet.component.scss | 2 +- .../product-owner-sidesheet.component.ts | 12 +- .../product-owner-sidesheet.service.ts | 14 +- .../tsb-namespace-admin-guard.service.ts | 14 +- imxweb/projects/tsb/src/lib/init.service.ts | 67 +- .../src/lib/no-data/no-data.component.html | 10 +- .../src/lib/no-data/no-data.component.scss | 9 +- .../tsb/src/lib/no-data/no-data.component.ts | 2 +- .../tsb/src/lib/no-data/no-data.module.ts | 19 +- .../report-button-ext.component.html | 2 +- .../report-button-ext.component.scss | 2 +- .../report-button-ext.component.ts | 29 +- .../report-button-ext.module.ts | 13 +- .../db-object-key-wrapper.interface.ts | 6 +- .../target-system/path-parameter-wrapper.ts | 2 +- .../target-system-dynamic-method.service.ts | 47 +- .../target-system/target-system.service.ts | 8 +- .../src/lib/test/common-test-mocks.spec.ts | 55 +- .../tsb/src/lib/tsb-api-client.service.ts | 11 +- .../projects/tsb/src/lib/tsb-config.module.ts | 21 +- imxweb/projects/tsb/src/public_api.ts | 2 +- imxweb/projects/tsb/src/test.ts | 31 +- imxweb/projects/tsb/tsconfig.lib.json | 9 +- imxweb/projects/tsb/tsconfig.lib.prod.json | 3 - imxweb/projects/tsb/tsconfig.spec.json | 15 +- imxweb/projects/tsb/tslint.json | 17 - imxweb/projects/uci/.compodocrc.json | 2 +- imxweb/projects/uci/.eslintrc.json | 35 + imxweb/projects/uci/.vscode/settings.json | 2 +- imxweb/projects/uci/imx-plugin-config.json | 8 +- imxweb/projects/uci/karma.conf.js | 11 +- .../projects/uci/ng-package.dynamic-ops.json | 26 + imxweb/projects/uci/ng-package.json | 2 +- imxweb/projects/uci/package.json | 2 +- imxweb/projects/uci/project.json | 70 + .../change-sidesheet.component.html | 27 +- .../change-sidesheet.component.scss | 16 +- .../changeview/change-sidesheet.component.ts | 46 +- .../lib/changeview/change-view.component.html | 79 +- .../lib/changeview/change-view.component.scss | 17 +- .../lib/changeview/change-view.component.ts | 192 +- .../src/lib/changeview/change-view.service.ts | 27 +- .../src/lib/changeview/changeview.module.ts | 31 +- imxweb/projects/uci/src/lib/init.service.ts | 61 +- .../uci/src/lib/uci-api-client.service.ts | 11 +- .../projects/uci/src/lib/uci-config.module.ts | 17 +- imxweb/projects/uci/src/public_api.ts | 2 +- imxweb/projects/uci/src/test.ts | 26 +- imxweb/projects/uci/tsconfig.lib.json | 9 +- imxweb/projects/uci/tsconfig.lib.prod.json | 3 - imxweb/projects/uci/tsconfig.spec.json | 15 +- imxweb/projects/uci/tslint.json | 17 - imxweb/remove-local-package-locks.js | 47 +- imxweb/shared/assets/variables.scss | 29 - imxweb/shared/scss/base/colors.scss | 46 + imxweb/shared/scss/base/fonts.scss | 6 + imxweb/shared/scss/base/margins-paddings.scss | 59 + imxweb/shared/scss/base/mixins.scss | 237 + imxweb/shared/scss/{ => base}/theme.scss | 5 + imxweb/shared/scss/base/variables.scss | 54 + imxweb/shared/scss/common-table.scss | 44 - imxweb/shared/scss/common/captcha-login.scss | 68 + .../scss/common/mitigating-controls.scss | 136 + imxweb/shared/scss/common/select.scss | 35 + imxweb/shared/scss/components/alert.scss | 88 + .../shared/scss/components/autocomplete.scss | 20 + imxweb/shared/scss/components/badge.scss | 44 + .../shared/scss/components/button-toggle.scss | 72 + imxweb/shared/scss/components/button.scss | 247 + imxweb/shared/scss/components/card.scss | 160 + imxweb/shared/scss/components/chart.scss | 63 + imxweb/shared/scss/components/checkbox.scss | 67 + imxweb/shared/scss/components/chips.scss | 58 + imxweb/shared/scss/components/clickable.scss | 12 + .../scss/components/data-source-toolbar.scss | 21 + .../shared/scss/components/date-picker.scss | 28 + imxweb/shared/scss/components/dialog.scss | 42 + imxweb/shared/scss/components/divider.scss | 19 + imxweb/shared/scss/components/download.scss | 9 + .../scss/components/expansion-panel.scss | 109 + imxweb/shared/scss/components/form-field.scss | 100 + imxweb/shared/scss/components/icon.scss | 216 + imxweb/shared/scss/components/input.scss | 64 + .../shared/scss/components/layout-grid.scss | 9 + imxweb/shared/scss/components/list.scss | 103 + imxweb/shared/scss/components/logo.scss | 14 + imxweb/shared/scss/components/masthead.scss | 41 + imxweb/shared/scss/components/menu.scss | 29 + imxweb/shared/scss/components/paginator.scss | 26 + .../shared/scss/components/progress-bar.scss | 66 + .../shared/scss/components/radio-button.scss | 61 + imxweb/shared/scss/components/ripple.scss | 13 + imxweb/shared/scss/components/scrollbar.scss | 14 + imxweb/shared/scss/components/search.scss | 19 + imxweb/shared/scss/components/select.scss | 115 + .../scss/components/side-navigation.scss | 438 + imxweb/shared/scss/components/sidesheet.scss | 61 + .../shared/scss/components/slide-toggle.scss | 41 + imxweb/shared/scss/components/slider.scss | 55 + imxweb/shared/scss/components/snackbar.scss | 9 + .../shared/scss/components/sort-header.scss | 9 + imxweb/shared/scss/components/spinner.scss | 31 + imxweb/shared/scss/components/stepper.scss | 34 + imxweb/shared/scss/components/table.scss | 328 + imxweb/shared/scss/components/tabs.scss | 262 + .../scss/components/theme-switcher.scss | 15 + .../shared/scss/components/time-picker.scss | 39 + imxweb/shared/scss/components/toolbar.scss | 63 + imxweb/shared/scss/components/tooltip.scss | 24 + .../scss/components/top-navigation.scss | 25 + imxweb/shared/scss/components/tree.scss | 117 + imxweb/shared/scss/side-navigation.scss | 176 - imxweb/shared/{assets => scss}/styles.scss | 354 +- imxweb/tsconfig.json | 43 +- imxweb/tslint.json | 158 - 3203 files changed, 89326 insertions(+), 85027 deletions(-) create mode 100644 .commit create mode 100644 imxweb/.eslintignore create mode 100644 imxweb/.eslintrc.json create mode 100644 imxweb/.prettierignore create mode 100644 imxweb/.prettierrc.json create mode 100644 imxweb/build-docs.js create mode 100644 imxweb/build/nx/Build.proj delete mode 100644 imxweb/changes/changesFrom9.1.1To9.2.0.md delete mode 100644 imxweb/imx-modules/elemental-ui_cadence-icon.tgz delete mode 100644 imxweb/imx-modules/elemental-ui_core.tgz create mode 100644 imxweb/imx-modules/imx-api-APC.tgz-hash create mode 100644 imxweb/imx-modules/imx-api-APC.tgz-version delete mode 100644 imxweb/imx-modules/imx-api-aad.tgz create mode 100644 imxweb/imx-modules/imx-api-aad.tgz-hash create mode 100644 imxweb/imx-modules/imx-api-aad.tgz-version delete mode 100644 imxweb/imx-modules/imx-api-aob.tgz create mode 100644 imxweb/imx-modules/imx-api-aob.tgz-hash create mode 100644 imxweb/imx-modules/imx-api-aob.tgz-version delete mode 100644 imxweb/imx-modules/imx-api-apc.tgz delete mode 100644 imxweb/imx-modules/imx-api-att.tgz create mode 100644 imxweb/imx-modules/imx-api-att.tgz-hash create mode 100644 imxweb/imx-modules/imx-api-att.tgz-version delete mode 100644 imxweb/imx-modules/imx-api-cpl.tgz create mode 100644 imxweb/imx-modules/imx-api-cpl.tgz-hash create mode 100644 imxweb/imx-modules/imx-api-cpl.tgz-version delete mode 100644 imxweb/imx-modules/imx-api-dpr.tgz create mode 100644 imxweb/imx-modules/imx-api-dpr.tgz-hash create mode 100644 imxweb/imx-modules/imx-api-dpr.tgz-version delete mode 100644 imxweb/imx-modules/imx-api-hds.tgz create mode 100644 imxweb/imx-modules/imx-api-hds.tgz-hash create mode 100644 imxweb/imx-modules/imx-api-hds.tgz-version delete mode 100644 imxweb/imx-modules/imx-api-o3e.tgz delete mode 100644 imxweb/imx-modules/imx-api-o3t.tgz delete mode 100644 imxweb/imx-modules/imx-api-olg.tgz create mode 100644 imxweb/imx-modules/imx-api-olg.tgz-hash create mode 100644 imxweb/imx-modules/imx-api-olg.tgz-version delete mode 100644 imxweb/imx-modules/imx-api-pol.tgz create mode 100644 imxweb/imx-modules/imx-api-pol.tgz-hash create mode 100644 imxweb/imx-modules/imx-api-pol.tgz-version delete mode 100644 imxweb/imx-modules/imx-api-qbm.tgz create mode 100644 imxweb/imx-modules/imx-api-qbm.tgz-hash create mode 100644 imxweb/imx-modules/imx-api-qbm.tgz-version delete mode 100644 imxweb/imx-modules/imx-api-qer.tgz create mode 100644 imxweb/imx-modules/imx-api-qer.tgz-hash create mode 100644 imxweb/imx-modules/imx-api-qer.tgz-version delete mode 100644 imxweb/imx-modules/imx-api-rmb.tgz create mode 100644 imxweb/imx-modules/imx-api-rmb.tgz-hash create mode 100644 imxweb/imx-modules/imx-api-rmb.tgz-version delete mode 100644 imxweb/imx-modules/imx-api-rms.tgz create mode 100644 imxweb/imx-modules/imx-api-rms.tgz-hash create mode 100644 imxweb/imx-modules/imx-api-rms.tgz-version delete mode 100644 imxweb/imx-modules/imx-api-rps.tgz create mode 100644 imxweb/imx-modules/imx-api-rps.tgz-hash create mode 100644 imxweb/imx-modules/imx-api-rps.tgz-version delete mode 100644 imxweb/imx-modules/imx-api-sac.tgz create mode 100644 imxweb/imx-modules/imx-api-sac.tgz-hash create mode 100644 imxweb/imx-modules/imx-api-sac.tgz-version delete mode 100644 imxweb/imx-modules/imx-api-tsb.tgz create mode 100644 imxweb/imx-modules/imx-api-tsb.tgz-hash create mode 100644 imxweb/imx-modules/imx-api-tsb.tgz-version delete mode 100644 imxweb/imx-modules/imx-api-uci.tgz create mode 100644 imxweb/imx-modules/imx-api-uci.tgz-hash create mode 100644 imxweb/imx-modules/imx-api-uci.tgz-version delete mode 100644 imxweb/imx-modules/imx-api.tgz delete mode 100644 imxweb/imx-modules/imx-qbm-dbts.tgz create mode 100644 imxweb/install-local-packages.js create mode 100644 imxweb/nx.json create mode 100644 imxweb/prebuild.js create mode 100644 imxweb/projects/aad/.eslintrc.json create mode 100644 imxweb/projects/aad/project.json delete mode 100644 imxweb/projects/aad/src/lib/aad-extension/licence-overview-button/licence-overview-button.component.scss delete mode 100644 imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-create-dialog.component.scss create mode 100644 imxweb/projects/aob/.eslintrc.json create mode 100644 imxweb/projects/aob/project.json delete mode 100644 imxweb/projects/aob/src/lib/applications/application-hyperview/application-hyperview.component.html delete mode 100644 imxweb/projects/aob/src/lib/applications/application-hyperview/application-hyperview.component.scss delete mode 100644 imxweb/projects/aob/src/lib/applications/application-hyperview/application-hyperview.component.ts delete mode 100644 imxweb/projects/aob/src/lib/applications/application-hyperview/application-hyperview.module.ts delete mode 100644 imxweb/projects/aob/src/lib/applications/edit-application/edit-application.component.scss delete mode 100644 imxweb/projects/aob/src/lib/applications/edit-application/service-category/service-category.component.scss delete mode 100644 imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/entitlement-edit-auto-add.component.scss delete mode 100644 imxweb/projects/aob/src/lib/global-kpi/kpi-tile/kpi-tile.component.scss delete mode 100644 imxweb/projects/aob/tslint.json create mode 100644 imxweb/projects/apc/.eslintrc.json create mode 100644 imxweb/projects/apc/project.json delete mode 100644 imxweb/projects/apc/tslint.json create mode 100644 imxweb/projects/att/.eslintrc.json rename imxweb/projects/{o3t/ng-package.dynamic.json => att/ng-package.dynamic-pwd.json} (84%) create mode 100644 imxweb/projects/att/project.json delete mode 100644 imxweb/projects/att/src/lib/attestation-display/attestation-display.component.scss rename imxweb/projects/{aob/src/lib/applications/application-hyperview/application-hyperview.service.ts => att/src/lib/new-user/new-user-metadata.service.ts} (64%) delete mode 100644 imxweb/projects/att/src/lib/new-user/open-sidesheet.component.scss delete mode 100644 imxweb/projects/att/src/lib/policies/attestation-cases/attestation-cases-tree-database.service.ts delete mode 100644 imxweb/projects/att/src/lib/runs/progress/progress.component.scss delete mode 100644 imxweb/projects/att/tslint.json create mode 100644 imxweb/projects/cpl/.eslintrc.json create mode 100644 imxweb/projects/cpl/project.json delete mode 100644 imxweb/projects/cpl/tslint.json create mode 100644 imxweb/projects/custom-app/.eslintrc.json create mode 100644 imxweb/projects/custom-app/project.json rename imxweb/projects/{o3t => custom-app}/src/test.ts (66%) delete mode 100644 imxweb/projects/custom-app/tslint.json create mode 100644 imxweb/projects/dpr/.eslintrc.json create mode 100644 imxweb/projects/dpr/project.json delete mode 100644 imxweb/projects/dpr/tslint.json create mode 100644 imxweb/projects/hds/.eslintrc.json create mode 100644 imxweb/projects/hds/project.json delete mode 100644 imxweb/projects/hds/tslint.json delete mode 100644 imxweb/projects/o3t/imx-plugin-config.json delete mode 100644 imxweb/projects/o3t/package.json delete mode 100644 imxweb/projects/o3t/src/lib/teams/team-channel-details/team-channel-details.component.html delete mode 100644 imxweb/projects/o3t/src/lib/teams/team-channel-details/team-channel-details.component.ts delete mode 100644 imxweb/projects/o3t/src/lib/teams/team-channels/team-channels.component.html delete mode 100644 imxweb/projects/o3t/src/lib/teams/team-channels/team-channels.component.ts delete mode 100644 imxweb/projects/o3t/src/lib/teams/team-details/team-details.component.html delete mode 100644 imxweb/projects/o3t/src/lib/teams/team-details/team-details.component.ts delete mode 100644 imxweb/projects/o3t/src/lib/teams/teams.component.html delete mode 100644 imxweb/projects/o3t/src/lib/teams/teams.component.ts delete mode 100644 imxweb/projects/o3t/src/lib/teams/teams.service.ts delete mode 100644 imxweb/projects/o3t/src/test/o3t-common-test-mocks.ts delete mode 100644 imxweb/projects/o3t/tslint.json create mode 100644 imxweb/projects/olg/.eslintrc.json create mode 100644 imxweb/projects/olg/project.json delete mode 100644 imxweb/projects/olg/tslint.json create mode 100644 imxweb/projects/pol/.eslintrc.json create mode 100644 imxweb/projects/pol/project.json rename imxweb/projects/{qbm/src/lib/object-sheet/navigation.service.ts => pol/src/lib/guards/policy-owner-or-admin-guard.service.ts} (55%) delete mode 100644 imxweb/projects/pol/src/lib/policies/policies-sidesheet/policies-sidesheet.component.scss delete mode 100644 imxweb/projects/pol/tslint.json rename imxweb/projects/{o3t => qam}/.compodocrc.json (55%) rename imxweb/projects/{o3t => qam}/Build.proj (75%) create mode 100644 imxweb/projects/qam/imx-plugin-config.json rename imxweb/projects/{o3t => qam}/karma.conf.js (78%) rename imxweb/projects/{uci => qam}/ng-package.dynamic.json (87%) rename imxweb/projects/{o3t => qam}/ng-package.json (62%) create mode 100644 imxweb/projects/qam/package.json create mode 100644 imxweb/projects/qam/project.json create mode 100644 imxweb/projects/qam/src/lib/TypedClient.ts create mode 100644 imxweb/projects/qam/src/lib/access-request/access-request-data.service.ts create mode 100644 imxweb/projects/qam/src/lib/access-request/access-request-sidesheet-data.interface.ts create mode 100644 imxweb/projects/qam/src/lib/access-request/access-request-sidesheet.component.html create mode 100644 imxweb/projects/qam/src/lib/access-request/access-request-sidesheet.component.scss create mode 100644 imxweb/projects/qam/src/lib/access-request/access-request-sidesheet.component.ts create mode 100644 imxweb/projects/qam/src/lib/access-request/access-request-tree-database.ts rename imxweb/projects/{qbm/src/lib/sidenav-tree/sidenav-tree.module.ts => qam/src/lib/access-request/access-request.module.ts} (63%) create mode 100644 imxweb/projects/qam/src/lib/access-request/access-request.service.ts create mode 100644 imxweb/projects/qam/src/lib/access-request/qam-resourcetree.ts create mode 100644 imxweb/projects/qam/src/lib/access/access.component.html create mode 100644 imxweb/projects/qam/src/lib/access/access.component.scss create mode 100644 imxweb/projects/qam/src/lib/access/access.component.ts create mode 100644 imxweb/projects/qam/src/lib/access/trustee-view.component.html create mode 100644 imxweb/projects/qam/src/lib/access/trustee-view.component.scss create mode 100644 imxweb/projects/qam/src/lib/access/trustee-view.component.ts create mode 100644 imxweb/projects/qam/src/lib/access/user-access.component.html rename imxweb/projects/{qer/src/lib/approval-workflows/approval-workflow-edit/approval-workflow-edit-info/approval-workflow-edit-info.component.ts => qam/src/lib/access/user-access.component.ts} (66%) create mode 100644 imxweb/projects/qam/src/lib/dug-activities/dug-activities.component.html create mode 100644 imxweb/projects/qam/src/lib/dug-activities/dug-activities.component.scss create mode 100644 imxweb/projects/qam/src/lib/dug-activities/dug-activities.component.ts rename imxweb/projects/{qer/src/lib/statistics/statistics-home-page/statistics-ordering-sidesheet/statistics-ordering-sidesheet-dialog/statistics-ordering-sidesheet-dialog.component.ts => qam/src/lib/dug-activities/dug-activities.service.ts} (53%) create mode 100644 imxweb/projects/qam/src/lib/dug-activities/dug-activity-entity.ts create mode 100644 imxweb/projects/qam/src/lib/dug-dashboards/dug-dashboards.component.html create mode 100644 imxweb/projects/qam/src/lib/dug-dashboards/dug-dashboards.component.scss create mode 100644 imxweb/projects/qam/src/lib/dug-dashboards/dug-dashboards.component.ts rename imxweb/projects/{qbm/src/lib/select/check-match.validator.ts => qam/src/lib/dug-dashboards/dug-dashboards.service.ts} (65%) create mode 100644 imxweb/projects/qam/src/lib/dug-overview/dug-overview.component.html create mode 100644 imxweb/projects/qam/src/lib/dug-overview/dug-overview.component.scss create mode 100644 imxweb/projects/qam/src/lib/dug-overview/dug-overview.component.ts create mode 100644 imxweb/projects/qam/src/lib/dug-overview/dug-overview.service.ts create mode 100644 imxweb/projects/qam/src/lib/dug/access-comparison.component.html create mode 100644 imxweb/projects/qam/src/lib/dug/access-comparison.component.ts create mode 100644 imxweb/projects/qam/src/lib/dug/dug-access-analysis.component.html create mode 100644 imxweb/projects/qam/src/lib/dug/dug-access-analysis.component.ts create mode 100644 imxweb/projects/qam/src/lib/dug/dug-access-analysis.service.ts create mode 100644 imxweb/projects/qam/src/lib/dug/dug-access-detail.component.html create mode 100644 imxweb/projects/qam/src/lib/dug/dug-access-detail.component.scss create mode 100644 imxweb/projects/qam/src/lib/dug/dug-access-detail.component.ts create mode 100644 imxweb/projects/qam/src/lib/dug/dug-access/assigned-permissions/assigned-permissions.component.html create mode 100644 imxweb/projects/qam/src/lib/dug/dug-access/assigned-permissions/assigned-permissions.component.scss create mode 100644 imxweb/projects/qam/src/lib/dug/dug-access/assigned-permissions/assigned-permissions.component.ts create mode 100644 imxweb/projects/qam/src/lib/dug/dug-access/assigned-permissions/trustee-entity.ts create mode 100644 imxweb/projects/qam/src/lib/dug/dug-access/box-icon/box-icon.component.html create mode 100644 imxweb/projects/qam/src/lib/dug/dug-access/box-icon/box-icon.component.scss create mode 100644 imxweb/projects/qam/src/lib/dug/dug-access/box-icon/box-icon.component.ts create mode 100644 imxweb/projects/qam/src/lib/dug/dug-access/dug-access.component.html create mode 100644 imxweb/projects/qam/src/lib/dug/dug-access/dug-access.component.scss create mode 100644 imxweb/projects/qam/src/lib/dug/dug-access/dug-access.component.ts rename imxweb/projects/{qbm/src/lib/select/select.module.ts => qam/src/lib/dug/dug-access/dug-access.module.ts} (50%) create mode 100644 imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/effective-permission-tree-database.ts create mode 100644 imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/effective-permission.component.html create mode 100644 imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/effective-permission.component.scss create mode 100644 imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/effective-permission.component.ts create mode 100644 imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/permission-member/permission-member.component.html create mode 100644 imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/permission-member/permission-member.component.scss create mode 100644 imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/permission-member/permission-member.component.ts create mode 100644 imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/trustee-entity-hierarchy.ts create mode 100644 imxweb/projects/qam/src/lib/dug/dug-activity/dug-activity.component.html create mode 100644 imxweb/projects/qam/src/lib/dug/dug-activity/dug-activity.component.scss create mode 100644 imxweb/projects/qam/src/lib/dug/dug-activity/dug-activity.component.ts rename imxweb/projects/{o3t/src/lib/o3t-config.module.ts => qam/src/lib/dug/dug-activity/dug-activity.module.ts} (74%) create mode 100644 imxweb/projects/qam/src/lib/dug/dug-reports/dug-report-entity.ts create mode 100644 imxweb/projects/qam/src/lib/dug/dug-reports/dug-reports.component.html create mode 100644 imxweb/projects/qam/src/lib/dug/dug-reports/dug-reports.component.scss create mode 100644 imxweb/projects/qam/src/lib/dug/dug-reports/dug-reports.component.ts create mode 100644 imxweb/projects/qam/src/lib/dug/dug-reports/dug-reports.service.ts create mode 100644 imxweb/projects/qam/src/lib/dug/dug-sidesheet.component.html create mode 100644 imxweb/projects/qam/src/lib/dug/dug-sidesheet.component.scss create mode 100644 imxweb/projects/qam/src/lib/dug/dug-sidesheet.component.ts create mode 100644 imxweb/projects/qam/src/lib/identity/identity.component.html create mode 100644 imxweb/projects/qam/src/lib/identity/identity.component.scss create mode 100644 imxweb/projects/qam/src/lib/identity/identity.component.ts create mode 100644 imxweb/projects/qam/src/lib/init.service.ts rename imxweb/projects/{o3t/src/lib/api.service.ts => qam/src/lib/qam-api-client.service.ts} (63%) create mode 100644 imxweb/projects/qam/src/lib/qam-config.module.ts create mode 100644 imxweb/projects/qam/src/lib/qam.scss rename imxweb/projects/{o3t/src/lib/api.service.spec.ts => qam/src/public_api.ts} (84%) create mode 100644 imxweb/projects/qam/src/test.ts rename imxweb/projects/{o3t => qam}/tsconfig.lib.json (68%) rename imxweb/projects/{o3t => qam}/tsconfig.lib.prod.json (75%) rename imxweb/projects/{o3t => qam}/tsconfig.spec.json (100%) rename imxweb/projects/{aad => qam}/tslint.json (100%) create mode 100644 imxweb/projects/qbm-app-landingpage/.eslintrc.json create mode 100644 imxweb/projects/qbm-app-landingpage/README.md create mode 100644 imxweb/projects/qbm-app-landingpage/project.json delete mode 100644 imxweb/projects/qbm-app-landingpage/tslint.json create mode 100644 imxweb/projects/qbm/.eslintrc.json create mode 100644 imxweb/projects/qbm/project.json create mode 100644 imxweb/projects/qbm/src/lib/admin/about/admin-about.service.ts rename imxweb/projects/qbm/src/lib/{temp-billboard/temp-billboard.module.ts => base/elemental-defaults.ts} (58%) create mode 100644 imxweb/projects/qbm/src/lib/base/sidesheet-helper.ts create mode 100644 imxweb/projects/qbm/src/lib/captcha/captcha.component.scss create mode 100644 imxweb/projects/qbm/src/lib/cdr/edit-bitmask/edit-bitmask.component.html create mode 100644 imxweb/projects/qbm/src/lib/cdr/edit-bitmask/edit-bitmask.component.scss create mode 100644 imxweb/projects/qbm/src/lib/cdr/edit-bitmask/edit-bitmask.component.ts delete mode 100644 imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-paginator.component.scss create mode 100644 imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-tree-sidesheet/filter-tree-sidesheet.component.html create mode 100644 imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-tree-sidesheet/filter-tree-sidesheet.component.scss create mode 100644 imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-tree-sidesheet/filter-tree-sidesheet.component.ts create mode 100644 imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-tree-sidesheet/filter-tree-sidesheet.model.ts delete mode 100644 imxweb/projects/qbm/src/lib/data-table/data-table-generic-column.component.scss create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-auto-table/data-view-auto-table.component.html create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-auto-table/data-view-auto-table.component.scss create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-auto-table/data-view-auto-table.component.ts create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-chipbar/data-view-chipbar.component.html create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-chipbar/data-view-chipbar.component.scss create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-chipbar/data-view-chipbar.component.ts create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-filter/data-view-filter.component.html create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-filter/data-view-filter.component.scss create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-filter/data-view-filter.component.ts create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-group/data-view-group.component.html create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-group/data-view-group.component.ts create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-nested-table/data-view-nested-table.component.html create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-nested-table/data-view-nested-table.component.ts create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-paginator/data-view-paginator.component.html create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-paginator/data-view-paginator.component.scss create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-paginator/data-view-paginator.component.ts create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-search/data-view-search.component.html create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-search/data-view-search.component.scss create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-search/data-view-search.component.ts create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-selection/data-view-selection.component.html create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-selection/data-view-selection.component.scss create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-selection/data-view-selection.component.ts create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-settings/data-view-settings.component.html create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-settings/data-view-settings.component.ts create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-source-factory.service.ts create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-source.spec.ts create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-source.ts create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-status/data-view-status.component.html create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-status/data-view-status.component.scss rename imxweb/projects/qbm/src/lib/{menu/menu-item/navigation-menu-item.ts => data-view/data-view-status/data-view-status.component.ts} (65%) create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-toolbar/data-view-toolbar.component.html create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-toolbar/data-view-toolbar.component.scss create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view-toolbar/data-view-toolbar.component.ts create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view.interface.ts create mode 100644 imxweb/projects/qbm/src/lib/data-view/data-view.module.ts delete mode 100644 imxweb/projects/qbm/src/lib/info-modal-dialog/info-badge/info-badge.component.scss delete mode 100644 imxweb/projects/qbm/src/lib/master-detail/master-detail.component.html delete mode 100644 imxweb/projects/qbm/src/lib/master-detail/master-detail.component.ts delete mode 100644 imxweb/projects/qbm/src/lib/menu/menu.component.html delete mode 100644 imxweb/projects/qbm/src/lib/menu/menu.component.scss delete mode 100644 imxweb/projects/qbm/src/lib/menu/menu.component.ts delete mode 100644 imxweb/projects/qbm/src/lib/menu/menu.module.ts rename imxweb/projects/{o3t/src/public_api.ts => qbm/src/lib/message-dialog/message-dialog.service.ts} (73%) delete mode 100644 imxweb/projects/qbm/src/lib/plugins/plugin-loader.service.ts create mode 100644 imxweb/projects/qbm/src/lib/processing-queue/processing-queue.interface.ts create mode 100644 imxweb/projects/qbm/src/lib/processing-queue/processing-queue.service.ts create mode 100644 imxweb/projects/qbm/src/lib/processing-queue/processing-queue.spec.ts delete mode 100644 imxweb/projects/qbm/src/lib/progressbar/progressbar.component.scss delete mode 100644 imxweb/projects/qbm/src/lib/select/autocomplete.component.html delete mode 100644 imxweb/projects/qbm/src/lib/select/autocomplete.component.scss delete mode 100644 imxweb/projects/qbm/src/lib/select/autocomplete.component.ts delete mode 100644 imxweb/projects/qbm/src/lib/select/select-data-source.ts delete mode 100644 imxweb/projects/qbm/src/lib/select/select.component.html delete mode 100644 imxweb/projects/qbm/src/lib/select/select.component.scss delete mode 100644 imxweb/projects/qbm/src/lib/select/select.component.ts delete mode 100644 imxweb/projects/qbm/src/lib/services/device-state.service.ts delete mode 100644 imxweb/projects/qbm/src/lib/side-navigation-view/side-navigation-view.component.scss create mode 100644 imxweb/projects/qbm/src/lib/sidenav-tree/custom-tree-control.component.html create mode 100644 imxweb/projects/qbm/src/lib/sidenav-tree/custom-tree-control.component.scss create mode 100644 imxweb/projects/qbm/src/lib/sidenav-tree/custom-tree-control.component.ts delete mode 100644 imxweb/projects/qbm/src/lib/temp-billboard/temp-billboard.component.ts delete mode 100644 imxweb/projects/qbm/src/lib/tile/tile-variables.scss create mode 100644 imxweb/projects/qbm/src/lib/timeline/timeline.model.ts delete mode 100644 imxweb/projects/qbm/tslint.json create mode 100644 imxweb/projects/qer-app-operationssupport/.eslintrc.json create mode 100644 imxweb/projects/qer-app-operationssupport/prebuild.js create mode 100644 imxweb/projects/qer-app-operationssupport/project.json delete mode 100644 imxweb/projects/qer-app-operationssupport/tslint.json create mode 100644 imxweb/projects/qer-app-portal/.eslintrc.json create mode 100644 imxweb/projects/qer-app-portal/project.json delete mode 100644 imxweb/projects/qer-app-portal/src/app/portal-doc-configuration.service.ts delete mode 100644 imxweb/projects/qer-app-portal/tslint.json create mode 100644 imxweb/projects/qer-app-pwdportal/.eslintrc.json create mode 100644 imxweb/projects/qer-app-pwdportal/project.json delete mode 100644 imxweb/projects/qer-app-pwdportal/tslint.json create mode 100644 imxweb/projects/qer/.eslintrc.json create mode 100644 imxweb/projects/qer/project.json create mode 100644 imxweb/projects/qer/src/lib/about/portal-about.service.ts delete mode 100644 imxweb/projects/qer/src/lib/addressbook/addressbook-detail/addressbook-detail.component.scss delete mode 100644 imxweb/projects/qer/src/lib/approval-workflows/approval-workflow-edit/approval-workflow-edit-info/approval-workflow-edit-info.component.html delete mode 100644 imxweb/projects/qer/src/lib/approval-workflows/approval-workflow-edit/approval-workflow-edit-info/approval-workflow-edit-info.component.scss delete mode 100644 imxweb/projects/qer/src/lib/approval-workflows/approval-workflow-styles.scss create mode 100644 imxweb/projects/qer/src/lib/delegation/delegation-guard.service.ts delete mode 100644 imxweb/projects/qer/src/lib/dynamic-exclusion-dialog/dynamic-exclusion-dialog.component.scss create mode 100644 imxweb/projects/qer/src/lib/filter-sqlwizard/filter-sqlwizard.service.ts delete mode 100644 imxweb/projects/qer/src/lib/guards/data-explorer-guard.service.ts delete mode 100644 imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/service-item-detail.component.scss rename imxweb/projects/{qbm/src/lib/doc/doc-chapter.service.ts => qer/src/lib/metadata/portal-metadata.service.ts} (52%) rename imxweb/projects/{qbm/src/lib/select/select-content-provider.interface.ts => qer/src/lib/new-request/constants.ts} (82%) delete mode 100644 imxweb/projects/qer/src/lib/new-request/new-request-peer-group/peer-group-discard-selected.component.scss rename imxweb/projects/qer/src/lib/object-hyperview/{object-hyperview-interface.ts => object-hyperview.interface.ts} (96%) create mode 100644 imxweb/projects/qer/src/lib/ops/about/ops-about.service.ts create mode 100644 imxweb/projects/qer/src/lib/ops/metadata/ops-metadata.service.ts rename imxweb/projects/{qbm/src/lib/menu/menu-item/group-menu-item.ts => qer/src/lib/ops/ops.service.ts} (64%) create mode 100644 imxweb/projects/qer/src/lib/ops/permissions/ops-permissions-helper.ts rename imxweb/projects/{qbm/src/lib/temp-billboard/temp-billboard.service.ts => qer/src/lib/ops/permissions/ops-permissions.service.ts} (63%) delete mode 100644 imxweb/projects/qer/src/lib/org-chart/variables.scss rename imxweb/projects/qer/src/lib/{profile => }/password-questions/confirm-password-matcher.ts (96%) rename imxweb/projects/qer/src/lib/{profile => }/password-questions/password-question.service.ts (83%) create mode 100644 imxweb/projects/qer/src/lib/password-questions/password-questions-sidesheet/password-questions-sidesheet.component.html create mode 100644 imxweb/projects/qer/src/lib/password-questions/password-questions-sidesheet/password-questions-sidesheet.component.scss rename imxweb/projects/qer/src/lib/{profile => }/password-questions/password-questions-sidesheet/password-questions-sidesheet.component.ts (66%) rename imxweb/projects/qer/src/lib/{profile => }/password-questions/password-questions-validator.ts (80%) create mode 100644 imxweb/projects/qer/src/lib/password-questions/password-questions.component.html create mode 100644 imxweb/projects/qer/src/lib/password-questions/password-questions.component.scss rename imxweb/projects/qer/src/lib/{profile => }/password-questions/password-questions.component.ts (75%) rename imxweb/projects/{o3t/src/lib/teams/teams.module.ts => qer/src/lib/password-questions/password-questions.module.ts} (50%) create mode 100644 imxweb/projects/qer/src/lib/password/about/pwd-about.service.ts create mode 100644 imxweb/projects/qer/src/lib/password/metadata/pwd-metadata.service.ts delete mode 100644 imxweb/projects/qer/src/lib/pattern-item-list/pattern-item-list.component.scss delete mode 100644 imxweb/projects/qer/src/lib/profile/password-questions/password-questions-sidesheet/password-questions-sidesheet.component.html delete mode 100644 imxweb/projects/qer/src/lib/profile/password-questions/password-questions-sidesheet/password-questions-sidesheet.component.scss delete mode 100644 imxweb/projects/qer/src/lib/profile/password-questions/password-questions.component.html delete mode 100644 imxweb/projects/qer/src/lib/profile/password-questions/password-questions.component.scss delete mode 100644 imxweb/projects/qer/src/lib/profile/security-keys/security-keys-sidesheet/security-keys-sidesheet.component.scss delete mode 100644 imxweb/projects/qer/src/lib/profile/security-keys/security-keys.component.scss create mode 100644 imxweb/projects/qer/src/lib/queue/queue-sidesheet/queue-sidesheet.component.html create mode 100644 imxweb/projects/qer/src/lib/queue/queue-sidesheet/queue-sidesheet.component.scss create mode 100644 imxweb/projects/qer/src/lib/queue/queue-sidesheet/queue-sidesheet.component.ts create mode 100644 imxweb/projects/qer/src/lib/queue/queue-status/queue-status.component.html create mode 100644 imxweb/projects/qer/src/lib/queue/queue-status/queue-status.component.scss create mode 100644 imxweb/projects/qer/src/lib/queue/queue-status/queue-status.component.ts create mode 100644 imxweb/projects/qer/src/lib/role-management/api-wrapper.ts delete mode 100644 imxweb/projects/qer/src/lib/service-item-tags/service-item-tags.component.scss create mode 100644 imxweb/projects/qer/src/lib/shopping-cart/cart-items-extension.service.ts create mode 100644 imxweb/projects/qer/src/lib/shopping-cart/cart-items.model.ts create mode 100644 imxweb/projects/qer/src/lib/shopping-cart/shopping-cart-button/shopping-cart-button.component.html create mode 100644 imxweb/projects/qer/src/lib/shopping-cart/shopping-cart-button/shopping-cart-button.component.scss create mode 100644 imxweb/projects/qer/src/lib/shopping-cart/shopping-cart-button/shopping-cart-button.component.ts create mode 100644 imxweb/projects/qer/src/lib/statistics/charts/chart-tile/table-stat-visual/table-stat-visual.component.html create mode 100644 imxweb/projects/qer/src/lib/statistics/charts/chart-tile/table-stat-visual/table-stat-visual.component.scss create mode 100644 imxweb/projects/qer/src/lib/statistics/charts/chart-tile/table-stat-visual/table-stat-visual.component.ts delete mode 100644 imxweb/projects/qer/src/lib/statistics/statistics-home-page/statistics-ordering-sidesheet/statistics-ordering-sidesheet-dialog/statistics-ordering-sidesheet-dialog.component.html delete mode 100644 imxweb/projects/qer/src/lib/statistics/statistics-home-page/statistics-ordering-sidesheet/statistics-ordering-sidesheet-dialog/statistics-ordering-sidesheet-dialog.component.scss create mode 100644 imxweb/projects/qer/src/lib/team-responsibilities/team-responsibility-assign-sidesheet/team-responsibility-assign-sidesheet.component.html create mode 100644 imxweb/projects/qer/src/lib/team-responsibilities/team-responsibility-assign-sidesheet/team-responsibility-assign-sidesheet.component.scss create mode 100644 imxweb/projects/qer/src/lib/team-responsibilities/team-responsibility-assign-sidesheet/team-responsibility-assign-sidesheet.component.ts create mode 100644 imxweb/projects/qer/src/lib/team-responsibilities/team-responsibility-dialog/team-responsibility-dialog.component.html create mode 100644 imxweb/projects/qer/src/lib/team-responsibilities/team-responsibility-dialog/team-responsibility-dialog.component.scss create mode 100644 imxweb/projects/qer/src/lib/team-responsibilities/team-responsibility-dialog/team-responsibility-dialog.component.ts create mode 100644 imxweb/projects/qer/src/lib/team-responsibilities/team-responsibility-sidesheet/team-responsibility-sidesheet.component.scss delete mode 100644 imxweb/projects/qer/src/lib/tiles/notification-tile/notification-tile.component.scss delete mode 100644 imxweb/projects/qer/tslint.json create mode 100644 imxweb/projects/rmb/.eslintrc.json create mode 100644 imxweb/projects/rmb/project.json delete mode 100644 imxweb/projects/rmb/src/lib/team-role/team-role.component.scss delete mode 100644 imxweb/projects/rmb/tslint.json create mode 100644 imxweb/projects/rms/.eslintrc.json create mode 100644 imxweb/projects/rms/project.json delete mode 100644 imxweb/projects/rms/tslint.json create mode 100644 imxweb/projects/rps/.eslintrc.json create mode 100644 imxweb/projects/rps/project.json delete mode 100644 imxweb/projects/rps/src/lib/report-button/parameter-sidesheet/parameter-sidesheet.component.scss create mode 100644 imxweb/projects/rps/src/lib/report-button/report-button-mail.component.html create mode 100644 imxweb/projects/rps/src/lib/report-button/report-button-mail.component.scss create mode 100644 imxweb/projects/rps/src/lib/report-button/report-button-mail.component.ts delete mode 100644 imxweb/projects/rps/tslint.json create mode 100644 imxweb/projects/sac/.eslintrc.json create mode 100644 imxweb/projects/sac/project.json delete mode 100644 imxweb/projects/sac/tslint.json create mode 100644 imxweb/projects/tsb/.eslintrc.json create mode 100644 imxweb/projects/tsb/project.json delete mode 100644 imxweb/projects/tsb/tslint.json create mode 100644 imxweb/projects/uci/.eslintrc.json create mode 100644 imxweb/projects/uci/ng-package.dynamic-ops.json create mode 100644 imxweb/projects/uci/project.json delete mode 100644 imxweb/projects/uci/tslint.json delete mode 100644 imxweb/shared/assets/variables.scss create mode 100644 imxweb/shared/scss/base/colors.scss create mode 100644 imxweb/shared/scss/base/fonts.scss create mode 100644 imxweb/shared/scss/base/margins-paddings.scss create mode 100644 imxweb/shared/scss/base/mixins.scss rename imxweb/shared/scss/{ => base}/theme.scss (99%) create mode 100644 imxweb/shared/scss/base/variables.scss delete mode 100644 imxweb/shared/scss/common-table.scss create mode 100644 imxweb/shared/scss/common/captcha-login.scss create mode 100644 imxweb/shared/scss/common/mitigating-controls.scss create mode 100644 imxweb/shared/scss/common/select.scss create mode 100644 imxweb/shared/scss/components/alert.scss create mode 100644 imxweb/shared/scss/components/autocomplete.scss create mode 100644 imxweb/shared/scss/components/badge.scss create mode 100644 imxweb/shared/scss/components/button-toggle.scss create mode 100644 imxweb/shared/scss/components/button.scss create mode 100644 imxweb/shared/scss/components/card.scss create mode 100644 imxweb/shared/scss/components/chart.scss create mode 100644 imxweb/shared/scss/components/checkbox.scss create mode 100644 imxweb/shared/scss/components/chips.scss create mode 100644 imxweb/shared/scss/components/clickable.scss create mode 100644 imxweb/shared/scss/components/data-source-toolbar.scss create mode 100644 imxweb/shared/scss/components/date-picker.scss create mode 100644 imxweb/shared/scss/components/dialog.scss create mode 100644 imxweb/shared/scss/components/divider.scss create mode 100644 imxweb/shared/scss/components/download.scss create mode 100644 imxweb/shared/scss/components/expansion-panel.scss create mode 100644 imxweb/shared/scss/components/form-field.scss create mode 100644 imxweb/shared/scss/components/icon.scss create mode 100644 imxweb/shared/scss/components/input.scss create mode 100644 imxweb/shared/scss/components/layout-grid.scss create mode 100644 imxweb/shared/scss/components/list.scss create mode 100644 imxweb/shared/scss/components/logo.scss create mode 100644 imxweb/shared/scss/components/masthead.scss create mode 100644 imxweb/shared/scss/components/menu.scss create mode 100644 imxweb/shared/scss/components/paginator.scss create mode 100644 imxweb/shared/scss/components/progress-bar.scss create mode 100644 imxweb/shared/scss/components/radio-button.scss create mode 100644 imxweb/shared/scss/components/ripple.scss create mode 100644 imxweb/shared/scss/components/scrollbar.scss create mode 100644 imxweb/shared/scss/components/search.scss create mode 100644 imxweb/shared/scss/components/select.scss create mode 100644 imxweb/shared/scss/components/side-navigation.scss create mode 100644 imxweb/shared/scss/components/sidesheet.scss create mode 100644 imxweb/shared/scss/components/slide-toggle.scss create mode 100644 imxweb/shared/scss/components/slider.scss create mode 100644 imxweb/shared/scss/components/snackbar.scss create mode 100644 imxweb/shared/scss/components/sort-header.scss create mode 100644 imxweb/shared/scss/components/spinner.scss create mode 100644 imxweb/shared/scss/components/stepper.scss create mode 100644 imxweb/shared/scss/components/table.scss create mode 100644 imxweb/shared/scss/components/tabs.scss create mode 100644 imxweb/shared/scss/components/theme-switcher.scss create mode 100644 imxweb/shared/scss/components/time-picker.scss create mode 100644 imxweb/shared/scss/components/toolbar.scss create mode 100644 imxweb/shared/scss/components/tooltip.scss create mode 100644 imxweb/shared/scss/components/top-navigation.scss create mode 100644 imxweb/shared/scss/components/tree.scss delete mode 100644 imxweb/shared/scss/side-navigation.scss rename imxweb/shared/{assets => scss}/styles.scss (64%) delete mode 100644 imxweb/tslint.json diff --git a/.commit b/.commit new file mode 100644 index 000000000..e601f31e4 --- /dev/null +++ b/.commit @@ -0,0 +1 @@ +e07ff3f1d48ed0b410f75a7e2e1ccd6491589055 diff --git a/imxweb/.eslintignore b/imxweb/.eslintignore new file mode 100644 index 000000000..3c3629e64 --- /dev/null +++ b/imxweb/.eslintignore @@ -0,0 +1 @@ +node_modules diff --git a/imxweb/.eslintrc.json b/imxweb/.eslintrc.json new file mode 100644 index 000000000..9f0f9060d --- /dev/null +++ b/imxweb/.eslintrc.json @@ -0,0 +1,64 @@ +{ + "root": true, + "ignorePatterns": ["projects/**/*"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": ["tsconfig.json"], + "createDefaultProgram": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@angular-eslint/recommended", + "plugin:prettier/recommended" + ], + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "imx", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "imx", + "style": "kebab-case" + } + ], + "max-classes-per-file": ["error", { "ignoreExpressions": true, "max": 1 }], + "no-bitwise": ["error"], + "prettier/prettier": [ + "error", + { + "endOfLine": "auto" + } + ] + } + }, + { + "files": ["*.html"], + "extends": ["plugin:@angular-eslint/template/recommended", "plugin:prettier/recommended"], + "rules": { + "@angular-eslint/template/accessibility-valid-aria": ["error"] + } + }, + { + "files": ["*.ts", "*.tsx"], + "extends": ["plugin:@nx/typescript"], + "rules": {} + }, + { + "files": ["*.spec.ts"], + "env": { + "jest": true + }, + "rules": {} + } + ] +} diff --git a/imxweb/.gitignore b/imxweb/.gitignore index 81c0b3a54..4a322fb8e 100644 --- a/imxweb/.gitignore +++ b/imxweb/.gitignore @@ -27,7 +27,7 @@ speed-measure-plugin.json # IDE - VSCode .vscode/* -!.vscode/settings.json +.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json @@ -54,3 +54,5 @@ documentation !.gitkeep debug.log .angular +.nx +*.env diff --git a/imxweb/.npmrc b/imxweb/.npmrc index cafe685a1..a702b5875 100644 --- a/imxweb/.npmrc +++ b/imxweb/.npmrc @@ -1 +1,3 @@ -package-lock=true +@imx-modules:registry=https://pkgs.dev.azure.com/1id/_packaging/OneIdentity/npm/registry/ +@elemental-ui:registry=https://pkgs.dev.azure.com/1id/_packaging/OneIdentity/npm/registry/ +# always-auth=true diff --git a/imxweb/.prettierignore b/imxweb/.prettierignore new file mode 100644 index 000000000..103bd516d --- /dev/null +++ b/imxweb/.prettierignore @@ -0,0 +1,5 @@ +# Add files here to ignore them from prettier formatting +/dist +/coverage +/.nx/cache +.angular diff --git a/imxweb/.prettierrc.json b/imxweb/.prettierrc.json new file mode 100644 index 000000000..05fd50599 --- /dev/null +++ b/imxweb/.prettierrc.json @@ -0,0 +1,7 @@ +{ + "trailingComma": "all", + "tabWidth": 2, + "semi": true, + "printWidth": 140, + "singleQuote": true +} \ No newline at end of file diff --git a/imxweb/.vscode/launch.json b/imxweb/.vscode/launch.json index f47b5aa84..3ab226a61 100644 --- a/imxweb/.vscode/launch.json +++ b/imxweb/.vscode/launch.json @@ -25,21 +25,21 @@ "request": "launch", "type": "pwa-chrome", "url": "http://localhost:4200/", - "webRoot": "${workspaceFolder}", + "webRoot": "${workspaceFolder}" }, { "name": "QER App Portal (Chrome)", "request": "launch", "type": "pwa-chrome", "url": "http://localhost:4200/", - "webRoot": "${workspaceFolder}", + "webRoot": "${workspaceFolder}" }, { "name": "QER App Portal (Chrome - SSL)", "request": "launch", "type": "pwa-chrome", "url": "https://localhost:4200/", - "webRoot": "${workspaceFolder}", + "webRoot": "${workspaceFolder}" }, { "name": "QER App Portal (Chrome, API Server)", @@ -91,7 +91,6 @@ "webpack:///ng://qbm/lib/*": "${workspaceFolder}/projects/qbm/src/lib/*", "webpack:///ng://qer/lib/*": "${workspaceFolder}/projects/qer/src/lib/*" } - }, - + } ] } diff --git a/imxweb/.vscode/settings.json b/imxweb/.vscode/settings.json index 4aad9f8c8..7c221c22c 100644 --- a/imxweb/.vscode/settings.json +++ b/imxweb/.vscode/settings.json @@ -1,8 +1,24 @@ { - "explorer.compactFolders": false, - "editor.tabSize": 2, - "git.ignoreLimitWarning": true, - "cSpell.words": [ - "requestable" - ] -} \ No newline at end of file + "explorer.compactFolders": false, + "editor.tabSize": 2, + "git.ignoreLimitWarning": true, + "editor.wordWrapColumn": 140, + "editor.wordWrap": "wordWrapColumn", + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit" + }, + "typescript.updateImportsOnFileMove.enabled": "always", + "eslint.options": { + "extensions": [".ts", ".html"] + }, + "eslint.validate": ["javascript", "typescript", "html"], + "eslint.format.enable": true, + "[html]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + } +} diff --git a/imxweb/angular.json b/imxweb/angular.json index dd712a936..c039f325a 100644 --- a/imxweb/angular.json +++ b/imxweb/angular.json @@ -40,11 +40,10 @@ "options": { "stylePreprocessorOptions": { "includePaths": [ - "./shared/assets", - "./shared/scss", "./node_modules", "./node_modules/@elemental-ui/cadence-icon", - "./node_modules/@elemental-ui/core" + "./node_modules/@elemental-ui/core", + "./shared/scss" ] }, "main": "projects/qbm/src/test.ts", @@ -53,10 +52,9 @@ } }, "lint": { - "builder": "@angular-devkit/build-angular:tslint", + "builder": "@angular-eslint/builder:lint", "options": { - "tsConfig": ["projects/qbm/tsconfig.lib.json", "projects/qbm/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + "lintFilePatterns": ["projects/qbm/**/*.ts", "projects/qbm/**/*.html"] } } } @@ -98,11 +96,10 @@ "options": { "stylePreprocessorOptions": { "includePaths": [ - "./shared/assets", - "./shared/scss", "./node_modules", "./node_modules/@elemental-ui/cadence-icon", - "./node_modules/@elemental-ui/core" + "./node_modules/@elemental-ui/core", + "./shared/scss" ] }, "main": "projects/qer/src/test.ts", @@ -111,10 +108,9 @@ } }, "lint": { - "builder": "@angular-devkit/build-angular:tslint", + "builder": "@angular-eslint/builder:lint", "options": { - "tsConfig": ["projects/qer/tsconfig.lib.json", "projects/qer/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + "lintFilePatterns": ["projects/qer/**/*.ts", "projects/qer/**/*.html"] } } } @@ -156,20 +152,18 @@ "karmaConfig": "projects/apc/karma.conf.js", "stylePreprocessorOptions": { "includePaths": [ - "./shared/assets", - "./shared/scss", "./node_modules", "./node_modules/@elemental-ui/cadence-icon", - "./node_modules/@elemental-ui/core" + "./node_modules/@elemental-ui/core", + "./shared/scss" ] } } }, "lint": { - "builder": "@angular-devkit/build-angular:tslint", + "builder": "@angular-eslint/builder:lint", "options": { - "tsConfig": ["projects/apc/tsconfig.lib.json", "projects/apc/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + "lintFilePatterns": ["projects/apc/**/*.ts", "projects/apc/**/*.html"] } } } @@ -201,6 +195,9 @@ "dynamic": { "project": "projects/att/ng-package.dynamic.json" }, + "dynamic-pwd": { + "project": "projects/att/ng-package.dynamic-pwd.json" + }, "remote-dev": { "tsConfig": "projects/att/tsconfig.lib.json" }, @@ -217,20 +214,18 @@ "karmaConfig": "projects/att/karma.conf.js", "stylePreprocessorOptions": { "includePaths": [ - "./shared/assets", - "./shared/scss", "./node_modules", "./node_modules/@elemental-ui/cadence-icon", - "./node_modules/@elemental-ui/core" + "./node_modules/@elemental-ui/core", + "./shared/scss" ] } } }, "lint": { - "builder": "@angular-devkit/build-angular:tslint", + "builder": "@angular-eslint/builder:lint", "options": { - "tsConfig": ["projects/att/tsconfig.lib.json", "projects/att/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + "lintFilePatterns": ["projects/att/**/*.ts", "projects/att/**/*.html"] } } } @@ -279,20 +274,18 @@ "codeCoverageExclude": ["projects/o3t/src/**/test/*"], "stylePreprocessorOptions": { "includePaths": [ - "./shared/assets", - "./shared/scss", "./node_modules", "./node_modules/@elemental-ui/cadence-icon", - "./node_modules/@elemental-ui/core" + "./node_modules/@elemental-ui/core", + "./shared/scss" ] } } }, "lint": { - "builder": "@angular-devkit/build-angular:tslint", + "builder": "@angular-eslint/builder:lint", "options": { - "tsConfig": ["projects/aad/tsconfig.lib.json", "projects/aad/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + "lintFilePatterns": ["projects/aad/**/*.ts", "projects/aad/**/*.html"] } } } @@ -340,20 +333,18 @@ "karmaConfig": "projects/cpl/karma.conf.js", "stylePreprocessorOptions": { "includePaths": [ - "./shared/assets", - "./shared/scss", "./node_modules", "./node_modules/@elemental-ui/cadence-icon", - "./node_modules/@elemental-ui/core" + "./node_modules/@elemental-ui/core", + "./shared/scss" ] } } }, "lint": { - "builder": "@angular-devkit/build-angular:tslint", + "builder": "@angular-eslint/builder:lint", "options": { - "tsConfig": ["projects/cpl/tsconfig.lib.json", "projects/cpl/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + "lintFilePatterns": ["projects/cpl/**/*.ts", "projects/cpl/**/*.html"] } } } @@ -401,20 +392,18 @@ "karmaConfig": "projects/dpr/karma.conf.js", "stylePreprocessorOptions": { "includePaths": [ - "./shared/assets", - "./shared/scss", "./node_modules", "./node_modules/@elemental-ui/cadence-icon", - "./node_modules/@elemental-ui/core" + "./node_modules/@elemental-ui/core", + "./shared/scss" ] } } }, "lint": { - "builder": "@angular-devkit/build-angular:tslint", + "builder": "@angular-eslint/builder:lint", "options": { - "tsConfig": ["projects/dpr/tsconfig.lib.json", "projects/dpr/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + "lintFilePatterns": ["projects/dpr/**/*.ts", "projects/dpr/**/*.html"] } } } @@ -463,82 +452,18 @@ "codeCoverageExclude": ["projects/hds/src/**/test/*"], "stylePreprocessorOptions": { "includePaths": [ - "./shared/assets", - "./shared/scss", - "./node_modules", - "./node_modules/@elemental-ui/cadence-icon", - "./node_modules/@elemental-ui/core" - ] - } - } - }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": ["projects/hds/tsconfig.lib.json", "projects/hds/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] - } - } - } - }, - "o3t": { - "root": "projects/o3t", - "sourceRoot": "projects/o3t/src", - "projectType": "library", - "schematics": { - "@schematics/angular:component": { - "style": "scss" - } - }, - "prefix": "imx", - "architect": { - "build": { - "builder": "@angular-devkit/build-angular:ng-packagr", - "options": { - "tsConfig": "projects/o3t/tsconfig.lib.json", - "project": "projects/o3t/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "projects/o3t/tsconfig.lib.prod.json" - }, - "development": { - "tsConfig": "projects/o3t/tsconfig.lib.json" - }, - "dynamic": { - "project": "projects/o3t/ng-package.dynamic.json" - }, - "remote-dev": { - "tsConfig": "projects/o3t/tsconfig.lib.json" - }, - "remote-qs": { - "tsConfig": "projects/o3t/tsconfig.lib.json" - } - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "projects/o3t/src/test.ts", - "tsConfig": "projects/o3t/tsconfig.spec.json", - "karmaConfig": "projects/o3t/karma.conf.js", - "codeCoverageExclude": ["projects/o3t/src/**/test/*"], - "stylePreprocessorOptions": { - "includePaths": [ - "./shared/assets", - "./shared/scss", "./node_modules", "./node_modules/@elemental-ui/cadence-icon", - "./node_modules/@elemental-ui/core" + "./node_modules/@elemental-ui/core", + "./shared/scss" ] } } }, "lint": { - "builder": "@angular-devkit/build-angular:tslint", + "builder": "@angular-eslint/builder:lint", "options": { - "tsConfig": ["projects/o3t/tsconfig.lib.json", "projects/o3t/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + "lintFilePatterns": ["projects/hds/**/*.ts", "projects/hds/**/*.html"] } } } @@ -587,20 +512,18 @@ "codeCoverageExclude": ["projects/olg/src/**/test/*"], "stylePreprocessorOptions": { "includePaths": [ - "./shared/assets", - "./shared/scss", "./node_modules", "./node_modules/@elemental-ui/cadence-icon", - "./node_modules/@elemental-ui/core" + "./node_modules/@elemental-ui/core", + "./shared/scss" ] } } }, "lint": { - "builder": "@angular-devkit/build-angular:tslint", + "builder": "@angular-eslint/builder:lint", "options": { - "tsConfig": ["projects/olg/tsconfig.lib.json", "projects/olg/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + "lintFilePatterns": ["projects/olg/**/*.ts", "projects/olg/**/*.html"] } } } @@ -648,20 +571,18 @@ "karmaConfig": "projects/pol/karma.conf.js", "stylePreprocessorOptions": { "includePaths": [ - "./shared/assets", - "./shared/scss", "./node_modules", "./node_modules/@elemental-ui/cadence-icon", - "./node_modules/@elemental-ui/core" + "./node_modules/@elemental-ui/core", + "./shared/scss" ] } } }, "lint": { - "builder": "@angular-devkit/build-angular:tslint", + "builder": "@angular-eslint/builder:lint", "options": { - "tsConfig": ["projects/pol/tsconfig.lib.json", "projects/pol/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + "lintFilePatterns": ["projects/pol/**/*.ts", "projects/pol/**/*.html"] } } } @@ -709,20 +630,18 @@ "karmaConfig": "projects/rmb/karma.conf.js", "stylePreprocessorOptions": { "includePaths": [ - "./shared/assets", - "./shared/scss", "./node_modules", "./node_modules/@elemental-ui/cadence-icon", - "./node_modules/@elemental-ui/core" + "./node_modules/@elemental-ui/core", + "./shared/scss" ] } } }, "lint": { - "builder": "@angular-devkit/build-angular:tslint", + "builder": "@angular-eslint/builder:lint", "options": { - "tsConfig": ["projects/rmb/tsconfig.lib.json", "projects/rmb/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + "lintFilePatterns": ["projects/rmb/**/*.ts", "projects/rmb/**/*.html"] } } } @@ -770,20 +689,18 @@ "karmaConfig": "projects/rms/karma.conf.js", "stylePreprocessorOptions": { "includePaths": [ - "./shared/assets", - "./shared/scss", "./node_modules", "./node_modules/@elemental-ui/cadence-icon", - "./node_modules/@elemental-ui/core" + "./node_modules/@elemental-ui/core", + "./shared/scss" ] } } }, "lint": { - "builder": "@angular-devkit/build-angular:tslint", + "builder": "@angular-eslint/builder:lint", "options": { - "tsConfig": ["projects/rms/tsconfig.lib.json", "projects/rms/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + "lintFilePatterns": ["projects/rms/**/*.ts", "projects/rms/**/*.html"] } } } @@ -831,20 +748,18 @@ "karmaConfig": "projects/rps/karma.conf.js", "stylePreprocessorOptions": { "includePaths": [ - "./shared/assets", - "./shared/scss", "./node_modules", "./node_modules/@elemental-ui/cadence-icon", - "./node_modules/@elemental-ui/core" + "./node_modules/@elemental-ui/core", + "./shared/scss" ] } } }, "lint": { - "builder": "@angular-devkit/build-angular:tslint", + "builder": "@angular-eslint/builder:lint", "options": { - "tsConfig": ["projects/rps/tsconfig.lib.json", "projects/rps/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + "lintFilePatterns": ["projects/rps/**/*.ts", "projects/rps/**/*.html"] } } } @@ -870,6 +785,9 @@ "production": { "tsConfig": "projects/sac/tsconfig.lib.prod.json" }, + "development": { + "tsConfig": "projects/sac/tsconfig.lib.json" + }, "dynamic": { "project": "projects/sac/ng-package.dynamic.json" } @@ -882,18 +800,14 @@ "tsConfig": "projects/sac/tsconfig.spec.json", "karmaConfig": "projects/sac/karma.conf.js", "stylePreprocessorOptions": { - "includePaths": [ - "./shared/assets", - "./shared/scss" - ] + "includePaths": ["./shared/assets", "./shared/scss"] } } }, "lint": { - "builder": "@angular-devkit/build-angular:tslint", + "builder": "@angular-eslint/builder:lint", "options": { - "tsConfig": ["projects/sac/tsconfig.lib.json", "projects/sac/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + "lintFilePatterns": ["projects/sac/**/*.ts", "projects/sac/**/*.html"] } } } @@ -941,20 +855,18 @@ "karmaConfig": "projects/tsb/karma.conf.js", "stylePreprocessorOptions": { "includePaths": [ - "./shared/assets", - "./shared/scss", "./node_modules", "./node_modules/@elemental-ui/cadence-icon", - "./node_modules/@elemental-ui/core" + "./node_modules/@elemental-ui/core", + "./shared/scss" ] } } }, "lint": { - "builder": "@angular-devkit/build-angular:tslint", + "builder": "@angular-eslint/builder:lint", "options": { - "tsConfig": ["projects/tsb/tsconfig.lib.json", "projects/tsb/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + "lintFilePatterns": ["projects/tsb/**/*.ts", "projects/tsb/**/*.html"] } } } @@ -978,8 +890,8 @@ "development": { "tsConfig": "projects/uci/tsconfig.lib.json" }, - "dynamic": { - "project": "projects/uci/ng-package.dynamic.json" + "dynamic-ops": { + "project": "projects/uci/ng-package.dynamic-ops.json" }, "remote-dev": { "tsConfig": "projects/uci/tsconfig.lib.json" @@ -997,26 +909,24 @@ "karmaConfig": "projects/uci/karma.conf.js", "stylePreprocessorOptions": { "includePaths": [ - "./shared/assets", - "./shared/scss", "./node_modules", "./node_modules/@elemental-ui/cadence-icon", - "./node_modules/@elemental-ui/core" + "./node_modules/@elemental-ui/core", + "./shared/scss" ] } } }, "lint": { - "builder": "@angular-devkit/build-angular:tslint", + "builder": "@angular-eslint/builder:lint", "options": { - "tsConfig": ["projects/uci/tsconfig.lib.json", "projects/uci/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + "lintFilePatterns": ["projects/uci/**/*.ts", "projects/uci/**/*.html"] } } } }, "qbm-app-landingpage": { - "root": "projects/qbm-app-landingpage/", + "root": "projects/qbm-app-landingpage", "sourceRoot": "projects/qbm-app-landingpage/src", "projectType": "application", "prefix": "imx", @@ -1027,13 +937,36 @@ }, "architect": { "build": { - "builder": "@angular-devkit/build-angular:browser", + "builder": "@angular-devkit/build-angular:application", "options": { "aot": true, - "outputPath": "dist/qbm-app-landingpage", + "outputPath": { + "base": "dist/qbm-app-landingpage", + "browser": "" + }, + "allowedCommonJsDependencies": [ + "lodash", + "highlight.js", + "file-saver", + "billboard.js", + "moment-timezone", + "core-js/fn/map", + "core-js/fn/set", + "core-js/fn/weak-map", + "core-js/fn/array/from", + "core-js/fn/object/assign", + "core-js/es/array/from", + "core-js/es/object/assign", + "core-js/es/map", + "core-js/es/set", + "core-js/es/weak-map", + "lodash.debounce", + "lodash.clamp", + "moment", + "@elemental-ui/cadence-icon/codepoints" + ], "index": "projects/qbm-app-landingpage/src/index.html", - "main": "projects/qbm-app-landingpage/src/main.ts", - "polyfills": "projects/qbm-app-landingpage/src/polyfills.ts", + "polyfills": ["projects/qbm-app-landingpage/src/polyfills.ts"], "tsConfig": "projects/qbm-app-landingpage/tsconfig.app.json", "assets": [ "projects/qbm-app-landingpage/src/assets", @@ -1050,25 +983,23 @@ } ], "styles": [ + "shared/scss/styles.scss", "projects/qbm-app-landingpage/src/styles.scss", - "shared/assets/styles.scss", - "shared/assets/variables.scss", "node_modules/swagger-ui-dist/swagger-ui.css" ], "stylePreprocessorOptions": { "includePaths": [ - "./shared/assets", - "./shared/scss", "./node_modules", "./node_modules/@elemental-ui/cadence-icon", - "./node_modules/@elemental-ui/core" + "./node_modules/@elemental-ui/core", + "./shared/scss" ] }, "scripts": [ - "node_modules/systemjs/dist/system.src.js", "node_modules/swagger-ui-dist/swagger-ui-bundle.js", "node_modules/swagger-ui-dist/swagger-ui-standalone-preset.js" - ] + ], + "browser": "projects/qbm-app-landingpage/src/main.ts" }, "configurations": { "production": { @@ -1081,11 +1012,9 @@ "optimization": true, "outputHashing": "all", "sourceMap": false, - "namedChunks": false, + "namedChunks": true, "aot": true, "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": true, "budgets": [ { "type": "initial", @@ -1099,9 +1028,7 @@ ] }, "development": { - "buildOptimizer": false, "optimization": false, - "vendorChunk": true, "extractLicenses": false, "sourceMap": true, "namedChunks": true @@ -1113,9 +1040,7 @@ "with": "../imxweb_envs/qbm-app-landingpage/environments/environment.remote-dev.ts" } ], - "buildOptimizer": false, "optimization": false, - "vendorChunk": true, "extractLicenses": false, "sourceMap": true, "namedChunks": true @@ -1127,9 +1052,7 @@ "with": "../imxweb_envs/qbm-app-landingpage/environments/environment.remote-qs.ts" } ], - "buildOptimizer": false, "optimization": false, - "vendorChunk": true, "extractLicenses": false, "sourceMap": true, "namedChunks": true @@ -1142,31 +1065,31 @@ "serve": { "builder": "@angular-devkit/build-angular:dev-server", "options": { - "browserTarget": "qbm-app-landingpage:build", - "disableHostCheck": true + "disableHostCheck": true, + "buildTarget": "qbm-app-landingpage:build" }, "configurations": { "production": { - "browserTarget": "qbm-app-landingpage:build:production" + "buildTarget": "qbm-app-landingpage:build:production" }, "development": { - "browserTarget": "qbm-app-landingpage:build:development" + "buildTarget": "qbm-app-landingpage:build:development" }, "remote-dev": { - "browserTarget": "qbm-app-landingpage:build:remote-dev" + "buildTarget": "qbm-app-landingpage:build:remote-dev" }, "remote-qs": { - "browserTarget": "qbm-app-landingpage:build:remote-qs" + "buildTarget": "qbm-app-landingpage:build:remote-qs" }, "es5": { - "browserTarget": "qbm-app-landingpage:build:es5" + "buildTarget": "qbm-app-landingpage:build:es5" } } }, "extract-i18n": { "builder": "@angular-devkit/build-angular:extract-i18n", "options": { - "browserTarget": "qbm-app-landingpage:build" + "buildTarget": "qbm-app-landingpage:build" } }, "test": { @@ -1186,29 +1109,27 @@ "output": "./assets" } ], - "styles": ["projects/qbm-app-landingpage/src/styles.scss", "shared/assets/styles.scss"], + "styles": ["projects/qbm-app-landingpage/src/styles.scss"], "stylePreprocessorOptions": { "includePaths": [ - "./shared/assets", - "./shared/scss", "./node_modules", "./node_modules/@elemental-ui/cadence-icon", - "./node_modules/@elemental-ui/core" + "./node_modules/@elemental-ui/core", + "./shared/scss" ] } } }, "lint": { - "builder": "@angular-devkit/build-angular:tslint", + "builder": "@angular-eslint/builder:lint", "options": { - "tsConfig": ["projects/qbm-app-landingpage/tsconfig.app.json", "projects/qbm-app-landingpage/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + "lintFilePatterns": ["projects/qbm-app-landingpage//**/*.ts", "projects/qbm-app-landingpage//**/*.html"] } } } }, "qer-app-operationssupport": { - "root": "projects/qer-app-operationssupport/", + "root": "projects/qer-app-operationssupport", "sourceRoot": "projects/qer-app-operationssupport/src", "projectType": "application", "prefix": "imx", @@ -1222,10 +1143,30 @@ "builder": "@angular-devkit/build-angular:browser", "options": { "aot": true, + "allowedCommonJsDependencies": [ + "lodash", + "highlight.js", + "file-saver", + "billboard.js", + "moment-timezone", + "core-js/fn/map", + "core-js/fn/set", + "core-js/fn/weak-map", + "core-js/fn/array/from", + "core-js/fn/object/assign", + "core-js/es/array/from", + "core-js/es/object/assign", + "core-js/es/map", + "core-js/es/set", + "core-js/es/weak-map", + "lodash.debounce", + "lodash.clamp", + "moment", + "@elemental-ui/cadence-icon/codepoints" + ], "outputPath": "dist/qer-app-operationssupport", "index": "projects/qer-app-operationssupport/src/index.html", - "main": "projects/qer-app-operationssupport/src/main.ts", - "polyfills": "projects/qer-app-operationssupport/src/polyfills.ts", + "polyfills": ["projects/qer-app-operationssupport/src/polyfills.ts"], "tsConfig": "projects/qer-app-operationssupport/tsconfig.app.json", "assets": [ "projects/qer-app-operationssupport/src/assets", @@ -1244,33 +1185,22 @@ "glob": "**/*", "input": "./node_modules/@elemental-ui/core/assets", "output": "./assets" - }, - { - "glob": "**/*", - "input": "./node_modules/systemjs-plugin-babel/", - "output": "./systemjs-plugin-babel" } ], "styles": [ "projects/qer-app-operationssupport/src/styles.scss", - "shared/assets/styles.scss", - "projects/qbm/src/lib/styles/imx-page-title.scss", - "shared/assets/variables.scss" + "shared/scss/styles.scss", + "projects/qbm/src/lib/styles/imx-page-title.scss" ], "stylePreprocessorOptions": { "includePaths": [ - "./shared/assets", - "./shared/scss", "./node_modules", "./node_modules/@elemental-ui/cadence-icon", - "./node_modules/@elemental-ui/core" + "./node_modules/@elemental-ui/core", + "./shared/scss" ] }, - "scripts": [ - "node_modules/systemjs/dist/system.src.js", - "node_modules/systemjs-plugin-babel/plugin-babel.js", - "node_modules/systemjs-plugin-babel/systemjs-babel-browser.js" - ] + "main": "projects/qer-app-operationssupport/src/main.ts" }, "configurations": { "production": { @@ -1283,11 +1213,9 @@ "optimization": true, "outputHashing": "all", "sourceMap": false, - "namedChunks": false, + "namedChunks": true, "aot": true, "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": true, "budgets": [ { "type": "initial", @@ -1301,9 +1229,7 @@ ] }, "development": { - "buildOptimizer": false, "optimization": false, - "vendorChunk": true, "extractLicenses": false, "sourceMap": true, "namedChunks": true @@ -1315,9 +1241,7 @@ "with": "../imxweb_envs/qer-app-operationssupport/environments/environment.remote-dev.ts" } ], - "buildOptimizer": false, "optimization": false, - "vendorChunk": true, "extractLicenses": false, "sourceMap": true, "namedChunks": true @@ -1329,9 +1253,7 @@ "with": "../imxweb_envs/qer-app-operationssupport/environments/environment.remote-qs.ts" } ], - "buildOptimizer": false, "optimization": false, - "vendorChunk": true, "extractLicenses": false, "sourceMap": true, "namedChunks": true @@ -1344,31 +1266,31 @@ "serve": { "builder": "@angular-devkit/build-angular:dev-server", "options": { - "browserTarget": "qer-app-operationssupport:build", - "disableHostCheck": true + "disableHostCheck": true, + "buildTarget": "qer-app-operationssupport:build" }, "configurations": { "production": { - "browserTarget": "qer-app-operationssupport:build:production" + "buildTarget": "qer-app-operationssupport:build:production" }, "development": { - "browserTarget": "qer-app-operationssupport:build:development" + "buildTarget": "qer-app-operationssupport:build:development" }, "remote-dev": { - "browserTarget": "qer-app-operationssupport:build:remote-dev" + "buildTarget": "qer-app-operationssupport:build:remote-dev" }, "remote-qs": { - "browserTarget": "qer-app-operationssupport:build:remote-dev" + "buildTarget": "qer-app-operationssupport:build:remote-dev" }, "es5": { - "browserTarget": "qer-app-operationssupport:build:es5" + "buildTarget": "qer-app-operationssupport:build:es5" } } }, "extract-i18n": { "builder": "@angular-devkit/build-angular:extract-i18n", "options": { - "browserTarget": "qer-app-operationssupport:build" + "buildTarget": "qer-app-operationssupport:build" } }, "test": { @@ -1378,17 +1300,15 @@ "polyfills": "projects/qer-app-operationssupport/src/polyfills.ts", "tsConfig": "projects/qer-app-operationssupport/tsconfig.spec.json", "karmaConfig": "projects/qer-app-operationssupport/karma.conf.js", - "styles": ["projects/qer-app-operationssupport/src/styles.scss", "shared/assets/styles.scss"], + "styles": ["projects/qer-app-operationssupport/src/styles.scss"], "stylePreprocessorOptions": { "includePaths": [ - "./shared/assets", - "./shared/scss", "./node_modules", "./node_modules/@elemental-ui/cadence-icon", - "./node_modules/@elemental-ui/core" + "./node_modules/@elemental-ui/core", + "./shared/scss" ] }, - "scripts": ["node_modules/systemjs/dist/system.src.js"], "assets": [ "projects/qer-app-operationssupport/src/assets", "projects/qer-app-operationssupport/src/appconfig.json", @@ -1401,16 +1321,15 @@ } }, "lint": { - "builder": "@angular-devkit/build-angular:tslint", + "builder": "@angular-eslint/builder:lint", "options": { - "tsConfig": ["projects/qer-app-operationssupport/tsconfig.app.json", "projects/qer-app-operationssupport/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + "lintFilePatterns": ["projects/qer-app-operationssupport//**/*.ts", "projects/qer-app-operationssupport//**/*.html"] } } } }, "qer-app-portal": { - "root": "projects/qer-app-portal/", + "root": "projects/qer-app-portal", "sourceRoot": "projects/qer-app-portal/src", "projectType": "application", "prefix": "imx", @@ -1424,10 +1343,30 @@ "builder": "@angular-devkit/build-angular:browser", "options": { "aot": true, + "allowedCommonJsDependencies": [ + "lodash", + "highlight.js", + "file-saver", + "billboard.js", + "moment-timezone", + "core-js/fn/map", + "core-js/fn/set", + "core-js/fn/weak-map", + "core-js/fn/array/from", + "core-js/fn/object/assign", + "core-js/es/array/from", + "core-js/es/object/assign", + "core-js/es/map", + "core-js/es/set", + "core-js/es/weak-map", + "lodash.debounce", + "lodash.clamp", + "moment", + "@elemental-ui/cadence-icon/codepoints" + ], "outputPath": "dist/qer-app-portal", "index": "projects/qer-app-portal/src/index.html", - "main": "projects/qer-app-portal/src/main.ts", - "polyfills": "projects/qer-app-portal/src/polyfills.ts", + "polyfills": ["projects/qer-app-portal/src/polyfills.ts"], "tsConfig": "projects/qer-app-portal/tsconfig.app.json", "assets": [ "projects/qer-app-portal/src/assets", @@ -1446,35 +1385,24 @@ "glob": "**/*", "input": "./node_modules/@elemental-ui/core/assets", "output": "./assets" - }, - { - "glob": "**/*", - "input": "./node_modules/systemjs-plugin-babel/", - "output": "./systemjs-plugin-babel" } ], "styles": [ + "shared/scss/styles.scss", "projects/qer-app-portal/src/styles.scss", "projects/qbm/src/lib/styles/imx-page-title.scss", "projects/qbm/src/lib/styles/data-explorer-common.scss", - "projects/qbm/src/lib/styles/data-explorer-details-common.scss", - "shared/assets/styles.scss", - "shared/assets/variables.scss" + "projects/qbm/src/lib/styles/data-explorer-details-common.scss" ], "stylePreprocessorOptions": { "includePaths": [ - "./shared/assets", - "./shared/scss", "./node_modules", "./node_modules/@elemental-ui/cadence-icon", - "./node_modules/@elemental-ui/core" + "./node_modules/@elemental-ui/core", + "./shared/scss" ] }, - "scripts": [ - "node_modules/systemjs/dist/system.src.js", - "node_modules/systemjs-plugin-babel/plugin-babel.js", - "node_modules/systemjs-plugin-babel/systemjs-babel-browser.js" - ] + "main": "projects/qer-app-portal/src/main.ts" }, "configurations": { "production": { @@ -1487,11 +1415,9 @@ "optimization": true, "outputHashing": "all", "sourceMap": false, - "namedChunks": false, + "namedChunks": true, "aot": true, "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": true, "budgets": [ { "type": "initial", @@ -1505,9 +1431,7 @@ ] }, "development": { - "buildOptimizer": false, "optimization": false, - "vendorChunk": true, "extractLicenses": false, "sourceMap": true, "namedChunks": true @@ -1519,9 +1443,7 @@ "with": "../imxweb_envs/qer-app-portal/environments/environment.remote-dev.ts" } ], - "buildOptimizer": false, "optimization": false, - "vendorChunk": true, "extractLicenses": false, "sourceMap": true, "namedChunks": true @@ -1533,9 +1455,7 @@ "with": "../imxweb_envs/qer-app-portal/environments/environment.remote-qs.ts" } ], - "buildOptimizer": false, "optimization": false, - "vendorChunk": true, "extractLicenses": false, "sourceMap": true, "namedChunks": true @@ -1548,30 +1468,30 @@ "serve": { "builder": "@angular-devkit/build-angular:dev-server", "options": { - "browserTarget": "qer-app-portal:build" + "buildTarget": "qer-app-portal:build" }, "configurations": { "production": { - "browserTarget": "qer-app-portal:build:production" + "buildTarget": "qer-app-portal:build:production" }, "development": { - "browserTarget": "qer-app-portal:build:development" + "buildTarget": "qer-app-portal:build:development" }, "remote-dev": { - "browserTarget": "qer-app-portal:build:remote-dev" + "buildTarget": "qer-app-portal:build:remote-dev" }, "remote-qs": { - "browserTarget": "qer-app-portal:build:remote-qs" + "buildTarget": "qer-app-portal:build:remote-qs" }, "es5": { - "browserTarget": "qer-app-portal:build:es5" + "buildTarget": "qer-app-portal:build:es5" } } }, "extract-i18n": { "builder": "@angular-devkit/build-angular:extract-i18n", "options": { - "browserTarget": "qer-app-portal:build" + "buildTarget": "qer-app-portal:build" } }, "test": { @@ -1584,22 +1504,19 @@ "styles": ["projects/qer-app-portal/src/styles.scss"], "stylePreprocessorOptions": { "includePaths": [ - "./shared/assets", - "./shared/scss", "./node_modules", "./node_modules/@elemental-ui/cadence-icon", - "./node_modules/@elemental-ui/core" + "./node_modules/@elemental-ui/core", + "./shared/scss" ] }, - "scripts": ["node_modules/systemjs/dist/system.src.js"], "assets": ["projects/qer-app-portal/src/appconfig.json", "projects/qer-app-portal/src/assets"] } }, "lint": { - "builder": "@angular-devkit/build-angular:tslint", + "builder": "@angular-eslint/builder:lint", "options": { - "tsConfig": ["projects/qer-app-portal/tsconfig.app.json", "projects/qer-app-portal/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + "lintFilePatterns": ["projects/qer-app-portal//**/*.ts", "projects/qer-app-portal//**/*.html"] } } } @@ -1633,8 +1550,7 @@ }, "remote-dev": { "tsConfig": "projects/aob/tsconfig.lib.json" - } - , + }, "remote-qs": { "tsConfig": "projects/aob/tsconfig.lib.json" } @@ -1648,20 +1564,18 @@ "karmaConfig": "projects/aob/karma.conf.js", "stylePreprocessorOptions": { "includePaths": [ - "./shared/assets", - "./shared/scss", "./node_modules", "./node_modules/@elemental-ui/cadence-icon", - "./node_modules/@elemental-ui/core" + "./node_modules/@elemental-ui/core", + "./shared/scss" ] } } }, "lint": { - "builder": "@angular-devkit/build-angular:tslint", + "builder": "@angular-eslint/builder:lint", "options": { - "tsConfig": ["projects/aob/tsconfig.lib.json", "projects/aob/tsconfig.spec.json"], - "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + "lintFilePatterns": ["projects/aob/**/*.ts", "projects/aob/**/*.html"] } } } @@ -1680,10 +1594,30 @@ "build": { "builder": "@angular-devkit/build-angular:browser", "options": { + "allowedCommonJsDependencies": [ + "lodash", + "highlight.js", + "file-saver", + "billboard.js", + "moment-timezone", + "core-js/fn/map", + "core-js/fn/set", + "core-js/fn/weak-map", + "core-js/fn/array/from", + "core-js/fn/object/assign", + "core-js/es/array/from", + "core-js/es/object/assign", + "core-js/es/map", + "core-js/es/set", + "core-js/es/weak-map", + "lodash.debounce", + "lodash.clamp", + "moment", + "@elemental-ui/cadence-icon/codepoints" + ], "outputPath": "dist/qer-app-pwdportal", "index": "projects/qer-app-pwdportal/src/index.html", - "main": "projects/qer-app-pwdportal/src/main.ts", - "polyfills": "projects/qer-app-pwdportal/src/polyfills.ts", + "polyfills": ["projects/qer-app-pwdportal/src/polyfills.ts"], "tsConfig": "projects/qer-app-pwdportal/tsconfig.app.json", "aot": true, "assets": [ @@ -1703,33 +1637,22 @@ "glob": "**/*", "input": "./node_modules/@elemental-ui/core/assets", "output": "./assets" - }, - { - "glob": "**/*", - "input": "./node_modules/systemjs-plugin-babel/", - "output": "./systemjs-plugin-babel" } ], "styles": [ + "shared/scss/styles.scss", "projects/qer-app-pwdportal/src/styles.scss", - "projects/qbm/src/lib/styles/imx-page-title.scss", - "shared/assets/styles.scss", - "shared/assets/variables.scss" + "projects/qbm/src/lib/styles/imx-page-title.scss" ], "stylePreprocessorOptions": { "includePaths": [ - "./shared/assets", - "./shared/scss", "./node_modules", "./node_modules/@elemental-ui/cadence-icon", - "./node_modules/@elemental-ui/core" + "./node_modules/@elemental-ui/core", + "./shared/scss" ] }, - "scripts": [ - "node_modules/systemjs/dist/system.src.js", - "node_modules/systemjs-plugin-babel/plugin-babel.js", - "node_modules/systemjs-plugin-babel/systemjs-babel-browser.js" - ] + "main": "projects/qer-app-pwdportal/src/main.ts" }, "configurations": { "production": { @@ -1742,10 +1665,8 @@ "optimization": true, "outputHashing": "all", "sourceMap": false, - "namedChunks": false, + "namedChunks": true, "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": true, "budgets": [ { "type": "initial", @@ -1759,9 +1680,7 @@ ] }, "development": { - "buildOptimizer": false, "optimization": false, - "vendorChunk": true, "extractLicenses": false, "sourceMap": true, "namedChunks": true @@ -1773,9 +1692,7 @@ "with": "../imxweb_envs/qer-app-pwdportal/environments/environment.remote-dev.ts" } ], - "buildOptimizer": false, "optimization": false, - "vendorChunk": true, "extractLicenses": false, "sourceMap": true, "namedChunks": true @@ -1787,9 +1704,7 @@ "with": "../imxweb_envs/qer-app-pwdportal/environments/environment.remote-qs.ts" } ], - "buildOptimizer": false, "optimization": false, - "vendorChunk": true, "extractLicenses": false, "sourceMap": true, "namedChunks": true @@ -1802,30 +1717,30 @@ "serve": { "builder": "@angular-devkit/build-angular:dev-server", "options": { - "browserTarget": "qer-app-pwdportal:build" + "buildTarget": "qer-app-pwdportal:build" }, "configurations": { "production": { - "browserTarget": "qer-app-pwdportal:build:production" + "buildTarget": "qer-app-pwdportal:build:production" }, "development": { - "browserTarget": "qer-app-pwdportal:build:development" + "buildTarget": "qer-app-pwdportal:build:development" }, "remote-dev": { - "browserTarget": "qer-app-pwdportal:build:remote-dev" + "buildTarget": "qer-app-pwdportal:build:remote-dev" }, "remote-qs": { - "browserTarget": "qer-app-pwdportal:build:remote-qs" + "buildTarget": "qer-app-pwdportal:build:remote-qs" }, "es5": { - "browserTarget": "qer-app-pwdportal:build:es5" + "buildTarget": "qer-app-pwdportal:build:es5" } } }, "extract-i18n": { "builder": "@angular-devkit/build-angular:extract-i18n", "options": { - "browserTarget": "qer-app-pwdportal:build" + "buildTarget": "qer-app-pwdportal:build" } }, "test": { @@ -1837,6 +1752,60 @@ "karmaConfig": "projects/qer-app-pwdportal/karma.conf.js", "assets": ["projects/qer-app-pwdportal/src/appconfig.json", "projects/qer-app-pwdportal/src/assets"], "styles": ["projects/qer-app-pwdportal/src/styles.scss"], + "stylePreprocessorOptions": { + "includePaths": [ + "./node_modules", + "./node_modules/@elemental-ui/cadence-icon", + "./node_modules/@elemental-ui/core", + "./shared/scss" + ] + } + } + }, + "lint": { + "builder": "@angular-eslint/builder:lint", + "options": { + "lintFilePatterns": ["projects/qer-app-pwdportal/**/*.ts", "projects/qer-app-pwdportal/**/*.html"] + } + } + } + }, + "qam": { + "root": "projects/qam", + "sourceRoot": "projects/qam/src", + "projectType": "library", + "prefix": "imx", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:ng-packagr", + "options": { + "tsConfig": "projects/qam/tsconfig.lib.json", + "project": "projects/qam/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "projects/qam/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "projects/qam/tsconfig.lib.json" + }, + "dynamic": { + "project": "projects/qam/ng-package.dynamic.json" + }, + "remote-dev": { + "tsConfig": "projects/qam/tsconfig.lib.json" + }, + "remote-qs": { + "tsConfig": "projects/qam/tsconfig.lib.json" + } + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/qam/src/test.ts", + "tsConfig": "projects/qam/tsconfig.spec.json", + "karmaConfig": "projects/qam/karma.conf.js", "stylePreprocessorOptions": { "includePaths": [ "./shared/assets", @@ -1845,15 +1814,14 @@ "./node_modules/@elemental-ui/cadence-icon", "./node_modules/@elemental-ui/core" ] - }, - "scripts": ["node_modules/systemjs/dist/system.src.js"] + } } }, "lint": { "builder": "@angular-devkit/build-angular:tslint", "options": { - "tsConfig": ["projects/qer-app-pwdportal/tsconfig.app.json", "projects/qer-app-pwdportal/tsconfig.spec.json"], - "exclude": ["**/node_modules/**"] + "tsConfig": ["projects/uci/tsconfig.lib.json", "projects/uci/tsconfig.spec.json"], + "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] } } } @@ -1870,12 +1838,35 @@ }, "architect": { "build": { - "builder": "@angular-devkit/build-angular:browser", - "options": { - "outputPath": "dist/custom-app", + "builder": "@angular-devkit/build-angular:application", + "options": { + "outputPath": { + "base": "dist/custom-app", + "browser": "" + }, + "allowedCommonJsDependencies": [ + "lodash", + "highlight.js", + "file-saver", + "billboard.js", + "moment-timezone", + "core-js/fn/map", + "core-js/fn/set", + "core-js/fn/weak-map", + "core-js/fn/array/from", + "core-js/fn/object/assign", + "core-js/es/array/from", + "core-js/es/object/assign", + "core-js/es/map", + "core-js/es/set", + "core-js/es/weak-map", + "lodash.debounce", + "lodash.clamp", + "moment", + "@elemental-ui/cadence-icon/codepoints" + ], "index": "projects/custom-app/src/index.html", - "main": "projects/custom-app/src/main.ts", - "polyfills": "projects/custom-app/src/polyfills.ts", + "polyfills": ["projects/custom-app/src/polyfills.ts"], "tsConfig": "projects/custom-app/tsconfig.app.json", "aot": true, "assets": [ @@ -1895,33 +1886,18 @@ "glob": "**/*", "input": "./node_modules/@elemental-ui/core/assets", "output": "./assets" - }, - { - "glob": "**/*", - "input": "./node_modules/systemjs-plugin-babel/", - "output": "./systemjs-plugin-babel" } ], - "styles": [ - "projects/custom-app/src/styles.scss", - "projects/qbm/src/lib/styles/imx-page-title.scss", - "shared/assets/styles.scss", - "shared/assets/variables.scss" - ], + "styles": ["projects/custom-app/src/styles.scss", "projects/qbm/src/lib/styles/imx-page-title.scss"], "stylePreprocessorOptions": { "includePaths": [ - "./shared/assets", - "./shared/scss", "./node_modules", "./node_modules/@elemental-ui/cadence-icon", - "./node_modules/@elemental-ui/core" + "./node_modules/@elemental-ui/core", + "./shared/scss" ] }, - "scripts": [ - "node_modules/systemjs/dist/system.src.js", - "node_modules/systemjs-plugin-babel/plugin-babel.js", - "node_modules/systemjs-plugin-babel/systemjs-babel-browser.js" - ] + "browser": "projects/custom-app/src/main.ts" }, "configurations": { "production": { @@ -1936,8 +1912,6 @@ "sourceMap": false, "namedChunks": false, "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": true, "budgets": [ { "type": "initial", @@ -1951,9 +1925,7 @@ ] }, "development": { - "buildOptimizer": false, "optimization": false, - "vendorChunk": true, "extractLicenses": false, "sourceMap": true, "namedChunks": true @@ -1965,9 +1937,7 @@ "with": "../imxweb_envs/custom-app/environments/environment.remote-dev.ts" } ], - "buildOptimizer": false, "optimization": false, - "vendorChunk": true, "extractLicenses": false, "sourceMap": true, "namedChunks": true @@ -1979,9 +1949,7 @@ "with": "../imxweb_envs/custom-app/environments/environment.remote-qs.ts" } ], - "buildOptimizer": false, "optimization": false, - "vendorChunk": true, "extractLicenses": false, "sourceMap": true, "namedChunks": true @@ -1994,30 +1962,30 @@ "serve": { "builder": "@angular-devkit/build-angular:dev-server", "options": { - "browserTarget": "custom-app:build" + "buildTarget": "custom-app:build" }, "configurations": { "production": { - "browserTarget": "custom-app:build:production" + "buildTarget": "custom-app:build:production" }, "development": { - "browserTarget": "custom-app:build:development" + "buildTarget": "custom-app:build:development" }, "remote-dev": { - "browserTarget": "custom-app:build:remote-dev" + "buildTarget": "custom-app:build:remote-dev" }, "remote-qs": { - "browserTarget": "custom-app:build:remote-qs" + "buildTarget": "custom-app:build:remote-qs" }, "es5": { - "browserTarget": "custom-app:build:es5" + "buildTarget": "custom-app:build:es5" } } }, "extract-i18n": { "builder": "@angular-devkit/build-angular:extract-i18n", "options": { - "browserTarget": "custom-app:build" + "buildTarget": "custom-app:build" } }, "test": { @@ -2031,27 +1999,25 @@ "styles": ["projects/custom-app/src/styles.scss"], "stylePreprocessorOptions": { "includePaths": [ - "./shared/assets", - "./shared/scss", "./node_modules", "./node_modules/@elemental-ui/cadence-icon", - "./node_modules/@elemental-ui/core" + "./node_modules/@elemental-ui/core", + "./shared/scss" ] - }, - "scripts": ["node_modules/systemjs/dist/system.src.js"] + } } }, "lint": { - "builder": "@angular-devkit/build-angular:tslint", + "builder": "@angular-eslint/builder:lint", "options": { - "tsConfig": ["projects/custom-app/tsconfig.app.json", "projects/custom-app/tsconfig.spec.json"], - "exclude": ["**/node_modules/**"] + "lintFilePatterns": ["projects/custom-app/**/*.ts", "projects/custom-app/**/*.html"] } } } } }, "cli": { - "analytics": "406eb5c5-3afb-4dab-b878-0cb0ade8d2aa" + "analytics": "406eb5c5-3afb-4dab-b878-0cb0ade8d2aa", + "schematicCollections": ["@angular-eslint/schematics", "@angular-eslint/schematics"] } } diff --git a/imxweb/build-docs.js b/imxweb/build-docs.js new file mode 100644 index 000000000..c41f487ca --- /dev/null +++ b/imxweb/build-docs.js @@ -0,0 +1,40 @@ +// Script to generate documentation via compodoc. +// Use: node build-docs.js +// must correspond to the physical path i.e qer-app-portal + +if (process.argv.length < 3) +{ + console.error("No project(s) given. Please provide at least one project name as an argument."); + process.exit(1); +} + +const child_process = require('child_process'); +const fs = require('fs'); +const path = require('path'); +const projects = process.argv.slice(2); + +projects.forEach(project => generateDoc(project)); + +function generateDoc(project = "") { + console.log(`Generating documentation for ${project}...`); + let tsconfig = path.join('projects', project, 'tsconfig.lib.json'); + if (!fs.existsSync(tsconfig)) { + // If lib doesn't exist, try app + tsconfig = path.join('projects', project, 'tsconfig.app.json'); + } + + if (!fs.existsSync(tsconfig)) { + // Both app and lib don't exist, bad dir + console.error(`${project} doesn't have a tsconfig.lib.json or tsconfig.app.json. Moving on...\n`); + return; + } + + const outputpath = path.join('documentation', project); + const child = child_process.spawnSync('compodoc', ['-p', tsconfig, '-d', outputpath], { encoding: 'utf8', shell: true }); + if (child.error) { + console.log('ERROR: ', child.error); + return; + } + + console.log(`Finished - documentation available at ${outputpath}\n`); +} diff --git a/imxweb/build/imx-prepare-workspace/Build.proj b/imxweb/build/imx-prepare-workspace/Build.proj index 9e5805132..f262aebd8 100644 --- a/imxweb/build/imx-prepare-workspace/Build.proj +++ b/imxweb/build/imx-prepare-workspace/Build.proj @@ -6,13 +6,13 @@ imxweb\build\imx-prepare-workspace $(VI_PROJECTDIR)\imxweb $(VI_PROJECTDIR)\bin\npm - --scripts-prepend-node-path=true + --scripts-prepend-node-path=true - - - + + + - \ No newline at end of file + diff --git a/imxweb/build/nx/Build.proj b/imxweb/build/nx/Build.proj new file mode 100644 index 000000000..4b0902df7 --- /dev/null +++ b/imxweb/build/nx/Build.proj @@ -0,0 +1,41 @@ + + + + + + imxweb\build\nx + $(VI_PROJECTDIR)\imxweb + $(VI_PROJECTDIR)\Assemblies + $(VI_PROJECTDIR)\imxweb\node_modules\@elemental-ui + $(VI_PROJECTDIR)\bin\npm + $(VI_PROJECTDIR)\bin\npx + + + + + + + true + + + + + false + + + + + + + + + + + + + + + + + + diff --git a/imxweb/changes/changesFrom9.1.1To9.2.0.md b/imxweb/changes/changesFrom9.1.1To9.2.0.md deleted file mode 100644 index cc1664eb8..000000000 --- a/imxweb/changes/changesFrom9.1.1To9.2.0.md +++ /dev/null @@ -1,1575 +0,0 @@ -# apc -*PackageAdded* - - -# att `(39 changes)` -EditPolicyGroupSidesheetComponent (*Class added*) - -OpenSidesheetComponent (*Class added*) - -PolicyGroupListComponent (*Class added*) - -PolicyGroupModule (*Class added*) - -AttestationCasesComponent (*1 change*) - -| Type | Change | -| -------- | ------- | -| *Property added* | hasSampleData | - - -AttestationHistoryComponent (*7 changes*) - -| Type | Change | -| -------- | ------- | -| *Property added* | attestorFilter | -| *Property added* | busyService | -| *Method added* | deleteConfigById | -| *Property added* | selectable | -| *Method added* | updateConfig | -| *Method added* | viewAssignmentAnalysis | -| *Property added* | withAssignmentAnalysis | - - -AttestationHistoryService (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *Method added* | exportAttestation | -| *MemberMismatch* | getGroupInfo(parameters?:AttestationCaseLoadParameters):Promise; | - - -canSeeAttestationPolicies (*1 change*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | export declare function canSeeAttestationPolicies(~~groups:string[]):boolean;,~~**features:string[]):boolean;** | - - -EditMasterDataComponent (*9 changes*) - -| Type | Change | -| -------- | ------- | -| *Property added* | contextId | -| *Property added* | objectType | -| *Property added* | objectUid | -| *Property added* | policyEditor | -| *Method added* | updateReadOnlySchedule | -| *MemberMismatch* | addControl(evt:UntypedFormControl, columnName:string):void; | -| *MemberMismatch* | readonly formArray:UntypedFormArray; | -| *MemberMismatch* | readonly formGroup:UntypedFormGroup; | -| *MemberMismatch* | objectProperties:{ [key: string]: { cdr:ColumnDependentReference; formControl?:UntypedFormControl; }; }; | - - -PolicyListComponent (*4 changes*) - -| Type | Change | -| -------- | ------- | -| *Property added* | busyService | -| *Method added* | deleteConfigById | -| *Property added* | menuLoading | -| *Method added* | updateConfig | - - -PolicyService (*4 changes*) - -| Type | Change | -| -------- | ------- | -| *Method added* | exportPolicies | -| *MemberMismatch* | canSeeAllAttestations(preProps:string[],~~groups:string[]):boolean;,~~ **features:string[]):boolean;** | -| *MemberMismatch* | canSeeAttestations(preProps:string[],~~groups:string[]):boolean;,~~ **features:string[]):boolean;** | -| *MemberMismatch* | getGroupInfo(parameters?:{ by?: string; def?: string; } &CollectionLoadParameters):Promise; | - - -RunsComponent (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *Method deleted* | onHelperDismissed | -| *Property deleted* | showHelper | - - -RunsGridComponent (*5 changes*) - -| Type | Change | -| -------- | ------- | -| *Method added* | deleteConfigById | -| *Property added* | entitySchema | -| *Method added* | getExportMethod | -| *Method added* | updateConfig | -| *MemberMismatch* | attestationRunConfig:RunStatisticsConfig| undefined; | - - - - -# cpl `(1 change)` -MitigatingControlsRulesService (*Class added*) - - - -# dpr `(8 changes)` -OutstandingComponent (*8 changes*) - -| Type | Change | -| -------- | ------- | -| *Property added* | busy | -| *Property added* | busyLoadingTable | -| *Method added* | canDeleteAllSelected | -| *Property added* | LdsOutstandingText | -| *Property added* | loadingTableData | -| *Method added* | onShowAllData | -| *Property added* | showAllData | -| *Property added* | tableDataCount | - - - - -# olg `(27 changes)` -MfaFormControlComponent (*Class added*) - -MfaComponent (*26 changes*) - -| Type | Change | -| -------- | ------- | -| *Property deleted* | activatedFactor | -| *Method deleted* | activateFactor | -| *Property deleted* | authCDRs | -| *Property deleted* | authFactors | -| *Property deleted* | checkingOTP | -| *Property deleted* | checkingPoll | -| *Property deleted* | formArray | -| *Property deleted* | formGroup | -| *Property deleted* | isActivated | -| *Method deleted* | isAuthenticator | -| *Method deleted* | isCDRValid | -| *Method deleted* | isOTP | -| *Method deleted* | isProtect | -| *Method deleted* | ngAfterContentChecked | -| *Method deleted* | ngOnInit | -| *Method deleted* | onClose | -| *Method deleted* | resetState | -| *Property deleted* | showCDRs | -| *Method deleted* | verifyPoll | -| *Method deleted* | verifyWithOTP | -| *Method deleted* | verifyWithOTPAndDevice | -| *Property added* | authForm | -| *Property added* | busyService | -| *Property added* | hasAuthenticators | -| *Property added* | isLoading | -| *Method added* | ngOnDestroy | - - - - -# pol `(11 changes)` -PolicyViolationApproverGuardService (*Class deleted*) - -PolicyAdminGuardService (*Class added*) - -PermissionsService (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *Method deleted* | isExceptionApprover | -| *Method added* | isQERPolicyAdmin | - - -PolicyViolationsComponent (*7 changes*) - -| Type | Change | -| -------- | ------- | -| *Property added* | busyService | -| *Method added* | deleteConfigById | -| *Property added* | entitySchema | -| *Property added* | isMControlPerViolation | -| *Property added* | selectedCompanyPolicy | -| *Method added* | updateConfig | -| *MemberMismatch* | viewDetails(~~selectedRulesViolation:PolicyViolation):Promise;,~~**selectedPolicyViolation:PolicyViolation):Promise;** | - - - - -# qbm `(320 changes)` -ImxTimeline (*Class deleted*) - -ImxTimelineItem (*Interface deleted*) - -ImxTimelineOptions (*Interface deleted*) - -MultiLanguageCaptions (*Interface deleted*) - -ObjectSheetInterface (*Interface deleted*) - -TabControlHelper (*Class deleted*) - -TimelineLocales (*Class deleted*) - -Busy (*Interface added*) - -BusyIndicatorComponent (*Class added*) - -BusyIndicatorModule (*Class added*) - -BusyService (*Class added*) - -CacheService (*Class added*) - -CdrFactoryService (*Class added*) - -CdrSidesheetComponent (*Class added*) - -CdrSidesheetConfig (*Interface added*) - -ConnectionComponent (*Class added*) - -CustomThemeModule (*Class added*) - -CustomThemeService (*Class added*) - -DataModelWrapper (*Interface added*) - -DataSourceToolbarExportMethod (*Interface added*) - -DataSourceToolbarViewConfig (*Interface added*) - -DocChapter (*Interface added*) - -DocChapterService (*Class added*) - -DocDocument (*Interface added*) - -DSTViewConfig (*Interface added*) - -DynamicDataApiControls (*Class added*) - -DynamicDataSource (*Class added*) - -ExcludedColumnsPipe (*Class added*) - -FilterTreeComponent (*Class added*) - -FilterWizardComponent (*Class added*) - -FilterWizardModule (*Class added*) - -getParameterSubsetForGrouping (*Function added*) - -HELP_CONTEXTUAL (*Variable added*) - -HelpContextualComponent (*Class added*) - -HelpContextualModule (*Class added*) - -HelpContextualService (*Class added*) - -HelpContextualValues (*TypeAlias added*) - -InfoBadgeComponent (*Class added*) - -InfoButtonComponent (*Class added*) - -InfoModalDialogModule (*Class added*) - -isConfigDefault (*Function added*) - -isDefaultId (*Function added*) - -LogDetailsSidesheetComponent (*Class added*) - -ObjectHistoryGridviewComponent (*Class added*) - -ParameterizedTextService (*Class added*) - -SelectedElementsComponent (*Class added*) - -SelectedElementsModule (*Class added*) - -SideNavigationComponent (*Interface added*) - -SideNavigationExtension (*Interface added*) - -SideNavigationFactory (*TypeAlias added*) - -SideNavigationViewComponent (*Class added*) - -SideNavigationViewModule (*Class added*) - -SidenavTreeComponent (*Class added*) - -SidenavTreeModule (*Class added*) - -TempBillboardComponent (*Class added*) - -TempBillboardModule (*Class added*) - -TextToken (*Interface added*) - -TranslationEditorComponent (*Class added*) - -TreeNodeInfo (*Interface added*) - -ApiClientFetch (*1 change*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | processRequest(methodDescriptor:MethodDescriptor, **opts?:{ signal?:AbortSignal; }):Promise;** | - - -AppConfigService (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *Method deleted* | getImxConfig | -| *Property added* | initializedSubject | - - -BaseCdr (*4 changes*) - -| Type | Change | -| -------- | ------- | -| *Property added* | isReadOnlyColumn | -| *Property added* | minLength | -| *Property added* | minlengthSubject | -| *Method added* | updateMinLength | - - -BulkPropertyEditorComponent (*1 change*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | formGroup:UntypedFormGroup; | - - -CdrEditor (*1 change*) - -| Type | Change | -| -------- | ------- | -| *PropertySignature added* | updateRequested | - - -CdrEditorComponent (*5 changes*) - -| Type | Change | -| -------- | ------- | -| *Property added* | description | -| *Property added* | editor | -| *Property added* | infotitle | -| *Method added* | update | -| *MemberMismatch* | controlCreated:EventEmitter>; | - - -ClientPropertyForTableColumns (*1 change*) - -| Type | Change | -| -------- | ------- | -| *PropertySignature added* | untranslatedDisplay | - - -ColumnDependentReference (*3 changes*) - -| Type | Change | -| -------- | ------- | -| *PropertySignature deleted* | title | -| *PropertySignature added* | minlengthSubject | -| *PropertySignature added* | valueConstraint | - - -ColumnOptions (*3 changes*) - -| Type | Change | -| -------- | ------- | -| *Property added* | isDefaultConfig | -| *Property added* | viewConfig | -| *MemberMismatch* | currentViewSettings:DataModelViewConfig|DSTViewConfig; | - - -ConfirmationService (*1 change*) - -| Type | Change | -| -------- | ------- | -| *Method added* | confirmGeneral | - - -createGroupData (*1 change*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | export declare function createGroupData(dataModel:DataModel, getGroupInfo:(parameters:GroupInfoLoadParameters) =>Promise, excludedColumns?:string[]):DataSourceToolbarGroupData; | - - -DataSourcePaginatorComponent (*4 changes*) - -| Type | Change | -| -------- | ------- | -| *Constructor added* | | -| *Property added* | hidePaginator | -| *Property added* | isLoading | -| *Property added* | showFirstLastButtons | - - -DataSourceToolbarComponent (*66 changes*) - -| Type | Change | -| -------- | ------- | -| *Method deleted* | showSelectedItems | -| *Method added* | addSearchFilter | -| *Method added* | applyConfig | -| *Method added* | applyDynamicPropsAsSelectedFilters | -| *Method added* | applyGroupBy | -| *Method added* | applySort | -| *Property added* | ascendingSortControl | -| *Property added* | busyService | -| *Property added* | canApplySort | -| *Property added* | canClearSort | -| *Property added* | canExport | -| *Property added* | canSave | -| *Method added* | canShowFilterWizard | -| *Method added* | changeConfigName | -| *Method added* | clearSearch | -| *Method added* | clearSort | -| *Property added* | currentFilterDisplayData | -| *Property added* | currentSortColumn | -| *Property added* | deleteConfigById | -| *Property added* | descArg | -| *Property added* | disableFilterWizard | -| *Property added* | disableSearch | -| *Property added* | filterWizardExpression | -| *Method added* | findAndSelectSortColumn | -| *Method added* | getSelectedFilterFromName | -| *Property added* | hasSavedConfigs | -| *Property added* | hasSortFunction | -| *Property added* | hasSortOptions | -| *Method added* | isConfigDefault | -| *Method added* | isDefaultId | -| *Property added* | isDescending | -| *Property added* | isEnterDisabled | -| *Method added* | isRedundant | -| *Property added* | isRegex | -| *Property added* | isSortApplied | -| *Property added* | isSortDesc | -| *Property added* | isStandaloneToolbar | -| *Property added* | localSearchColumns | -| *Method added* | onSelectedFilterRemoved | -| *Method added* | openExportSidesheet | -| *Method added* | removeConfigIndex | -| *Method added* | removeFilterWizard | -| *Method added* | removeSearchTerm | -| *Method added* | saveConfig | -| *Property added* | savedConfigsTrigger | -| *Property added* | searchApi | -| *Property added* | searchResults$ | -| *Property added* | searchTerms | -| *Property added* | selectedFilterType | -| *Property added* | selectedSortControl | -| *Property added* | selection | -| *Method added* | selectSort | -| *Property added* | showClearFilterOption | -| *Method added* | showFilterWizard | -| *Property added* | sortControl | -| *Property added* | sortOptions | -| *Property added* | sortOptionsFilter | -| *Property added* | sortWarningThreshold | -| *Method added* | toggleDefaultConfig | -| *Method added* | toggleSort | -| *Property added* | updateConfig | -| *Property added* | useThemedStyle | -| *MemberMismatch* | clearFilters(**emit?:boolean):void;** | -| *MemberMismatch* | clearGroupedBy(**emit?:boolean):void;** | -| *MemberMismatch* | resetView(**emit?:boolean):Promise;** | -| *MemberMismatch* | searchControl:FormControl; | - - -DataSourceToolbarFilter (*1 change*) - -| Type | Change | -| -------- | ------- | -| *PropertySignature added* | Column | - - -DataSourceToolBarGroup (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *PropertySignature added* | navigationState | -| *MemberMismatch* | getData:(parameter?:CollectionLoadParameters) =>Promise; | - - -DataSourceToolbarGroupData (*1 change*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | currentGrouping?:{ display: string; getData: (parameter?:CollectionLoadParameters) =>Promise; navigationState?:CollectionLoadParameters; }; | - - -DataSourceToolbarSelectedFilter (*1 change*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | selectedOption:DataModelFilterOptionExtended; | - - -DataSourceToolbarSettings (*3 changes*) - -| Type | Change | -| -------- | ------- | -| *PropertySignature deleted* | identifierForSessionStore | -| *PropertySignature added* | exportMethod | -| *PropertySignature added* | viewConfig | - - -DataSourceWrapper (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | getDstSettings(parameters?:CollectionLoadParameters, **requestOpts?:ApiRequestOptions):Promise;** | -| *MemberMismatch* | getGroupDstSettings(parameters:CollectionLoadParameters, **requestOpts?:ApiRequestOptions):Promise;** | - - -DataTableComponent (*7 changes*) - -| Type | Change | -| -------- | ------- | -| *Method deleted* | onOpenSelectionDialog | -| *Property added* | debouncedHighlightRow | -| *Property added* | groupPaginatorInformation | -| *Property added* | isLoading | -| *Method added* | overallGroupingStateChanged | -| *Property added* | showGroupPaginator | -| *MemberMismatch* | highlightRow(entity:TypedEntity, **event?:MouseEvent):void;** | - - -DataTileBadge (*1 change*) - -| Type | Change | -| -------- | ------- | -| *PropertySignature added* | textColor | - - -DataTileComponent (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *Property deleted* | selectedHint | -| *Property added* | selected | - - -DataTilesComponent (*5 changes*) - -| Type | Change | -| -------- | ------- | -| *Constructor added* | | -| *Property added* | isLoading | -| *Method added* | onTileSelected | -| *Property added* | selected | -| *Property added* | selectedEntity | - - -DataTreeComponent (*14 changes*) - -| Type | Change | -| -------- | ------- | -| *Property deleted* | dialog | -| *Method deleted* | onOpenSelectionDialog | -| *Method added* | add | -| *Method added* | deleteNode | -| *Method added* | expandNode | -| *Method added* | getEntityById | -| *Method added* | hasChildren | -| *Method added* | isExpanded | -| *Property added* | isLoading | -| *Property added* | isNodeSelectable | -| *Method added* | onOpenSelectionSidesheet | -| *Property added* | sidesheet | -| *Property added* | treeRendered | -| *Method added* | updateNode | - - -DataTreeWrapperComponent (*10 changes*) - -| Type | Change | -| -------- | ------- | -| *Method added* | add | -| *Method added* | deleteNode | -| *Method added* | expandNode | -| *Method added* | getEntityById | -| *Method added* | hasChildren | -| *Property added* | hideSelection | -| *Method added* | isExpanded | -| *Property added* | isNodeSelectable | -| *Property added* | treeRendered | -| *Method added* | updateNode | - - -DateComponent (*3 changes*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | shadowDate:UntypedFormControl; | -| *MemberMismatch* | shadowText:UntypedFormControl; | -| *MemberMismatch* | shadowTime:UntypedFormControl; | - - -EditBinaryComponent (*1 change*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | readonly control:UntypedFormControl; | - - -EditBooleanComponent (*1 change*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | readonly control:UntypedFormControl; | - - -EditDateComponent (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *Property added* | updateRequested | -| *MemberMismatch* | readonly control:UntypedFormControl; | - - -EditDefaultComponent (*1 change*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | readonly control:UntypedFormControl; | - - -EditFkComponent (*3 changes*) - -| Type | Change | -| -------- | ------- | -| *Property added* | candidatesTotalCount | -| *Property added* | updateRequested | -| *MemberMismatch* | readonly control:UntypedFormControl; | - - -EditLimitedValueComponent (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *Method added* | removeAssignment | -| *MemberMismatch* | readonly control:UntypedFormControl; | - - -EditMultiLimitedValueComponent (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *Property added* | updateRequested | -| *MemberMismatch* | control:UntypedFormArray; | - - -EditMultilineComponent (*1 change*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | readonly control:UntypedFormControl; | - - -EditMultiValueComponent (*1 change*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | readonly control:UntypedFormControl; | - - -EditNumberComponent (*1 change*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | readonly control:UntypedFormControl; | - - -EntityColumnContainer (*1 change*) - -| Type | Change | -| -------- | ------- | -| *Property deleted* | title | - - -EntityColumnEditorComponent (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *Property added* | editor | -| *Method added* | update | - - -EntitySelectComponent (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | readonly control:UntypedFormControl; | -| *MemberMismatch* | controlCreated:EventEmitter>; | - - -FkAdvancedPickerComponent (*3 changes*) - -| Type | Change | -| -------- | ------- | -| *Method deleted* | showSelected | -| *Property added* | selectedEntityCandidates | -| *Property added* | tableNames | - - -FkCandidatesComponent (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *Property added* | busyService | -| *Property added* | entitySchema | - - -FkSelectorComponent (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *Property added* | busyService | -| *Method added* | setSelectedClass | - - -GlobalErrorHandler (*1 change*) - -| Type | Change | -| -------- | ------- | -| *Method added* | resetMessage | - - -HyperviewComponent (*17 changes*) - -| Type | Change | -| -------- | ------- | -| *Method deleted* | getConnectors | -| *Method deleted* | getHeight | -| *Method deleted* | getWidth | -| *Property added* | _shapes | -| *Property added* | containerElem | -| *Method added* | ngAfterViewInit | -| *Method added* | ngOnDestroy | -| *Method added* | onReset | -| *Method added* | onViewChanged | -| *Property added* | rootElem | -| *Method added* | setupLayout | -| *Property added* | shapeList | -| *Property added* | showResetButton | -| *Property added* | viewChanged | -| *MemberMismatch* | connectors:Connector[]; | -| *MemberMismatch* | elRef:ElementRef; | -| *MemberMismatch* | set shapes(value:ShapeData[]); | - - -ImxTranslationProviderService (*4 changes*) - -| Type | Change | -| -------- | ------- | -| *Property deleted* | MultiLanguageCaptions | -| *Property added* | CultureFormat | -| *Method added* | GetCultures | -| *MemberMismatch* | init(culture?:string, **cultureFormat?:string):Promise;** | - - -ISessionState (*1 change*) - -| Type | Change | -| -------- | ------- | -| *PropertySignature added* | cultureFormat | - - -LineChartOptions (*1 change*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | additionalLines:GridLineOptions[]; | - - -LoginComponent (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *Method added* | createNewAccount | -| *Property added* | newUserConfigProvider | - - -MastHeadComponent (*6 changes*) - -| Type | Change | -| -------- | ------- | -| *Property added* | extensions | -| *Method added* | getDynamicExtensions | -| *Property added* | menuItems | -| *Method added* | openConnection | -| *Property added* | productName | -| *Method added* | showExtension | - - -MastHeadService (*1 change*) - -| Type | Change | -| -------- | ------- | -| *Method added* | getConnectionData | - - -MenuService (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *Constructor added* | | -| *MemberMismatch* | getMenuItems(preProps:string[],~~**groups:string[]**,~~ **features:string[]**, allowEmpty?:boolean, projectConfig?:ProjectConfig, groups?:string[]):Promise; | - - -MessageParameter (*1 change*) - -| Type | Change | -| -------- | ------- | -| *PropertySignature added* | Parameter | - - -MultiSelectFormcontrolComponent (*1 change*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | readonly searchControl:UntypedFormControl; | - - -ObjectHistoryApiService (*1 change*) - -| Type | Change | -| -------- | ------- | -| *Method added* | getHistoryComparisonData | - - -ObjectHistoryComponent (*24 changes*) - -| Type | Change | -| -------- | ------- | -| *Property deleted* | effectiveViewMode | -| *Property deleted* | viewMode | -| *Property deleted* | viewModeTimeline | -| *Property added* | compareDateFormControl | -| *Property added* | historyComparisonData | -| *Property added* | lookIcons | -| *Property added* | looks | -| *Method added* | ngOnDestroy | -| *Method added* | onLookSelectionChanged | -| *Method added* | onViewModeChange | -| *Property added* | selectedLook | -| *Method added* | setTimeline | -| *Property added* | stateOverviewItems | -| *Property added* | timelineFrom | -| *Property added* | timelineFromDateFormControl | -| *Property added* | timelineFromString | -| *Property added* | timelineFromTimeFormControl | -| *Property added* | timelineTo | -| *Property added* | timelineToDateFormControl | -| *Property added* | timelineToString | -| *Property added* | timelineToTimeFormControl | -| *Property added* | viewModeStateComparison | -| *Property added* | viewModeStateOverview | -| *MemberMismatch* | refresh(**fetchRemote:boolean):Promise;** | - - -OrderedListComponent (*5 changes*) - -| Type | Change | -| -------- | ------- | -| *Method deleted* | ngOnInit | -| *Property added* | isReadOnly | -| *Property added* | placeholder | -| *Property added* | theme | -| *Property added* | valueChanged | - - -ParameterizedTextComponent (*1 change*) - -| Type | Change | -| -------- | ------- | -| *Property added* | textReady | - - -SearchBarComponent (*1 change*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | tables:UntypedFormControl; | - - -SelectComponent (*1 change*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | readonly chipListCtrl:UntypedFormControl; | - - -SessionState (*1 change*) - -| Type | Change | -| -------- | ------- | -| *Property added* | cultureFormat | - - -SqlWizardApiService (*1 change*) - -| Type | Change | -| -------- | ------- | -| *Property added* | implemented | - - -SqlWizardComponent (*10 changes*) - -| Type | Change | -| -------- | ------- | -| *Property added* | andConditionLabel | -| *Property added* | expressionList | -| *Property added* | isImplemented | -| *Method added* | logOpText | -| *Method added* | ngAfterViewInit | -| *Method added* | onOperatorChanged | -| *Property added* | orConditionLabel | -| *Method added* | removeAllExpressions | -| *MemberMismatch* | emitChanges():Promise; | -| *MemberMismatch* | ngOnChanges(changes:SimpleChanges):Promise; | - - -SystemInfoService (*1 change*) - -| Type | Change | -| -------- | ------- | -| *Method added* | getImxConfig | - - -TileComponent (*1 change*) - -| Type | Change | -| -------- | ------- | -| *Property added* | loadingState | - - -TreeDatabase (*3 changes*) - -| Type | Change | -| -------- | ------- | -| *Property added* | busyService | -| *Method added* | createNode | -| *Method added* | getId | - - -TypedEntitySelectComponent (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | readonly control:UntypedFormControl; | -| *MemberMismatch* | readonly controlCreated:EventEmitter; | - - - - -# qer `(225 changes)` -DefaultSheetComponent (*Class deleted*) - -IDataExplorerComponent (*Interface deleted*) - -ObjectSheetComponent (*Class deleted*) - -ObjectSheetModule (*Class deleted*) - -ObjectsheetPersonModule (*Class deleted*) - -ObjectSheetService (*Class deleted*) - -additionalColumnsForServiceItemsKey (*Variable added*) - -ApprovalWorkFlowModule (*Class added*) - -ArchivedRequestsComponent (*Class added*) - -ArchivedRequestsModule (*Class added*) - -AuthenticationFactors (*Interface added*) - -ChartInfoTyped (*Class added*) - -DashboardService (*Class added*) - -DataManagementService (*Class added*) - -DecisionStepSevice (*Class added*) - -isAuditor (*Function added*) - -isRoleAdmin (*Function added*) - -isRoleStatistics (*Function added*) - -MyResponsibilitiesRegistryService (*Class added*) - -MyResponsibilitiesViewModule (*Class added*) - -NewRequestComponent (*Class added*) - -NewRequestModule (*Class added*) - -NewRequestSelectionService (*Class added*) - -NotificationRegistryService (*Class added*) - -NotificationStreamService (*Class added*) - -ObjectHyperviewComponent (*Class added*) - -ObjectHyperviewModule (*Class added*) - -ObjectHyperviewService (*Class added*) - -OpSupportUserService (*Class added*) - -ParameterContainer (*Class added*) - -PasswordQuestionsComponent (*Class added*) - -RecommendationSidesheetComponent (*Class added*) - -RelatedApplicationsModule (*Class added*) - -RequestHistoryFilterComponent (*Class added*) - -RequestTableComponent (*Class added*) - -ResourcesModule (*Class added*) - -RiskConfigComponent (*Class added*) - -RiskConfigModule (*Class added*) - -RoleDetailComponent (*Class added*) - -RoleEntitlementActionService (*Class added*) - -RoleRecommendationResultItem (*Class added*) - -RoleRecommendationsComponent (*Class added*) - -SettingsComponent (*Class added*) - -StatisticsForObjectsComponent (*Class added*) - -StatisticsModule (*Class added*) - -TeamResponsibilitiesModule (*Class added*) - -TermsOfUseItem (*Interface added*) - -TermsOfUseService (*Class added*) - -TermsOfUseViewerComponent (*Class added*) - -UserProcessComponent (*Class added*) - -UserProcessModule (*Class added*) - -ViewConfigService (*Class added*) - -ViewDevicesComponent (*Class added*) - -ViewDevicesModule (*Class added*) - -ViewDevicesSidesheetComponent (*Class added*) - -AddressbookComponent (*1 change*) - -| Type | Change | -| -------- | ------- | -| *Property added* | busyService | - - -BadgeTileComponent (*1 change*) - -| Type | Change | -| -------- | ------- | -| *Property added* | loadingState | - - -BaseTreeEntitlement (*1 change*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | getCollection(id:string, navigationState?:CollectionLoadParameters, **objectKeyForFiltering?:string):Promise>; | - - -DataExplorerIdentitiesComponent (*12 changes*) - -| Type | Change | -| -------- | ------- | -| *Property deleted* | downloadOptions | -| *Property added* | busyService | -| *Property added* | contextId | -| *Method added* | deleteConfigById | -| *Property added* | dynamicReport | -| *Property added* | extensions | -| *Method added* | getDynamicMenuItems | -| *Property added* | isAuditor | -| *Property added* | isPersonAdmin | -| *Method added* | personsManagedReport | -| *Method added* | showDynamicReport | -| *Method added* | updateConfig | - - -DataExplorerRegistryService (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | getNavItems(preProps:string[],~~**groups:string[]):IDataExplorerExtension[];**,~~ **features:string[]**, **projectConfig?:ProjectConfig**, groups?:string[]):SideNavigationExtension[]; | -| *MemberMismatch* | registerFactory(...factories:SideNavigationFactory[]):void; | - - -DecisionReasonComponent (*7 changes*) - -| Type | Change | -| -------- | ------- | -| *Method deleted* | checkReason | -| *Method added* | ngOnInit | -| *Method added* | updateMinLength | -| *MemberMismatch* | addReasonStandard(~~**control:AbstractControl):void;**,~~control?:AbstractControl):void; | -| *MemberMismatch* | controlCreated:EventEmitter>; | -| *MemberMismatch* | reasonFreetext:BaseCdr; | -| *MemberMismatch* | reasonStandard:BaseCdr; | - - -DelegationComponent (*20 changes*) - -| Type | Change | -| -------- | ------- | -| *Property deleted* | isLoadingElements | -| *Property deleted* | projectConfig | -| *Method deleted* | resetForms | -| *Method deleted* | showSelected | -| *Property added* | busyService | -| *Property added* | cdrPersonSender | -| *Property added* | delegationObjects | -| *Property added* | initialized | -| *Property added* | isLoading | -| *Property added* | isManager | -| *Method added* | resetDelegation | -| *Property added* | senderFormGroup | -| *Property added* | stepper | -| *Property added* | withSubordinates | -| *MemberMismatch* | addControl(group:UntypedFormGroup, name:string, control:AbstractControl):void; | -| *MemberMismatch* | readonly delegationForm:UntypedFormGroup; | -| *MemberMismatch* | readonly delegationTypeForm:UntypedFormGroup; | -| *MemberMismatch* | readonly recipientFormGroup:UntypedFormGroup; | -| *MemberMismatch* | readonly rolesForm:UntypedFormGroup; | -| *MemberMismatch* | searchControl:UntypedFormControl; | - - -DynamicExclusionDialogComponent (*1 change*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | dynamicExclusionForm:UntypedFormGroup; | - - -IconTileComponent (*1 change*) - -| Type | Change | -| -------- | ------- | -| *Property added* | loadingState | - - -IdentitiesReportsService (*3 changes*) - -| Type | Change | -| -------- | ------- | -| *Method added* | viewReport | -| *MemberMismatch* | personsManagedReport(~~historyDays:number,~~personId:string, **subtitle:string):Promise;** | -| *MemberMismatch* | **personsReport(~~historyDays:numb~~personId:string):string;**,~~er,~~person:PortalPersonReports|PortalAdminPerson):Promise; | - - -IdentitiesService (*5 changes*) - -| Type | Change | -| -------- | ------- | -| *Method added* | exportAdminPerson | -| *Method added* | exportPerson | -| *MemberMismatch* | createEmptyEntity():Promise; | -| *MemberMismatch* | getGroupedAllPerson(columns:string, navigationState:CollectionLoadParameters):Promise; | -| *MemberMismatch* | getPersonInteractive(uid:string):Promise>; | - - -IdentityRoleMembershipsComponent (*1 change*) - -| Type | Change | -| -------- | ------- | -| *Property added* | busyService | - - -IdentitySidesheetComponent (*9 changes*) - -| Type | Change | -| -------- | ------- | -| *Property deleted* | managedReportDownload | -| *Property deleted* | reportDownload | -| *Method added* | personsManagedReport | -| *Method added* | personsReport | -| *Property added* | tabs | -| *MemberMismatch* | data:{ isAdmin: boolean; projectConfig:QerProjectConfig; selectedIdentity:PortalPersonReports|PortalAdminPerson; canEdit: boolean; }; | -| *MemberMismatch* | readonly detailsFormGroup:UntypedFormGroup; | -| *MemberMismatch* | isActiveFormControl:UntypedFormControl; | -| *MemberMismatch* | isSecurityIncidentFormControl:UntypedFormControl; | - - -IRoleEntitlements (*1 change*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | getCollection(id:string, navigationState?:CollectionLoadParameters, **objectKeyForFiltering?:string):Promise>; | - - -ItshopRequest (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *Property added* | canCopyItems | -| *Property added* | isArchived | - - -ItshopService (*1 change*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | getPeerGroupMemberships(parameters:(CollectionLoadParameters|ServiceItemParameters), **requestOpts?:ApiRequestOptions):Promise>; | - - -JustificationService (*1 change*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | createCdr(justificationType:JustificationType~~reasonType?:number):Promise;,~~):Promise; | - - -JustificationType (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *EnumMember added* | approvePolicyViolation | -| *EnumMember added* | denyPolicyViolation | - - -OwnerControlComponent (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | formControlCreated:EventEmitter>; | -| *MemberMismatch* | ownerSelectionCtrl:UntypedFormControl; | - - -ParameterDataService (*4 changes*) - -| Type | Change | -| -------- | ------- | -| *Property added* | entityService | -| *Property added* | logger | -| *MemberMismatch* | createInteractiveParameterCategoryColumns(parameterCategories:ParameterCategory[],~~**getFkProviderItems:(parameter:ParameterData) =>FkProviderItem[]**,~~ getFkProvider:(parameter:ParameterData) =>IFkCandidateProvider, typedEntity:ReadWriteExtTypedEntity<{ Parameters?: { [key: string]:ParameterData[][]; }; },CategoryParameterWrite>, callbackOnChange?:() => void):ParameterCategoryColumn[]; | -| *MemberMismatch* | createInteractiveParameterColumns(parameters:ParameterData[],~~**getFkProviderItems:(parameter:ParameterData) =>FkProviderItem[]**,~~ getFkProvider:(parameter:ParameterData) =>IFkCandidateProvider, typedEntity:ReadWriteExtTypedEntity):IEntityColumn[]; | - - -PasscodeViewerComponent (*1 change*) - -| Type | Change | -| -------- | ------- | -| *Property added* | LdsExplanation | - - -PersonService (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | createFkProviderItem(fkRelation:MetaTableRelationData, **filter?:FilterData[]):FkProviderItem;** | -| *MemberMismatch* | getGroupInfo(parameters?:PersonAllLoadParameters):Promise; | - - -ProfileComponent (*5 changes*) - -| Type | Change | -| -------- | ------- | -| *Property added* | canManageSecurityKeys | -| *Property added* | canShowEntitlementsHyperview | -| *Property added* | isShowEntitlementsHyperview | -| *Property added* | selectedContextId | -| *MemberMismatch* | form:UntypedFormGroup; | - - -QerPermissionsService (*11 changes*) - -| Type | Change | -| -------- | ------- | -| *Method added* | hasFeatures | -| *Method added* | isAuditor | -| *Method added* | isCancelPwO | -| *Method added* | isPasswordHelpdesk | -| *Method added* | isResourceAdmin | -| *Method added* | isRoleAdmin | -| *Method added* | isRoleStatistics | -| *Method added* | isRuleAdmin | -| *Method added* | isShopStatistics | -| *Method added* | isStatistics | -| *Method added* | isStructStatistics | - - -RequestInfoComponent (*3 changes*) - -| Type | Change | -| -------- | ------- | -| *Property added* | isApproval | -| *Property added* | isLoading | -| *Method added* | ngOnDestroy | - - -RequestParameterDataEntity (*1 change*) - -| Type | Change | -| -------- | ------- | -| *PropertySignature added* | isArchived | - - -RequestsComponent (*1 change*) - -| Type | Change | -| -------- | ------- | -| *Property added* | busyService | - - -RequestsService (*3 changes*) - -| Type | Change | -| -------- | ------- | -| *Property deleted* | LdsShelfExplanation | -| *Property deleted* | LdsShopDetails | -| *Property deleted* | LdsShopEntitlements | - - -RiskAnalysisComponent (*23 changes*) - -| Type | Change | -| -------- | ------- | -| *Property deleted* | Description | -| *Method deleted* | DisplayTypeOfCalculation | -| *Method deleted* | DisplayWeight | -| *Method deleted* | GetDisplayValue1 | -| *Method deleted* | GetResultRisk | -| *Method deleted* | GetRiskCalcFormula | -| *Method deleted* | GetRiskObjectTop | -| *Method deleted* | IsFromAttribute | -| *Method deleted* | IsIncOrDec | -| *Method deleted* | IsShowChildSourceDisplay | -| *Property deleted* | LdsMaximumOfAllAssignments | -| *Property deleted* | RiskObject | -| *Property added* | description | -| *Method added* | displayTypeOfCalculation | -| *Method added* | displayWeight | -| *Method added* | getDisplayValue1 | -| *Method added* | getResultRisk | -| *Method added* | getRiskCalcFormula | -| *Method added* | getRiskObjectTop | -| *Method added* | isFromAttribute | -| *Method added* | isIncOrDec | -| *Method added* | isShowChildSourceDisplay | -| *Property added* | riskObject | - - -RoleService (*13 changes*) - -| Type | Change | -| -------- | ------- | -| *Property deleted* | AERoleTag | -| *Property deleted* | DepartmentTag | -| *Property deleted* | LocalityTag | -| *Property deleted* | ProfitCenterTag | -| *Method added* | canCreate | -| *Property added* | canEdit | -| *Method added* | canHaveStatistics | -| *Method added* | getExportMethod | -| *Method added* | getRecommendations | -| *Method added* | getRoleTranslateKeys | -| *Method added* | hasHierarchy | -| *MemberMismatch* | getEntitlements(args:{ id: string; navigationState?:CollectionLoadParameters; objectKey?: string; }):Promise>; | -| *MemberMismatch* | setSidesheetData(args:{ ownershipInfo:OwnershipInformation; entity:IEntity; isAdmin: boolean; canEdit: boolean; }):void; | - - -RolesOverviewComponent (*12 changes*) - -| Type | Change | -| -------- | ------- | -| *Property added* | busyService | -| *Property added* | canCreate | -| *Property added* | contextId | -| *Method added* | createNew | -| *Method added* | deleteConfigById | -| *Method added* | getChildCreationText | -| *Method added* | getCreationText | -| *Property added* | hasHierarchy | -| *Method added* | ngOnDestroy | -| *Method added* | updateConfig | -| *Property added* | viewConfig | -| *Property added* | viewConfigPath | - - -ServiceCategoriesService (*1 change*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | getById(uidAccProductGroup:string):Promise>; | - - -ServiceItemsEditFormComponent (*10 changes*) - -| Type | Change | -| -------- | ------- | -| *Property deleted* | formArray | -| *Property added* | alertExtId | -| *Property added* | busyService | -| *Method added* | getColumn | -| *Property added* | ServiceItemsEditFormComponent | -| *MemberMismatch* | formControlCreated:EventEmitter>; | -| *MemberMismatch* | readonly formGroup:FormGroup; | -| *MemberMismatch* | isInActiveFormControl:FormControl; | -| *MemberMismatch* | onRequestableToggleChanged(checkboxChange:MatSlideToggleChange):Promise; | -| *MemberMismatch* | serviceItem:TypedEntity; | - - -ServiceItemTagsComponent (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | readonly control:UntypedFormControl; | -| *MemberMismatch* | readonly controlCreated:EventEmitter>; | - - -SourceDetectiveComponent (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *Property added* | busy | -| *Method added* | grabText | - - -UserModelService (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *Method added* | getFeatures | -| *MemberMismatch* | getDirectReports(**uidperson?:string):Promise;** | - - -WorkflowDataWrapper (*1 change*) - -| Type | Change | -| -------- | ------- | -| *Method added* | userAskedLastQuestion | - - - - -# rps `(3 changes)` -ReportButtonComponent (*1 change*) - -| Type | Change | -| -------- | ------- | -| *Method added* | ngOnDestroy | - - -SubscriptionsComponent (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *Property added* | busyService | -| *Method added* | viewReportSubscription | - - - - -# sac -*PackageAdded* - - -# tsb `(28 changes)` -AccountsExtComponent (*1 change*) - -| Type | Change | -| -------- | ------- | -| *Property added* | busyService | - - -AccountSidesheetComponent (*3 changes*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | readonly detailsFormGroup:UntypedFormGroup; | -| *MemberMismatch* | get formArray():UntypedFormArray; | -| *MemberMismatch* | neverConnectFormControl:UntypedFormControl; | - - -AccountsService (*1 change*) - -| Type | Change | -| -------- | ------- | -| *Method added* | exportAccounts | - - -DataExplorerAccountsComponent (*4 changes*) - -| Type | Change | -| -------- | ------- | -| *Property added* | busyService | -| *Property added* | contextId | -| *Method added* | deleteConfigById | -| *Method added* | updateConfig | - - -DataExplorerGroupsComponent (*11 changes*) - -| Type | Change | -| -------- | ------- | -| *Property deleted* | dataExplorerFilters | -| *Property added* | busyService | -| *Property added* | contextId | -| *Method added* | deleteConfigById | -| *Property added* | itemsAreNotRequestable | -| *Property added* | itemsAreRequestable | -| *Property added* | ó | -| *Method added* | updateConfig | -| *Property added* | usedInSidesheet | -| *MemberMismatch* | bulkUpdateSelected(**request:boolean):Promise;** | -| *MemberMismatch* | requestableBulkUpdateCtrl:UntypedFormControl; | - - -DataExplorerNoDataComponent (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *Constructor deleted* | | -| *Property deleted* | webApp | - - -GroupSidesheetComponent (*4 changes*) - -| Type | Change | -| -------- | ------- | -| *MemberMismatch* | readonly detailsFormGroup:UntypedFormGroup; | -| *MemberMismatch* | get formArray():UntypedFormArray; | -| *MemberMismatch* | readonly serviceItemFormGroup:UntypedFormGroup; | -| *MemberMismatch* | get siFormArray():UntypedFormArray; | - - -GroupsService (*2 changes*) - -| Type | Change | -| -------- | ------- | -| *Method added* | exportGroups | -| *Method added* | exportGroupsResp | - - - - diff --git a/imxweb/compodoc/samples/menu.md b/imxweb/compodoc/samples/menu.md index 421a402b8..c34997550 100644 --- a/imxweb/compodoc/samples/menu.md +++ b/imxweb/compodoc/samples/menu.md @@ -81,7 +81,7 @@ Here is an extract of the file. > Code ``` ts -import { ProjectConfig } from 'imx-api-qbm'; +import { ProjectConfig } from '@imx-modules/imx-api-qbm'; import { NavigationCommandsMenuItem } from './navigation-commands-menu-item.interface'; /** Represents a single menu item. */ diff --git a/imxweb/custom-theme/custom-theme.scss b/imxweb/custom-theme/custom-theme.scss index 3b4abeec5..9317ec063 100644 --- a/imxweb/custom-theme/custom-theme.scss +++ b/imxweb/custom-theme/custom-theme.scss @@ -3,7 +3,7 @@ $theme_font_family: 'Source Sans Pro, "Helvetica Neue", sans-serif' !default; @use 'sass:math'; @use '@angular/material' as mat; @use '@elemental-ui/core/src/styles/theming/theme' as theme; -@use '../shared/scss/theme' as imx-theme; +@use '../shared/scss/base/theme' as imx-theme; @import '@elemental-ui/core/src/styles/functions/to_number'; @@ -23,7 +23,7 @@ $custom-typography: mat.define-typography-config( $font-family: $theme_font_family, $button: mat.define-typography-level(14px, 14px, 400), ); -@include mat.core($custom-typography); +@include mat.core(); // Define the custom theme. $primary: mat.define-palette($gunmetal-palette); @@ -37,10 +37,10 @@ $theme: mat.define-light-theme( accent: $accent, warn: $warn, ), + typography: $custom-typography ) ); // Rename the $theme-name variable to your theme's name. (e.g.space-theme) $theme-name: 'custom-theme'; - @include theme.theme($theme-name, $theme); @include imx-theme.theme($theme-name, $theme); diff --git a/imxweb/custom-theme/package.json b/imxweb/custom-theme/package.json index e7f5dfe70..cfa30ae7b 100644 --- a/imxweb/custom-theme/package.json +++ b/imxweb/custom-theme/package.json @@ -1,8 +1,8 @@ { "name": "custom-theme", - "version": "9.2.1", + "version": "9.3.0", "private": true, "scripts": { "build": "sass custom-theme.scss:custom-theme.css --load-path=..\\node_modules" } -} \ No newline at end of file +} diff --git a/imxweb/imx-modules/elemental-ui_cadence-icon.tgz b/imxweb/imx-modules/elemental-ui_cadence-icon.tgz deleted file mode 100644 index 946eb80030ebb66654ae2e9e75de32461465da59..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 323247 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ⅇ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

T&OC#+n$bj5_#U%MtAFd7V|krwam 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≷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 diff --git a/imxweb/imx-modules/elemental-ui_core.tgz b/imxweb/imx-modules/elemental-ui_core.tgz deleted file mode 100644 index 3ee82f45a27742e5739a140a276a904e158fcd85..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1322765 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 x*2}UtUUc{6@N<`Yiq*a0#CE*E+(#(!chS3;o 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�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�^tJ3-j0(u{kT|BTdhQOKB9Jtlb-ePZ;hqJGwC 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 @0(mxl*Ki7E}q+w3-?75kGWPhpwrx15+y7;`G0B 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

?Gb_1yz3UKw!mM2 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

)}d8aJ%4JbQSff9(eQ4K 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 O_UC`;HlnrwU=jcrSoP`v diff --git a/imxweb/imx-modules/imx-api-att.tgz-hash b/imxweb/imx-modules/imx-api-att.tgz-hash new file mode 100644 index 000000000..c2c48f0ee --- /dev/null +++ b/imxweb/imx-modules/imx-api-att.tgz-hash @@ -0,0 +1,8 @@ +E6573E34E07D56A25D5A5E5AE4B4B8944C02BAC5479D6A7870003C698401BC6E7D3B8FC6E11FA25628D36EAA6DD0F9A71021A0114B808ADF1FC86E7A17246DCE tsconfig.json +52822F64AEA2B42445A1A01278F864061078D3E60DF1F530F19FEA5E077643DE3CAC8B4A4F065F238F2ED260EF8A0AEC94E5711DF64D75DC9CEB1A4C35A865D8 rollup.config.js +565D30DE7CA344FE6BB6724E1208DEE942D0CEDCE31BDD06F11B8C9E8DA76466100FA64B33FB780C2822F4EAC1B170EF1A2E73BDE93498BCFA185923278A07EE .npmrc +650E93B82E1A115D0AB586DF3CD5DC1B8AD97440B674233F028E6E60211D1695C871D3FCFD17A1438B5E1A9AC686C2766A5DA5E41564D580BC181C8B51DEAA25 package-lock.json +D592D0D46B7F3E2D414AC57346585076D1348FEB583CF518DFB3B5318FC712AAEECD4EECB10BD04A50BD1DB3181B5AA27EFD29D2CD060DD8EB27B87778F5D611 imx-api-att.es5.js +9FA5D239DE9D86A4EABAF8CD3416967917C5F77A88F2DDB8D0F48F28E72EB14B2D754B78A3CF972428227A06813FAFDA48F80253BB8EC1D49677AA2FE569B8D6 imx-api-att.umd.js +A9C0B61DDB3B45B9F836C8BF15DA912EC954166DEEC833354ADD50790761401B38B559EDFCF7794D6FD4747F668376F6FF86DC5AB0DE5415BFAC3FC5451815A9 index.d.ts +6AEA9388D02C74B1291133E9912F11230EDF939B38C78096D3EA7B7A23FC1DB58989314C0C685DE1657BB53D377D4A523D77C06E13E8C7918402C7B8A6DE6758 TypedClient.d.ts \ No newline at end of file diff --git a/imxweb/imx-modules/imx-api-att.tgz-version b/imxweb/imx-modules/imx-api-att.tgz-version new file mode 100644 index 000000000..f6d8051fb --- /dev/null +++ b/imxweb/imx-modules/imx-api-att.tgz-version @@ -0,0 +1 @@ +9.3.22 \ No newline at end of file diff --git a/imxweb/imx-modules/imx-api-cpl.tgz b/imxweb/imx-modules/imx-api-cpl.tgz deleted file mode 100644 index ccdd8c8bb172e2890c68eb0ef5611a2540f83a78..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26565 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

8lvSr_e@&^^NNK7( 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 E2R1{;Y5)KL diff --git a/imxweb/imx-modules/imx-api-dpr.tgz-hash b/imxweb/imx-modules/imx-api-dpr.tgz-hash new file mode 100644 index 000000000..5e5dd4da8 --- /dev/null +++ b/imxweb/imx-modules/imx-api-dpr.tgz-hash @@ -0,0 +1,8 @@ +E6573E34E07D56A25D5A5E5AE4B4B8944C02BAC5479D6A7870003C698401BC6E7D3B8FC6E11FA25628D36EAA6DD0F9A71021A0114B808ADF1FC86E7A17246DCE tsconfig.json +52822F64AEA2B42445A1A01278F864061078D3E60DF1F530F19FEA5E077643DE3CAC8B4A4F065F238F2ED260EF8A0AEC94E5711DF64D75DC9CEB1A4C35A865D8 rollup.config.js +565D30DE7CA344FE6BB6724E1208DEE942D0CEDCE31BDD06F11B8C9E8DA76466100FA64B33FB780C2822F4EAC1B170EF1A2E73BDE93498BCFA185923278A07EE .npmrc +63D59BCAF1A8C9E75F18E88F1E246514A85F9C76F99A5D6A533A2350C8324525217861E5822E3B23CDE7CBEF579C770AF4A8B0F579E2AC089B3DD2D2DE87C3BF package-lock.json +83786E1E42F09187BBB054BC411FA873CDC2490CF3D8D314ABF65287EBB02ABCA3E9F2DEAA598FED148B16C854C32998AAC29EF9B5767B7E1997D15ACE36912E imx-api-dpr.es5.js +88F0722ECC7B9714306878D4758EA39A6DB1FDE1B2D20AB0633A8B51551793C630B14B604775C6FAB9524E8F384446A3CC2CD2B84055633A6FAE50F629A0989E imx-api-dpr.umd.js +A9C0B61DDB3B45B9F836C8BF15DA912EC954166DEEC833354ADD50790761401B38B559EDFCF7794D6FD4747F668376F6FF86DC5AB0DE5415BFAC3FC5451815A9 index.d.ts +08AEFE7502506F6A80EC9668AC926D526D3D9CEDCF9CB41A724CD8EB8977D71AAC26DDD5E3D0A81F9EDD739D4FAFDA3D139CA65390E0C4E9C82C673B541A6C0B TypedClient.d.ts \ No newline at end of file diff --git a/imxweb/imx-modules/imx-api-dpr.tgz-version b/imxweb/imx-modules/imx-api-dpr.tgz-version new file mode 100644 index 000000000..dd5a52084 --- /dev/null +++ b/imxweb/imx-modules/imx-api-dpr.tgz-version @@ -0,0 +1 @@ +9.3.12 \ No newline at end of file diff --git a/imxweb/imx-modules/imx-api-hds.tgz b/imxweb/imx-modules/imx-api-hds.tgz deleted file mode 100644 index 7fdbb754079d0901e7c85116dea85cb862ccbf54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12160 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 CwUPS( diff --git a/imxweb/imx-modules/imx-api-hds.tgz-hash b/imxweb/imx-modules/imx-api-hds.tgz-hash new file mode 100644 index 000000000..f439207a7 --- /dev/null +++ b/imxweb/imx-modules/imx-api-hds.tgz-hash @@ -0,0 +1,8 @@ +E6573E34E07D56A25D5A5E5AE4B4B8944C02BAC5479D6A7870003C698401BC6E7D3B8FC6E11FA25628D36EAA6DD0F9A71021A0114B808ADF1FC86E7A17246DCE tsconfig.json +52822F64AEA2B42445A1A01278F864061078D3E60DF1F530F19FEA5E077643DE3CAC8B4A4F065F238F2ED260EF8A0AEC94E5711DF64D75DC9CEB1A4C35A865D8 rollup.config.js +565D30DE7CA344FE6BB6724E1208DEE942D0CEDCE31BDD06F11B8C9E8DA76466100FA64B33FB780C2822F4EAC1B170EF1A2E73BDE93498BCFA185923278A07EE .npmrc +50D3FF0D1CBF2C5819735327C66A5CDF6F9F9CBAF8BF48886E543DF728838AB4023F1C23706714289088EBE9746B9526DB387D68F1C4907E94F4065D5F71E77A package-lock.json +07563FC9B15DFEC950221DF20891852455D99305A09D9CBFF868F8CFAA28C677326A2BBFD56406A5112D1A176F98F34400DB6E196F927EE8650A62CD3D034259 imx-api-hds.es5.js +41CA534194748019989AFC0D5A6A1CC6F35937DADBD24EC58E6C5397A8FFB99FE4474B27EF3B7B39D1C7D1A12FA7C9CE4B1E57D984D7FE6C22704A3B8C36DBA7 imx-api-hds.umd.js +A9C0B61DDB3B45B9F836C8BF15DA912EC954166DEEC833354ADD50790761401B38B559EDFCF7794D6FD4747F668376F6FF86DC5AB0DE5415BFAC3FC5451815A9 index.d.ts +86207A4E0751DED19530D9ACEE5EBA7C325884CC81D212E6E6FBF096197D6244F80B5035541D49B198E45AB71CA3C8834CBC43AF232064646345D20157AEDB4C TypedClient.d.ts \ No newline at end of file diff --git a/imxweb/imx-modules/imx-api-hds.tgz-version b/imxweb/imx-modules/imx-api-hds.tgz-version new file mode 100644 index 000000000..b31669625 --- /dev/null +++ b/imxweb/imx-modules/imx-api-hds.tgz-version @@ -0,0 +1 @@ +9.3.13 \ No newline at end of file diff --git a/imxweb/imx-modules/imx-api-o3e.tgz b/imxweb/imx-modules/imx-api-o3e.tgz deleted file mode 100644 index 99a49756b1486a6275028a623bdb178fd4d6fb65..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26180 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ΞjolMsc)BS#vSn&dYJp3|HTat$*EM|VF16N#VX2FO@@b}PAI1*;Xe z5DoV!!sl@`1>dNZ>Tlto*lU|`oJL}7;eePGX&P9Q$hUa2tUQm^c?YA=$(1w%~TY0M~Jkroq~~2V)HSAAkJ6{a5{O|IZu#!&G-C zjzRrI{PEP%$;p)ECM3UT7Vwfoj?PZVgo%V{j2&>*d1uzu-3#*U?$M-4I8eX;&wpC5 zAso%9Jh=b-XMb#1|NN)tPRLhZk*uHbRnx_syqN6afKNV*bJ(`)*2K{1T*2e}_laLP z6uAC>k7qBYV+!2fwPEVRzAqf^Ao_dXyZO%^&lR@ErpGS-?`8knSN)fXUtY}3#f@RT zK+s<}x@(*F6yg8x4IuR(qcLuh7^Yrf+k^hM2a&enuXLc-{crnkqKntvR|hZOP?QcL zbnWZz8<28Nh?!$LuV`Q}@t|Lgc;J_z}mTs!uR?Dq!|_h|o6$?0>E{|MPD z*8+L+|MJ^|HwUkh^8f19U;nD)zm7j+M&B~pQ5BlHLz1E7vPBu`wW zMtw%qs9fXHsO+^i(SrnCY)^@h`2MK2@NRjsj}f})KMaZMIe;V{aYYw!G-b(fg$>vd zOHKo0D(sSe3{V^3w~jP#h|J)pLTAaS678Qvi-e`{L|=SyJc(~|qK^V7Pg3Bpp-xIn z>-zKZuh296@zPIs`d+`*7N%D`aDsP!T$p$M1b^3;ZqWVmDjXZy!E>P~&_*y1&Di(p zlpaOue_$qhjA`N_89R4O!{IlI@(qsiv+lVBMsanIr{f8Ro9V8Ld;Q=f^B49YiDdCG znBbLI3k=gzFn=`y80d~=8qVZ~Q+%WyhP-1IQpIwAdi}FwFXsNQbfr4|;wG`_V<6fV zmX+#X06?~q$b%)j*P39Zr{v?kb99i!3*A8}M71|0W7{@$&5DgQ)hDLr=(NBEOaHYz z1N{w@K4)2g1MpDDr{sHbz;~If8UlUvl0Q1vF_uLiy%J5r3cy5TZhUlrb!uDYvgmkY zD0}{qHEiM>VOV&=ARH(=7(SE^mzNwk!a&1Sg8wqpAqs}YL222cib2i@qsvTywrE%! z?3Ep=7}kvNG@D=o3e9sCXfHY}sx=JayglMMn&q05GQJ-I60TV^+jW+d6rE!FnkDL2 zB&7*Ya?hXx@@442e8)8T-;&5-k!UbcM>Ju_jIN@%+dobn4|#=FvB3R z##}9lTC+oZsn-AaMRV@-=}+J$VTM%0mV)RX=&+nvKwA%MjYT@(4`~!RKs$8ybQ#RR zkH+*fkj5+?8AUXuS>_FwdXS-! z<~_jXN8R~OcNp`1zuOO4ZlC-~_60mepL3?6p98M&zcH$_b}6c|$5FDei2};duOaH` z*En{gk3#hA|1I>h{(Gv=9UX)YY}sd5r#LGT$Pizv$aC}n&KK>X86PFINAk#DsSTwz zl-k&a+DJ;GtiH&(bCXgO$vFx&M9ys)e{ewncsDSV1;6tX+kDA&W(@_alH=VR9+Bc< zxeHWrUe1}bEKjqy^As~HX3k=cRk%2L5iOT}=`3aWmc3e#?XJ~EDNDa+E>KzI(iN(=jVEwrZdth!aG|1RBo_e%&nb z6DtoA`$?*-7%)e&qS((tHMH(9c8DWdA+M%(a{W=)L5&^;Ltv#6KX}}WR2yaIN1FKq zy?hh7T~5RA$zvvf05CE$T>U$CEY*o$$m#Zd1s;)<@F42D0w(B@1*=N-x4#{h-&83U zdR8oypne(NDZX#5cxXdBBxD=8WPc<0*{zAcvNS>KY zfRdS}7@StHn#jbQ(`y)wv@L7OO7u-VB_ItjDt_9bEgF>tO*62TQDH+gN>I|PdRij@ zs$`}qMx_<3CNe6Y>3z9I8Wo|eWF0hQzKPAGiB&IVXGWY^!ppKb#}FBV`T`e}C>3Om$$*;eHoQmZYfL@{=b~u|~ z7dvmR^s5$mGiTOe4kwM*(Jcpz;39S&G^MNjmWNVQekN}PiDTuWQnf;ggfD0$hS^Z{ z(r~TxTF{Tn@WBOmPTmQ=2RpTAc{$;DxY_fSOw*DIZvB_h805eVNbJ!gtR^OO3y1R${%SYU%A7 zU#?iJtsYKXxjGq3A_JNiRyiI?uYIwqB7LwzCvWCwzl<#i?_up5(FHDj)ceKL(kF7 z$WMNB98*>EAu;9=8MJsne=yV5rab6vYSzsnnw}vNppD88OdHRE;&vF${vJ*WUE^(n z>=aVLd7WSqk!dPIe-lLLMKg**_GN>u$<)*Zf$Kb-MssXX{p^BOBs@e(!pXyRRTZ)= z3H@r(+-;+1#hCCKxIr-ekS^oe8&G=|VL`_fjFp6yOM;snaYo>O(+tnow=`7Ym#5%0 z*JTz`VU)V$+HwNexH$T=!3pCoP=G*n0PHWQVwr=<0FbBotJ`wayCg;m-m}szOz;Uv z;8F2FtD@D<$3rxZUXB6N0rn_Z8iC!Shx}&K*Rl{$4IBZG`ms(l`erTw?Aa8~IZs_I zsIi&?e;4ET6|2}aYSbWh2MoJ|D0T;rF>E~z;;0>@u{{W}Ehuu+j1!2Gcu)ELQDn36 z2?ls$*lsmuw-&T6Nf~iGyE=u0K1eAPx95zeow^5cI*8zRZJF4}xl&X0ORd;zw+?wG zXJQpV>y#NOH}+XN5HeV@X-EBzwIRy>*pbb%%W=A*uNsum?zw_opMb3-bp%%Nvp(ky!(S_3>gcvQN1@hx+TS`#$wRVEn~lh-@p-HOOuG<6lv_;1j{VAlpK%InA9i&E^bavZP`2r7$Yr=UAM ze}8;Ve*7H|(1F4T8`sLBT>MluV`S{wa9oe@Vr!o5*W-oWp$h*Yl55`|fFzq;02tQ9Tud=q z!cLx9PI^}d&2i?ye1Fi#v@epKetSVCw;D1nhFWYaIY5WNpQfyaLu3-vKnV*a!tVvO zRRo5dVFzZqYUPob5=`@Q84_$9)w`_d0?PND3BXCSc)-yBPBwE9Frw2;PMZ=+5;C?q{cdLu#=_0Y4FzmEgBp@ckfa z$Ue}`f7o6@O~(PN64dy63YqCeN@W5;a6VfGsNh6&DR$r44kB>z%quxzxy>XMkpq{u zI(hHkKlU$v1E-HsKxXzma1HB}y2f3_QcK)?ZyNryuD`by@j2J!>D_gH@tSB9DT&2p zU&}6x%2vCu6;r;fv%;*4=izb+gXpWSw(6WRn?OU}ZTox_Nk#uxMP12cHkI&Z4aslB z>W8Zt*cq7DzT+sk>z5)&wMQWvRLGmSu--&erro7gky>v?pS5sV;%4cF&@PI;u8R1L zu>G9`1)=d<==IX1otgGYOT3~RaypU@$ zYfNld8Y{cVy(Mk3;{EQL)QI3hwj3wauQbVwCOBTE}=(TH?AS%hGw~@OKO~_qWuM(>7rb8R@+HLq6#b$HgZ9@{b ze`%tU?t$e*(fd$j2vAaJvH7nxS;aI9PEdk~4_&&XTtKd%tE#zw!7i}O83eLoyc zwVbcx5Wc()u=xah%<1m`IN11F>~kP~GY`JjTg1=H-|Aqz?Pd!tmw#Yi?{YpEdl_w) z*m1FW3$WKT3v>s|{<7%urptTVyx&jK-@>A6>mJ1Mx18a9!@;P%<-86!KQF0Kd@;ps z!_MdU7xv9e=P0%Z5PU!5=@ES(NLIrPY-b^w+c$hd$`q9!CE0zsf5`JbjN!k`Ymd13 z=8tb+V^H4+@(0NK-6XMe^yTMIP^YJw0|9R^-R$NQudu9x+|6`E%n+V~2g<_F4Ko1q zZd{MrEdpTJ;&WYP(mT3>LA}lDXsk1GYW>f+@DpPB z6~K5Lb;oe8yBlHVgbDe#u~(srai3W`?{$}Yar?*hhuoKhTaSo=XK)I7yRtq6DBj~^ z8ku=g{y7=>K?dpTBl7{p$k=)QPLW5G2nA zGgD2PGYIeRgkBsRFhJXb=t7tx)1bF>YHG5VnI3q?blue+{N=@`2U`ppV`C6n2ssEc z59RL^3I@;y@)txrM&rT`Vxh!3b_ip7omD+@py0`Pn|=VI9@IVHpwK7qmRR1i2$&-M z(>c6Y?;Cn0Aey1b>Sq|3c4^1(*9gnd8+Ed*5seW^yMDCdFhPm!NdJC7c%XwL9fUAdSoDq zK1amm6PjH#p7aT^6N8{Bf*(v;_HT1BVY-0q9J_U?=f^2D7zri^KZdl%{b-ausEt>T3yAqDh&WBfD>pTyD`*$utl!aPIB@L;dN` zeEIq7D&O7}iRXw5&io1AU=ntt3HE`@Q|av-w-e%5=y!u@gA7Z(S>j(I%du9-3N0<9kz+hT|`W{Uu1Kg5a{se)EO$ibFCS=#>=G zIGmyov4ECpCHfiR94JoV4X)l-jw1Iqeq-wODRO(qq_>lN#ZCiW3)5j9}9ZQ`DB z@ppCc;2QH*9T8`wFt`d?F@UA#JPuYZt#+F8dpV%O;NfH94nyV9KysUDPq)8u4Mz`y z%h-z!gMc43@oywXv??MzclnKHgp4RWa-`jl5v{=TW3<>td2l+J zL4wWf07S4!IbLLZcXKuC;#MU&eA#_M2!)vXb`5H``wu}BI) zt10=(-T&L^`84EO5-O__`1j+6zHUmWJhu_&H!_KWpgN!gaK|tl{c$6hV_0>3MFrAn zK&cA8GB5k6Gn< zE8K~C)KD0`u3mU@*bz=zH7J|_N&<_`dO!)+*#0z2nyY=f*TEK~_0k27Sm*EOILC;V z-J{ZtNMd(l472PAM>FjS5JUosW=RF&ZkZ3tR0NFNhUogQ>!`Q<(m0cuzBd(Mvg4wM zzvb+`7)5BAsP2t3ug;7UMUow5Kuhx-XwMkkYvRBmmV|*Q{&6Qax`%r#>r7~h4ZXjI z!QS?1Gzat5W@hl0DIqhB$l_QP)l-l(9H|xI*-zZ(u=KzPgV6tkiI%i?dtsu(_<$I{ zIR;-u=%iMs(~)l4`I>g1g}oe24PdfmHXLfIEQrLGJkZ4kE6v$E?gD#)N5N-EN8>K0 zNYm+tBq8cWrAQSKh3L~NL1K!?qE6SmMFm(q8*0DDNv-wIE%IcO$BOXkXVR$~M|7yg z(lZ`0YowD6rCXQUZSiCE#+GtXtqEh!B_7Rj2Fn!Gr70JR@pmEc#~=4yY*Zg~uENp# z{UI+DD$3`pwLYdN-3aEWHFO=9n|l7x^$?v)jC3BRt2u)9&X3YylHDF%I#9bjtggVA z@U-5&8t~@dl}3zXtGxEO7+w`nlH%5Dv+wE~?I`nBId2|n=c-jhhfJHBaFpg=56q@d zU(LSk8$>BX8Smx@YXhXBFx!GD_IjhQ9Hxn8yqhEKQ5V^kJ*0f6GIb>NO%tilhFU$z z;6G(=jIpj+Bax$4+Dt7!>FNl*`A$y|{3mEExn=3z3`k)>)ZIZ5L!Sle^!qWd-8zT@ zO0nOz`%@7HmbWaDZ`DUR5sytH?(19m*zY*};sM#%Bqpq!{Mn-5BBi*A=YD=@DxXoQOBE3_B zqt;nQx9tP3X+P*opcRzoBc+IH2OK!$n2G?B8Y=Dwrp(|$q)$$oy2zCLTDtM zkGl9~5ChAsBsp80O8&CCcAo1`&TwU<8j!GGj5!(jda@a(S?>{}iEx3r5o6EHnpj}Dm zdyzYeP>I{PNS4)p@SN?RI=5lIwCMZHYj@@cx__D;gJVWnxp-Kv`(@eG^_(YV{L|Y1Gh8~aX?m>l_&84 z;+_pa^Et5$A)dj5(?V-t(tAWU!{iuHVVyp3P$srU*1u~#ib18AUwUKL*|B-RS$+qH zy11tVP6U$A!peYvzT>Am7|A&Li!@A<4U&?+c6A9L@_``NcM)KL5RLwkbN5})(6Xfo z+w-IqptQuf92ijLS(Ext2?-^JBi84E5|K(yB~Fzp4c?}U4bOw)rDV$iqS|1@693Y1 z0_8HRrfI4&O5a<(x7aA$Gg}5OA_pKd6|mFEVx^_j9%&+3G*`)5OSQo0HonT1yNbzR z6k*br^dU=wf^|0W$F1;&h|ENU2nfHYz%mJFn3{w3c=$0A7$BkJP4uu6^Vg^12;Ga1$Zm5lF#gzb`b5#BX=H<>ib;5f@ZLj!rUi3ElBO$t#a!|kdvbC+ zck2i;-<(YHL#Ib{KP?QdsUH;USsZ-EWnzBtKJ&MP*r^M}S4PexD;DC{j5OC7VO`f= zYMRhyV~gpO5M`kUPnf{(k-RF=p?;iUI(Nidl%QB2)bhD9pAXK6)2Lc%{>68X!9@CkKK;N*u4q6`n z6DBKAGNNs<*KplSBe(oOM78sDu_*mJOoy#JrmK&uaS;}aHlo@ly4;^EMo>{yFnv;8 zUFW&}3VA~su(m}Hj#16I-vuk>Bl z?`PV@V7^r=1`(sgrVK-5hmjz$8@+%X!KQ?4d0Y)O{dVLPLhn8JWcm7r$i-}XAN*#w ziMN-TBAal6@f z;>k`w?_^w}ZDw%f@RqDG!v+W}=)BSe+>4tmLQ!r9pthsb5~R_&1!^X$wE(3Y^#r8= zz21Nll0>8spdqENAs9Dl1YlgPfD5Tr+RKas0jQJTElq3(h88^0UP?ij*KWi-~`8_5`LL5kvB?rx@w_``#ahg+!*cr)^`S{cbjK8Yv;FL?oM)m^QndW=HyMYD3rhY&fq~^R-)pXrMtjDdq3%uh6Bv zv9eUJrmt0F6bfqwBMw(c01^N4v$ghr=!xk@9;LOPn$n4@T5>@t+c;`xmn@Y1!4V43 zB9YhTxBY2;SqcoJ!_GFn;5Oe3X|@o0?)`H{8ue_sGL4lYu};E#5|p-bgOXxZQc|AC zbFs3NQu5^G`}jECiA-CM8SZR$ay0XAWjbq~@IvN{3E;Wz7zCQYo=^wr2_P(X(>52n z>5t?{z{%6#Fd$=+8FwSZb}#_xoV#;p;ZF5_5wOxS*RX{08^zfhE+la*h_s-^Xt3csP-6WSPE%m zPn&23A3UEH=_izWMJQ{r5A}e+h_dkKs(RNon(!`frS==M$m-jAUdm^{DQJa_az= zxXw2Ff%jm#13E-Y`TVAMm+$h%xUINnb8S@(Yx7jMB^~oRtag9RDkQtJ>5t;3K~AK= zwQM!BaMr{eALP@)bhg-Rrn1|>!;6Z`>s#S1@*KkHA$!GRmP?@mv|&cT&>6g!({stZ zkN@ro>KUW`-oxOdX+Jg>XgPfNh0qZy9z4I%9q#q8vBXOIGsu>OB0>Q@dONCGG@Z36t_ zG+O1;EJ;BxE&YQb6_S6DFj--{&p8(!Wi|4O>(<~5HzBPmr=SENjvT?8bNqQ8N@hFO z^60}uJVib4XXwz{c$F#QxemN8X#2RZK6V~1yQ_`)5D{cG_xMCVzUljnM&ec`_!81R zEZk;HTsg+pgpjf0cvJyUXimt?LV+OXwAaD{ z2g%XYFBGRilBA9BBv%^2HRUgG)%L(I~4 zDis({3Fzl_>M0@r-$;qA3O)Ew!f#_C7eluJWi`ifsV*2QRl`viXudg>DWnCNeccI3 z&@^My4?;*GSA>>6Deu#NKMZ@6dp|3F_T2c_>x)HoiZQ}&ju^{0gBTHM_D*&R&qa7L zqz@~G78|vP`u_1o1p`-7N!nqY#Av#30#3RoJ;Bi}h;}p?1-! z20fm3-d#BxT-XL#O+s!`jiTx*p^P|Le?X%%RV*NbbTPz@A;ru-Ecb3xKQ znxo{!D~uPO=^@f)GKO8j=h{5SAjuFr%?|fSbeT?zqkam!jM4Yc3#V~PHJA|IhlHWd zwRx5eFl}Spa%5B#d`ikek!?BR|M01)s~)yof?8rl>u-ixd$cEGymhpp^0U5=n|0KY zuV9nJp6w)?=$!nNVm^INyjHE2z5#o*84|N~GDr%uQelc4esd3!?{Tt>OcV%*pCk^0 zfKd+vt7|O7?UUc)bWKf{i^IEoONAS=O2v2=3UebdSQwaP z;0D3Xx+X6n_3@(S8_>D)X@im4VNL6=It(Gh1dh_6K#I2;$PGxH?2(UqJLbDf#?Jg@ z+$fZfMF;Qi%d1dO+PJPgy-h2_7fhGt=q&%vU0(CV7fjHKX^dR{Z?^WyvMy3ehg zhP>-~Cxa%ny_Bjkh2t`m5*1>b7@zx4u5>Z`oyHSt@tcbqv8sw z60WGxt-olpZ&bVyt1)kWDteL5PA|l3sk#_-RI71qUcIPWP1Kt`+C>tdX`+vUzP)pm zT}i2fS^K^+(G{1Yj^Z0u>-=f$w&CRZ>8&1l(|we#(LKWbk@L;=A@0$r9wh6GQ7&bY zYb2G@f3HyrpuAogL6Ui#8pEX@L_tzbH>^?!qm<^+ji4-ybRjG&*_jW`a3LhqjgUk) zS`5wL>l2azG8arYqM{igDa)c8C6RulEKmF?l+#SV(;9Rbo>Nmr6}o@O&{5tfKE5#)q^K(Df^Z$w9wb~Vh2?kH?2#}mp{>sA_7yMx0{rPrsfSrrD^MjycaBzt+pJ)Zy z?df`Ac=lQY9SEF>k5T=PY4OM#Re&L$jLRHhvngg14!99?jlVaru~~c}93SB6GGTi!nLEflaQiNS)N9PY-T#3rrts0N;%E)<1~eT@3&6sa+}b;I!aa9(0@!*EaZw1`c3I@+G1w^gA$ zL-iK7`L%N(j;UT;0I>gfDp}W@Ndg7c!%ma~d%)kMWV)y9o0A;v=rijc=PB1?ey{Mu z@YCXvM-Nr#<$eARo-lt#2SyHjz%vMJZEfQ9`-VXXn5o%<7B6h!LZ3{2V5UsR6KYez zGfF){=4!kLx6pd1N0+ug0BH}9gulBcMPs)jpAj71K_EToBR_?4ebGV4Q4EfKDg3(R zcO#&#>ymTTcRu$Qv!D2p@KFykU;tAU+?+vq|Ly-4sbmWsp&J-$F*s-F9U}II7 zs{eV^Hem6iu*lKAWxyDVagMavqph%G-OQd1g!9~IYPef#-1^91v3mPMa=HOKI zL4u-thIdZ>13P~}&JbrF>J!HVXCm*hJU!h!wi~h&e1eX6l)Zt{Mmg4v(;t*?C|p=z z+#@~we_tJ&`V$m}jE1g@A1s?Q9uu;@kr)Jy%A>ALQ`bvcoIE6>;6E=0|Ee#CYA5t? z63DARJt!_0=E6{JM86tYZm6i0wAviFA^ozI0m(-=jcSSqmUDm%jaVmDjG>SiYwmbu zw7u&nPr;|G-k=$g3GIqwuzV8Onu> z+t~LS523X-J2w!B2G+ooqHK(HPqT^44{H8LFC3*G7*nr*z9b-5iB{Cpo+f;W!}KLx zrFw__p`}b$&i@nY27!%$2G|P7NPxr@lXj>OL)QTWyA7F;L;CHBX$U=+u^m-A8{#fu zc~t~ma!|@UT;Ui|CUEiQiT<4@KhN5g^ZBxt$lwFjG&=tgi;$`3aHZ~r8zJPK7|?1a zEd&xh^ljazK~3Q9`FFJ4MkN$4~agapAQ#FgXr2kXPDs*9UYo9_&Z1O-uIru zPI&?VU)I`dlp?Y>_-5Yk!d*4ybGYslLv_d4<6jg#$)hvdXY~jb9tnB#HS4axK@iilQoT4;M;@rRX$pB5n z)n~1q7Y!+?jJe>~-SC=j9JvtdXli6%AxZ$FSSltBO2-z03;PKfjtmyoj>8A12pKLr z5@nSQ#}__{p9Gyn1(&TWbAO%TnZsB7M#J#HhqNq`5-I&yb3QT{+{_#L`- z5o(QcmWg0eb6C-t$oX{r$KI_Y?~b5@Yc`2@*s+UIA&UpQ={%}rttRo<4aG^wqPt)^KVyUbueOikveV7K@&VGso2@+itlP~=uLXVFl-UMjO0M3S7i zi;*s+M#9$lUAbl!gih&HMRM+8ab(~6zYqdyD}=~#G^&r%ergQR+iErESAs`NYh>j< zfN6_jg$O%42K)(n`VfuQ*`OTI*NmrCHjUUfTzkT{;S$=+J>#oct)zaxG%Wd=m{ElM zglFwLNEV7xRW3}H`(vU>SkOZp&C~}#5IJauB~_@W6@F+VX)r2V;^6y^Th zo)pl<_VaH3rt`NV6seUW$N|ordJ_&*DfZ-k4UJFWT>~`lNqxH*rvs7u*Z<2y(`@Pe zz5gE%?Ou)dn`uoF1YLmduti=>14Q?Z>I zd^;chrMB%}DNDRviLEr*0UOyc;ZDn)kB@5x(RjHK$&0>ZDT2!t$bZXFG4Fr7=qE7> zm$e0Q{AwJ>=ccZgmo3Eaj3hNjbh{cg@!(f=B=Sk0l@Z#vAJA&D{k{%}w2Fzg8yd9#l#$ z;_Yfi6{%bCse`n`#fI4mPREVDTFtCX*`+S|da=uLzS}pbV$pXi8$N`om{WqMy(Syc<9McR5j5 z>|#q}%T2vL`^VDFo{h(o1g+TE%Icg0ZJrIrh0Yi`G5b2fco>Pi z>`DWfzb%;g>sW4!c}0<&nza&_)n?JFxp&w!O(mYm?yA&!BsFZaSOJYlZwIPRbo(zi z$}9(ptFDoJn%kqldylT#?WVC7R~u{8%GJxgVYhm%`Y6Ufk)3>cTBU1BVa6Nzb+2rV zOhn?aX2dqNyLtamv&Y2TvXN>&+r{RIXpPag`o?f=j`JYhg7hIU?qT}b0IbAv*6_Jq z5fDcqU+=cTtylBtjQ8pRajqVEJglOR@$>^+t7+!x>_eY=u6&Hfi5?^Z>nyi^mS2}v zcVxzn%w`Ll%t+%4w>JcZQtwFmnbKDUm}~9YjFd#lJqv(!dP1E z_PAJo>^IFWd-<)sa=Jg(rk!o)s?5N}xG!6%%3)4=GkwY&sYHb`f1y0hgnQ71zlmXN zK1O~y$$}_Aw2{O#zseSWJC$~uslNG~w86EEuP)*1bd7#|dTp52aj2FC%hVn~K zujffu6KoBNLted%^)K-ozN?(Q&w!_#FbL<_(upPNJH-{5ERR^m^NCL|BskE3Kg z%TI!xl;<0u0pt?&BXlgGH%R_lBW)QzNG6dV05LY6D^)nQ$)i{!Is^3hzcde2c zVEV^@*wFk#0_v7Cwuek-(JEgB7FZ$J2`2~&gcQUDqw`W= zV@ar#R%l^Z2M3+NIX>Yj!Pk5}?GEwZ0;J@WY+hPQS1J(Y6-M$AMPl$b&H_n-@hiQZ zsjsUaLd8pR7dFUfo=K@KxX|S?=-C$@QxeB6qnZq1qFcte^4U>!#vzl&rKhE%Qj*QY zV91LJbW|lyhMczCK@D4Lf`fOKP1cA`-Qw#F;#yw^T6?SWX0tQpUnK^cW!0jrtZoZV zh3B0b=$&MCb1b8eoIDu{TA7r42w%bqf0!tGRI140d%6Wvpq7cncoceLAf$6chRP`` zY1b;0%E}04-gQ9xvry^pyz=TGD;iTwLpg+CLpr6XcW(kCaaoM$i@*! zgABG8c3Oh`atq2G@kvGD|IiUynI9RZD@s!AKC>W>Q%M-$nH~tereyPfel%-Kw+V$!Vr*lgLbH{gs2{AVOq6sgEc{=t|l&C@8Q@TZ&8lYad?~gN*ppWBTwr>nr&#! z_z#BtYF)Lbp_&JX9jPhGlL56U(&I*u(IPe6t1Vd7)KWpYIjZkxFO)(R@NNS4FhXV% z^tK0jdZR?fqiUGTTE1;U*CwyzbgaCcbUCnUjJ9_JK=tuXGuKjZ64|JvIbiE_sA=R&S&VwvY`-> zi`niW^ur#;$x@3?v4X(Uy+AK;pRY&q&~PYsX9A`|fF?c6pIxxan~GgTyy&(NKyJP? z0mX$Yz`hszOYs z1yux-oVozB{#z|UCMpxCk+NeOYEcHAf?x`3HTF1lA9HCB;QD2P8_~$!V5fpEaoP@D zXN}y1{?UGnEYiY59xd+|o`+QRG-8hjgQ_e%vm*n=iWL@mbcgjOvjvG9(Wwm2?GDmZ zS4~xBo$5RGBz)tCT+$=;3G2PyjC-(T*qyO#VIo8pG8U}H;H+Z5KPv+}XJm^*|GDXV_6?e~!L3Ea*e9B*v*i)}jgEQCw8J?{ zP6KCeWO3SI23aJ@bPg(Kz24eZCvTL3@i&jE;_MPF=tICE)Ow=p!U6*>ge~`|1gI`! z_jpQ>A9Fda+7Ftv!y~_8R}ATM4>|ZSyAL^gQK&*8Cdbv<-=LkXH_~mKoU$7oSx^+o zFv?cc_@%qeCvLXi*sCXA$-^U5@FU0P-Rws=ptSZr7To<@ z)|;#i=kRtu7Q!Q1Xm8_a_4Ma(zDB!eGIn8OPrc#?c2=s@?OuSGqR|^h1w0S4X+0} z5+>AvSBNWyY;T?-h9P38q+&u@`>k5<(18L$RkMSM*NBTR%h;ND5+DF=mYroL1p-n5 zqTj=^qJ$K2vjWr=vSzqQuwcnNsj@_6AsB>@v*AgyM1q%`2{GBL8Rpvs1TIm=Vol+X zN+G%q|M8zIp?wSP)%Q0cPETsl+SDPlHa=2|fjS$a4n1ZgmBmzK@1+F$ba=C5AC&$b zpA}r)qQamJD*iK1rM*L)U_OYzeC>8ttGB+d#9-c42pZTNYIefa9_2xo?i}h?x>hl< zbsp!4Y?rIaZbU@&U55EjJfmW2=GXoe4r4_aMIu=TPFL0QHV^%BN ze2}{IaM9c%R=})UW`JRHOIVAzfQfsKObJ774z55&FaSFGr2e+53C;0Ma%00SOHdEuj)n*NhH^xcV1n(>EdS83uv1!mtAp`BXq${+B@?M1Mb%bx-|73F&74A2LY_jUc~tAl+DM8~*|N#v1tyTaOn| zBg_aiPu4gG7cFt@10?9eU90Mv3r>u=Rc8Y8fTq~=n2_qXrFIh{tRBg-C8{l$@}HtC z&b$dXZRx2x*=V#d3j>4V84w_H`hwcQjZX*l8b+3o*HK{GJe*38fk4^J!eFoEUhxB$ zNEB$;$-6F?RxjEx*6RI(k`KzVN?2*7qE%15So4aERJPB|Ws-Usf11|DzP5_NfMO7? zr!Dzh)}(>;j@9tdlXYCxCMtZBmPs{sB#m%dbvW2ImhW~-q*i&`RF$#c`vNt2Qj z&nBXs#Mz~<8f`jo%2$8*8>eIviBBZm<~N4YW)j&$A|}sJI7bSTNOAc~S<-p4CPiw3 zg#TPQOIgXX=`i2b;CQ>7D6N(d>83U_3OzJ7C&l!g@)ZAS*#t6T&C6(EW3=T4*0E1# zJIY&2L#{L9=cWBx5%mv#7VXhJ{TH2=Usma--_!fD#j+w}kFI!JhJL!TF?|@eVE#9I zFL{nA%g9twf9Oe)Ik14u2(+@+F_bCjH6#7xYl6$h!-!;|Ps->N5`9VW z&+Nf8yIUAZG+m`*NiBwD>^LY;^6j5}%-j3j@T^Gdbo7~zC+qT>*QD-MWhCqqjzwbd z3OE~WVrV7Tm|v|XM;I5ZxN#B{Z{IR%B{br4Si#!0>Mw!X8U_Pus@M9IT%~svf1uMw zh_ANvWeunkHIU{BLr&V_oT^mIU8V2bsf*&nfgF;HXR>eT&l>NB_c2~oRTZj2GgP%= zC~uq)rLZn#DXc0PC%J6ON?j|Y7rD=n(IVw;#yTlgG`IK;FDokHTm4ws8t)5oVMW@7 zb>$m-ye`5Cy)QSp!DOX{YfSYPp~``6@N7f2(xdu}Rjye}Q5RyaaVLi1jAwl4lggcy zO9m!2I_d5sTvwl{M{`0ebqV%P{z6hUsXF+#Yk!~MkIJE+#vk!7EE?~cm24l#JUt*+ z`R-*G+K0w>Q!I6bwQM;q$4&o>_>gJ`!`B8ymz+M%yqEkHZ_V4J9_HDsm&i=h7%!Av zs=MZfYIR(9sxV>azeY7xE!CHE8ktn6na@~9`(2DFWg(YL zuKUsyugVYVmr(oFVYwsEsOWk!N0rJC+I($5Hl~O22V*v$%9Mwy-fBF7=&k<394d_A~wD0udG5 zc9_A9?@#vZDnTuEzYHpgiso~5Hr=H?#S&TWjO{VnrMc{ajHyWHM#NCIH7BC@iB$p{ zA^6s`;M}-lKCBh$Bb0cm#xC7HnkQ+oP}UCp(j*jb56NHVMUwqVHk(>SD=yQ^m;B9f zm8wRb*|q%AVn${o**Yb@SHe=-9s_Smwn<#^&&8TC^H8XQ^oIhsa)P>WbD(1IwRZZs z#LH@?)BUK6$hX|BD8NedP>h|UF;%6n#Q!kfgvOqxrj)AVGw~c}rx!v@^4mk~3<)>s zakoT2NuY0#jZ`pi(|`g%`3bLD;4CX0=iD9r8jaVjd{c#`UlZd2ejo(?5i9zX;++;= z1Z<23Y=i6s;M6d4t8<&-!j-}%6a2SreYE^wgufDSJiE6(6-#5og_(A_Us0GcT+~ku zyoBvUMcVa#MEOkXBrp{a$&K`^;w&-isFyVRWVs|wuSgm&0xZ&nO?mlnOIxrHakHlv z%+CFk8u~7-p9@qHY&@653FmE!9zRUv%)gNT#V2j_y2mm4Ey#cDxJqKj`1 zGH>W?e?Qyzd87%Q{-7?M{;d%A`{D2d`n?Sn=pWCX17Io^86-Z+&sH+LE0g^Yx5QvX|}gSR0#IxwBg+bPuD= zJ}EkGZf+6g5^x%)E37-NiZ9Iv(gHRN__#BP|bR z_~60KLtp&Qmj4}yI+fe{eaX}xUQ9kOE`EuhXJfNkTiH)Y!SC=$p7D-y(BB<2!DVj= zitcG4CAtcJGRlXXiwJyAA6#AkRmCfWyBTcFPa{Pn$n%+CN*L zY0%U^c}E``kzXEpkrT1=_4`ng$ z3*+CjbHI*oD?^X(1#-Gg9iCnJ=NhcPH*R!rfaEs@@pD$l>!fSe>$4?in`wXIFRAxz zIBXxtC2GdHNk{RBW)PkV2L>Lo^TF_&51Jg5G8a~`}%@2h+08wN-&Jf$%?2Fwqx-X6m`!Quk( z^$a3@CDKt zU%(o;t-lwNe^#@9&@3gM#3^$Pvr=J@33>2nYttB-k*~0+tTY{lOWezip>%$r$W<_UwB?$IeZ-P^ z4z2szqicKR6NEf_iA#*Wu)D>lLA#Djv@wn9hq4ypdT3%=tmlAIS?oE%2PB4Gy*sgI z4bi_Tsz@JsiOFUR|I;=x7a5kOGV|6GghUN zmOIjTz{D-3(@Gr18@OZuVp@dQz|F-8{)qq4Kgl;iT2`g5(dV4@Z=z&kP0AHSiz?pr|1?45=)+R4 zo@OZmH7P44T?qc^b|SmpsCxa^`e3s$udit=Ykr`fdT=r=;FX=bps%`AhKk~yvMAVh ztS}6!%KhU@RjiMHm7eI0nw27nwFHshaDsx9tyo`_5~6b| zILv)p<)L7NR&gnEI@hv_eVWUQP}gNBW5RbR7m=#-ck$^!6pQdmb6Z!%aFMn_OYcg2 z!u}w=+8)c%io)-Bp&bc79O5_nJsPd(cYj`&gW$k+`eGyZxc7B)cbAck4cFy2?U{hb zw1Wi3I2M0drO{Z7kXOlCo2$PX5zJ>F?T)ESgSf4DE>6=aE!@nj9@$}dDjxB`j81qLhx(U(u+;NflE=d7F^x(+-yLV0LLc~ZnH(7uQ z-j4sDH8G6WJp&M9#1df>J3AQUBNbU^7u)vMFr^Dp@Ju%p&~tze^wQ8?qq=zTabe~G zhss4Hb+26pJk6*=Kz zc{tqck4AztL0*rEaOMd#YJAw;UP&Wiz?IEEE|i8j2h|;Nrrht z)M5B}*z~FFCkK6xgO`a+c$0oSFTH$>3L~x@ParaEO?uuYN#z`*VXD`7gVNd+% zds4zKK_WpCW_}_+pLYr}Mj`;LrUs+nw>CQ3wfHQ3$35&CydFW5dKiP}5t@F+-{=E! z8oJ3~u1R+{Rv~GyGF&c;fTFx`LR%`D(O)N2LI>gtbaUoj>18v z9OCLkQ#Pd!a^*mqz$qeA&=4keVvkDWjmP!xy=uqvVkt*CV9eVHe4z6eg4SZ+xC+3~5z^Wlo91X4MNDF*aXNYf3E zb-*->?ivfE_#O_(zQITR{{qD$GzsoLpIb!9S^{O-p}tb^EeTqilWHpoR(`{2&kC|1 zFRhcLo>yqQyq3vwR-U_2&F604)T!U5qS{^`xFL;cvRPu)Y{cYqpUYVb)mHFOvW7%+ zNf1E({G|?is5;f_HuEw+kH=uskE{f>GMaO zWLr9VsgT~|m!FVUNKV91Wt#q+-Ej6tOB-`gMogY*)dN+|I}ms&C8~9YP^^!FcS3Ni zQ~hA7V_EQK%XD|yDr;73N{a;2XP}Bwa8ue7r&YMA@!KgmVsF7*q~s(nPSWHfzCM6O zjY=;cBXOJTwLCJH z)gY6)DLs)$*)(sl7fI31*Lh?bl1ie23Hbn`!2l89go_DGE<~St3d!LB$3ZRzX!R5q z%Ao|BVX#ySuQ6JnAZCHhaW)}Dy_r+kS+w3$X_Wt008#?2{aQ@dRKu=3(wgnY%Q8jI zMG4I(k5PSzLsXk7Dkbd`#-}nv$!I@wtkuG%yge1N*_pY-f_`))va98gr!FD@f=4;> zRE}p-Tuz6r#^khX2^K$PeCQ$SZxIM4dF98lPa41(43~hHXrmv}`3UP(BeL_zS(fq; zeeIg@V^JKYBM8y3_R6J(4-}3|B_nPkoQ_sfkH(nMjf1?gM_L=mTf%PgCP*d_zW@n} z7sgwS4Y*kw!_UGPmW|wLp3ppz6aMSezE2jlYToCQMxeA0*l8cI4eOgcJ}ABYRq~>i z69xLJrbNk!6E=Pyt3<57+(bp{sp_QFk6mSO%*`WGGvO;9kCNl8m{Ov}X>5E{DV}Wd zUUG|b$UlEXbk3XXYiiIi1uv1?o*8#wiL0z0D8x)z^`(^Qs$;8uk4(+*X zca2VEPmpZlEY`NiZPtmDoj&1AY}6!cWotR0(zI!o9w|{;vlb1u{4|N0^~mo38#CyM z6Ciagp!{G+dKas&!Rc#ooxcXxXpp3bH`%%vb}4_!t+r@O^LNmi46;0uTVxf?R!{Df zwYkaJ@a|bVp`0$=RBIqMRN)3&%^{)oubed&I_k=`uO=c!kLSYH)|GdY*SET+zaPOh zukJ>_^cAkh5rC`b)>pUVZr#5o)@)2~8#l=sj{Q};^tA!+2qJx7=#T#BkN))a^Zx-x K2CFClvI7923vA{9 diff --git a/imxweb/imx-modules/imx-api-pol.tgz-hash b/imxweb/imx-modules/imx-api-pol.tgz-hash new file mode 100644 index 000000000..71085417c --- /dev/null +++ b/imxweb/imx-modules/imx-api-pol.tgz-hash @@ -0,0 +1,8 @@ +E6573E34E07D56A25D5A5E5AE4B4B8944C02BAC5479D6A7870003C698401BC6E7D3B8FC6E11FA25628D36EAA6DD0F9A71021A0114B808ADF1FC86E7A17246DCE tsconfig.json +52822F64AEA2B42445A1A01278F864061078D3E60DF1F530F19FEA5E077643DE3CAC8B4A4F065F238F2ED260EF8A0AEC94E5711DF64D75DC9CEB1A4C35A865D8 rollup.config.js +565D30DE7CA344FE6BB6724E1208DEE942D0CEDCE31BDD06F11B8C9E8DA76466100FA64B33FB780C2822F4EAC1B170EF1A2E73BDE93498BCFA185923278A07EE .npmrc +6650C495A60736E43488B9095D7590C9C29DEA5285B0D225714A2E46A4A8364DDB5721492D30B69E501642360A6FB2D76986DC2D95239835DEA8D188DB3AD58B package-lock.json +F4F7D44A8143CBD66C1DF60EDF8BA4D4EDD623264FDBCAA1C62CA5868EEA34E1FE68A2B190E5CB8B98BD1E0CD7DAD0BFF6974217676A53A43CB77AD784C220DC imx-api-pol.es5.js +B83F4FA14FD6DD1C6BE267DDD888180499F635A0BD227F27AA9DB61983D96A5B57DA6014AF8727FEFCF93F5CED1C488154D287DF77971B002451231E7A2AEADC imx-api-pol.umd.js +A9C0B61DDB3B45B9F836C8BF15DA912EC954166DEEC833354ADD50790761401B38B559EDFCF7794D6FD4747F668376F6FF86DC5AB0DE5415BFAC3FC5451815A9 index.d.ts +31243B62C6F90BC0CB1E5E4F301132CB009987AB8A8E71A7ECE04ECE96B1230366DC42336DF69B150C28D293E541F774103FA9CD1F2D45E8070274AD0124EC2D TypedClient.d.ts \ No newline at end of file diff --git a/imxweb/imx-modules/imx-api-pol.tgz-version b/imxweb/imx-modules/imx-api-pol.tgz-version new file mode 100644 index 000000000..7f3a19c60 --- /dev/null +++ b/imxweb/imx-modules/imx-api-pol.tgz-version @@ -0,0 +1 @@ +9.3.15 \ No newline at end of file diff --git a/imxweb/imx-modules/imx-api-qbm.tgz b/imxweb/imx-modules/imx-api-qbm.tgz deleted file mode 100644 index 46b5dbf33864bcd1ed0c0f63b08e8e7a1b59bc48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 70381 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

+qSi_ZENE< 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⋘4md$k(P0P7MAs-_j&&8H& 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 z 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 diff --git a/imxweb/imx-modules/imx-api-rmb.tgz-hash b/imxweb/imx-modules/imx-api-rmb.tgz-hash new file mode 100644 index 000000000..c5e9f6fcb --- /dev/null +++ b/imxweb/imx-modules/imx-api-rmb.tgz-hash @@ -0,0 +1,8 @@ +E6573E34E07D56A25D5A5E5AE4B4B8944C02BAC5479D6A7870003C698401BC6E7D3B8FC6E11FA25628D36EAA6DD0F9A71021A0114B808ADF1FC86E7A17246DCE tsconfig.json +52822F64AEA2B42445A1A01278F864061078D3E60DF1F530F19FEA5E077643DE3CAC8B4A4F065F238F2ED260EF8A0AEC94E5711DF64D75DC9CEB1A4C35A865D8 rollup.config.js +565D30DE7CA344FE6BB6724E1208DEE942D0CEDCE31BDD06F11B8C9E8DA76466100FA64B33FB780C2822F4EAC1B170EF1A2E73BDE93498BCFA185923278A07EE .npmrc +86AFA56072C21BE0DEB85EA60F5A98562045696FB5CA8CC532E4ABB64B892D610802815239802FCE0F31B3CF77A7724761DC8BD7EDCA62FC414F87C576F33A75 package-lock.json +929B4F3A86A56C8E588A2AB61813A717CC98DA671FBEAB38109DCBE50AA56674C575A9279B3D07BABC212B3D74564EC4CAFD00D0196B81A9C1BE47B6C0DC0E5C imx-api-rmb.es5.js +C69F70341DBEF2BD265DFF6984F80E2D3720C6ABA74DEB019AE33D93A52DD6BB48F2F5DC6CD04D9F5E777B9F5BD96776C3C49C3CFB28917716D052A3885DB361 imx-api-rmb.umd.js +A9C0B61DDB3B45B9F836C8BF15DA912EC954166DEEC833354ADD50790761401B38B559EDFCF7794D6FD4747F668376F6FF86DC5AB0DE5415BFAC3FC5451815A9 index.d.ts +387A84FF5CBABD45F4F12250C85142A72D454A7C18DD3A151FACF0AE013C8A87850CF55B0DAAA92DA8C22E33B384EF4191B0ED9DA948B7C239EE79E1151CB4A2 TypedClient.d.ts \ No newline at end of file diff --git a/imxweb/imx-modules/imx-api-rmb.tgz-version b/imxweb/imx-modules/imx-api-rmb.tgz-version new file mode 100644 index 000000000..b83a0529d --- /dev/null +++ b/imxweb/imx-modules/imx-api-rmb.tgz-version @@ -0,0 +1 @@ +9.3.14 \ No newline at end of file diff --git a/imxweb/imx-modules/imx-api-rms.tgz b/imxweb/imx-modules/imx-api-rms.tgz deleted file mode 100644 index 9a025df64393af20e7a5a8f6e92f3e84b719546d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22920 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 diff --git a/imxweb/imx-modules/imx-api-rms.tgz-hash b/imxweb/imx-modules/imx-api-rms.tgz-hash new file mode 100644 index 000000000..681e83967 --- /dev/null +++ b/imxweb/imx-modules/imx-api-rms.tgz-hash @@ -0,0 +1,8 @@ +E6573E34E07D56A25D5A5E5AE4B4B8944C02BAC5479D6A7870003C698401BC6E7D3B8FC6E11FA25628D36EAA6DD0F9A71021A0114B808ADF1FC86E7A17246DCE tsconfig.json +52822F64AEA2B42445A1A01278F864061078D3E60DF1F530F19FEA5E077643DE3CAC8B4A4F065F238F2ED260EF8A0AEC94E5711DF64D75DC9CEB1A4C35A865D8 rollup.config.js +565D30DE7CA344FE6BB6724E1208DEE942D0CEDCE31BDD06F11B8C9E8DA76466100FA64B33FB780C2822F4EAC1B170EF1A2E73BDE93498BCFA185923278A07EE .npmrc +A7823BFEFD618DA121D929CB01BC0E802C1A1BB7E5070488D29636C16064D9DE1A735EFB587EB59E2B7F2E21BA24147EAEBFB8F9C7640FDE8AFDA48B4014DB60 package-lock.json +78F8EB3AD6E84838A65250B501F66A4903E98FD9CBFA638EF5F69835E2E84FB7330C31331D4D171EEF47ADA38E317BE0D2BD908CCC589D12179EF97773071E07 imx-api-rms.es5.js +4D72FBBED125028B803B35C1400361EE739256D50162516B69A38FC1B7C81EE651B85E22ADEAE38B13190DDA70DC5BFAA9864C0F14923366B9F4006E9951F9C3 imx-api-rms.umd.js +A9C0B61DDB3B45B9F836C8BF15DA912EC954166DEEC833354ADD50790761401B38B559EDFCF7794D6FD4747F668376F6FF86DC5AB0DE5415BFAC3FC5451815A9 index.d.ts +E25B0F42CD3319EA3399980BF257E98553BD7F91DB3025211112EAAB263B428CFCF5A0D3CE44213586CCC1FA1A7E3906ECCF3EFABF1169A2387EA3C35CECD13E TypedClient.d.ts \ No newline at end of file diff --git a/imxweb/imx-modules/imx-api-rms.tgz-version b/imxweb/imx-modules/imx-api-rms.tgz-version new file mode 100644 index 000000000..7f3a19c60 --- /dev/null +++ b/imxweb/imx-modules/imx-api-rms.tgz-version @@ -0,0 +1 @@ +9.3.15 \ No newline at end of file diff --git a/imxweb/imx-modules/imx-api-rps.tgz b/imxweb/imx-modules/imx-api-rps.tgz deleted file mode 100644 index 1ca610ba9390566afa7f0c4a4d95700097227f67..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27001 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�&GV-CH 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 t&ZkE|BWt(n#mWE=|QzW_7I&CCD* diff --git a/imxweb/imx-modules/imx-api-rps.tgz-hash b/imxweb/imx-modules/imx-api-rps.tgz-hash new file mode 100644 index 000000000..b4781008b --- /dev/null +++ b/imxweb/imx-modules/imx-api-rps.tgz-hash @@ -0,0 +1,8 @@ +E6573E34E07D56A25D5A5E5AE4B4B8944C02BAC5479D6A7870003C698401BC6E7D3B8FC6E11FA25628D36EAA6DD0F9A71021A0114B808ADF1FC86E7A17246DCE tsconfig.json +52822F64AEA2B42445A1A01278F864061078D3E60DF1F530F19FEA5E077643DE3CAC8B4A4F065F238F2ED260EF8A0AEC94E5711DF64D75DC9CEB1A4C35A865D8 rollup.config.js +565D30DE7CA344FE6BB6724E1208DEE942D0CEDCE31BDD06F11B8C9E8DA76466100FA64B33FB780C2822F4EAC1B170EF1A2E73BDE93498BCFA185923278A07EE .npmrc +C5704137F79EB2367E6BE59ACE98D80CE56754F94BB127C31C9340C904EE80C65EC2ABC3589D2FCA1EFEBA4A46F7D9B51215749B17140713B42EE4969DE69518 package-lock.json +8453C5AA074E6D08FFDBC48610666382964E84A634B5123A9EBBD82F6B3A6A6ABFC550C43EF4168F6CD9F8A29C8A707877F4A7208346C529197C2BD6EEDD2836 imx-api-rps.es5.js +D668E526C8EEF45F3D82E93DA5482E45AF2D240D3FBCB371DF496247755681A91F568BF0331A3764FB004201A305E0731965E4C8A1220C92DE8E52EA718982AD imx-api-rps.umd.js +A9C0B61DDB3B45B9F836C8BF15DA912EC954166DEEC833354ADD50790761401B38B559EDFCF7794D6FD4747F668376F6FF86DC5AB0DE5415BFAC3FC5451815A9 index.d.ts +FE1124AA9FC9EA22AACA5950ED2353F3E5A01F2420199AB1CD5F7B77514D1B197FE3C00DBC36CFA58790C1AAC6BFF92A0E75F97AF7B808F0200BAD215975D2E9 TypedClient.d.ts \ No newline at end of file diff --git a/imxweb/imx-modules/imx-api-rps.tgz-version b/imxweb/imx-modules/imx-api-rps.tgz-version new file mode 100644 index 000000000..b31669625 --- /dev/null +++ b/imxweb/imx-modules/imx-api-rps.tgz-version @@ -0,0 +1 @@ +9.3.13 \ No newline at end of file diff --git a/imxweb/imx-modules/imx-api-sac.tgz b/imxweb/imx-modules/imx-api-sac.tgz deleted file mode 100644 index e6d9c8a8d5d98cb4753206c98621e84038a78b96..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23970 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 InE;Fi0NTtLJpcdz diff --git a/imxweb/imx-modules/imx-api-sac.tgz-hash b/imxweb/imx-modules/imx-api-sac.tgz-hash new file mode 100644 index 000000000..d29212d5a --- /dev/null +++ b/imxweb/imx-modules/imx-api-sac.tgz-hash @@ -0,0 +1,8 @@ +E6573E34E07D56A25D5A5E5AE4B4B8944C02BAC5479D6A7870003C698401BC6E7D3B8FC6E11FA25628D36EAA6DD0F9A71021A0114B808ADF1FC86E7A17246DCE tsconfig.json +52822F64AEA2B42445A1A01278F864061078D3E60DF1F530F19FEA5E077643DE3CAC8B4A4F065F238F2ED260EF8A0AEC94E5711DF64D75DC9CEB1A4C35A865D8 rollup.config.js +565D30DE7CA344FE6BB6724E1208DEE942D0CEDCE31BDD06F11B8C9E8DA76466100FA64B33FB780C2822F4EAC1B170EF1A2E73BDE93498BCFA185923278A07EE .npmrc +E9D96285E5605F83D361337C45EAF837C6B98953E0E6CBAA6E32DE1AD79456CCB96C83692777D2F2B9E4E88628744F5684B9124A2E466C4476EC15CABDF1C02B package-lock.json +E2940C3988A70E1207CEA91B4DC037ACA8F074C5C12EF893C3B271AA38D13CA90A83EBD094C9F61EC7E323D1B6E87B76C214861378DC5E8BCBB4AFE5C6B4118A imx-api-sac.es5.js +A448F5720F129E04732623CBBA1FC774B440A9C3E109B8947BFA284334B4F67098486CB89BDD1140EEC23C1AEB8B70433131F2B5CC443F28C3A5B7021E839BAA imx-api-sac.umd.js +A9C0B61DDB3B45B9F836C8BF15DA912EC954166DEEC833354ADD50790761401B38B559EDFCF7794D6FD4747F668376F6FF86DC5AB0DE5415BFAC3FC5451815A9 index.d.ts +EA80B5F2C3DBFB37A9EC0B47F75E27B9FF22790FE79345DD18B439EF814D5A81D4BBF3DD2573AF84DB21383259AE116B615795A5541962A95C9D21577DB67ECA TypedClient.d.ts \ No newline at end of file diff --git a/imxweb/imx-modules/imx-api-sac.tgz-version b/imxweb/imx-modules/imx-api-sac.tgz-version new file mode 100644 index 000000000..b31669625 --- /dev/null +++ b/imxweb/imx-modules/imx-api-sac.tgz-version @@ -0,0 +1 @@ +9.3.13 \ No newline at end of file diff --git a/imxweb/imx-modules/imx-api-tsb.tgz b/imxweb/imx-modules/imx-api-tsb.tgz deleted file mode 100644 index 36900ff1a9b2f9ac4d522f4156e79a0227bfd8c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 66352 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 diff --git a/imxweb/imx-modules/imx-api-tsb.tgz-hash b/imxweb/imx-modules/imx-api-tsb.tgz-hash new file mode 100644 index 000000000..d4bafad61 --- /dev/null +++ b/imxweb/imx-modules/imx-api-tsb.tgz-hash @@ -0,0 +1,8 @@ +E6573E34E07D56A25D5A5E5AE4B4B8944C02BAC5479D6A7870003C698401BC6E7D3B8FC6E11FA25628D36EAA6DD0F9A71021A0114B808ADF1FC86E7A17246DCE tsconfig.json +52822F64AEA2B42445A1A01278F864061078D3E60DF1F530F19FEA5E077643DE3CAC8B4A4F065F238F2ED260EF8A0AEC94E5711DF64D75DC9CEB1A4C35A865D8 rollup.config.js +565D30DE7CA344FE6BB6724E1208DEE942D0CEDCE31BDD06F11B8C9E8DA76466100FA64B33FB780C2822F4EAC1B170EF1A2E73BDE93498BCFA185923278A07EE .npmrc +5ACEBD4DC745DA9CE2D036CB7F2EA66869B6F6B54A9D22046E5C64ED874200A6A2A774324A2437B51038DA9C53DECBB788CF10A325E0A91DAB950BDC9269B285 package-lock.json +08D32E9ED2B997E7AF64A728E56BB3481601CF6B51FBCF9C36A8A391EA1AD076CA651E5EE528B11578E32FC5BDE12BFBCBDBD5A00ACC87FD70BB5470B20FFEC7 imx-api-tsb.es5.js +3D239E9A14EF960A576D0F5C0476C161256A768D889F300EABBDAA00857D94E1AEE8A70957CF91EC2015125183691684AC0B3514F545DE8C85BA4D81ABCA6C52 imx-api-tsb.umd.js +A9C0B61DDB3B45B9F836C8BF15DA912EC954166DEEC833354ADD50790761401B38B559EDFCF7794D6FD4747F668376F6FF86DC5AB0DE5415BFAC3FC5451815A9 index.d.ts +EEE6A4B2D50739CBEA7D7F3E33EEE2C2FBC924E9A25AFCAC30982732F04B6582BF3BCA934105666C733EFC1EA685F7FBA2A804EDCBB539B01784FD2DABE5ECF4 TypedClient.d.ts \ No newline at end of file diff --git a/imxweb/imx-modules/imx-api-tsb.tgz-version b/imxweb/imx-modules/imx-api-tsb.tgz-version new file mode 100644 index 000000000..b83a0529d --- /dev/null +++ b/imxweb/imx-modules/imx-api-tsb.tgz-version @@ -0,0 +1 @@ +9.3.14 \ No newline at end of file diff --git a/imxweb/imx-modules/imx-api-uci.tgz b/imxweb/imx-modules/imx-api-uci.tgz deleted file mode 100644 index 51648c9cbac7c6f4575b6898d91fc6e55d7ed7fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7724 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 Z=o;6lxtgoFn(Lds{tq!ma_<0g008*5@L&J{ diff --git a/imxweb/imx-modules/imx-qbm-dbts.tgz b/imxweb/imx-modules/imx-qbm-dbts.tgz deleted file mode 100644 index b54da09fde6d15ee1f347c6724d1c7917d8ee6de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 150441 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⪙93P-3L&&QK*bD>wrY~lVewl9!VHPU4ZcrV9fY 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 z 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 I(E!>E0AId=1ONa4 diff --git a/imxweb/install-local-packages.js b/imxweb/install-local-packages.js new file mode 100644 index 000000000..74c75fc5e --- /dev/null +++ b/imxweb/install-local-packages.js @@ -0,0 +1,68 @@ +const child_process = require('child_process'); +const fs = require('fs'); +const path = require('path'); +const readline = require('readline').createInterface({ input: process.stdin, output: process.stdout }); + +// --skip-dialog will auto overwrite feeds if imx-modules are present + +const imxDir = 'imx-modules'; +const question = (str) => new Promise((resolve) => readline.question(str, resolve)); +const steps = { + start: async () => { + return steps.checkForModules(); + }, + checkForModules: async () => { + if (fs.existsSync(imxDir)) { + if (process.env.npm_config_skip_dialog || process.env.npm_config_buildid) { + return steps.overwrite(); + } + const answer = await question('Found imx-modules - Do you want to overwrite the feeds? (y/n)'); + return answer.toLowerCase() === 'y' ? steps.overwrite() : steps.keepDefault(); + } + console.log('No imx-modules found, keeping default feeds.'); + return steps.end(); + }, + overwrite: async () => { + overwrite(); + return steps.end(); + }, + keepDefault: async () => { + console.log('Keeping default feeds.'); + return steps.end(); + }, + end: async () => readline.close(), +}; + +steps.start(); + +function overwrite() { + console.log('Overwriting with...'); + let installArg = ''; + fs.readdirSync(imxDir) + .filter((file) => file.endsWith('.tgz')) + .forEach((file) => { + if (file.includes('imx-')) { + const filePath = path.join(imxDir, file); + const baseName = path.parse(file).name; + installArg += '@' + imxDir + '/' + baseName + '@' + filePath + ' '; + console.log(filePath); + } else if (file.includes('cadence-icon')) { + const filePath = path.join(imxDir, file); + installArg += '@elemental-ui/cadence-icon' + '@' + filePath + ' '; + console.log(filePath); + } else if (file.includes('core')) { + const filePath = path.join(imxDir, file); + installArg += '@elemental-ui/core' + '@' + filePath + ' '; + console.log(filePath); + } + }); + + const child = child_process.spawnSync('npm', ['i', installArg, '--save=false'], { encoding: 'utf8', shell: true }); + if (child.status === 0) { + console.log('Overwrite Finished'); + } else { + console.log('There was an error:') + console.error(child.output); + process.exit(1); + } +} diff --git a/imxweb/nx.json b/imxweb/nx.json new file mode 100644 index 000000000..b7f8f83d2 --- /dev/null +++ b/imxweb/nx.json @@ -0,0 +1,48 @@ +{ + "$schema": "./node_modules/nx/schemas/nx-schema.json", + "defaultBase": "master", + "workspaceLayout": { + "appsDir": "projects", + "libsDir": "projects" + }, + "parallel": 5, + "targetDefaults": { + "build": { + "cache": true, + "dependsOn": ["^build"], + "inputs": ["production", "^production"] + }, + "lint": { + "cache": true, + "inputs": ["default", "{workspaceRoot}/.eslintrc.json", "{workspaceRoot}/.eslintignore", "{workspaceRoot}/eslint.config.js"] + }, + "test": { + "cache": true, + "inputs": ["testing", "^production", "{projectRoot}/karma.conf.js"] + }, + "e2e": { + "cache": true + } + }, + "namedInputs": { + "sharedGlobals": ["{workspaceRoot}/package.json", "{workspaceRoot}/shared/**/*"], + "default": ["{projectRoot}/src/**/*", "sharedGlobals"], + "production": ["default", "!{projectRoot}/**/*.spec.ts"], + "testing": ["default"] + }, + "generators": { + "@nx/angular:application": { + "style": "scss", + "linter": "eslint", + "unitTestRunner": "karma", + "e2eTestRunner": "none" + }, + "@nx/angular:library": { + "linter": "eslint", + "unitTestRunner": "karma" + }, + "@nx/angular:component": { + "style": "scss" + } + } +} diff --git a/imxweb/package-lock.json b/imxweb/package-lock.json index 6514de508..d478ebd26 100644 --- a/imxweb/package-lock.json +++ b/imxweb/package-lock.json @@ -1,109 +1,117 @@ { "name": "imxweb", - "version": "9.2.1", - "lockfileVersion": 2, + "version": "9.3.0", + "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "imxweb", - "version": "9.2.1", + "version": "9.3.0", "hasInstallScript": true, "dependencies": { - "@angular/animations": "^14.2.12", - "@angular/cdk": "^14.2.7", - "@angular/common": "^14.2.12", - "@angular/compiler": "^14.2.12", - "@angular/core": "^14.2.12", - "@angular/forms": "^14.2.12", - "@angular/material": "^14.2.7", - "@angular/material-moment-adapter": "^14.2.7", - "@angular/platform-browser": "^14.2.12", - "@angular/platform-browser-dynamic": "^14.2.12", - "@angular/router": "^14.2.12", - "@elemental-ui/cadence-icon": "file:imx-modules/elemental-ui_cadence-icon.tgz", - "@elemental-ui/core": "file:imx-modules/elemental-ui_core.tgz", - "@ngx-translate/core": "^11.0.1", - "@ngx-translate/http-loader": "^4.0.0", - "@types/cytoscape": "^3.19.9", - "@types/cytoscape-edgehandles": "^4.0.0", - "@types/systemjs": "^6.1.1", - "applicationinsights-js": "^1.0.21", - "billboard.js": "^3.11.3", - "core-js": "^2.6.9", - "cytoscape": "^3.23.0", + "@angular/animations": "^18.2.2", + "@angular/cdk": "^18.2.2", + "@angular/common": "^18.2.2", + "@angular/compiler": "^18.2.2", + "@angular/core": "^18.2.2", + "@angular/forms": "^18.2.2", + "@angular/material": "^18.2.2", + "@angular/material-moment-adapter": "^18.2.2", + "@angular/platform-browser": "^18.2.2", + "@angular/platform-browser-dynamic": "^18.2.2", + "@angular/router": "^18.2.2", + "@ngx-translate/core": "^15.0.0", + "@ngx-translate/http-loader": "^8.0.0", + "billboard.js": "^3.12.4", + "core-js": "^3.37.1", + "cytoscape": "^3.29.2", "cytoscape-edgehandles": "^4.0.1", "d3-hierarchy": "^3.1.2", "d3-scale": "^4.0.2", - "element-resize-detector": "^1.1.13", - "file-saver": "^2.0.2", - "moment-timezone": "^0.5.38", - "ngx-logger": "^4.1.2", - "rxjs": "~6.5.4", - "smooth-scrollbar": "^8.5.1", - "swagger-ui-dist": "~4.12.0", - "systemjs": "^0.21.6", - "systemjs-plugin-babel": "0.0.25", - "tslib": "^2.0.0", + "fs-extra": "^11.2.0", + "moment-timezone": "^0.5.45", + "ng-recaptcha-2": "^14.0.0", + "ngx-logger": "^5.0.12", + "nx": "^19.7.0", + "rxjs": "^7.8.1", + "swagger-ui-dist": "~5.17.14", + "tslib": "^2.6.3", "url-polyfill": "^1.1.12", "web-animations-js": "^2.3.2", - "whatwg-fetch": "^3.0.0", - "zone.js": "~0.11.5" + "whatwg-fetch": "^3.6.20", + "zone.js": "~0.14.7" }, "devDependencies": { - "@angular-devkit/build-angular": "^14.2.12", - "@angular/cli": "^14.2.12", - "@angular/compiler-cli": "^14.2.12", - "@angular/language-service": "^14.2.12", - "@compodoc/compodoc": "^1.1.10", - "@types/d3-hierarchy": "^3.1.0", - "@types/d3-scale": "^4.0.2", - "@types/jasmine": "~3.6.0", - "@types/jasminewd2": "^2.0.10", - "@types/node": "^16.11.7", - "angular-in-memory-web-api": "^0.14.0", - "copy-and-watch": "^0.1.5", - "jasmine": "^3.4.0", - "jasmine-core": "^3.8.0", - "jasmine-spec-reporter": "~5.0.0", + "@angular-devkit/build-angular": "^18.2.2", + "@angular-devkit/core": "^18.2.2", + "@angular-devkit/schematics": "^18.2.2", + "@angular-eslint/builder": "^18.2.2", + "@angular-eslint/eslint-plugin": "^18.2.2", + "@angular-eslint/eslint-plugin-template": "^18.2.2", + "@angular-eslint/schematics": "^18.2.2", + "@angular-eslint/template-parser": "^18.2.2", + "@angular-eslint/utils": "^18.2.2", + "@angular/cli": "^18.2.2", + "@angular/compiler-cli": "^18.2.2", + "@angular/language-service": "^18.2.2", + "@compodoc/compodoc": "1.1.25", + "@nx/angular": "^19.7.0", + "@nx/js": "^19.7.0", + "@nx/workspace": "^19.7.0", + "@schematics/angular": "^18.2.2", + "@types/cytoscape": "^3.21.4", + "@types/cytoscape-edgehandles": "^4.0.4", + "@types/d3-hierarchy": "^3.1.7", + "@types/d3-scale": "^4.0.8", + "@types/jasmine": "~5.1.4", + "@types/jasminewd2": "^2.0.13", + "@types/node": "^20.14.9", + "@typescript-eslint/eslint-plugin": "^7.14.1", + "@typescript-eslint/parser": "^7.14.1", + "angular-in-memory-web-api": "^0.18.0", + "copy-and-watch": "^0.1.8", + "eslint": "^8.44.0", + "eslint-config-prettier": "^8.8.0", + "jasmine": "^5.1.0", + "jasmine-core": "^5.1.2", + "jasmine-spec-reporter": "~7.0.0", "karma": "^6.4.3", - "karma-chrome-launcher": "~3.1.0", + "karma-chrome-launcher": "~3.2.0", "karma-cli": "^2.0.0", - "karma-coverage-istanbul-reporter": "~2.0.1", + "karma-coverage-istanbul-reporter": "~3.0.3", "karma-ie-launcher": "^1.0.0", - "karma-jasmine": "~4.0.0", - "karma-jasmine-html-reporter": "^1.5.0", - "karma-junit-reporter": "^1.2.0", - "karma-viewport": "^1.0.4", - "ng-mocks": "^14.5.2", - "ng-packagr": "^14.2.2", - "protractor": "^7.0.0", - "terser": "^5.15.1", - "ts-node": "~7.0.0", - "tslint": "~6.1.0", - "typemoq": "^2.1.0", - "typescript": "~4.6.4" + "karma-jasmine": "~5.1.0", + "karma-jasmine-html-reporter": "^2.1.0", + "karma-junit-reporter": "^2.0.1", + "karma-viewport": "^1.0.9", + "ng-mocks": "^14.13.0", + "ng-packagr": "^18.2.1", + "prettier": "3.3.2", + "terser": "^5.31.1", + "ts-node": "^10.9.2", + "typescript": "^5.4.5" }, "optionalDependencies": { - "imx-api-aad": "file:imx-modules/imx-api-aad.tgz", - "imx-api-aob": "file:imx-modules/imx-api-aob.tgz", - "imx-api-apc": "file:imx-modules/imx-api-apc.tgz", - "imx-api-att": "file:imx-modules/imx-api-att.tgz", - "imx-api-cpl": "file:imx-modules/imx-api-cpl.tgz", - "imx-api-dpr": "file:imx-modules/imx-api-dpr.tgz", - "imx-api-hds": "file:imx-modules/imx-api-hds.tgz", - "imx-api-o3e": "file:imx-modules/imx-api-o3e.tgz", - "imx-api-o3t": "file:imx-modules/imx-api-o3t.tgz", - "imx-api-olg": "file:imx-modules/imx-api-olg.tgz", - "imx-api-pol": "file:imx-modules/imx-api-pol.tgz", - "imx-api-qbm": "file:imx-modules/imx-api-qbm.tgz", - "imx-api-qer": "file:imx-modules/imx-api-qer.tgz", - "imx-api-rmb": "file:imx-modules/imx-api-rmb.tgz", - "imx-api-rms": "file:imx-modules/imx-api-rms.tgz", - "imx-api-rps": "file:imx-modules/imx-api-rps.tgz", - "imx-api-sac": "file:imx-modules/imx-api-sac.tgz", - "imx-api-tsb": "file:imx-modules/imx-api-tsb.tgz", - "imx-api-uci": "file:imx-modules/imx-api-uci.tgz", - "imx-qbm-dbts": "file:imx-modules/imx-qbm-dbts.tgz" + "@elemental-ui/cadence-icon": "^3.1.101", + "@elemental-ui/core": "^18.0.1", + "@imx-modules/imx-api-aad": "~9.3.0", + "@imx-modules/imx-api-aob": "~9.3.0", + "@imx-modules/imx-api-apc": "~9.3.0", + "@imx-modules/imx-api-att": "~9.3.0", + "@imx-modules/imx-api-cpl": "~9.3.0", + "@imx-modules/imx-api-dpr": "~9.3.0", + "@imx-modules/imx-api-hds": "~9.3.0", + "@imx-modules/imx-api-olg": "~9.3.0", + "@imx-modules/imx-api-pol": "~9.3.0", + "@imx-modules/imx-api-qbm": "~9.3.0", + "@imx-modules/imx-api-qer": "~9.3.0", + "@imx-modules/imx-api-rmb": "~9.3.0", + "@imx-modules/imx-api-rms": "~9.3.0", + "@imx-modules/imx-api-rps": "~9.3.0", + "@imx-modules/imx-api-sac": "~9.3.0", + "@imx-modules/imx-api-tsb": "~9.3.0", + "@imx-modules/imx-api-uci": "~9.3.0", + "@imx-modules/imx-qbm-dbts": "~9.3.0" } }, "node_modules/@adobe/css-tools": { @@ -119,145 +127,147 @@ "dev": true }, "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@angular-devkit/architect": { - "version": "0.1402.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1402.12.tgz", - "integrity": "sha512-LuK26pyaqyClEbY0n4/WIh3irUuA8wwmMmEj8uW4boziuJWv7U42lJJRF3VwkchiyOIp8qiKg995K6IoeXkWgA==", + "version": "0.1802.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.3.tgz", + "integrity": "sha512-WQ2AmkUKy1bqrDlNfozW8+VT2Tv/Fdmu4GIXps3ytZANyAKiIvTzmmql2cRCXXraa9FNMjLWNvz+qolDxWVdYQ==", "dev": true, "dependencies": { - "@angular-devkit/core": "14.2.12", - "rxjs": "6.6.7" + "@angular-devkit/core": "18.2.3", + "rxjs": "7.8.1" }, "engines": { - "node": "^14.15.0 || >=16.10.0", + "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-devkit/architect/node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/@angular-devkit/architect/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "node_modules/@angular-devkit/build-angular": { - "version": "14.2.13", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-14.2.13.tgz", - "integrity": "sha512-FJZKQ3xYFvEJ807sxVy4bCVyGU2NMl3UUPNfLIdIdzwwDEP9tx/cc+c4VtVPEZZfU8jVenu8XOvL6L0vpjt3yg==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "2.2.0", - "@angular-devkit/architect": "0.1402.13", - "@angular-devkit/build-webpack": "0.1402.13", - "@angular-devkit/core": "14.2.13", - "@babel/core": "7.18.10", - "@babel/generator": "7.18.12", - "@babel/helper-annotate-as-pure": "7.18.6", - "@babel/plugin-proposal-async-generator-functions": "7.18.10", - "@babel/plugin-transform-async-to-generator": "7.18.6", - "@babel/plugin-transform-runtime": "7.18.10", - "@babel/preset-env": "7.18.10", - "@babel/runtime": "7.18.9", - "@babel/template": "7.18.10", - "@discoveryjs/json-ext": "0.5.7", - "@ngtools/webpack": "14.2.13", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.2.3.tgz", + "integrity": "sha512-uUQba0SIskKORHcPayt7LpqPRKD//48EW92SgGHEArn2KklM+FSYBOA9OtrJeZ/UAcoJpdLDtvyY4+S7oFzomg==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "2.3.0", + "@angular-devkit/architect": "0.1802.3", + "@angular-devkit/build-webpack": "0.1802.3", + "@angular-devkit/core": "18.2.3", + "@angular/build": "18.2.3", + "@babel/core": "7.25.2", + "@babel/generator": "7.25.0", + "@babel/helper-annotate-as-pure": "7.24.7", + "@babel/helper-split-export-declaration": "7.24.7", + "@babel/plugin-transform-async-generator-functions": "7.25.0", + "@babel/plugin-transform-async-to-generator": "7.24.7", + "@babel/plugin-transform-runtime": "7.24.7", + "@babel/preset-env": "7.25.3", + "@babel/runtime": "7.25.0", + "@discoveryjs/json-ext": "0.6.1", + "@ngtools/webpack": "18.2.3", + "@vitejs/plugin-basic-ssl": "1.1.0", "ansi-colors": "4.1.3", - "babel-loader": "8.2.5", - "babel-plugin-istanbul": "6.1.1", - "browserslist": "^4.9.1", - "cacache": "16.1.2", - "copy-webpack-plugin": "11.0.0", - "critters": "0.0.16", - "css-loader": "6.7.1", - "esbuild-wasm": "0.15.5", - "glob": "8.0.3", - "https-proxy-agent": "5.0.1", - "inquirer": "8.2.4", - "jsonc-parser": "3.1.0", + "autoprefixer": "10.4.20", + "babel-loader": "9.1.3", + "browserslist": "^4.21.5", + "copy-webpack-plugin": "12.0.2", + "critters": "0.0.24", + "css-loader": "7.1.2", + "esbuild-wasm": "0.23.0", + "fast-glob": "3.3.2", + "http-proxy-middleware": "3.0.0", + "https-proxy-agent": "7.0.5", + "istanbul-lib-instrument": "6.0.3", + "jsonc-parser": "3.3.1", "karma-source-map-support": "1.4.0", - "less": "4.1.3", - "less-loader": "11.0.0", + "less": "4.2.0", + "less-loader": "12.2.0", "license-webpack-plugin": "4.0.2", - "loader-utils": "3.2.1", - "mini-css-extract-plugin": "2.6.1", - "minimatch": "5.1.0", - "open": "8.4.0", + "loader-utils": "3.3.1", + "magic-string": "0.30.11", + "mini-css-extract-plugin": "2.9.0", + "mrmime": "2.0.0", + "open": "10.1.0", "ora": "5.4.1", - "parse5-html-rewriting-stream": "6.0.1", - "piscina": "3.2.0", - "postcss": "8.4.31", - "postcss-import": "15.0.0", - "postcss-loader": "7.0.1", - "postcss-preset-env": "7.8.0", - "regenerator-runtime": "0.13.9", + "parse5-html-rewriting-stream": "7.0.0", + "picomatch": "4.0.2", + "piscina": "4.6.1", + "postcss": "8.4.41", + "postcss-loader": "8.1.1", "resolve-url-loader": "5.0.0", - "rxjs": "6.6.7", - "sass": "1.54.4", - "sass-loader": "13.0.2", - "semver": "7.5.3", - "source-map-loader": "4.0.0", + "rxjs": "7.8.1", + "sass": "1.77.6", + "sass-loader": "16.0.0", + "semver": "7.6.3", + "source-map-loader": "5.0.0", "source-map-support": "0.5.21", - "stylus": "0.59.0", - "stylus-loader": "7.0.0", - "terser": "5.14.2", - "text-table": "0.2.0", + "terser": "5.31.6", "tree-kill": "1.2.2", - "tslib": "2.4.0", - "webpack": "5.76.1", - "webpack-dev-middleware": "5.3.3", - "webpack-dev-server": "4.11.0", - "webpack-merge": "5.8.0", + "tslib": "2.6.3", + "vite": "5.4.0", + "watchpack": "2.4.1", + "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" }, "engines": { - "node": "^14.15.0 || >=16.10.0", + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, "optionalDependencies": { - "esbuild": "0.15.5" + "esbuild": "0.23.0" }, "peerDependencies": { - "@angular/compiler-cli": "^14.0.0", - "@angular/localize": "^14.0.0", - "@angular/service-worker": "^14.0.0", + "@angular/compiler-cli": "^18.0.0", + "@angular/localize": "^18.0.0", + "@angular/platform-server": "^18.0.0", + "@angular/service-worker": "^18.0.0", + "@web/test-runner": "^0.18.0", + "browser-sync": "^3.0.2", + "jest": "^29.5.0", + "jest-environment-jsdom": "^29.5.0", "karma": "^6.3.0", - "ng-packagr": "^14.0.0", + "ng-packagr": "^18.0.0", "protractor": "^7.0.0", "tailwindcss": "^2.0.0 || ^3.0.0", - "typescript": ">=4.6.2 <4.9" + "typescript": ">=5.4 <5.6" }, "peerDependenciesMeta": { "@angular/localize": { "optional": true }, + "@angular/platform-server": { + "optional": true + }, "@angular/service-worker": { "optional": true }, + "@web/test-runner": { + "optional": true + }, + "browser-sync": { + "optional": true + }, + "jest": { + "optional": true + }, + "jest-environment-jsdom": { + "optional": true + }, "karma": { "optional": true }, @@ -272,91 +282,20 @@ } } }, - "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/architect": { - "version": "0.1402.13", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1402.13.tgz", - "integrity": "sha512-n0ISBuvkZHoOpAzuAZql1TU9VLHUE9e/a9g4VNOPHewjMzpN02VqeGKvJfOCKtzkCs6gVssIlILm2/SXxkIFxQ==", - "dev": true, - "dependencies": { - "@angular-devkit/core": "14.2.13", - "rxjs": "6.6.7" - }, - "engines": { - "node": "^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/core": { - "version": "14.2.13", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.13.tgz", - "integrity": "sha512-aIefeZcbjghQg/V6U9CTLtyB5fXDJ63KwYqVYkWP+i0XriS5A9puFgq2u/OVsWxAfYvqpDqp5AdQ0g0bi3CAsA==", - "dev": true, - "dependencies": { - "ajv": "8.11.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.1.0", - "rxjs": "6.6.7", - "source-map": "0.7.4" - }, - "engines": { - "node": "^14.15.0 || >=16.10.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/@angular-devkit/build-angular/node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/@angular-devkit/build-angular/node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, - "node_modules/@angular-devkit/build-angular/node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/rxjs/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "node_modules/@angular-devkit/build-angular/node_modules/terser": { - "version": "5.14.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", - "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", + "version": "5.31.6", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.6.tgz", + "integrity": "sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==", "dev": true, "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -368,59 +307,45 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", "dev": true }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.1402.13", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1402.13.tgz", - "integrity": "sha512-K27aJmuw86ZOdiu5PoGeGDJ2v7g2ZCK0bGwc8jzkjTLRfvd4FRKIIZumGv3hbQ3vQRLikiU6WMDRTFyCZky/EA==", + "version": "0.1802.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1802.3.tgz", + "integrity": "sha512-/Nixv9uAg6v/OPoZa0PB0zi+iezzBkgLrnrJnestny5B536l9WRtsw97RjeQDu+x2BClQsxNe8NL2A7EvjVD6w==", "dev": true, "dependencies": { - "@angular-devkit/architect": "0.1402.13", - "rxjs": "6.6.7" + "@angular-devkit/architect": "0.1802.3", + "rxjs": "7.8.1" }, "engines": { - "node": "^14.15.0 || >=16.10.0", + "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": { "webpack": "^5.30.0", - "webpack-dev-server": "^4.0.0" - } - }, - "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/architect": { - "version": "0.1402.13", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1402.13.tgz", - "integrity": "sha512-n0ISBuvkZHoOpAzuAZql1TU9VLHUE9e/a9g4VNOPHewjMzpN02VqeGKvJfOCKtzkCs6gVssIlILm2/SXxkIFxQ==", - "dev": true, - "dependencies": { - "@angular-devkit/core": "14.2.13", - "rxjs": "6.6.7" - }, - "engines": { - "node": "^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "webpack-dev-server": "^5.0.2" } }, - "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/core": { - "version": "14.2.13", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.13.tgz", - "integrity": "sha512-aIefeZcbjghQg/V6U9CTLtyB5fXDJ63KwYqVYkWP+i0XriS5A9puFgq2u/OVsWxAfYvqpDqp5AdQ0g0bi3CAsA==", + "node_modules/@angular-devkit/core": { + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.3.tgz", + "integrity": "sha512-vbFs+ofNK9OWeMIcFarFjegXVklhtSdLTEFKZ9trDVr8alTJdjI9AiYa6OOUTDAyq0hqYxV26xlCisWAPe7s5w==", "dev": true, "dependencies": { - "ajv": "8.11.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.1.0", - "rxjs": "6.6.7", + "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": "^14.15.0 || >=16.10.0", + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, @@ -433,197 +358,275 @@ } } }, - "node_modules/@angular-devkit/build-webpack/node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "node_modules/@angular-devkit/schematics": { + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.3.tgz", + "integrity": "sha512-N3tRAzBW2yWQhebvc1Ha18XTMSXOQTfr8HNjx7Fasx0Fg1tNyGR612MJNZw6je/PqyItKeUHOhztvFMfCQjRyg==", "dev": true, "dependencies": { - "tslib": "^1.9.0" + "@angular-devkit/core": "18.2.3", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.11", + "ora": "5.4.1", + "rxjs": "7.8.1" }, "engines": { - "npm": ">=2.0.0" + "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-devkit/build-webpack/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "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==", + "dev": true, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "*" + } + }, + "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==", "dev": true }, - "node_modules/@angular-devkit/core": { - "version": "14.2.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.12.tgz", - "integrity": "sha512-tg1+deEZdm3fgk2BQ6y7tujciL6qhtN5Ums266lX//kAZeZ4nNNXTBT+oY5xgfjvmLbW+xKg0XZrAS0oIRKY5g==", + "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==", "dev": true, "dependencies": { - "ajv": "8.11.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.1.0", - "rxjs": "6.6.7", - "source-map": "0.7.4" - }, - "engines": { - "node": "^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "@angular-eslint/bundled-angular-compiler": "18.3.0", + "@angular-eslint/utils": "18.3.0" }, "peerDependencies": { - "chokidar": "^3.5.2" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } + "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "*" } }, - "node_modules/@angular-devkit/core/node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "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==", "dev": true, "dependencies": { - "tslib": "^1.9.0" + "@angular-eslint/bundled-angular-compiler": "18.3.0", + "@angular-eslint/utils": "18.3.0", + "aria-query": "5.3.0", + "axobject-query": "4.1.0" }, - "engines": { - "npm": ">=2.0.0" + "peerDependencies": { + "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "*" } }, - "node_modules/@angular-devkit/core/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/@angular-devkit/schematics": { - "version": "14.2.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-14.2.12.tgz", - "integrity": "sha512-MN5yGR+SSSPPBBVMf4cifDJn9u0IYvxiHst+HWokH2AkBYy+vB1x8jYES2l1wkiISD7nvjTixfqX+Y95oMBoLg==", + "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==", "dev": true, "dependencies": { - "@angular-devkit/core": "14.2.12", - "jsonc-parser": "3.1.0", - "magic-string": "0.26.2", - "ora": "5.4.1", - "rxjs": "6.6.7" + "@angular-eslint/eslint-plugin": "18.3.0", + "@angular-eslint/eslint-plugin-template": "18.3.0", + "ignore": "5.3.2", + "semver": "7.6.3", + "strip-json-comments": "3.1.1" }, - "engines": { - "node": "^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "peerDependencies": { + "@angular-devkit/core": ">= 18.0.0 < 19.0.0", + "@angular-devkit/schematics": ">= 18.0.0 < 19.0.0" } }, - "node_modules/@angular-devkit/schematics/node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "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==", "dev": true, "dependencies": { - "tslib": "^1.9.0" + "@angular-eslint/bundled-angular-compiler": "18.3.0", + "eslint-scope": "^8.0.2" }, - "engines": { - "npm": ">=2.0.0" + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "*" } }, - "node_modules/@angular-devkit/schematics/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "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==", + "dev": true, + "dependencies": { + "@angular-eslint/bundled-angular-compiler": "18.3.0" + }, + "peerDependencies": { + "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "*" + } }, "node_modules/@angular/animations": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-14.3.0.tgz", - "integrity": "sha512-QoBcIKy1ZiU+4qJsAh5Ls20BupWiXiZzKb0s6L9/dntPt5Msr4Ao289XR2P6O1L+kTsCprH9Kt41zyGQ/bkRqg==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.2.3.tgz", + "integrity": "sha512-rIATopHr83lYR0X05buHeHssq9CGw0I0YPIQcpUTGnlqIpJcQVCf7jCFn4KGZrE9V55hFY3MD4S28njlwCToQQ==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^14.15.0 || >=16.10.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/core": "18.2.3" + } + }, + "node_modules/@angular/build": { + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.2.3.tgz", + "integrity": "sha512-USrD2Zvcb1te2dnqhH7JZ5XeJDg/t7fjUHR4f93vvMrnrncwCjLoHbHpz01HCHfcIVRgsYUdAmAi1iG7vpak7w==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "2.3.0", + "@angular-devkit/architect": "0.1802.3", + "@babel/core": "7.25.2", + "@babel/helper-annotate-as-pure": "7.24.7", + "@babel/helper-split-export-declaration": "7.24.7", + "@babel/plugin-syntax-import-attributes": "7.24.7", + "@inquirer/confirm": "3.1.22", + "@vitejs/plugin-basic-ssl": "1.1.0", + "browserslist": "^4.23.0", + "critters": "0.0.24", + "esbuild": "0.23.0", + "fast-glob": "3.3.2", + "https-proxy-agent": "7.0.5", + "listr2": "8.2.4", + "lmdb": "3.0.13", + "magic-string": "0.30.11", + "mrmime": "2.0.0", + "parse5-html-rewriting-stream": "7.0.0", + "picomatch": "4.0.2", + "piscina": "4.6.1", + "rollup": "4.20.0", + "sass": "1.77.6", + "semver": "7.6.3", + "vite": "5.4.0", + "watchpack": "2.4.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" }, "peerDependencies": { - "@angular/core": "14.3.0" + "@angular/compiler-cli": "^18.0.0", + "@angular/localize": "^18.0.0", + "@angular/platform-server": "^18.0.0", + "@angular/service-worker": "^18.0.0", + "less": "^4.2.0", + "postcss": "^8.4.0", + "tailwindcss": "^2.0.0 || ^3.0.0", + "typescript": ">=5.4 <5.6" + }, + "peerDependenciesMeta": { + "@angular/localize": { + "optional": true + }, + "@angular/platform-server": { + "optional": true + }, + "@angular/service-worker": { + "optional": true + }, + "less": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tailwindcss": { + "optional": true + } } }, "node_modules/@angular/cdk": { - "version": "14.2.7", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-14.2.7.tgz", - "integrity": "sha512-/tEsYaUbDSnfEmKVvAMramIptmhI67O+9STjOV0i+74XR2NospeK0fkbywIANu1n3w6AHGMotvRWJrjmbCElFg==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.2.3.tgz", + "integrity": "sha512-lUcpYTxPZuntJ1FK7V2ugapCGMIhT6TUDjIGgXfS9AxGSSKgwr8HNs6Ze9pcjYC44UhP40sYAZuiaFwmE60A2A==", "dependencies": { "tslib": "^2.3.0" }, "optionalDependencies": { - "parse5": "^5.0.0" + "parse5": "^7.1.2" }, "peerDependencies": { - "@angular/common": "^14.0.0 || ^15.0.0", - "@angular/core": "^14.0.0 || ^15.0.0", + "@angular/common": "^18.0.0 || ^19.0.0", + "@angular/core": "^18.0.0 || ^19.0.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/cli": { - "version": "14.2.12", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-14.2.12.tgz", - "integrity": "sha512-G/785b6jIIX7J+zS8RHaCT1OYzqANw5hJlnLf8tLgmaadLMVNQvIrvHTYtmD86pbqCYyVDLoMxefxRIwMHJuqw==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.3.tgz", + "integrity": "sha512-40258vuliH6+p8QSByZe5EcIXSj0iR3PNF6yuusClR/ByToHOnmuPw7WC+AYr0ooozmqlim/EjQe4/037OUB3w==", "dev": true, "dependencies": { - "@angular-devkit/architect": "0.1402.12", - "@angular-devkit/core": "14.2.12", - "@angular-devkit/schematics": "14.2.12", - "@schematics/angular": "14.2.12", + "@angular-devkit/architect": "0.1802.3", + "@angular-devkit/core": "18.2.3", + "@angular-devkit/schematics": "18.2.3", + "@inquirer/prompts": "5.3.8", + "@listr2/prompt-adapter-inquirer": "2.0.15", + "@schematics/angular": "18.2.3", "@yarnpkg/lockfile": "1.1.0", - "ansi-colors": "4.1.3", - "debug": "4.3.4", - "ini": "3.0.0", - "inquirer": "8.2.4", - "jsonc-parser": "3.1.0", - "npm-package-arg": "9.1.0", - "npm-pick-manifest": "7.0.1", - "open": "8.4.0", - "ora": "5.4.1", - "pacote": "13.6.2", - "resolve": "1.22.1", - "semver": "7.5.3", + "ini": "4.1.3", + "jsonc-parser": "3.3.1", + "listr2": "8.2.4", + "npm-package-arg": "11.0.3", + "npm-pick-manifest": "9.1.0", + "pacote": "18.0.6", + "resolve": "1.22.8", + "semver": "7.6.3", "symbol-observable": "4.0.0", - "uuid": "8.3.2", - "yargs": "17.5.1" + "yargs": "17.7.2" }, "bin": { "ng": "bin/ng.js" }, "engines": { - "node": "^14.15.0 || >=16.10.0", + "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/common": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-14.3.0.tgz", - "integrity": "sha512-pV9oyG3JhGWeQ+TFB0Qub6a1VZWMNZ6/7zEopvYivdqa5yDLLDSBRWb6P80RuONXyGnM1pa7l5nYopX+r/23GQ==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.3.tgz", + "integrity": "sha512-NFL4yXXImSCH7i1xnHykUjHa9vl9827fGiwSV2mnf7LjSUsyDzFD8/54dNuYN9OY8AUD+PnK0YdNro6cczVyIA==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^14.15.0 || >=16.10.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "14.3.0", + "@angular/core": "18.2.3", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-14.3.0.tgz", - "integrity": "sha512-E15Rh0t3vA+bctbKnBCaDmLvc3ix+ZBt6yFZmhZalReQ+KpOlvOJv+L9oiFEgg+rYVl2QdvN7US1fvT0PqswLw==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.2.3.tgz", + "integrity": "sha512-Il3ljs0j1GaYoqYFdShjUP1ryck5xTOaA8uQuRgqwU0FOwEDfugSAM3Qf7nJx/sgxTM0Lm/Nrdv2u6i1gZWeuQ==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^14.15.0 || >=16.10.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "14.3.0" + "@angular/core": "18.2.3" }, "peerDependenciesMeta": { "@angular/core": { @@ -632,120 +635,118 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-14.3.0.tgz", - "integrity": "sha512-eoKpKdQ2X6axMgzcPUMZVYl3bIlTMzMeTo5V29No4BzgiUB+QoOTYGNJZkGRyqTNpwD9uSBJvmT2vG9+eC4ghQ==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.2.3.tgz", + "integrity": "sha512-BcmqYKnkcJTkGjuPztClZNQve7tdI290J5F3iZBx6c7/vaw8EU8EGZtpWYZpgiVn5S6jhcKyc1dLF9ggO9vftg==", "dev": true, "dependencies": { - "@babel/core": "^7.17.2", + "@babel/core": "7.25.2", + "@jridgewell/sourcemap-codec": "^1.4.14", "chokidar": "^3.0.0", "convert-source-map": "^1.5.1", - "dependency-graph": "^0.11.0", - "magic-string": "^0.26.0", - "reflect-metadata": "^0.1.2", + "reflect-metadata": "^0.2.0", "semver": "^7.0.0", - "sourcemap-codec": "^1.4.8", "tslib": "^2.3.0", "yargs": "^17.2.1" }, "bin": { "ng-xi18n": "bundles/src/bin/ng_xi18n.js", "ngc": "bundles/src/bin/ngc.js", - "ngcc": "bundles/ngcc/main-ngcc.js" + "ngcc": "bundles/ngcc/index.js" }, "engines": { - "node": "^14.15.0 || >=16.10.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/compiler": "14.3.0", - "typescript": ">=4.6.2 <4.9" + "@angular/compiler": "18.2.3", + "typescript": ">=5.4 <5.6" } }, "node_modules/@angular/core": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-14.3.0.tgz", - "integrity": "sha512-wYiwItc0Uyn4FWZ/OAx/Ubp2/WrD3EgUJ476y1XI7yATGPF8n9Ld5iCXT08HOvc4eBcYlDfh90kTXR6/MfhzdQ==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.3.tgz", + "integrity": "sha512-VGhMJxj7d0rYpqVfQrcGRB7EE/BCziotft/I/YPl6bOMPSAvMukG7DXQuJdYpNrr62ks78mlzHlZX/cdmB9Prw==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^14.15.0 || >=16.10.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { "rxjs": "^6.5.3 || ^7.4.0", - "zone.js": "~0.11.4 || ~0.12.0" + "zone.js": "~0.14.10" } }, "node_modules/@angular/forms": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-14.3.0.tgz", - "integrity": "sha512-fBZZC2UFMom2AZPjGQzROPXFWO6kvCsPDKctjJwClVC8PuMrkm+RRyiYRdBbt2qxWHEqOZM2OCQo73xUyZOYHw==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.2.3.tgz", + "integrity": "sha512-+OBaAH0e8hue9eyLnbgpxg1/X9fps6bwXECfJ0nL5BDPU5itZ428YJbEnj5bTx0hEbqfTRiV4LgexdI+D9eOpw==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^14.15.0 || >=16.10.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "14.3.0", - "@angular/core": "14.3.0", - "@angular/platform-browser": "14.3.0", + "@angular/common": "18.2.3", + "@angular/core": "18.2.3", + "@angular/platform-browser": "18.2.3", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/language-service": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-14.3.0.tgz", - "integrity": "sha512-Sij3OQzj1UGs1O8H9PxVAY/o27+oqZwQRnib66rsWvtbIBTjHp4FV3dTs5iVcr62GGv4V4Mff/2I82NP10GPQg==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-18.2.3.tgz", + "integrity": "sha512-bTZ1O7s0uJqKdd9ImCleRS9Wg6yVy2ZXchnS5ap2gYJx51MJgwOM/fL6is0OsovtZG/UJaKK5FeEqUUxNqZJVA==", "dev": true, "engines": { - "node": "^14.15.0 || >=16.10.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" } }, "node_modules/@angular/material": { - "version": "14.2.7", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-14.2.7.tgz", - "integrity": "sha512-WXHh8pEStpgkXZJmYOg2cI8BSHkV82ET4XTJCNPdveumaCn1UYnaNzsXD13kw5z+zmy8CufhFEzdXTrv/yt7KQ==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-18.2.3.tgz", + "integrity": "sha512-JFfvXaMHMhskncaxxus4sDvie9VYdMkfYgfinkLXpZlPFyn1IzjDw0c1BcrcsuD7UxQVZ/v5tucCgq1FQfGRpA==", "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { - "@angular/animations": "^14.0.0 || ^15.0.0", - "@angular/cdk": "14.2.7", - "@angular/common": "^14.0.0 || ^15.0.0", - "@angular/core": "^14.0.0 || ^15.0.0", - "@angular/forms": "^14.0.0 || ^15.0.0", - "@angular/platform-browser": "^14.0.0 || ^15.0.0", + "@angular/animations": "^18.0.0 || ^19.0.0", + "@angular/cdk": "18.2.3", + "@angular/common": "^18.0.0 || ^19.0.0", + "@angular/core": "^18.0.0 || ^19.0.0", + "@angular/forms": "^18.0.0 || ^19.0.0", + "@angular/platform-browser": "^18.0.0 || ^19.0.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/material-moment-adapter": { - "version": "14.2.7", - "resolved": "https://registry.npmjs.org/@angular/material-moment-adapter/-/material-moment-adapter-14.2.7.tgz", - "integrity": "sha512-uadhugTqgETxCTis4da/TpYfIUQDQSfGAxgH1n93eFNhGdW+aof3T4uKcbZ5d18RzU+lgO6RELuPnWW2PPlmCA==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/material-moment-adapter/-/material-moment-adapter-18.2.3.tgz", + "integrity": "sha512-PPwnU4FmrJ6rCZxMn0C6fOPPks75NuVSS5zmZGh9uuTWQuwYnL6ihmRYnFOMW64pPPOJPvkmbwf88zj4PLG1Aw==", "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { - "@angular/core": "^14.0.0 || ^15.0.0", - "@angular/material": "14.2.7", + "@angular/core": "^18.0.0 || ^19.0.0", + "@angular/material": "18.2.3", "moment": "^2.18.1" } }, "node_modules/@angular/platform-browser": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-14.3.0.tgz", - "integrity": "sha512-w9Y3740UmTz44T0Egvc+4QV9sEbO61L+aRHbpkLTJdlEGzHByZvxJmJyBYmdqeyTPwc/Zpy7c02frlpfAlyB7A==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.3.tgz", + "integrity": "sha512-M2ob4zN7tAcL2mx7U6KnZNqNFPFl9MlPBE0FrjQjIzAjU0wSYPIJXmaPu9aMUp9niyo+He5iX98I+URi2Yc99g==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^14.15.0 || >=16.10.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/animations": "14.3.0", - "@angular/common": "14.3.0", - "@angular/core": "14.3.0" + "@angular/animations": "18.2.3", + "@angular/common": "18.2.3", + "@angular/core": "18.2.3" }, "peerDependenciesMeta": { "@angular/animations": { @@ -754,45 +755,39 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-14.3.0.tgz", - "integrity": "sha512-rneZiMrIiYRhrkQvdL40E2ErKRn4Zdo6EtjBM9pAmWeyoM8oMnOZb9gz5vhrkNWg06kVMVg0yKqluP5How7j3A==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.2.3.tgz", + "integrity": "sha512-nWi9ZxN4KpbJkttIckFO1PCoW0+gb/18xFO+JWyLBAtcbsudj/Mv0P/fdOaSfQdLkPhZfORr3ZcfiTkhmuGyEg==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^14.15.0 || >=16.10.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "14.3.0", - "@angular/compiler": "14.3.0", - "@angular/core": "14.3.0", - "@angular/platform-browser": "14.3.0" + "@angular/common": "18.2.3", + "@angular/compiler": "18.2.3", + "@angular/core": "18.2.3", + "@angular/platform-browser": "18.2.3" } }, "node_modules/@angular/router": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-14.3.0.tgz", - "integrity": "sha512-uip0V7w7k7xyxxpTPbr7EuMnYLj3FzJrwkLVJSEw3TMMGHt5VU5t4BBa9veGZOta2C205XFrTAHnp8mD+XYY1w==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-18.2.3.tgz", + "integrity": "sha512-fvD9eSDIiIbeYoUokoWkXzu7/ZaxlzKPUHFqX1JuKuH5ciQDeT/d7lp4mj31Bxammhohzi3+z12THJYsCkj/iQ==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^14.15.0 || >=16.10.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "14.3.0", - "@angular/core": "14.3.0", - "@angular/platform-browser": "14.3.0", + "@angular/common": "18.2.3", + "@angular/core": "18.2.3", + "@angular/platform-browser": "18.2.3", "rxjs": "^6.5.3 || ^7.4.0" } }, - "node_modules/@assemblyscript/loader": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", - "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==", - "dev": true - }, "node_modules/@babel/code-frame": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", @@ -807,35 +802,35 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.0.tgz", - "integrity": "sha512-P4fwKI2mjEb3ZU5cnMJzvRsRKGBUcs8jvxIoRmr6ufAY9Xk2Bz7JubRTTivkw55c7WQJfTECeqYVa+HZ0FzREg==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz", + "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", - "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.10", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helpers": "^7.18.9", - "@babel/parser": "^7.18.10", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.10", - "@babel/types": "^7.18.10", - "convert-source-map": "^1.7.0", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", + "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-module-transforms": "^7.25.2", + "@babel/helpers": "^7.25.0", + "@babel/parser": "^7.25.0", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.2", + "@babel/types": "^7.25.2", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", - "semver": "^6.3.0" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -845,6 +840,12 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -855,40 +856,27 @@ } }, "node_modules/@babel/generator": { - "version": "7.18.12", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz", - "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", + "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", "dev": true, "dependencies": { - "@babel/types": "^7.18.10", - "@jridgewell/gen-mapping": "^0.3.2", + "@babel/types": "^7.25.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", - "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", + "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -908,12 +896,12 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.8.tgz", - "integrity": "sha512-oU+UoqCHdp+nWVDkpldqIQL/i/bvAv53tRqLG/s+cOXxe66zOYLU7ar/Xs3LdmBihrUMEUhwu6dMZwbNOYDwvw==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", + "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.24.8", + "@babel/compat-data": "^7.25.2", "@babel/helper-validator-option": "^7.24.8", "browserslist": "^4.23.1", "lru-cache": "^5.1.1", @@ -933,9 +921,9 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.0.tgz", - "integrity": "sha512-GYM6BxeQsETc9mnct+nIIpf63SAyzvyYN7UB/IlTyd+MBg06afFGp0mIeUqGyWgS2mxad6vqbMrHVlaL3m70sQ==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.4.tgz", + "integrity": "sha512-ro/bFs3/84MDgDmMwbcHgDa8/E6J3QKNTk4xJJnVeFtGE+tL0K26E3pNxhYz2b67fJpt7Aphw5XcploKXuCvCQ==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", @@ -943,7 +931,7 @@ "@babel/helper-optimise-call-expression": "^7.24.7", "@babel/helper-replace-supers": "^7.25.0", "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/traverse": "^7.25.0", + "@babel/traverse": "^7.25.4", "semver": "^6.3.1" }, "engines": { @@ -953,18 +941,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -975,9 +951,9 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.0.tgz", - "integrity": "sha512-q0T+dknZS+L5LDazIP+02gEZITG5unzvb6yIjcmj5i0eFrs5ToBV2m2JGH4EsE/gtP8ygEGLGApBgRIZkTm7zg==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.2.tgz", + "integrity": "sha512-+wqVGP+DFmqwFD3EH6TMTfUNeqDehV3E/dl+Sd54eaXqm17tEUNbEIn4sVivVowbvUpOtIGxdo3GoXyDH9N/9g==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", @@ -991,18 +967,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -1013,38 +977,19 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", - "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", + "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" + "resolve": "^1.14.2" }, "peerDependencies": { - "@babel/core": "^7.4.0-0" - } - }, - "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", - "dev": true, - "engines": { - "node": ">=6.9.0" + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/@babel/helper-member-expression-to-functions": { @@ -1074,15 +1019,15 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.0.tgz", - "integrity": "sha512-bIkOa2ZJYn7FHnepzr5iX9Kmz8FjIz4UKzJ9zhX3dnYuVW0xul9RuR3skBfoLu+FPTQw90EHW9rJsSZhyLQ3fQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", + "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", "dev": true, "dependencies": { "@babel/helper-module-imports": "^7.24.7", "@babel/helper-simple-access": "^7.24.7", "@babel/helper-validator-identifier": "^7.24.7", - "@babel/traverse": "^7.25.0" + "@babel/traverse": "^7.25.2" }, "engines": { "node": ">=6.9.0" @@ -1129,18 +1074,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-replace-supers": { "version": "7.25.0", "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.0.tgz", @@ -1184,6 +1117,18 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", + "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-string-parser": { "version": "7.24.8", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", @@ -1225,42 +1170,14 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-wrap-function/node_modules/@babel/template": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", - "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.25.0", - "@babel/types": "^7.25.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helpers": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", - "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.6.tgz", + "integrity": "sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==", "dev": true, "dependencies": { "@babel/template": "^7.25.0", - "@babel/types": "^7.25.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers/node_modules/@babel/template": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", - "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.25.0", - "@babel/types": "^7.25.0" + "@babel/types": "^7.25.6" }, "engines": { "node": ">=6.9.0" @@ -1282,10 +1199,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.0.tgz", - "integrity": "sha512-CzdIU9jdP0dg7HdyB+bHvDJGagUv+qtzZt5rYCWwW6tITNqV9odjp6Qu41gkG0ca5UfdDUWrKkiAnHHdGRnOrA==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", + "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", "dev": true, + "dependencies": { + "@babel/types": "^7.25.6" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -1294,13 +1214,13 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.0.tgz", - "integrity": "sha512-dG0aApncVQwAUJa8tP1VHTnmU67BeIQvKafd3raEx315H54FfkZSz3B/TT+33ZQAjatGJA79gZqTtqL5QZUKXw==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.3.tgz", + "integrity": "sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.24.8", - "@babel/traverse": "^7.25.0" + "@babel/traverse": "^7.25.3" }, "engines": { "node": ">=6.9.0" @@ -1372,226 +1292,15 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz", - "integrity": "sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-async-generator-functions instead.", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-remap-async-to-generator": "^7.18.9", - "@babel/plugin-syntax-async-generators": "^7.8.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", - "dev": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-class-static-block": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", - "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-static-block instead.", - "dev": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-proposal-dynamic-import": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", - "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-dynamic-import instead.", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-export-namespace-from": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", - "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-export-namespace-from instead.", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-json-strings": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", - "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-json-strings instead.", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-json-strings": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", - "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-logical-assignment-operators instead.", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", - "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-numeric-separator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", - "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator instead.", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", - "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead.", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.20.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-optional-catch-binding": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", - "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-catch-binding instead.", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-optional-chaining": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", - "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-private-methods": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", - "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead.", + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.24.7.tgz", + "integrity": "sha512-RL9GR0pUG5Kc8BUWLNDm2T5OpYwSX15r98I0IkgmRQTXuELq/OynH8xtMTMvTJFjXbMWFVTKtYkTaYQsuAwQlQ==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-create-class-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/plugin-syntax-decorators": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1601,17 +1310,10 @@ } }, "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", - "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.", + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - }, "engines": { "node": ">=6.9.0" }, @@ -1619,23 +1321,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", - "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-unicode-property-regex instead.", - "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-async-generators": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", @@ -1675,6 +1360,21 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.24.7.tgz", + "integrity": "sha512-Ui4uLJJrRV1lb38zg1yYTmRKmiZLiftDEvZN2iq3kd9kUFU+PttmzTbAFC2ucRk/XJmtek6G23gPsuZbhrT8fQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-dynamic-import": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", @@ -1700,12 +1400,12 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.7.tgz", - "integrity": "sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.25.6.tgz", + "integrity": "sha512-aABl0jHw9bZ2karQ/uUD6XP4u0SG22SJrOHFoL6XB1R7dTovOP4TzTlsxOYC5yQ1pdscVK2JTUnF6QL3ARoAiQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1753,6 +1453,21 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", + "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-logical-assignment-operators": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", @@ -1855,14 +1570,29 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-unicode-sets-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", - "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.4.tgz", + "integrity": "sha512-uMOCoHVU52BsSWxPOMVv5qKRdeSlPuImUCB2dlPuBSU+W2/ROE7/Zg8F2Kepbk+8yBa68LlRKxO+xgEVWorsDg==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.24.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1905,14 +1635,14 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", - "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz", + "integrity": "sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-remap-async-to-generator": "^7.18.6" + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-remap-async-to-generator": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1952,13 +1682,13 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.7.tgz", - "integrity": "sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.4.tgz", + "integrity": "sha512-nZeZHyCWPfjkdU5pA/uHiTaDAFUEqkpzf1YoQT2NeSynCGYq9rxfyI3XpQbfx/a0hSnFH6TGlEXvae5Vi7GD8g==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-class-features-plugin": "^7.25.4", + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1985,16 +1715,16 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.0.tgz", - "integrity": "sha512-xyi6qjr/fYU304fiRwFbekzkqVJZ6A7hOjWZd+89FVcBqPV3S9Wuozz82xdpLspckeaafntbzglaW4pqpzvtSw==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.4.tgz", + "integrity": "sha512-oexUfaQle2pF/b6E0dwsxQtAol9TLSO88kQvym6HHBWFliV2lGdrPieX+WgMRLSJDVzdYywk7jXbLPuO2KLTLg==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-compilation-targets": "^7.25.2", "@babel/helper-plugin-utils": "^7.24.8", "@babel/helper-replace-supers": "^7.25.0", - "@babel/traverse": "^7.25.0", + "@babel/traverse": "^7.25.4", "globals": "^11.1.0" }, "engines": { @@ -2004,18 +1734,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-classes/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/plugin-transform-computed-properties": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz", @@ -2032,20 +1750,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-computed-properties/node_modules/@babel/template": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", - "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.25.0", - "@babel/types": "^7.25.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/plugin-transform-destructuring": { "version": "7.24.8", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.8.tgz", @@ -2206,12 +1910,12 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.7.tgz", - "integrity": "sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.2.tgz", + "integrity": "sha512-HQI+HcTbm9ur3Z2DkO+jgESMAMcYLuN/A7NRw9juzxAezN9AvqvUTnpKP/9kkYANz6u7dFlAyOu44ejuGySlfw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -2464,13 +2168,13 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.7.tgz", - "integrity": "sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.4.tgz", + "integrity": "sha512-ao8BG7E2b/URaUQGqN3Tlsg+M3KlHY6rJ1O1gXAEUnZoyNQnvKyH87Kfg+FoxSeyWUB8ISZZsC91C44ZuBFytw==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-class-features-plugin": "^7.25.4", + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -2497,18 +2201,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-private-property-in-object/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/plugin-transform-property-literals": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.7.tgz", @@ -2556,17 +2248,17 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.10.tgz", - "integrity": "sha512-q5mMeYAdfEbpBAgzl7tBre/la3LeCxmDO1+wMXRdPWbcoMjR3GiXlCLk7JBZVVye0bqTGNMbt0yYVXX1B1jEWQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.7.tgz", + "integrity": "sha512-YqXjrk4C+a1kZjewqt+Mmu2UuV1s07y8kqcUf4qYLnoqemhR4gRQikhdAhSVJioMjVTu6Mo6pAbaypEA3jY6fw==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.9", - "babel-plugin-polyfill-corejs2": "^0.3.2", - "babel-plugin-polyfill-corejs3": "^0.5.3", - "babel-plugin-polyfill-regenerator": "^0.4.0", - "semver": "^6.3.0" + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.1", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -2660,6 +2352,25 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.25.2.tgz", + "integrity": "sha512-lBwRvjSmqiMYe/pS0+1gggjJleUJi7NzjvQ1Fkqtt69hBa/0t1YuW/MLQMAPixfwaQOHUXsd6jeU3Z+vdGv3+A==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.24.7", + "@babel/helper-create-class-features-plugin": "^7.25.0", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", + "@babel/plugin-syntax-typescript": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-transform-unicode-escapes": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.7.tgz", @@ -2708,13 +2419,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.7.tgz", - "integrity": "sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.4.tgz", + "integrity": "sha512-qesBxiWkgN1Q+31xUE9RcMk79eOXXDCv6tfyGMRSs4RGlioSg2WVyQAm07k726cSE56pa+Kb0y9epX2qaXzTvA==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.2", + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -2724,38 +2435,29 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.10.tgz", - "integrity": "sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.18.8", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-async-generator-functions": "^7.18.10", - "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-class-static-block": "^7.18.6", - "@babel/plugin-proposal-dynamic-import": "^7.18.6", - "@babel/plugin-proposal-export-namespace-from": "^7.18.9", - "@babel/plugin-proposal-json-strings": "^7.18.6", - "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", - "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.18.9", - "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", - "@babel/plugin-proposal-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-private-methods": "^7.18.6", - "@babel/plugin-proposal-private-property-in-object": "^7.18.6", - "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.3.tgz", + "integrity": "sha512-QsYW7UeAaXvLPX9tdVliMJE7MD7M6MLYVTovRTIwhoYQVFHR1rM4wO8wqAezYi3/BpSD+NzVCZ69R6smWiIi8g==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.25.2", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-validator-option": "^7.24.8", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.3", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.0", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.0", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.0", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.18.6", + "@babel/plugin-syntax-import-assertions": "^7.24.7", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", @@ -2765,45 +2467,62 @@ "@babel/plugin-syntax-optional-chaining": "^7.8.3", "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-transform-arrow-functions": "^7.18.6", - "@babel/plugin-transform-async-to-generator": "^7.18.6", - "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.18.9", - "@babel/plugin-transform-classes": "^7.18.9", - "@babel/plugin-transform-computed-properties": "^7.18.9", - "@babel/plugin-transform-destructuring": "^7.18.9", - "@babel/plugin-transform-dotall-regex": "^7.18.6", - "@babel/plugin-transform-duplicate-keys": "^7.18.9", - "@babel/plugin-transform-exponentiation-operator": "^7.18.6", - "@babel/plugin-transform-for-of": "^7.18.8", - "@babel/plugin-transform-function-name": "^7.18.9", - "@babel/plugin-transform-literals": "^7.18.9", - "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.18.6", - "@babel/plugin-transform-modules-commonjs": "^7.18.6", - "@babel/plugin-transform-modules-systemjs": "^7.18.9", - "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.18.6", - "@babel/plugin-transform-new-target": "^7.18.6", - "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.18.8", - "@babel/plugin-transform-property-literals": "^7.18.6", - "@babel/plugin-transform-regenerator": "^7.18.6", - "@babel/plugin-transform-reserved-words": "^7.18.6", - "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.18.9", - "@babel/plugin-transform-sticky-regex": "^7.18.6", - "@babel/plugin-transform-template-literals": "^7.18.9", - "@babel/plugin-transform-typeof-symbol": "^7.18.9", - "@babel/plugin-transform-unicode-escapes": "^7.18.10", - "@babel/plugin-transform-unicode-regex": "^7.18.6", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.18.10", - "babel-plugin-polyfill-corejs2": "^0.3.2", - "babel-plugin-polyfill-corejs3": "^0.5.3", - "babel-plugin-polyfill-regenerator": "^0.4.0", - "core-js-compat": "^3.22.1", - "semver": "^6.3.0" + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.24.7", + "@babel/plugin-transform-async-generator-functions": "^7.25.0", + "@babel/plugin-transform-async-to-generator": "^7.24.7", + "@babel/plugin-transform-block-scoped-functions": "^7.24.7", + "@babel/plugin-transform-block-scoping": "^7.25.0", + "@babel/plugin-transform-class-properties": "^7.24.7", + "@babel/plugin-transform-class-static-block": "^7.24.7", + "@babel/plugin-transform-classes": "^7.25.0", + "@babel/plugin-transform-computed-properties": "^7.24.7", + "@babel/plugin-transform-destructuring": "^7.24.8", + "@babel/plugin-transform-dotall-regex": "^7.24.7", + "@babel/plugin-transform-duplicate-keys": "^7.24.7", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.0", + "@babel/plugin-transform-dynamic-import": "^7.24.7", + "@babel/plugin-transform-exponentiation-operator": "^7.24.7", + "@babel/plugin-transform-export-namespace-from": "^7.24.7", + "@babel/plugin-transform-for-of": "^7.24.7", + "@babel/plugin-transform-function-name": "^7.25.1", + "@babel/plugin-transform-json-strings": "^7.24.7", + "@babel/plugin-transform-literals": "^7.25.2", + "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", + "@babel/plugin-transform-member-expression-literals": "^7.24.7", + "@babel/plugin-transform-modules-amd": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.8", + "@babel/plugin-transform-modules-systemjs": "^7.25.0", + "@babel/plugin-transform-modules-umd": "^7.24.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", + "@babel/plugin-transform-new-target": "^7.24.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", + "@babel/plugin-transform-numeric-separator": "^7.24.7", + "@babel/plugin-transform-object-rest-spread": "^7.24.7", + "@babel/plugin-transform-object-super": "^7.24.7", + "@babel/plugin-transform-optional-catch-binding": "^7.24.7", + "@babel/plugin-transform-optional-chaining": "^7.24.8", + "@babel/plugin-transform-parameters": "^7.24.7", + "@babel/plugin-transform-private-methods": "^7.24.7", + "@babel/plugin-transform-private-property-in-object": "^7.24.7", + "@babel/plugin-transform-property-literals": "^7.24.7", + "@babel/plugin-transform-regenerator": "^7.24.7", + "@babel/plugin-transform-reserved-words": "^7.24.7", + "@babel/plugin-transform-shorthand-properties": "^7.24.7", + "@babel/plugin-transform-spread": "^7.24.7", + "@babel/plugin-transform-sticky-regex": "^7.24.7", + "@babel/plugin-transform-template-literals": "^7.24.7", + "@babel/plugin-transform-typeof-symbol": "^7.24.8", + "@babel/plugin-transform-unicode-escapes": "^7.24.7", + "@babel/plugin-transform-unicode-property-regex": "^7.24.7", + "@babel/plugin-transform-unicode-regex": "^7.24.7", + "@babel/plugin-transform-unicode-sets-regex": "^7.24.7", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.4", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "core-js-compat": "^3.37.1", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -2822,14 +2541,12 @@ } }, "node_modules/@babel/preset-modules": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6.tgz", - "integrity": "sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg==", + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", "@babel/types": "^7.4.4", "esutils": "^2.0.2" }, @@ -2837,6 +2554,25 @@ "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, + "node_modules/@babel/preset-typescript": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.24.7.tgz", + "integrity": "sha512-SyXRe3OdWwIwalxDg5UtJnJQO+YPcTfwiIY2B0Xlddh9o7jpWLvv8X1RthIeDOxQ+O1ML5BLPCONToObyVQVuQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-validator-option": "^7.24.7", + "@babel/plugin-syntax-jsx": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.7", + "@babel/plugin-transform-typescript": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/regjsgen": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", @@ -2844,42 +2580,42 @@ "dev": true }, "node_modules/@babel/runtime": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", - "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz", + "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==", "dev": true, "dependencies": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", + "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/code-frame": "^7.24.7", + "@babel/parser": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.25.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.1.tgz", - "integrity": "sha512-LrHHoWq08ZpmmFqBAzN+hUdWwy5zt7FGa/hVwMcOqW6OVtwqaoD5utfuGYU87JYxdZgLUvktAsn37j/sYR9siA==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz", + "integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.0", - "@babel/parser": "^7.25.0", + "@babel/generator": "^7.25.6", + "@babel/parser": "^7.25.6", "@babel/template": "^7.25.0", - "@babel/types": "^7.25.0", + "@babel/types": "^7.25.6", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -2888,12 +2624,12 @@ } }, "node_modules/@babel/traverse/node_modules/@babel/generator": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", - "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz", + "integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==", "dev": true, "dependencies": { - "@babel/types": "^7.25.0", + "@babel/types": "^7.25.6", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -2902,38 +2638,10 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/@babel/template": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", - "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.25.0", - "@babel/types": "^7.25.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/types": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.0.tgz", - "integrity": "sha512-LcnxQSsd9aXOIgmmSpvZ/1yo46ra2ESYyqLcryaBZOghxy5qqOBjvCWP5JfkI8yl9rlxRgdLTTMCQQRcN2hdCg==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", + "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.24.8", @@ -3054,994 +2762,782 @@ "yarn": ">= 1.13.0" } }, - "node_modules/@compodoc/compodoc/node_modules/@babel/core": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.9.tgz", - "integrity": "sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==", + "node_modules/@compodoc/compodoc/node_modules/ajv": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", "dev": true, "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.9", - "@babel/helper-compilation-targets": "^7.24.8", - "@babel/helper-module-transforms": "^7.24.9", - "@babel/helpers": "^7.24.8", - "@babel/parser": "^7.24.8", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.8", - "@babel/types": "^7.24.9", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@compodoc/compodoc/node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@compodoc/compodoc/node_modules/@babel/generator": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", - "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", + "node_modules/@compodoc/compodoc/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "@babel/types": "^7.25.0", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=6.9.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@compodoc/compodoc/node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", - "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", + "node_modules/@compodoc/compodoc/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@compodoc/compodoc/node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", - "dev": true, "engines": { - "node": ">=6.9.0" + "node": ">=10" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@compodoc/compodoc/node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz", - "integrity": "sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==", + "node_modules/@compodoc/compodoc/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7" + "color-name": "~1.1.4" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=7.0.0" } }, - "node_modules/@compodoc/compodoc/node_modules/@babel/preset-env": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.0.tgz", - "integrity": "sha512-vYAA8PrCOeZfG4D87hmw1KJ1BPubghXP1e2MacRFwECGNKL76dkA38JEwYllbvQCpf/kLxsTtir0b8MtxKoVCw==", + "node_modules/@compodoc/compodoc/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@compodoc/compodoc/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "dependencies": { - "@babel/compat-data": "^7.25.0", - "@babel/helper-compilation-targets": "^7.24.8", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-validator-option": "^7.24.8", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.0", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.0", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.0", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.0", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.24.7", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.24.7", - "@babel/plugin-transform-async-generator-functions": "^7.25.0", - "@babel/plugin-transform-async-to-generator": "^7.24.7", - "@babel/plugin-transform-block-scoped-functions": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.25.0", - "@babel/plugin-transform-class-properties": "^7.24.7", - "@babel/plugin-transform-class-static-block": "^7.24.7", - "@babel/plugin-transform-classes": "^7.25.0", - "@babel/plugin-transform-computed-properties": "^7.24.7", - "@babel/plugin-transform-destructuring": "^7.24.8", - "@babel/plugin-transform-dotall-regex": "^7.24.7", - "@babel/plugin-transform-duplicate-keys": "^7.24.7", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.0", - "@babel/plugin-transform-dynamic-import": "^7.24.7", - "@babel/plugin-transform-exponentiation-operator": "^7.24.7", - "@babel/plugin-transform-export-namespace-from": "^7.24.7", - "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.25.0", - "@babel/plugin-transform-json-strings": "^7.24.7", - "@babel/plugin-transform-literals": "^7.24.7", - "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", - "@babel/plugin-transform-member-expression-literals": "^7.24.7", - "@babel/plugin-transform-modules-amd": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.8", - "@babel/plugin-transform-modules-systemjs": "^7.25.0", - "@babel/plugin-transform-modules-umd": "^7.24.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", - "@babel/plugin-transform-new-target": "^7.24.7", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", - "@babel/plugin-transform-numeric-separator": "^7.24.7", - "@babel/plugin-transform-object-rest-spread": "^7.24.7", - "@babel/plugin-transform-object-super": "^7.24.7", - "@babel/plugin-transform-optional-catch-binding": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.8", - "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.24.7", - "@babel/plugin-transform-private-property-in-object": "^7.24.7", - "@babel/plugin-transform-property-literals": "^7.24.7", - "@babel/plugin-transform-regenerator": "^7.24.7", - "@babel/plugin-transform-reserved-words": "^7.24.7", - "@babel/plugin-transform-shorthand-properties": "^7.24.7", - "@babel/plugin-transform-spread": "^7.24.7", - "@babel/plugin-transform-sticky-regex": "^7.24.7", - "@babel/plugin-transform-template-literals": "^7.24.7", - "@babel/plugin-transform-typeof-symbol": "^7.24.8", - "@babel/plugin-transform-unicode-escapes": "^7.24.7", - "@babel/plugin-transform-unicode-property-regex": "^7.24.7", - "@babel/plugin-transform-unicode-regex": "^7.24.7", - "@babel/plugin-transform-unicode-sets-regex": "^7.24.7", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.4", - "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.37.1", - "semver": "^6.3.1" - }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=8" } }, - "node_modules/@compodoc/compodoc/node_modules/@babel/preset-env/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/@compodoc/compodoc/node_modules/jsonc-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", + "dev": true + }, + "node_modules/@compodoc/compodoc/node_modules/magic-string": { + "version": "0.30.10", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", + "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", "dev": true, - "bin": { - "semver": "bin/semver.js" + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" } }, - "node_modules/@compodoc/compodoc/node_modules/@babel/preset-modules": { - "version": "0.1.6-no-external-plugins", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", - "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "node_modules/@compodoc/compodoc/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" + "has-flag": "^4.0.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + "engines": { + "node": ">=8" } }, - "node_modules/@compodoc/compodoc/node_modules/@babel/template": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", - "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", + "node_modules/@compodoc/live-server": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@compodoc/live-server/-/live-server-1.2.3.tgz", + "integrity": "sha512-hDmntVCyjjaxuJzPzBx68orNZ7TW4BtHWMnXlIVn5dqhK7vuFF/11hspO1cMmc+2QTYgqde1TBcb3127S7Zrow==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.25.0", - "@babel/types": "^7.25.0" + "chokidar": "^3.5.2", + "colors": "1.4.0", + "connect": "^3.7.0", + "cors": "latest", + "event-stream": "4.0.1", + "faye-websocket": "0.11.x", + "http-auth": "4.1.9", + "http-auth-connect": "^1.0.5", + "morgan": "^1.10.0", + "object-assign": "latest", + "open": "8.4.0", + "proxy-middleware": "latest", + "send": "latest", + "serve-index": "^1.9.1" + }, + "bin": { + "live-server": "live-server.js" }, "engines": { - "node": ">=6.9.0" + "node": ">=0.10.0" } }, - "node_modules/@compodoc/compodoc/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "node_modules/@compodoc/live-server/node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, "engines": { - "node": ">=6.0.0" + "node": ">=8" } }, - "node_modules/@compodoc/compodoc/node_modules/ajv": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", - "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", + "node_modules/@compodoc/live-server/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@compodoc/compodoc/node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "node_modules/@compodoc/live-server/node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dev": true, "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/@compodoc/compodoc/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" + "is-docker": "^2.0.0" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@compodoc/compodoc/node_modules/argparse": { - "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 - }, - "node_modules/@compodoc/compodoc/node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", - "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.6.2", - "semver": "^6.3.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@compodoc/compodoc/node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" } }, - "node_modules/@compodoc/compodoc/node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.4.tgz", - "integrity": "sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==", + "node_modules/@compodoc/live-server/node_modules/open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.1", - "core-js-compat": "^3.36.1" + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@compodoc/compodoc/node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", - "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2" + "engines": { + "node": ">=12" }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@compodoc/compodoc/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@compodoc/ngd-core": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@compodoc/ngd-core/-/ngd-core-2.1.1.tgz", + "integrity": "sha512-Z+wE6wWZYVnudRYg6qunDlyh3Orw39Ib66Gvrz5kX5u7So+iu3tr6sQJdqH6yGS3hAjig5avlfhWLlgsb6/x1Q==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ansi-colors": "^4.1.3", + "fancy-log": "^2.0.0", + "typescript": "^5.0.4" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">= 10.0.0" } }, - "node_modules/@compodoc/compodoc/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@compodoc/ngd-transformer": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@compodoc/ngd-transformer/-/ngd-transformer-2.1.3.tgz", + "integrity": "sha512-oWxJza7CpWR8/FeWYfE6j+jgncnGBsTWnZLt5rD2GUpsGSQTuGrsFPnmbbaVLgRS5QIVWBJYke7QFBr/7qVMWg==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@aduh95/viz.js": "3.4.0", + "@compodoc/ngd-core": "~2.1.1", + "dot": "^2.0.0-beta.1", + "fs-extra": "^11.1.1" }, "engines": { - "node": ">=7.0.0" + "node": ">= 10.0.0" } }, - "node_modules/@compodoc/compodoc/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@compodoc/compodoc/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "node_modules/@compodoc/compodoc/node_modules/cosmiconfig": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", - "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, "dependencies": { - "env-paths": "^2.2.1", - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0" + "@jridgewell/trace-mapping": "0.3.9" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": ">=12" } }, - "node_modules/@compodoc/compodoc/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@compodoc/compodoc/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@discoveryjs/json-ext": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.1.tgz", + "integrity": "sha512-boghen8F0Q8D+0/Q1/1r6DUEieUJ8w2a1gIknExMSHBsJFOr2+0KUfHiVYBvucPwl3+RU5PFBK833FjFCh3BhA==", "dev": true, "engines": { - "node": ">=8" + "node": ">=14.17.0" } }, - "node_modules/@compodoc/compodoc/node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, + "node_modules/@emnapi/core": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.2.0.tgz", + "integrity": "sha512-E7Vgw78I93we4ZWdYCb4DGAwRROGkMIXk7/y87UmANR+J6qsWusmC3gLt0H+O0KOt5e6O38U8oJamgbudrES/w==", "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "@emnapi/wasi-threads": "1.0.1", + "tslib": "^2.4.0" } }, - "node_modules/@compodoc/compodoc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, + "node_modules/@emnapi/runtime": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.2.0.tgz", + "integrity": "sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ==", "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "tslib": "^2.4.0" } }, - "node_modules/@compodoc/compodoc/node_modules/jsonc-parser": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", - "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", - "dev": true - }, - "node_modules/@compodoc/compodoc/node_modules/magic-string": { - "version": "0.30.10", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", - "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", - "dev": true, + "node_modules/@emnapi/wasi-threads": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.1.tgz", + "integrity": "sha512-iIBu7mwkq4UQGeMEM8bLwNK962nXdhodeScX4slfQnRhEMMzvYivHhutCIk8uojvmASXXPC2WNEjwxFWk72Oqw==", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" + "tslib": "^2.4.0" } }, - "node_modules/@compodoc/compodoc/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.0.tgz", + "integrity": "sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==", + "cpu": [ + "ppc64" + ], "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=18" } }, - "node_modules/@compodoc/compodoc/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "node_modules/@esbuild/android-arm": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.0.tgz", + "integrity": "sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==", + "cpu": [ + "arm" + ], "dev": true, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=18" } }, - "node_modules/@compodoc/compodoc/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "node_modules/@esbuild/android-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.0.tgz", + "integrity": "sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==", + "cpu": [ + "arm64" + ], "dev": true, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node": ">=18" } }, - "node_modules/@compodoc/compodoc/node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "node_modules/@esbuild/android-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.0.tgz", + "integrity": "sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "tslib": "^2.1.0" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@compodoc/compodoc/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.0.tgz", + "integrity": "sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=10" + "node": ">=18" } }, - "node_modules/@compodoc/compodoc/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.0.tgz", + "integrity": "sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/@compodoc/compodoc/node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.0.tgz", + "integrity": "sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==", + "cpu": [ + "arm64" + ], "dev": true, "optional": true, - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, + "os": [ + "freebsd" + ], "engines": { - "node": ">=14.17" + "node": ">=18" } }, - "node_modules/@compodoc/compodoc/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.0.tgz", + "integrity": "sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==", + "cpu": [ + "x64" + ], "dev": true, - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" + "optional": true, + "os": [ + "freebsd" ], - "bin": { - "uuid": "dist/bin/uuid" + "engines": { + "node": ">=18" } }, - "node_modules/@compodoc/live-server": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@compodoc/live-server/-/live-server-1.2.3.tgz", - "integrity": "sha512-hDmntVCyjjaxuJzPzBx68orNZ7TW4BtHWMnXlIVn5dqhK7vuFF/11hspO1cMmc+2QTYgqde1TBcb3127S7Zrow==", + "node_modules/@esbuild/linux-arm": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.0.tgz", + "integrity": "sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==", + "cpu": [ + "arm" + ], "dev": true, - "dependencies": { - "chokidar": "^3.5.2", - "colors": "1.4.0", - "connect": "^3.7.0", - "cors": "latest", - "event-stream": "4.0.1", - "faye-websocket": "0.11.x", - "http-auth": "4.1.9", - "http-auth-connect": "^1.0.5", - "morgan": "^1.10.0", - "object-assign": "latest", - "open": "8.4.0", - "proxy-middleware": "latest", - "send": "latest", - "serve-index": "^1.9.1" - }, - "bin": { - "live-server": "live-server.js" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=0.10.0" + "node": ">=18" } }, - "node_modules/@compodoc/live-server/node_modules/proxy-middleware": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/proxy-middleware/-/proxy-middleware-0.15.0.tgz", - "integrity": "sha512-EGCG8SeoIRVMhsqHQUdDigB2i7qU7fCsWASwn54+nPutYO8n4q6EiwMzyfWlC+dzRFExP+kvcnDFdBDHoZBU7Q==", + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.0.tgz", + "integrity": "sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==", + "cpu": [ + "arm64" + ], "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=0.8.0" + "node": ">=18" } }, - "node_modules/@compodoc/ngd-core": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@compodoc/ngd-core/-/ngd-core-2.1.1.tgz", - "integrity": "sha512-Z+wE6wWZYVnudRYg6qunDlyh3Orw39Ib66Gvrz5kX5u7So+iu3tr6sQJdqH6yGS3hAjig5avlfhWLlgsb6/x1Q==", + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.0.tgz", + "integrity": "sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==", + "cpu": [ + "ia32" + ], "dev": true, - "dependencies": { - "ansi-colors": "^4.1.3", - "fancy-log": "^2.0.0", - "typescript": "^5.0.4" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 10.0.0" + "node": ">=18" } }, - "node_modules/@compodoc/ngd-core/node_modules/typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.0.tgz", + "integrity": "sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==", + "cpu": [ + "loong64" + ], "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=14.17" + "node": ">=18" } }, - "node_modules/@compodoc/ngd-transformer": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@compodoc/ngd-transformer/-/ngd-transformer-2.1.3.tgz", - "integrity": "sha512-oWxJza7CpWR8/FeWYfE6j+jgncnGBsTWnZLt5rD2GUpsGSQTuGrsFPnmbbaVLgRS5QIVWBJYke7QFBr/7qVMWg==", + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.0.tgz", + "integrity": "sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==", + "cpu": [ + "mips64el" + ], "dev": true, - "dependencies": { - "@aduh95/viz.js": "3.4.0", - "@compodoc/ngd-core": "~2.1.1", - "dot": "^2.0.0-beta.1", - "fs-extra": "^11.1.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 10.0.0" + "node": ">=18" } }, - "node_modules/@csstools/postcss-cascade-layers": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz", - "integrity": "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==", + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.0.tgz", + "integrity": "sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==", + "cpu": [ + "ppc64" + ], "dev": true, - "dependencies": { - "@csstools/selector-specificity": "^2.0.2", - "postcss-selector-parser": "^6.0.10" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">=18" } }, - "node_modules/@csstools/postcss-color-function": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", - "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.0.tgz", + "integrity": "sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==", + "cpu": [ + "riscv64" + ], "dev": true, - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">=18" } }, - "node_modules/@csstools/postcss-font-format-keywords": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", - "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.0.tgz", + "integrity": "sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==", + "cpu": [ + "s390x" + ], "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">=18" } }, - "node_modules/@csstools/postcss-hwb-function": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", - "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", + "node_modules/@esbuild/linux-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.0.tgz", + "integrity": "sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">=18" } }, - "node_modules/@csstools/postcss-ic-unit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", - "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.0.tgz", + "integrity": "sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - }, + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">=18" } }, - "node_modules/@csstools/postcss-is-pseudo-class": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", - "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.0.tgz", + "integrity": "sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@csstools/selector-specificity": "^2.0.0", - "postcss-selector-parser": "^6.0.10" - }, + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">=18" } }, - "node_modules/@csstools/postcss-nested-calc": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz", - "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==", + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.0.tgz", + "integrity": "sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">=18" } }, - "node_modules/@csstools/postcss-normalize-display-values": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", - "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.0.tgz", + "integrity": "sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">=18" } }, - "node_modules/@csstools/postcss-oklab-function": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", - "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.0.tgz", + "integrity": "sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">=18" } }, - "node_modules/@csstools/postcss-progressive-custom-properties": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", - "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.0.tgz", + "integrity": "sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==", + "cpu": [ + "ia32" + ], "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" + "node": ">=18" } }, - "node_modules/@csstools/postcss-stepped-value-functions": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", - "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", + "node_modules/@esbuild/win32-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.0.tgz", + "integrity": "sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">=18" } }, - "node_modules/@csstools/postcss-text-decoration-shorthand": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz", - "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dev": true, "dependencies": { - "postcss-value-parser": "^4.2.0" + "eslint-visitor-keys": "^3.3.0" }, "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "peerDependencies": { - "postcss": "^8.2" + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@csstools/postcss-trigonometric-functions": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", - "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", + "node_modules/@eslint-community/regexpp": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", + "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "dependencies": { - "postcss-value-parser": "^4.2.0" + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^14 || >=16" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "url": "https://opencollective.com/eslint" } }, - "node_modules/@csstools/postcss-unset-value": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", - "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", + "node_modules/@eslint/eslintrc/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, - "engines": { - "node": "^12 || ^14 || >=16" + "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": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@csstools/selector-specificity": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz", - "integrity": "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==", + "node_modules/@eslint/eslintrc/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, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, "engines": { - "node": "^14 || ^16 || >=18" + "node": ">=8" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss-selector-parser": "^6.0.10" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "node_modules/@eslint/eslintrc/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/@eslint/eslintrc/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, + "dependencies": { + "brace-expansion": "^1.1.7" + }, "engines": { - "node": ">=10.0.0" + "node": "*" } }, - "node_modules/@elemental-ui/cadence-icon": { - "version": "3.1.64", - "resolved": "file:imx-modules/elemental-ui_cadence-icon.tgz", - "integrity": "sha512-NQjPGHdJxemkw1qzlPaAV+DWLF+biAAa3/4kZxM3CzllA+SNivECMpkyDMcKqBCXLmcys6pxvSo88dTpgILREw==" - }, - "node_modules/@elemental-ui/core": { - "version": "14.0.11", - "resolved": "file:imx-modules/elemental-ui_core.tgz", - "integrity": "sha512-d7TqH//IKajEOF2yuJGntTy78I5YmpmNb/j9SpCccXfVq6ym96sxtiYuyh4lVT3CUkKdzeVZCok75w/RLlo+VQ==", - "dependencies": { - "tslib": "^2.0.0" + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" }, - "peerDependencies": { - "@angular/common": "^14.2.12", - "@angular/core": "^14.2.12", - "@angular/material-moment-adapter": "^14.2.7", - "@elemental-ui/cadence-icon": "^3.1.64", - "billboard.js": "^3.6.2", - "file-saver": "^2.0.2", - "moment-timezone": "^0.5.37", - "smooth-scrollbar": "^8.8.1" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.5.tgz", - "integrity": "sha512-UHkDFCfSGTuXq08oQltXxSZmH1TXyWsL+4QhZDWvvLl6mEJQqk3u7/wq1LjhrrAXYIllaTtRSzUXl4Olkf2J8A==", - "cpu": [ - "loong64" - ], + "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==", "dev": true, - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=12" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@foliojs-fork/fontkit": { @@ -4094,165 +3590,334 @@ "integrity": "sha512-59SgoZ3EXbkfSX7b63tsou/SDGzwUEK6MuB5sKqgVK1/XE0fxmpsOb9DQI8LXW3KfGnAjImCGhhEb7uPPAUVNA==", "dev": true }, - "node_modules/@gar/promisify": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", - "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", - "dev": true - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "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==", + "deprecated": "Use @eslint/config-array instead", "dev": true, "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" }, "engines": { - "node": ">=12" + "node": ">=10.10.0" } }, - "node_modules/@isaacs/cliui/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==", + "node_modules/@humanwhocodes/config-array/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, - "engines": { - "node": ">=12" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/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, + "dependencies": { + "brace-expansion": "^1.1.7" }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "engines": { + "node": "*" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, "engines": { - "node": ">=12" + "node": ">=12.22" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@isaacs/cliui/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true + }, + "node_modules/@inquirer/checkbox": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-2.5.0.tgz", + "integrity": "sha512-sMgdETOfi2dUHT8r7TT1BTKOwNvdDGFDXYWtQ2J69SvlYNntk9I/gJe7r5yvMwwsuKnYbuRs3pNhx4tgNck5aA==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@inquirer/core": "^9.1.0", + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.3", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" }, "engines": { - "node": ">=7.0.0" + "node": ">=18" } }, - "node_modules/@isaacs/cliui/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "node_modules/@inquirer/confirm": { + "version": "3.1.22", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.1.22.tgz", + "integrity": "sha512-gsAKIOWBm2Q87CDfs9fEo7wJT3fwWIJfnDGMn9Qy74gBnNFOACDNfhUzovubbJjWnKLGBln7/NcSmZwj5DuEXg==", "dev": true, "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "@inquirer/core": "^9.0.10", + "@inquirer/type": "^1.5.2" }, "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@inquirer/core": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.1.0.tgz", + "integrity": "sha512-RZVfH//2ytTjmaBIzeKT1zefcQZzuruwkpTwwbe/i2jTl4o9M+iML5ChULzz6iw1Ok8iUBBsRCjY2IEbD8Ft4w==", + "dev": true, + "dependencies": { + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.3", + "@types/mute-stream": "^0.0.4", + "@types/node": "^22.5.2", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "cli-spinners": "^2.9.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=18" } }, - "node_modules/@isaacs/cliui/node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/@inquirer/core/node_modules/@types/node": { + "version": "22.5.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz", + "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", "dev": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "undici-types": "~6.19.2" } }, - "node_modules/@isaacs/cliui/node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@inquirer/editor": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-2.2.0.tgz", + "integrity": "sha512-9KHOpJ+dIL5SZli8lJ6xdaYLPPzB8xB9GZItg39MBybzhxA16vxmszmQFrRwbOA918WA2rvu8xhDEg/p6LXKbw==", "dev": true, + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", + "external-editor": "^3.1.0" + }, "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/@isaacs/cliui/node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "node_modules/@inquirer/expand": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-2.3.0.tgz", + "integrity": "sha512-qnJsUcOGCSG1e5DTOErmv2BPQqrtT6uzqn1vI/aYGiPKq+FgslGZmtdnXbhuI7IlT7OByDoEEqdnhUnVR2hhLw==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } }, - "node_modules/@isaacs/cliui/node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/@inquirer/figures": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.5.tgz", + "integrity": "sha512-79hP/VWdZ2UVc9bFGJnoQ/lQMpL74mGgzSYX1xUqCVk7/v73vJCMw1VuyWN1jGkZ9B3z7THAbySqGbCNefcjfA==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-2.3.0.tgz", + "integrity": "sha512-XfnpCStx2xgh1LIRqPXrTNEEByqQWoxsWYzNRSEUxJ5c6EQlhMogJ3vHKu8aXuTacebtaZzMAHwEL0kAflKOBw==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.1" + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3" }, "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "node_modules/@inquirer/number": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-1.1.0.tgz", + "integrity": "sha512-ilUnia/GZUtfSZy3YEErXLJ2Sljo/mf9fiKc08n18DdwdmDbOzRcTv65H1jjDvlsAuvdFXf4Sa/aL7iw/NanVA==", "dev": true, "dependencies": { - "ansi-regex": "^6.0.1" + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/password": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-2.2.0.tgz", + "integrity": "sha512-5otqIpgsPYIshqhgtEwSspBQE40etouR8VIxzpJkv9i0dVHIpyhiivbkH9/dGiMLdyamT54YRdGJLfl8TFnLHg==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", + "ansi-escapes": "^4.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/prompts": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-5.3.8.tgz", + "integrity": "sha512-b2BudQY/Si4Y2a0PdZZL6BeJtl8llgeZa7U2j47aaJSCeAl1e4UI7y8a9bSkO3o/ZbZrgT5muy/34JbsjfIWxA==", + "dev": true, + "dependencies": { + "@inquirer/checkbox": "^2.4.7", + "@inquirer/confirm": "^3.1.22", + "@inquirer/editor": "^2.1.22", + "@inquirer/expand": "^2.1.22", + "@inquirer/input": "^2.2.9", + "@inquirer/number": "^1.0.10", + "@inquirer/password": "^2.1.22", + "@inquirer/rawlist": "^2.2.4", + "@inquirer/search": "^1.0.7", + "@inquirer/select": "^2.4.7" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/rawlist": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-2.3.0.tgz", + "integrity": "sha512-zzfNuINhFF7OLAtGHfhwOW2TlYJyli7lOUoJUXw/uyklcwalV6WRXBXtFIicN8rTRK1XTiPWB4UY+YuW8dsnLQ==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/search": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-1.1.0.tgz", + "integrity": "sha512-h+/5LSj51dx7hp5xOn4QFnUaKeARwUCLs6mIhtkJ0JYPBLmEYjdHSYh7I6GrLg9LwpJ3xeX0FZgAG1q0QdCpVQ==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.3", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/select": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-2.5.0.tgz", + "integrity": "sha512-YmDobTItPP3WcEI86GvPo+T2sRHkxxOq/kXmsBjHS5BVXUgvgZ5AfJjkvQvZr03T81NnI3KrrRuMzeuYUQRFOA==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.3", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/type": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.5.3.tgz", + "integrity": "sha512-xUQ14WQGR/HK5ei+2CvgcwoH9fQ4PgPGmVFSN0pc1+fVyDL3MREhyAY7nxEErSu6CkllBM3D7e3e+kOvtu+eIg==", + "dev": true, + "dependencies": { + "mute-stream": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { @@ -4272,28 +3937,44 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "node_modules/@jest/types/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", @@ -4308,80 +3989,79 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/@jest/types/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/@jest/types/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.1" + "color-name": "~1.1.4" }, "engines": { - "node": ">=8" + "node": ">=7.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "node_modules/@jest/types/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@jest/types/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, "engines": { "node": ">=8" } }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "node_modules/@jest/types/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, "engines": { "node": ">=8" } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "engines": { "node": ">=6.0.0" @@ -4397,33 +4077,19 @@ } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", - "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { @@ -4436,4936 +4102,5386 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", - "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", - "dev": true - }, - "node_modules/@ngtools/webpack": { - "version": "14.2.13", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-14.2.13.tgz", - "integrity": "sha512-RQx/rGX7K/+R55x1R6Ax1JzyeHi8cW11dEXpzHWipyuSpusQLUN53F02eMB4VTakXsL3mFNWWy4bX3/LSq8/9w==", + "node_modules/@jsonjoy.com/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", "dev": true, "engines": { - "node": "^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" }, "peerDependencies": { - "@angular/compiler-cli": "^14.0.0", - "typescript": ">=4.6.2 <4.9", - "webpack": "^5.54.0" + "tslib": "2" } }, - "node_modules/@ngx-translate/core": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-11.0.1.tgz", - "integrity": "sha512-nBCa1ZD9fAUY/3eskP3Lql2fNg8OMrYIej1/5GRsfcutx9tG/5fZLCv9m6UCw1aS+u4uK/vXjv1ctG/FdMvaWg==", + "node_modules/@jsonjoy.com/json-pack": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.1.0.tgz", + "integrity": "sha512-zlQONA+msXPPwHWZMKFVS78ewFczIll5lXiVPwFPCZUsrOKdxc2AvxU1HoNBmMRhqDZUR9HkC3UOm+6pME6Xsg==", + "dev": true, "dependencies": { - "tslib": "^1.9.0" + "@jsonjoy.com/base64": "^1.1.1", + "@jsonjoy.com/util": "^1.1.2", + "hyperdyperid": "^1.2.0", + "thingies": "^1.20.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" }, "peerDependencies": { - "@angular/core": ">=7.0.0", - "rxjs": ">=6.3.0" + "tslib": "2" } }, - "node_modules/@ngx-translate/core/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@ngx-translate/http-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@ngx-translate/http-loader/-/http-loader-4.0.0.tgz", - "integrity": "sha512-x8LumqydWD7eX9yQTAVeoCM9gFUIGVTUjZqbxdAUavAA3qVnk9wCQux7iHLPXpydl8vyQmLoPQR+fFU+DUDOMA==", - "dependencies": { - "tslib": "^1.9.0" + "node_modules/@jsonjoy.com/util": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.3.0.tgz", + "integrity": "sha512-Cebt4Vk7k1xHy87kHY7KSPLT77A7Ev7IfOblyLZhtYEhrdQ6fX4EoLq3xOQ3O/DRMEh2ok5nyC180E+ABS8Wmw==", + "dev": true, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" }, "peerDependencies": { - "@angular/common": ">=7.0.0", - "@ngx-translate/core": ">=11.0.0", - "rxjs": ">=6.3.0" + "tslib": "2" } }, - "node_modules/@ngx-translate/http-loader/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "dev": true }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@listr2/prompt-adapter-inquirer": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-2.0.15.tgz", + "integrity": "sha512-MZrGem/Ujjd4cPTLYDfCZK2iKKeiO/8OX13S6jqxldLs0Prf2aGqVlJ77nMBqMv7fzqgXEgjrNHLXcKR8l9lOg==", "dev": true, "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@inquirer/type": "^1.5.1" }, "engines": { - "node": ">= 8" + "node": ">=18.0.0" + }, + "peerDependencies": { + "@inquirer/prompts": ">= 3 < 6" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/@lmdb/lmdb-darwin-arm64": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.0.13.tgz", + "integrity": "sha512-uiKPB0Fv6WEEOZjruu9a6wnW/8jrjzlZbxXscMB8kuCJ1k6kHpcBnuvaAWcqhbI7rqX5GKziwWEdD+wi2gNLfA==", + "cpu": [ + "arm64" + ], "dev": true, - "engines": { - "node": ">= 8" - } + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@lmdb/lmdb-darwin-x64": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.0.13.tgz", + "integrity": "sha512-bEVIIfK5mSQoG1R19qA+fJOvCB+0wVGGnXHT3smchBVahYBdlPn2OsZZKzlHWfb1E+PhLBmYfqB5zQXFP7hJig==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lmdb/lmdb-linux-arm": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.0.13.tgz", + "integrity": "sha512-Yml1KlMzOnXj/tnW7yX8U78iAzTk39aILYvCPbqeewAq1kSzl+w59k/fiVkTBfvDi/oW/5YRxL+Fq+Y1Fr1r2Q==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-linux-arm64": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.0.13.tgz", + "integrity": "sha512-afbVrsMgZ9dUTNUchFpj5VkmJRxvht/u335jUJ7o23YTbNbnpmXif3VKQGCtnjSh+CZaqm6N3CPG8KO3zwyZ1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-linux-x64": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.0.13.tgz", + "integrity": "sha512-vOtxu0xC0SLdQ2WRXg8Qgd8T32ak4SPqk5zjItRszrJk2BdeXqfGxBJbP7o4aOvSPSmSSv46Lr1EP4HXU8v7Kg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-win32-x64": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.0.13.tgz", + "integrity": "sha512-UCrMJQY/gJnOl3XgbWRZZUvGGBuKy6i0YNSptgMzHBjs+QYDYR1Mt/RLTOPy4fzzves65O1EDmlL//OzEqoLlA==", + "cpu": [ + "x64" + ], "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@material/layout-grid": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@material/layout-grid/-/layout-grid-14.0.0.tgz", + "integrity": "sha512-tAce0PR/c85VI2gf1HUdM0Y15ZWpfZWAFIwaCRW1+jnOLWnG1/aOJYLlzqtVEv2m0TS1R1WRRGN3Or+CWvpDRA==", + "optional": true, + "peer": true, "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" + "tslib": "^2.1.0" } }, - "node_modules/@npmcli/fs": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", - "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", + "node_modules/@module-federation/bridge-react-webpack-plugin": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@module-federation/bridge-react-webpack-plugin/-/bridge-react-webpack-plugin-0.6.1.tgz", + "integrity": "sha512-or5A6sACCuRhHALlL0ZlXKhmCJeb8smZzEYlrmedjvEOmkLnKjIRgRpZchtbn+iePTf60snlbuelsEhuOXe1gg==", "dev": true, "dependencies": { - "@gar/promisify": "^1.1.3", - "semver": "^7.3.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "@module-federation/sdk": "0.6.1", + "@types/semver": "7.5.8", + "semver": "7.6.3" } }, - "node_modules/@npmcli/git": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-3.0.2.tgz", - "integrity": "sha512-CAcd08y3DWBJqJDpfuVL0uijlq5oaXaOJEKHKc4wqrjd00gkvTZB+nFuLn+doOOKddaQS9JfqtNoFCO2LCvA3w==", + "node_modules/@module-federation/dts-plugin": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@module-federation/dts-plugin/-/dts-plugin-0.6.1.tgz", + "integrity": "sha512-lOBH/GnI5T/iWmzIS8E/DaI58VHCYBDUh5oWpIvqpgVd5hYMITX4azOmZsurZpJK/COjEddYfMprXALVeUn/UQ==", "dev": true, "dependencies": { - "@npmcli/promise-spawn": "^3.0.0", - "lru-cache": "^7.4.4", - "mkdirp": "^1.0.4", - "npm-pick-manifest": "^7.0.0", - "proc-log": "^2.0.0", - "promise-inflight": "^1.0.1", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^2.0.2" + "@module-federation/managers": "0.6.1", + "@module-federation/sdk": "0.6.1", + "@module-federation/third-party-dts-extractor": "0.6.1", + "adm-zip": "^0.5.10", + "ansi-colors": "^4.1.3", + "axios": "^1.7.4", + "chalk": "3.0.0", + "fs-extra": "9.1.0", + "isomorphic-ws": "5.0.0", + "koa": "2.15.3", + "lodash.clonedeepwith": "4.5.0", + "log4js": "6.9.1", + "node-schedule": "2.1.1", + "rambda": "^9.1.0", + "ws": "8.17.1" }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "peerDependencies": { + "typescript": "^4.9.0 || ^5.0.0", + "vue-tsc": ">=1.0.24" + }, + "peerDependenciesMeta": { + "vue-tsc": { + "optional": true + } } }, - "node_modules/@npmcli/git/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "node_modules/@module-federation/dts-plugin/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": ">=12" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@npmcli/git/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "node_modules/@module-federation/dts-plugin/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", "dev": true, "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">= 8" + "node": ">=8" } }, - "node_modules/@npmcli/installed-package-contents": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz", - "integrity": "sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==", + "node_modules/@module-federation/dts-plugin/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1" - }, - "bin": { - "installed-package-contents": "index.js" + "color-name": "~1.1.4" }, "engines": { - "node": ">= 10" + "node": ">=7.0.0" } }, - "node_modules/@npmcli/move-file": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", - "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", - "deprecated": "This functionality has been moved to @npmcli/fs", + "node_modules/@module-federation/dts-plugin/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@module-federation/dts-plugin/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=10" } }, - "node_modules/@npmcli/node-gyp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-2.0.0.tgz", - "integrity": "sha512-doNI35wIe3bBaEgrlPfdJPaCpUR89pJWep4Hq3aRdh6gKazIVWfs0jHttvSSoq47ZXgC7h73kDsUl8AoIQUB+A==", + "node_modules/@module-federation/dts-plugin/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=8" } }, - "node_modules/@npmcli/promise-spawn": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-3.0.0.tgz", - "integrity": "sha512-s9SgS+p3a9Eohe68cSI3fi+hpcZUmXq5P7w0kMlAsWVtR7XbK3ptkZqKT2cK1zLDObJ3sR+8P59sJE0w/KTL1g==", + "node_modules/@module-federation/dts-plugin/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "infer-owner": "^1.0.4" + "has-flag": "^4.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=8" } }, - "node_modules/@npmcli/run-script": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-4.2.1.tgz", - "integrity": "sha512-7dqywvVudPSrRCW5nTHpHgeWnbBtz8cFkOuKrecm6ih+oO9ciydhWt6OF7HlqupRRmB8Q/gECVdB9LMfToJbRg==", + "node_modules/@module-federation/enhanced": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@module-federation/enhanced/-/enhanced-0.6.1.tgz", + "integrity": "sha512-2oNvyqNZObp1qwr1SwEoSzpGx+MbEcVOBDw1hJrpzlMrAbGADcFlv9N1V+hnAc600jmtaHJC1GiyA2rOFWWEnw==", "dev": true, "dependencies": { - "@npmcli/node-gyp": "^2.0.0", - "@npmcli/promise-spawn": "^3.0.0", - "node-gyp": "^9.0.0", - "read-package-json-fast": "^2.0.3", - "which": "^2.0.2" + "@module-federation/bridge-react-webpack-plugin": "0.6.1", + "@module-federation/dts-plugin": "0.6.1", + "@module-federation/managers": "0.6.1", + "@module-federation/manifest": "0.6.1", + "@module-federation/rspack": "0.6.1", + "@module-federation/runtime-tools": "0.6.1", + "@module-federation/sdk": "0.6.1", + "btoa": "^1.2.1", + "upath": "2.0.1" }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "peerDependencies": { + "typescript": "^4.9.0 || ^5.0.0", + "vue-tsc": ">=1.0.24", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vue-tsc": { + "optional": true + }, + "webpack": { + "optional": true + } } }, - "node_modules/@npmcli/run-script/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "node_modules/@module-federation/managers": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@module-federation/managers/-/managers-0.6.1.tgz", + "integrity": "sha512-3ScFROdL2zXZGSd8OvD65ZYYUbrR3djdpNmPIBkug/m7sHl0VFZRDKjHof7PfDvcaQFAeMoSSYRbNAyUfheXBQ==", "dev": true, "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" + "@module-federation/sdk": "0.6.1", + "find-pkg": "2.0.0", + "fs-extra": "9.1.0" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "node_modules/@module-federation/managers/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, - "optional": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, "engines": { - "node": ">=14" + "node": ">=10" } }, - "node_modules/@rollup/plugin-json": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-4.1.0.tgz", - "integrity": "sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==", + "node_modules/@module-federation/manifest": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@module-federation/manifest/-/manifest-0.6.1.tgz", + "integrity": "sha512-WPvUFaeqaxtYcQxzcYEzwlhPNAssd2l+Q82oyBX/dEM+0k1JN/NPdiWoywieY6lNCBSVPASwLut7H/L1rK8OOg==", "dev": true, "dependencies": { - "@rollup/pluginutils": "^3.0.8" - }, - "peerDependencies": { - "rollup": "^1.20.0 || ^2.0.0" + "@module-federation/dts-plugin": "0.6.1", + "@module-federation/managers": "0.6.1", + "@module-federation/sdk": "0.6.1", + "chalk": "3.0.0", + "find-pkg": "2.0.0" } }, - "node_modules/@rollup/plugin-node-resolve": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.3.0.tgz", - "integrity": "sha512-Lus8rbUo1eEcnS4yTFKLZrVumLPY+YayBdWXgFSHYhTT2iJbMhoaaBL3xl5NCdeRytErGr8tZ0L71BMRmnlwSw==", + "node_modules/@module-federation/manifest/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "@rollup/pluginutils": "^3.1.0", - "@types/resolve": "1.17.1", - "deepmerge": "^4.2.2", - "is-builtin-module": "^3.1.0", - "is-module": "^1.0.0", - "resolve": "^1.19.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">= 10.0.0" + "node": ">=8" }, - "peerDependencies": { - "rollup": "^2.42.0" + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@rollup/pluginutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", - "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "node_modules/@module-federation/manifest/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", "dev": true, "dependencies": { - "@types/estree": "0.0.39", - "estree-walker": "^1.0.1", - "picomatch": "^2.2.2" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">= 8.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0" + "node": ">=8" } }, - "node_modules/@schematics/angular": { - "version": "14.2.12", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-14.2.12.tgz", - "integrity": "sha512-nCxoFzH/uh5vqhaAjAfQIBgGuCdV3RMjdSwD0VQ+GFiFvTe8rqFyDl+qpNCgETz4LwmGHb5HNjDH9+VyVLgfZQ==", + "node_modules/@module-federation/manifest/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "@angular-devkit/core": "14.2.12", - "@angular-devkit/schematics": "14.2.12", - "jsonc-parser": "3.1.0" + "color-name": "~1.1.4" }, "engines": { - "node": "^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "node": ">=7.0.0" } }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", + "node_modules/@module-federation/manifest/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/@thednp/event-listener": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@thednp/event-listener/-/event-listener-2.0.5.tgz", - "integrity": "sha512-Zns+CFEAIKIEyqmuBZ3K2DSvk5IppaWcioghxLZPMrzkV034aOA38lP7NIKSxkeu0Eqd4UPxC06FksO6Pb/tmA==", + "node_modules/@module-federation/manifest/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": ">=16", - "pnpm": ">=8.6.0" + "node": ">=8" } }, - "node_modules/@thednp/shorty": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@thednp/shorty/-/shorty-2.0.3.tgz", - "integrity": "sha512-ngKP9/wQxM6JPDFjO6ak8lSz38ZA6cIFQy3gZbZM3xgUqArBr+VG9aoSoLHHEuaObyd9q9Jq/T0Wez7qrck0Gw==", + "node_modules/@module-federation/manifest/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, "engines": { - "node": ">=16", - "pnpm": ">=8.6.0" + "node": ">=8" } }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "node_modules/@module-federation/rspack": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@module-federation/rspack/-/rspack-0.6.1.tgz", + "integrity": "sha512-cxzKSX/+TpIRobyPnstwA1Zy81Ro/YFVdSmY4TxvKl3yGZPLcQhPK6zsqcdsZp2BpWubI6boPaRbZmth5eXmYg==", "dev": true, - "engines": { - "node": ">= 10" + "dependencies": { + "@module-federation/bridge-react-webpack-plugin": "0.6.1", + "@module-federation/dts-plugin": "0.6.1", + "@module-federation/managers": "0.6.1", + "@module-federation/manifest": "0.6.1", + "@module-federation/runtime-tools": "0.6.1", + "@module-federation/sdk": "0.6.1" + }, + "peerDependencies": { + "typescript": "^4.9.0 || ^5.0.0", + "vue-tsc": ">=1.0.24" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vue-tsc": { + "optional": true + } } }, - "node_modules/@ts-morph/common": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.23.0.tgz", - "integrity": "sha512-m7Lllj9n/S6sOkCkRftpM7L24uvmfXQFedlW/4hENcuJH1HHm9u5EgxZb9uVjQSCGrbBWBkOGgcTxNg36r6ywA==", + "node_modules/@module-federation/runtime": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.6.1.tgz", + "integrity": "sha512-57NvdAwrQagw7bEAjvIav8sU0srTT5O+ul8hl86j2a3I5gYBF6NN2QGx4w5d/exgXUcqmUQZBV6I3q5NrcRzfg==", "dev": true, "dependencies": { - "fast-glob": "^3.3.2", - "minimatch": "^9.0.3", - "mkdirp": "^3.0.1", - "path-browserify": "^1.0.1" + "@module-federation/sdk": "0.6.1" } }, - "node_modules/@ts-morph/common/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/@module-federation/runtime-tools": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.6.1.tgz", + "integrity": "sha512-pnc0VZb3jjxrRI1t/K+QfSQ4Viim6VwoQQIeefBLzd2SIMD/LhrYfGzz/glrVHGnAFpxOE/pVANdqlknTMAerw==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "@module-federation/runtime": "0.6.1", + "@module-federation/webpack-bundler-runtime": "0.6.1" } }, - "node_modules/@ts-morph/common/node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "node_modules/@module-federation/sdk": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.6.1.tgz", + "integrity": "sha512-OjDW2sYz4iaZnu4rpTlYDzjXAQiLLzCPwIFM+UFvRtdgWN08Bjn5sDRgMzP2x2ziLzonwmEFDEK9Sl2h85NtJg==", + "dev": true + }, + "node_modules/@module-federation/third-party-dts-extractor": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@module-federation/third-party-dts-extractor/-/third-party-dts-extractor-0.6.1.tgz", + "integrity": "sha512-65Xs4CRXkxLyGYhC4+XJ2Y7qcDsReI+xWt7bREe057PI/ZYp9FAbBO3M5E1eLVHeDdwcU5IQ9RigzGau527VbQ==", "dev": true, - "bin": { - "mkdirp": "dist/cjs/src/bin.js" + "dependencies": { + "find-pkg": "2.0.0", + "fs-extra": "9.1.0", + "resolve": "1.22.8" + } + }, + "node_modules/@module-federation/third-party-dts-extractor/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "node_modules/@module-federation/webpack-bundler-runtime": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.6.1.tgz", + "integrity": "sha512-Gl8iSAS/xvdlicQPFA7URkwUU2HhjQ1L7tHL7s9TWecuDb12Pj0DMkRqE3QimYvitXtvgDqk9B7YUz+diYuCFA==", "dev": true, "dependencies": { - "@types/connect": "*", - "@types/node": "*" + "@module-federation/runtime": "0.6.1", + "@module-federation/sdk": "0.6.1" } }, - "node_modules/@types/bonjour": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", - "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", + "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@types/node": "*" - } + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", + "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@types/node": "*" - } + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/@types/connect-history-api-fallback": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", - "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", + "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", + "cpu": [ + "arm" + ], "dev": true, - "dependencies": { - "@types/express-serve-static-core": "*", - "@types/node": "*" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", - "dev": true + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", + "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/cors": { - "version": "2.8.13", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", - "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", + "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@types/node": "*" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/cytoscape": { - "version": "3.19.10", - "resolved": "https://registry.npmjs.org/@types/cytoscape/-/cytoscape-3.19.10.tgz", - "integrity": "sha512-PLsKQcsUd05nz4PYyulIhjkLnlq9oD2WYpswrWOjoqtFZEuuBje0f9fi2zTG5/yfTf5+Gpllf/MPcFmfDzZ24w==" + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", + "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/@types/cytoscape-edgehandles": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/cytoscape-edgehandles/-/cytoscape-edgehandles-4.0.0.tgz", - "integrity": "sha512-150ohT1VxeS6fa414KLvv6B85CdHAFLI/rInFdCrzhOBgSwStSNonu0rypthliq9AxDjZ+wPOI5hB7VJOyu0wQ==", + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.4.tgz", + "integrity": "sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==", "dependencies": { - "@types/cytoscape": "*" + "@emnapi/core": "^1.1.0", + "@emnapi/runtime": "^1.1.0", + "@tybys/wasm-util": "^0.9.0" } }, - "node_modules/@types/d3-hierarchy": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", - "integrity": "sha512-9hjRTVoZjRFR6xo8igAJyNXQyPX6Aq++Nhb5ebrUF414dv4jr2MitM2fWiOY475wa3Za7TOS2Gh9fmqEhLTt0A==", - "dev": true - }, - "node_modules/@types/d3-scale": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.3.tgz", - "integrity": "sha512-PATBiMCpvHJSMtZAMEhc2WyL+hnzarKzI6wAHYjhsonjWJYGq5BXTzQjv4l8m2jO183/4wZ90rKvSeT7o72xNQ==", + "node_modules/@ngtools/webpack": { + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.3.tgz", + "integrity": "sha512-DDuBHcu23qckt43SexBJaPEIeMc/HKaFOidILZM9D4gU4C9VroMActdR218dvQ802QfL0S46t5Ykz8ENprIfjA==", "dev": true, - "dependencies": { - "@types/d3-time": "*" + "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": { + "@angular/compiler-cli": "^18.0.0", + "typescript": ">=5.4 <5.6", + "webpack": "^5.54.0" } }, - "node_modules/@types/d3-selection": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.5.tgz", - "integrity": "sha512-xCB0z3Hi8eFIqyja3vW8iV01+OHGYR2di/+e+AiOcXIOrY82lcvWW8Ke1DYE/EUVMsBl4Db9RppSBS3X1U6J0w==" - }, - "node_modules/@types/d3-time": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz", - "integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==", - "dev": true + "node_modules/@ngx-translate/core": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-15.0.0.tgz", + "integrity": "sha512-Am5uiuR0bOOxyoercDnAA3rJVizo4RRqJHo8N3RqJ+XfzVP/I845yEnMADykOHvM6HkVm4SZSnJBOiz0Anx5BA==", + "engines": { + "node": "^16.13.0 || >=18.10.0" + }, + "peerDependencies": { + "@angular/common": ">=16.0.0", + "@angular/core": ">=16.0.0", + "rxjs": "^6.5.5 || ^7.4.0" + } }, - "node_modules/@types/d3-transition": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.3.tgz", - "integrity": "sha512-/S90Od8Id1wgQNvIA8iFv9jRhCiZcGhPd2qX0bKF/PS+y0W5CrXKgIiELd2CvG1mlQrWK/qlYh3VxicqG1ZvgA==", - "dependencies": { - "@types/d3-selection": "*" + "node_modules/@ngx-translate/http-loader": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@ngx-translate/http-loader/-/http-loader-8.0.0.tgz", + "integrity": "sha512-SFMsdUcmHF5OdZkL1CHEoSAwbP5EbAOPTLLboOCRRoOg21P4GJx+51jxGdJeGve6LSKLf4Pay7BkTwmE6vxYlg==", + "engines": { + "node": "^16.13.0 || >=18.10.0" + }, + "peerDependencies": { + "@angular/common": ">=16.0.0", + "@angular/core": ">=16.0.0", + "@ngx-translate/core": ">=15.0.0", + "rxjs": "^6.5.5 || ^7.4.0" } }, - "node_modules/@types/eslint": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.1.tgz", - "integrity": "sha512-XpNDc4Z5Tb4x+SW1MriMVeIsMoONHCkWFMkR/aPJbzEsxqHy+4Glu/BqTdPrApfDeMaXbtNh6bseNgl5KaWrSg==", + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" + "engines": { + "node": ">= 8" } }, - "node_modules/@types/estree": { - "version": "0.0.39", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", - "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", - "dev": true - }, - "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/@types/express-serve-static-core": { - "version": "4.19.5", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz", - "integrity": "sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==", + "node_modules/@npmcli/agent": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz", + "integrity": "sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==", "dev": true, "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "node_modules/@npmcli/agent/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true }, - "node_modules/@types/http-proxy": { - "version": "1.17.14", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", - "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", + "node_modules/@npmcli/fs": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", + "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", "dev": true, "dependencies": { - "@types/node": "*" + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@types/jasmine": { - "version": "3.6.11", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.6.11.tgz", - "integrity": "sha512-S6pvzQDvMZHrkBz2Mcn/8Du7cpr76PlRJBAoHnSDNbulULsH5dp0Gns+WRyNX5LHejz/ljxK4/vIHK/caHt6SQ==", - "dev": true - }, - "node_modules/@types/jasminewd2": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.10.tgz", - "integrity": "sha512-J7mDz7ovjwjc+Y9rR9rY53hFWKATcIkrr9DwQWmOas4/pnIPJTXawnzjwpHm3RSxz/e3ZVUvQ7cRbd5UQLo10g==", + "node_modules/@npmcli/git": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-5.0.8.tgz", + "integrity": "sha512-liASfw5cqhjNW9UFd+ruwwdEf/lbOAQjLL2XY2dFW/bkJheXDYZgOyul/4gVvEV4BWkTXjYGmDqMw9uegdbJNQ==", "dev": true, "dependencies": { - "@types/jasmine": "*" + "@npmcli/promise-spawn": "^7.0.0", + "ini": "^4.1.3", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^9.0.0", + "proc-log": "^4.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", - "dev": true - }, - "node_modules/@types/karma": { - "version": "6.3.4", - "resolved": "https://registry.npmjs.org/@types/karma/-/karma-6.3.4.tgz", - "integrity": "sha512-aefuFcs4e4NAOi1Ue4AP9fR2TQv45NFpdaLXdg+3FxDDndk/4T6LoHRTmKtRVqkwtQPh5Ntc3CxsoOgzldAAfg==", + "node_modules/@npmcli/git/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", "dev": true, - "dependencies": { - "@types/node": "*", - "log4js": "^6.4.1" - } - }, - "node_modules/@types/lodash": { - "version": "4.14.196", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.196.tgz", - "integrity": "sha512-22y3o88f4a94mKljsZcanlNWPzO0uBsBdzLAngf2tp533LzZcQzb6+eZPJ+vCTt+bqF2XnvT9gejTLsAcJAJyQ==", - "optional": true - }, - "node_modules/@types/lodash.isequal": { - "version": "4.5.6", - "resolved": "https://registry.npmjs.org/@types/lodash.isequal/-/lodash.isequal-4.5.6.tgz", - "integrity": "sha512-Ww4UGSe3DmtvLLJm2F16hDwEQSv7U0Rr8SujLUA2wHI2D2dm8kPu6Et+/y303LfjTIwSBKXB/YTUcAKpem/XEg==", - "optional": true, - "dependencies": { - "@types/lodash": "*" + "engines": { + "node": ">=16" } }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true - }, - "node_modules/@types/node": { - "version": "16.18.104", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.104.tgz", - "integrity": "sha512-OF3keVCbfPlkzxnnDBUZJn1RiCJzKeadjiW0xTEb0G1SUJ5gDVb3qnzZr2T4uIFvsbKJbXy1v2DN7e2zaEY7jQ==", + "node_modules/@npmcli/git/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true }, - "node_modules/@types/node-forge": { - "version": "1.3.11", - "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", - "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "node_modules/@npmcli/git/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dev": true, "dependencies": { - "@types/node": "*" + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" } }, - "node_modules/@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true - }, - "node_modules/@types/q": { - "version": "0.0.32", - "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", - "integrity": "sha512-qYi3YV9inU/REEfxwVcGZzbS3KG/Xs90lv0Pr+lDtuVjBPGd1A+eciXzVSaRvLify132BfcvhvEjeVahrUl0Ug==", - "dev": true - }, - "node_modules/@types/qs": { - "version": "6.9.15", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", - "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==", - "dev": true - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true - }, - "node_modules/@types/resolve": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", - "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "node_modules/@npmcli/installed-package-contents": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.1.0.tgz", + "integrity": "sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==", "dev": true, "dependencies": { - "@types/node": "*" + "npm-bundled": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", - "dev": true - }, - "node_modules/@types/selenium-webdriver": { - "version": "3.0.22", - "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.22.tgz", - "integrity": "sha512-Nh76NUqvfsZHG5ot5gMlHNNHQvbRvv5UpM4FH3K1HuUGeq4scNlRoKVKSOP/EGIYHhJ2IUXyQc+38jvZLxfB2Q==", - "dev": true - }, - "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "node_modules/@npmcli/node-gyp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz", + "integrity": "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==", "dev": true, - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@types/serve-index": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", - "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", + "node_modules/@npmcli/package-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-5.2.0.tgz", + "integrity": "sha512-qe/kiqqkW0AGtvBjL8TJKZk/eBBSpnJkUWvHdQ9jM2lKHXRYYJuyNpJPlJw3c8QjC2ow6NZYiLExhUaeJelbxQ==", "dev": true, "dependencies": { - "@types/express": "*" + "@npmcli/git": "^5.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^7.0.0", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^6.0.0", + "proc-log": "^4.0.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@types/serve-static": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", - "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "node_modules/@npmcli/promise-spawn": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-7.0.2.tgz", + "integrity": "sha512-xhfYPXoV5Dy4UkY0D+v2KkwvnDfiA/8Mt3sWCGI/hM03NsYIH8ZaG6QzS9x7pje5vHZBZJ2v6VRFVTWACnqcmQ==", "dev": true, "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@types/sockjs": { - "version": "0.3.36", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", - "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", + "node_modules/@npmcli/promise-spawn/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", "dev": true, - "dependencies": { - "@types/node": "*" + "engines": { + "node": ">=16" } }, - "node_modules/@types/systemjs": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@types/systemjs/-/systemjs-6.13.1.tgz", - "integrity": "sha512-Jxo2/uif1WpkabfyvWpFmPWFPDdwKUmyL7xWzjtxNALEu2pgce+eISjbf0Vr+SsK/D9savO5kTRcf+COLK5eiQ==" - }, - "node_modules/@types/ws": { - "version": "8.5.12", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", - "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", + "node_modules/@npmcli/promise-spawn/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dev": true, "dependencies": { - "@types/node": "*" + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" } }, - "node_modules/@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "node_modules/@npmcli/redact": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-2.0.1.tgz", + "integrity": "sha512-YgsR5jCQZhVmTJvjduTOIHph0L73pK8xwMVaDY0PatySqVM9AZj93jpoXYSJqfHFxFkN9dmqTw6OiqExsS3LPw==", "dev": true, - "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "node_modules/@npmcli/run-script": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-8.1.0.tgz", + "integrity": "sha512-y7efHHwghQfk28G2z3tlZ67pLG0XdfYbcVG26r7YIXALRsrVQcTq4/tdenSmdOrEsNahIYA/eh8aEVROWGFUDg==", "dev": true, "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@xtuc/long": "4.2.2" + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/package-json": "^5.0.0", + "@npmcli/promise-spawn": "^7.0.0", + "node-gyp": "^10.0.0", + "proc-log": "^4.0.0", + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", - "dev": true + "node_modules/@npmcli/run-script/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "engines": { + "node": ">=16" + } }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "node_modules/@npmcli/run-script/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" } }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "node_modules/@nrwl/angular": { + "version": "19.7.0", + "resolved": "https://registry.npmjs.org/@nrwl/angular/-/angular-19.7.0.tgz", + "integrity": "sha512-VZJnKzCKkwyBsEK3avp7gALtm2927bXEAeliBgCDHpaC8x0+E8Pev5NQdsuheO+p5jIDWZbwoNpmtmFjeIMtfA==", "dev": true, "dependencies": { - "@xtuc/ieee754": "^1.2.0" + "@nx/angular": "19.7.0", + "tslib": "^2.3.0" } }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "node_modules/@nrwl/devkit": { + "version": "19.7.0", + "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-19.7.0.tgz", + "integrity": "sha512-6c/lapm4o9xQLF1MFeHkbScDIUwgBmPAWHYBtg7qlCba6dv9o4xLj41689hLorq3TyT1d66FTdJ91J+u/s//Ow==", "dev": true, "dependencies": { - "@xtuc/long": "4.2.2" + "@nx/devkit": "19.7.0" } }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", - "dev": true - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "node_modules/@nrwl/js": { + "version": "19.7.0", + "resolved": "https://registry.npmjs.org/@nrwl/js/-/js-19.7.0.tgz", + "integrity": "sha512-RwetapXpRYsI3dwbXSVeahmf7ZLf2Tq9pH/91c9E0zAgvC1EwBCEpc7IC7N1HJejEQt9nA3ZcWxhAM+wo4EDuA==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" + "@nx/js": "19.7.0" } }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", - "dev": true, + "node_modules/@nrwl/tao": { + "version": "19.7.0", + "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-19.7.0.tgz", + "integrity": "sha512-Y7LwFAq6U38U486eL9L1G6D32Z2e+f6bJRqhbAqX7j4eAHPv0ALpQuum5uGms2yywL25qwXZPKWtAHi6nbC6Ag==", "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "nx": "19.7.0", + "tslib": "^2.3.0" + }, + "bin": { + "tao": "index.js" } }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "node_modules/@nrwl/web": { + "version": "19.7.0", + "resolved": "https://registry.npmjs.org/@nrwl/web/-/web-19.7.0.tgz", + "integrity": "sha512-AuoCEvNW/Yx0ZFp9gF33b70hNaK+GDawhYv/U+0s0kgD54kKFQSSIjT46wLgOP/P9LfqTztS4hlpXR4TCQwWqQ==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" + "@nx/web": "19.7.0" } }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "node_modules/@nrwl/webpack": { + "version": "19.7.0", + "resolved": "https://registry.npmjs.org/@nrwl/webpack/-/webpack-19.7.0.tgz", + "integrity": "sha512-0LujfuAM+zDIXQjbCNYYGu62L352EljyrM5quZB1Y0Dm5NjQ6EOmAfIchULXy5999Xzv0sdmVZ0LPMoBYlNepg==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@nx/webpack": "19.7.0" } }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "node_modules/@nrwl/workspace": { + "version": "19.7.0", + "resolved": "https://registry.npmjs.org/@nrwl/workspace/-/workspace-19.7.0.tgz", + "integrity": "sha512-z0vqA+a9iuAmkvSa4Bw4Ushvy8zAyOqKwQnfYDT6ue4LCTo4c1ll+q2RPgKj1kbFt5iJ1lsnDxXj+6xnmCwozQ==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@xtuc/long": "4.2.2" + "@nx/workspace": "19.7.0" } }, - "node_modules/@xmldom/xmldom": { - "version": "0.8.10", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", - "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", - "optional": true, - "engines": { - "node": ">=10.0.0" + "node_modules/@nx/angular": { + "version": "19.7.0", + "resolved": "https://registry.npmjs.org/@nx/angular/-/angular-19.7.0.tgz", + "integrity": "sha512-jhI6pJ/pTND7GCVYpNClJnA5OJKCZauq4XbWeO3BVdsWYZyxsf/cxYTCtjbeYHcciAllSTNl+82tyv55OL6L0g==", + "dev": true, + "dependencies": { + "@module-federation/enhanced": "~0.6.0", + "@nrwl/angular": "19.7.0", + "@nx/devkit": "19.7.0", + "@nx/eslint": "19.7.0", + "@nx/js": "19.7.0", + "@nx/web": "19.7.0", + "@nx/webpack": "19.7.0", + "@nx/workspace": "19.7.0", + "@phenomnomnominal/tsquery": "~5.0.1", + "@typescript-eslint/type-utils": "^7.16.0", + "chalk": "^4.1.0", + "find-cache-dir": "^3.3.2", + "magic-string": "~0.30.2", + "minimatch": "9.0.3", + "piscina": "^4.4.0", + "semver": "^7.5.3", + "tslib": "^2.3.0", + "webpack": "^5.88.0", + "webpack-merge": "^5.8.0" + }, + "peerDependencies": { + "@angular-devkit/build-angular": ">= 16.0.0 < 19.0.0", + "@angular-devkit/core": ">= 16.0.0 < 19.0.0", + "@angular-devkit/schematics": ">= 16.0.0 < 19.0.0", + "@schematics/angular": ">= 16.0.0 < 19.0.0", + "rxjs": "^6.5.3 || ^7.5.0" } }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, - "node_modules/@yarnpkg/lockfile": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", - "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", - "dev": true - }, - "node_modules/abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "deprecated": "Use your platform's native atob() and btoa() methods instead", - "dev": true - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "node_modules/@nx/angular/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "color-convert": "^2.0.1" }, "engines": { - "node": ">= 0.6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/adjust-sourcemap-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", - "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "node_modules/@nx/angular/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { - "loader-utils": "^2.0.0", - "regex-parser": "^2.2.11" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=8.9" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/adjust-sourcemap-loader/node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "node_modules/@nx/angular/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" + "color-name": "~1.1.4" }, "engines": { - "node": ">=8.9.0" + "node": ">=7.0.0" } }, - "node_modules/adm-zip": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", - "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==", - "dev": true, - "engines": { - "node": ">=6.0" - } + "node_modules/@nx/angular/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "node_modules/@nx/angular/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "dependencies": { - "debug": "4" - }, "engines": { - "node": ">= 6.0.0" + "node": ">=8" } }, - "node_modules/agentkeepalive": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.3.0.tgz", - "integrity": "sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==", + "node_modules/@nx/angular/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "debug": "^4.1.0", - "depd": "^2.0.0", - "humanize-ms": "^1.2.1" + "has-flag": "^4.0.0" }, "engines": { - "node": ">= 8.0.0" + "node": ">=8" } }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "node_modules/@nx/angular/node_modules/webpack-merge": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", "dev": true, "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=10.0.0" } }, - "node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "node_modules/@nx/devkit": { + "version": "19.7.0", + "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-19.7.0.tgz", + "integrity": "sha512-/COQC55ADJ1y3bJJrgacswrZ/EmuMGpPbnz8T9oilmqNXZWLli0ViKs+J4QMCXTDPV1kpJNkJqyzV8TR/AfCsw==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "@nrwl/devkit": "19.7.0", + "ejs": "^3.1.7", + "enquirer": "~2.3.6", + "ignore": "^5.0.4", + "minimatch": "9.0.3", + "semver": "^7.5.3", + "tmp": "~0.2.1", + "tslib": "^2.3.0", + "yargs-parser": "21.1.1" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "peerDependencies": { + "nx": ">= 17 <= 20" } }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "node_modules/@nx/eslint": { + "version": "19.7.0", + "resolved": "https://registry.npmjs.org/@nx/eslint/-/eslint-19.7.0.tgz", + "integrity": "sha512-1yrgr7rbjNz9AUwdG1mVa8qRBQopCMpoi8xcxEelh7rqZ8jg0FtfEkhSpG9LWOv7LDaIop9pgT25uZE4sruRbg==", "dev": true, "dependencies": { - "ajv": "^8.0.0" + "@nx/devkit": "19.7.0", + "@nx/js": "19.7.0", + "@nx/linter": "19.7.0", + "semver": "^7.5.3", + "tslib": "^2.3.0", + "typescript": "~5.4.2" }, "peerDependencies": { - "ajv": "^8.0.0" + "@zkochan/js-yaml": "0.0.7", + "eslint": "^8.0.0 || ^9.0.0" }, "peerDependenciesMeta": { - "ajv": { + "@zkochan/js-yaml": { "optional": true } } }, - "node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "node_modules/@nx/eslint/node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, - "peerDependencies": { - "ajv": "^8.8.2" + "engines": { + "node": ">=14.17" } }, - "node_modules/angular-in-memory-web-api": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/angular-in-memory-web-api/-/angular-in-memory-web-api-0.14.0.tgz", - "integrity": "sha512-8RLFBpXZONDQxYGKiheaYQQl3iydesCrhWLuzDD6AsQDcOF+HEvIuOfBdJaTWKfqyNZNWjHvXzIyT0bUIunb/A==", - "dev": true, - "dependencies": { + "node_modules/@nx/js": { + "version": "19.7.0", + "resolved": "https://registry.npmjs.org/@nx/js/-/js-19.7.0.tgz", + "integrity": "sha512-DDRcg6cxJF3amVzRMEtoGqcpeZyXi6rX/ASyADJPgoDdSx1k97dyjzfEEoELMP0YRxRWSPZfzD2y0q0BolQvjA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.2", + "@babel/plugin-proposal-decorators": "^7.22.7", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-runtime": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/preset-typescript": "^7.22.5", + "@babel/runtime": "^7.22.6", + "@nrwl/js": "19.7.0", + "@nx/devkit": "19.7.0", + "@nx/workspace": "19.7.0", + "babel-plugin-const-enum": "^1.0.1", + "babel-plugin-macros": "^2.8.0", + "babel-plugin-transform-typescript-metadata": "^0.3.1", + "chalk": "^4.1.0", + "columnify": "^1.6.0", + "detect-port": "^1.5.1", + "fast-glob": "3.2.7", + "fs-extra": "^11.1.0", + "ignore": "^5.0.4", + "js-tokens": "^4.0.0", + "jsonc-parser": "3.2.0", + "minimatch": "9.0.3", + "npm-package-arg": "11.0.1", + "npm-run-path": "^4.0.1", + "ora": "5.3.0", + "semver": "^7.5.3", + "source-map-support": "0.5.19", + "ts-node": "10.9.1", + "tsconfig-paths": "^4.1.2", "tslib": "^2.3.0" }, "peerDependencies": { - "@angular/common": "^14.0.0", - "@angular/core": "^14.0.0", - "rxjs": "^6.5.3 || ^7.4.0" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "engines": { - "node": ">=6" + "verdaccio": "^5.0.4" + }, + "peerDependenciesMeta": { + "verdaccio": { + "optional": true + } } }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "node_modules/@nx/js/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "type-fest": "^0.21.3" + "color-convert": "^2.0.1" }, "engines": { "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-html-community": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", - "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", - "dev": true, - "engines": [ - "node >= 0.8.0" - ], - "bin": { - "ansi-html": "bin/ansi-html" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@nx/js/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/@nx/js/node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, "dependencies": { - "color-convert": "^1.9.0" + "restore-cursor": "^3.1.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "node_modules/@nx/js/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "color-name": "~1.1.4" }, "engines": { - "node": ">= 8" + "node": ">=7.0.0" } }, - "node_modules/apache-crypt": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/apache-crypt/-/apache-crypt-1.2.6.tgz", - "integrity": "sha512-072WetlM4blL8PREJVeY+WHiUh1R5VNt2HfceGS8aKqttPHcmqE5pkKuXPz/ULmJOFkc8Hw3kfKl6vy7Qka6DA==", + "node_modules/@nx/js/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@nx/js/node_modules/fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", "dev": true, "dependencies": { - "unix-crypt-td-js": "^1.1.4" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" }, "engines": { "node": ">=8" } }, - "node_modules/apache-md5": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/apache-md5/-/apache-md5-1.1.8.tgz", - "integrity": "sha512-FCAJojipPn0bXjuEpjOOOMN8FZDkxfWWp4JGN9mifU2IhxvKyXZYqpzPHdnTSUpmPDy+tsslB6Z1g+Vg6nVbYA==", + "node_modules/@nx/js/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { "node": ">=8" } }, - "node_modules/append-transform": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", - "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", + "node_modules/@nx/js/node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, + "node_modules/@nx/js/node_modules/npm-package-arg": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.1.tgz", + "integrity": "sha512-M7s1BD4NxdAvBKUPqqRW957Xwcl/4Zvo8Aj+ANrzvIPzGJZElrH7Z//rSaec2ORcND6FHHLnZeY8qgTpXDMFQQ==", "dev": true, "dependencies": { - "default-require-extensions": "^2.0.0" + "hosted-git-info": "^7.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" }, "engines": { - "node": ">=4" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/applicationinsights-js": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/applicationinsights-js/-/applicationinsights-js-1.0.21.tgz", - "integrity": "sha512-AUkkm8OWfCgbBuMe7kSAwUFpc1e2y+WisieQx/VgCS+BT/0AubmnGZ1yQ+zkENVriM9qArKNjLqTQp38x995wg==", - "deprecated": "This package has been moved to @microsoft/applicationinsights-web. Please install that package instead for the latest updates & features" - }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "dev": true - }, - "node_modules/are-we-there-yet": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", - "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", - "deprecated": "This package is no longer supported.", - "dev": true, - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "node_modules/@nx/js/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" + "mimic-fn": "^2.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=6" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "dev": true - }, - "node_modules/array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "node_modules/@nx/js/node_modules/ora": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.3.0.tgz", + "integrity": "sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==", "dev": true, "dependencies": { - "array-uniq": "^1.0.1" + "bl": "^4.0.3", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "log-symbols": "^4.0.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "node_modules/@nx/js/node_modules/proc-log": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", + "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "node_modules/@nx/js/node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "node_modules/@nx/js/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/@nx/js/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "node_modules/@nx/js/node_modules/source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", "dev": true, "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "dev": true, - "engines": { - "node": ">=0.8" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "node_modules/@nx/js/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "lodash": "^4.17.14" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true - }, - "node_modules/atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true, - "bin": { - "atob": "bin/atob.js" + "has-flag": "^4.0.0" }, "engines": { - "node": ">= 4.5.0" + "node": ">=8" } }, - "node_modules/autoprefixer": { - "version": "10.4.14", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", - "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", + "node_modules/@nx/js/node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - } - ], "dependencies": { - "browserslist": "^4.21.5", - "caniuse-lite": "^1.0.30001464", - "fraction.js": "^4.2.0", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" }, "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" }, "peerDependencies": { - "postcss": "^8.1.0" + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } } }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "node_modules/@nx/linter": { + "version": "19.7.0", + "resolved": "https://registry.npmjs.org/@nx/linter/-/linter-19.7.0.tgz", + "integrity": "sha512-bJvyXuG3OQLkpqS/1MZU+Re67yZ46lOMWIm8MX06ifxdt01kJIFnaTHFBcRrOrcAotaqcHnzJY0ik6rUFTjUSQ==", "dev": true, "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@nx/eslint": "19.7.0" } }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "dev": true, + "node_modules/@nx/nx-darwin-arm64": { + "version": "19.7.0", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-19.7.0.tgz", + "integrity": "sha512-xlgTO3QZVM+DnKM5j3vZrXqKqFkf9XPDLnh+LtMx6GKjkVdnz2TCDo/wwwBB2IjBE+nSK3Fvl15CmWLnI2GGWg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "*" + "node": ">= 10" } }, - "node_modules/aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", - "dev": true + "node_modules/@nx/nx-darwin-x64": { + "version": "19.7.0", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-19.7.0.tgz", + "integrity": "sha512-tDT5Dk65+F+KWIybhT1IU5i+uBdlS3pa3SPvPIEvW0sJ+vgugCxDN7iKlrJgYdxYFDx9g+WHRRguLe9gKpVA2Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } }, - "node_modules/babel-loader": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", - "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", - "dev": true, - "dependencies": { - "find-cache-dir": "^3.3.1", - "loader-utils": "^2.0.0", - "make-dir": "^3.1.0", - "schema-utils": "^2.6.5" - }, + "node_modules/@nx/nx-freebsd-x64": { + "version": "19.7.0", + "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-19.7.0.tgz", + "integrity": "sha512-7dg7zMQ56F53Fw107dMmNcnD5SPW+gd2aJAkE9hrG0byz9oC5TpXNDpL0/eUSrrAESqF7xYclpfCzLzkMN2Iaw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">= 8.9" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "webpack": ">=2" + "node": ">= 10" } }, - "node_modules/babel-loader/node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, + "node_modules/@nx/nx-linux-arm-gnueabihf": { + "version": "19.7.0", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-19.7.0.tgz", + "integrity": "sha512-Qc315NapRWvlasVWsncV1v4f6hL+QLHxM/8WPMbkvKNCdOjiaFgNFfjrFMbzevGqAIx3X5X5T6XKXHRblDPwXw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8.9.0" + "node": ">= 10" } }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, + "node_modules/@nx/nx-linux-arm64-gnu": { + "version": "19.7.0", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-19.7.0.tgz", + "integrity": "sha512-nyxVymloy6DrSAIJDVBf3PkOoz4lXzZxj1AHgj4zdETllY/T2WXODzzy4PyN0sqF5ljX68FODVXbxpkIcS0EQQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">= 10" } }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", - "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.3", - "semver": "^6.1.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node_modules/@nx/nx-linux-arm64-musl": { + "version": "19.7.0", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-19.7.0.tgz", + "integrity": "sha512-BN/mwZGaVgcHJhuGbu1W6EcFPbtDc8pY0cTIYkqKLzHlFfWLdRvLLJVeUGmc9ubvU3lqQLWX/lRi6Nd8RZINjg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" } }, - "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" + "node_modules/@nx/nx-linux-x64-gnu": { + "version": "19.7.0", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-19.7.0.tgz", + "integrity": "sha512-ATcIPJdLQRYIMmbMQzbZzYVEo/unmSVq+9+/fah3okv+/5je+/tJcnf777WS4qP6ysBv2InE+wk2F/g6TNpJvg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" } }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz", - "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.2", - "core-js-compat": "^3.21.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node_modules/@nx/nx-linux-x64-musl": { + "version": "19.7.0", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-19.7.0.tgz", + "integrity": "sha512-IwP5Are8N2nBWnPygA0AS8xRl7+Wn3oTBkIPESjWs5WzfyMGTlVL/R2/8GB1hc8fdr2dd+3R7LuMZpZ/d1G7xg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" } }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", - "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node_modules/@nx/nx-win32-arm64-msvc": { + "version": "19.7.0", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-19.7.0.tgz", + "integrity": "sha512-+8EfLC760GzaZ4wvcWdYnoO1kw4xOrVnBTuijgkZeTPvBPPrKOi7y2bv9tG2LklE5GTt8mkmxCSEkQfSxsGIhw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "node_modules/@nx/nx-win32-x64-msvc": { + "version": "19.7.0", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-19.7.0.tgz", + "integrity": "sha512-h6d3zBSjhJlGxjBXKGgLqrgxMrkobdyU5KO0zjrQ3+PWrdrtw4jrIhKxW3KoFOzjbDPMmwNH/Bd7aYwZ/RMlEw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "node_modules/@nx/web": { + "version": "19.7.0", + "resolved": "https://registry.npmjs.org/@nx/web/-/web-19.7.0.tgz", + "integrity": "sha512-I3W2FXU6yUsccc7AlRTKw4MLE+hHji05vk8ziI/FWQW0U0zXbBIA/oefFibajITn4fQr4dZxTXTqNtUzsKBlhA==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "dependencies": { + "@nrwl/web": "19.7.0", + "@nx/devkit": "19.7.0", + "@nx/js": "19.7.0", + "chalk": "^4.1.0", + "detect-port": "^1.5.1", + "http-server": "^14.1.0", + "tslib": "^2.3.0" + } }, - "node_modules/base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "node_modules/@nx/web/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": "^4.5.0 || >= 5.9" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "node_modules/@nx/web/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { - "safe-buffer": "5.1.2" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">= 0.8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", - "dev": true - }, - "node_modules/batch-processor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/batch-processor/-/batch-processor-1.0.0.tgz", - "integrity": "sha512-xoLQD8gmmR32MeuBHgH0Tzd5PuSZx71ZsbhVxOCRbgktZEPe4SQy7s9Z50uPp0F/f7iw2XmkHN2xkgbMfckMDA==" - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "node_modules/@nx/web/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "tweetnacl": "^0.14.3" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "node_modules/bcryptjs": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", - "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==", + "node_modules/@nx/web/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "node_modules/@nx/web/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": "*" - } - }, - "node_modules/billboard.js": { - "version": "3.12.4", - "resolved": "https://registry.npmjs.org/billboard.js/-/billboard.js-3.12.4.tgz", - "integrity": "sha512-iXKDrci2UVH9J0b5Y8gRsr650bQk2TfIRycBAgVZZjdHkwmKDdTLD0D2YxbVe3GvcP60Tog2HypQV64QlAuTwQ==", - "dependencies": { - "@types/d3-selection": "^3.0.0", - "@types/d3-transition": "^3.0.0", - "d3-axis": "^3.0.0", - "d3-brush": "^3.0.0", - "d3-drag": "^3.0.0", - "d3-dsv": "^3.0.1", - "d3-ease": "^3.0.1", - "d3-hierarchy": "^3.1.2", - "d3-interpolate": "^3.0.1", - "d3-scale": "^4.0.2", - "d3-selection": "^3.0.0", - "d3-shape": "^3.2.0", - "d3-time-format": "^4.1.0", - "d3-transition": "^3.0.1", - "d3-zoom": "^3.0.0" + "node": ">=8" } }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "node_modules/@nx/web/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, "engines": { "node": ">=8" } }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "node_modules/@nx/webpack": { + "version": "19.7.0", + "resolved": "https://registry.npmjs.org/@nx/webpack/-/webpack-19.7.0.tgz", + "integrity": "sha512-MAMYS8gj6XGwnoAhCYI/Wcutkn+URCSTOEcfDhlua9oDlpAkX7jUKNmyKQpQHEvSpoBMvcRy+wy69IwzVYlj/w==", "dev": true, "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "@babel/core": "^7.23.2", + "@module-federation/enhanced": "^0.6.0", + "@module-federation/sdk": "^0.6.0", + "@nrwl/webpack": "19.7.0", + "@nx/devkit": "19.7.0", + "@nx/js": "19.7.0", + "@phenomnomnominal/tsquery": "~5.0.1", + "ajv": "^8.12.0", + "autoprefixer": "^10.4.9", + "babel-loader": "^9.1.2", + "browserslist": "^4.21.4", + "chalk": "^4.1.0", + "copy-webpack-plugin": "^10.2.4", + "css-loader": "^6.4.0", + "css-minimizer-webpack-plugin": "^5.0.0", + "express": "^4.19.2", + "fork-ts-checker-webpack-plugin": "7.2.13", + "http-proxy-middleware": "^3.0.0", + "less": "4.1.3", + "less-loader": "11.1.0", + "license-webpack-plugin": "^4.0.2", + "loader-utils": "^2.0.3", + "mini-css-extract-plugin": "~2.4.7", + "parse5": "4.0.0", + "postcss": "^8.4.38", + "postcss-import": "~14.1.0", + "postcss-loader": "^6.1.1", + "rxjs": "^7.8.0", + "sass": "^1.42.1", + "sass-loader": "^12.2.0", + "source-map-loader": "^5.0.0", + "style-loader": "^3.3.0", + "stylus": "^0.59.0", + "stylus-loader": "^7.1.0", + "terser-webpack-plugin": "^5.3.3", + "ts-loader": "^9.3.1", + "tsconfig-paths-webpack-plugin": "4.0.0", + "tslib": "^2.3.0", + "webpack": "^5.80.0", + "webpack-dev-server": "^5.0.4", + "webpack-node-externals": "^3.0.0", + "webpack-subresource-integrity": "^5.1.0" } }, - "node_modules/blocking-proxy": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-1.0.1.tgz", - "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==", + "node_modules/@nx/webpack/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "blocking-proxy": "built/lib/bin.js" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=6.9.x" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "node_modules/@nx/webpack/node_modules/array-union": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz", + "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==", "dev": true, - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/@nx/webpack/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { - "ms": "2.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/body-parser/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "node_modules/@nx/webpack/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "color-name": "~1.1.4" }, "engines": { - "node": ">=0.10.0" + "node": ">=7.0.0" } }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "node_modules/@nx/webpack/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/body-parser/node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "node_modules/@nx/webpack/node_modules/copy-webpack-plugin": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-10.2.4.tgz", + "integrity": "sha512-xFVltahqlsRcyyJqQbDY6EYTtyQZF9rf+JPjwHObLdPFMEISqkFkr7mFoVOC6BfYS/dNThyoQKvziugm+OnwBg==", "dev": true, "dependencies": { - "ee-first": "1.1.1" + "fast-glob": "^3.2.7", + "glob-parent": "^6.0.1", + "globby": "^12.0.2", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 12.20.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" } }, - "node_modules/bonjour-service": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", - "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", + "node_modules/@nx/webpack/node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.3", - "multicast-dns": "^7.2.5" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } }, - "node_modules/bootstrap.native": { - "version": "5.0.13", - "resolved": "https://registry.npmjs.org/bootstrap.native/-/bootstrap.native-5.0.13.tgz", - "integrity": "sha512-SiiTxaK3LjuOjPaXEnDBQNY3w0t28Qdx6I8drortuFg6Ch3q6cWoOxlFHThcGOPewziVarQAA4WPE00GFQmbWQ==", + "node_modules/@nx/webpack/node_modules/css-loader": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", + "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", "dev": true, "dependencies": { - "@thednp/event-listener": "^2.0.4", - "@thednp/shorty": "^2.0.0" + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" }, "engines": { - "node": ">=16", - "pnpm": ">=8.6.0" + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } } }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/@nx/webpack/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0" + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "node_modules/@nx/webpack/node_modules/globby": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-12.2.0.tgz", + "integrity": "sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA==", "dev": true, "dependencies": { - "fill-range": "^7.1.1" + "array-union": "^3.0.1", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.7", + "ignore": "^5.1.9", + "merge2": "^1.4.1", + "slash": "^4.0.0" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/brotli": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", - "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "node_modules/@nx/webpack/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "dependencies": { - "base64-js": "^1.1.2" + "engines": { + "node": ">=8" } }, - "node_modules/browserslist": { - "version": "4.23.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz", - "integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==", + "node_modules/@nx/webpack/node_modules/less": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", + "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "dependencies": { - "caniuse-lite": "^1.0.30001640", - "electron-to-chromium": "^1.4.820", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.1.0" + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^2.3.0" }, "bin": { - "browserslist": "cli.js" + "lessc": "bin/lessc" }, "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/browserstack": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.6.1.tgz", - "integrity": "sha512-GxtFjpIaKdbAyzHfFDKixKO8IBT7wR3NjbzrGc78nNs/Ciys9wU3/nBtsqsWv5nDSrdI5tz0peKuzCPuNXNUiw==", - "dev": true, - "dependencies": { - "https-proxy-agent": "^2.2.1" + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "source-map": "~0.6.0" } }, - "node_modules/browserstack/node_modules/agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "node_modules/@nx/webpack/node_modules/less-loader": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.1.0.tgz", + "integrity": "sha512-C+uDBV7kS7W5fJlUjq5mPBeBVhYpTIm5gB09APT9o3n/ILeaXVsiSFTbZpTJCJwQ/Crczfn3DmfQFwxYusWFug==", "dev": true, "dependencies": { - "es6-promisify": "^5.0.0" + "klona": "^2.0.4" }, "engines": { - "node": ">= 4.0.0" + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "less": "^3.5.0 || ^4.0.0", + "webpack": "^5.0.0" } }, - "node_modules/browserstack/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/@nx/webpack/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, "dependencies": { - "ms": "^2.1.1" + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" } }, - "node_modules/browserstack/node_modules/https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "node_modules/@nx/webpack/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", "dev": true, + "optional": true, "dependencies": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" + "pify": "^4.0.1", + "semver": "^5.6.0" }, "engines": { - "node": ">= 4.5.0" + "node": ">=6" } }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "node_modules/@nx/webpack/node_modules/make-dir/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "optional": true, + "bin": { + "semver": "bin/semver" } }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "node_modules/@nx/webpack/node_modules/mini-css-extract-plugin": { + "version": "2.4.7", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.4.7.tgz", + "integrity": "sha512-euWmddf0sk9Nv1O0gfeeUAvAkoSlWncNLF77C0TP2+WoPvy8mAHKOzMajcCz2dzvyt3CNgxb1obIEVFIRxaipg==", "dev": true, + "dependencies": { + "schema-utils": "^4.0.0" + }, "engines": { - "node": ">=6" + "node": ">= 12.13.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" } }, - "node_modules/builtins": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", - "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", - "dev": true, - "dependencies": { - "semver": "^7.0.0" - } + "node_modules/@nx/webpack/node_modules/parse5": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "dev": true }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "node_modules/@nx/webpack/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true, + "optional": true, "engines": { - "node": ">= 0.8" + "node": ">=6" } }, - "node_modules/cacache": { - "version": "16.1.2", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.2.tgz", - "integrity": "sha512-Xx+xPlfCZIUHagysjjOAje9nRo8pRDczQCcXb4J2O0BLtH+xeVue6ba4y1kfJfQMAnM2mkcoMIAyOctlaRGWYA==", + "node_modules/@nx/webpack/node_modules/postcss-loader": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", + "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", "dev": true, "dependencies": { - "@npmcli/fs": "^2.1.0", - "@npmcli/move-file": "^2.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "glob": "^8.0.1", - "infer-owner": "^1.0.4", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", - "tar": "^6.1.11", - "unique-filename": "^1.1.1" + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.5" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/cacache/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, - "engines": { - "node": ">=12" + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" } }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "node_modules/@nx/webpack/node_modules/sass-loader": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", + "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", "dev": true, "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "klona": "^2.0.4", + "neo-async": "^2.6.2" }, "engines": { - "node": ">= 0.4" + "node": ">= 12.13.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==", - "dev": true, - "engines": { - "node": "*" + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + } } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/@nx/webpack/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", "dev": true, "engines": { - "node": ">=6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "node_modules/@nx/webpack/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, + "optional": true, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001643", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001643.tgz", - "integrity": "sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "dev": true - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/@nx/webpack/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "node_modules/cheerio": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", - "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "node_modules/@nx/workspace": { + "version": "19.7.0", + "resolved": "https://registry.npmjs.org/@nx/workspace/-/workspace-19.7.0.tgz", + "integrity": "sha512-ZN6SL+ImNlr2b5ibpbAGfm+yiDmm2Q/mgNuX58YnwKq4u8xr9AWERzGmzkb1dwFB8EXMhjNdYwVWBWl1nC7+Hw==", "dev": true, "dependencies": { - "cheerio-select": "^2.1.0", - "dom-serializer": "^2.0.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "htmlparser2": "^8.0.1", - "parse5": "^7.0.0", - "parse5-htmlparser2-tree-adapter": "^7.0.0" - }, - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + "@nrwl/workspace": "19.7.0", + "@nx/devkit": "19.7.0", + "chalk": "^4.1.0", + "enquirer": "~2.3.6", + "nx": "19.7.0", + "tslib": "^2.3.0", + "yargs-parser": "21.1.1" } }, - "node_modules/cheerio-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", - "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "node_modules/@nx/workspace/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "boolbase": "^1.0.0", - "css-select": "^5.1.0", - "css-what": "^6.1.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1" + "color-convert": "^2.0.1" }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/cheerio/node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "dev": true, - "dependencies": { - "entities": "^4.4.0" + "engines": { + "node": ">=8" }, "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "node_modules/@nx/workspace/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">= 8.10.0" + "node": ">=10" }, "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, - "engines": { - "node": ">=10" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "node_modules/@nx/workspace/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, "engines": { - "node": ">=6.0" + "node": ">=7.0.0" } }, - "node_modules/circular-json": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", - "deprecated": "CircularJSON is in maintenance only, flatted is its successor.", + "node_modules/@nx/workspace/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "node_modules/@nx/workspace/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "node_modules/@nx/workspace/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "restore-cursor": "^3.1.0" + "has-flag": "^4.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/cli-spinners": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.0.tgz", - "integrity": "sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==", + "node_modules/@phenomnomnominal/tsquery": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", + "integrity": "sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==", "dev": true, - "engines": { - "node": ">=6" + "dependencies": { + "esquery": "^1.4.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "typescript": "^3 || ^4 || ^5" } }, - "node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, + "optional": true, "engines": { - "node": ">= 10" + "node": ">=14" } }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "node_modules/@rollup/plugin-json": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz", + "integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==", "dev": true, "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true, + "@rollup/pluginutils": "^5.1.0" + }, "engines": { - "node": ">=0.8" + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } } }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "node_modules/@rollup/plugin-node-resolve": { + "version": "15.2.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz", + "integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==", "dev": true, "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-builtin-module": "^3.2.1", + "is-module": "^1.0.0", + "resolve": "^1.22.1" }, "engines": { - "node": ">=6" + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } } }, - "node_modules/code-block-writer": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.2.tgz", - "integrity": "sha512-XfXzAGiStXSmCIwrkdfvc7FS5Dtj8yelCtyOf2p2skCAfvLd6zu0rGzuS9NSCO3bq1JKpFZ7tbKdKlcd5occQA==", - "dev": true - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/@rollup/pluginutils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", + "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", "dev": true, "dependencies": { - "color-name": "1.1.3" + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } } }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "node_modules/@rollup/pluginutils/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "bin": { - "color-support": "bin.js" + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true + "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==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] }, - "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "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==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "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==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "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==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "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==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "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==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "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==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "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==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "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==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "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==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "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==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "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==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "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==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "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==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "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==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "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==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/wasm-node": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/wasm-node/-/wasm-node-4.21.2.tgz", + "integrity": "sha512-AJCfdXkpe5EX+jfWOMYuFl3ZomTQyfx4V4geRmChdTwAo05FdpnobwqtYn0mo7Mf1qVN7mniI7kdG98vKDVd2g==", "dev": true, + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, "engines": { - "node": ">=0.1.90" + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "node_modules/@schematics/angular": { + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.3.tgz", + "integrity": "sha512-whSON70z9HYb4WboVXmPFE/RLKJJQLWNzNcUyi8OSDZkQbJnYgPp0///n738m26Y/XeJDv11q1gESy+Zl2AdUw==", "dev": true, "dependencies": { - "delayed-stream": "~1.0.0" + "@angular-devkit/core": "18.2.3", + "@angular-devkit/schematics": "18.2.3", + "jsonc-parser": "3.3.1" }, "engines": { - "node": ">= 0.8" + "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/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "node_modules/@sigstore/bundle": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.3.2.tgz", + "integrity": "sha512-wueKWDk70QixNLB363yHc2D2ItTgYiMTdPwK8D9dKQMR3ZQ0c35IxP5xnwQ8cNLoCgCRcHf14kE+CLIvNX1zmA==", "dev": true, + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2" + }, "engines": { - "node": ">=18" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true + "node_modules/@sigstore/core": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-1.1.0.tgz", + "integrity": "sha512-JzBqdVIyqm2FRQCulY6nbQzMpJJpSiJ8XXWMhtOX9eKgaXXpfNOF53lzQEjIydlStnd/eFtuC1dW4VYdD93oRg==", + "dev": true, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } }, - "node_modules/compare-versions": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", - "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", - "dev": true + "node_modules/@sigstore/protobuf-specs": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.3.2.tgz", + "integrity": "sha512-c6B0ehIWxMI8wiS/bj6rHMPqeFvngFV7cDU/MY+B16P9Z3Mp9k8L93eYZ7BYzSickzuqAQqAq0V956b3Ju6mLw==", + "dev": true, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "node_modules/@sigstore/sign": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-2.3.2.tgz", + "integrity": "sha512-5Vz5dPVuunIIvC5vBb0APwo7qKA4G9yM48kPWJT+OEERs40md5GoUR1yedwpekWZ4m0Hhw44m6zU+ObsON+iDA==", "dev": true, "dependencies": { - "mime-db": ">= 1.43.0 < 2" + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "make-fetch-happen": "^13.0.1", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1" }, "engines": { - "node": ">= 0.6" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "node_modules/@sigstore/tuf": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-2.3.4.tgz", + "integrity": "sha512-44vtsveTPUpqhm9NCrbU8CWLe3Vck2HO1PNLw7RIajbB7xhtn5RBPm1VNSCMwqGYHhDsBJG8gDF0q4lgydsJvw==", "dev": true, "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" + "@sigstore/protobuf-specs": "^0.3.2", + "tuf-js": "^2.2.1" }, "engines": { - "node": ">= 0.8.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/compression/node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "node_modules/@sigstore/verify": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-1.2.1.tgz", + "integrity": "sha512-8iKx79/F73DKbGfRf7+t4dqrc0bRr0thdPrxAtCKWRm/F0tG71i6O1rvlnScncJLLBZHn3h8M3c1BSUAb9yu8g==", "dev": true, + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.1.0", + "@sigstore/protobuf-specs": "^0.3.2" + }, "engines": { - "node": ">= 0.8" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", "dev": true, - "dependencies": { - "ms": "2.0.0" + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", "dev": true }, - "node_modules/connect": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "node_modules/@thednp/event-listener": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@thednp/event-listener/-/event-listener-2.0.6.tgz", + "integrity": "sha512-6u55ydv4+2VHwHU8EJaJXa40QzZ7XOXVo74MMPnGCSzbl0q3yqHfQh8r0Sw/50rutHxecLVQBM/C9Fr0c+m+ew==", "dev": true, - "dependencies": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" - }, "engines": { - "node": ">= 0.10.0" + "node": ">=16", + "pnpm": ">=8.6.0" } }, - "node_modules/connect-history-api-fallback": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", - "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "node_modules/@thednp/shorty": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@thednp/shorty/-/shorty-2.0.3.tgz", + "integrity": "sha512-ngKP9/wQxM6JPDFjO6ak8lSz38ZA6cIFQy3gZbZM3xgUqArBr+VG9aoSoLHHEuaObyd9q9Jq/T0Wez7qrck0Gw==", "dev": true, "engines": { - "node": ">=0.8" + "node": ">=16", + "pnpm": ">=8.6.0" } }, - "node_modules/connect/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", "dev": true, - "dependencies": { - "ms": "2.0.0" + "engines": { + "node": ">=10.13.0" } }, - "node_modules/connect/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "dev": true - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "node_modules/@ts-morph/common": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.23.0.tgz", + "integrity": "sha512-m7Lllj9n/S6sOkCkRftpM7L24uvmfXQFedlW/4hENcuJH1HHm9u5EgxZb9uVjQSCGrbBWBkOGgcTxNg36r6ywA==", "dev": true, "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" + "fast-glob": "^3.3.2", + "minimatch": "^9.0.3", + "mkdirp": "^3.0.1", + "path-browserify": "^1.0.1" } }, - "node_modules/content-disposition/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "node_modules/@ts-morph/common/node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", "dev": true, + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, "engines": { - "node": ">= 0.6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", "dev": true }, - "node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", "dev": true }, - "node_modules/copy-and-watch": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/copy-and-watch/-/copy-and-watch-0.1.6.tgz", - "integrity": "sha512-lTdQK2V/7T3fsRXwiG9CORZMtiTgjIW1SMUdWctV1CagV4bzXgjXz0WS+/zpeotZ27RJvoPeR21T4pZEnUzAUQ==", - "dev": true, - "dependencies": { - "chokidar": "3.5.2", - "colors": "1.4.0", - "glob": "7.2.0", - "glob-parent": "6.0.2" - }, - "bin": { - "copy-and-watch": "bin/copy-and-watch" - }, - "engines": { - "node": ">=10" - } + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true }, - "node_modules/copy-and-watch/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, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true }, - "node_modules/copy-and-watch/node_modules/chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", + "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==", "dev": true, - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/copy-and-watch/node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/@tufjs/models": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-2.0.1.tgz", + "integrity": "sha512-92F7/SFyufn4DXsha9+QfKnN03JGqtMFMXgSHbZOo8JG59WkTni7UzAouNQDf7AuP9OAMxVOPQcqG3sB7w+kkg==", "dev": true, "dependencies": { - "is-glob": "^4.0.1" + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.4" }, "engines": { - "node": ">= 6" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/copy-and-watch/node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "node_modules/@tufjs/models/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/copy-and-watch/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "node_modules/@tybys/wasm-util": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", + "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", "dev": true, "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" + "@types/connect": "*", + "@types/node": "*" } }, - "node_modules/copy-and-watch/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/@types/bonjour": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" + "@types/node": "*" } }, - "node_modules/copy-anything": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", - "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "dev": true, "dependencies": { - "is-what": "^3.14.1" - }, - "funding": { - "url": "https://github.com/sponsors/mesqueeb" + "@types/node": "*" } }, - "node_modules/copy-webpack-plugin": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", - "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", + "node_modules/@types/connect-history-api-fallback": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", "dev": true, "dependencies": { - "fast-glob": "^3.2.11", - "glob-parent": "^6.0.1", - "globby": "^13.1.1", - "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" + "@types/express-serve-static-core": "*", + "@types/node": "*" } }, - "node_modules/copy-webpack-plugin/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true }, - "node_modules/copy-webpack-plugin/node_modules/schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "node_modules/@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "@types/node": "*" } }, - "node_modules/core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", - "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", - "hasInstallScript": true + "node_modules/@types/cytoscape": { + "version": "3.21.7", + "resolved": "https://registry.npmjs.org/@types/cytoscape/-/cytoscape-3.21.7.tgz", + "integrity": "sha512-dP4UByJtfu5GjMJuv58yCIRxjCp4cP0Wp+Qd46L3Gom0hcV4OPmSOLqt83vArNcYRZLFCAyAk+lcC8oqQtcsqw==", + "dev": true }, - "node_modules/core-js-compat": { - "version": "3.37.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.1.tgz", - "integrity": "sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==", + "node_modules/@types/cytoscape-edgehandles": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/cytoscape-edgehandles/-/cytoscape-edgehandles-4.0.4.tgz", + "integrity": "sha512-e88HA5fpRxISQ/x8MbRDIDcTc0fLfFZSF0NmHHsTdtCWscWbv+Ty9oYizVXGDqpkKCx2FUdEK60WTf6eauSkMg==", "dev": true, "dependencies": { - "browserslist": "^4.23.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" + "@types/cytoscape": "*" } }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", "dev": true }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "node_modules/@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", "dev": true, "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" + "@types/d3-time": "*" } }, - "node_modules/critters": { - "version": "0.0.16", - "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.16.tgz", - "integrity": "sha512-JwjgmO6i3y6RWtLYmXwO5jMd+maZt8Tnfu7VVISmEWyQqfLpB8soBswf8/2bu6SBXxtKA68Al3c+qIG1ApT68A==", - "dev": true, + "node_modules/@types/d3-selection": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.10.tgz", + "integrity": "sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg==" + }, + "node_modules/@types/d3-time": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", + "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==", + "dev": true + }, + "node_modules/@types/d3-transition": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.8.tgz", + "integrity": "sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==", "dependencies": { - "chalk": "^4.1.0", - "css-select": "^4.2.0", - "parse5": "^6.0.1", - "parse5-htmlparser2-tree-adapter": "^6.0.1", - "postcss": "^8.3.7", - "pretty-bytes": "^5.3.0" + "@types/d3-selection": "*" } }, - "node_modules/critters/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" } }, - "node_modules/critters/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@types/express-serve-static-core": { + "version": "4.19.5", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz", + "integrity": "sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" } }, - "node_modules/critters/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@types/grecaptcha": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/grecaptcha/-/grecaptcha-3.0.9.tgz", + "integrity": "sha512-fFxMtjAvXXMYTzDFK5NpcVB7WHnrHVLl00QzEGpuFxSAC789io6M+vjcn+g5FTEamIJtJr/IHkCDsqvJxeWDyw==" + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true + }, + "node_modules/@types/http-proxy": { + "version": "1.17.15", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.15.tgz", + "integrity": "sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==", "dev": true, "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "@types/node": "*" } }, - "node_modules/critters/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", "dev": true }, - "node_modules/critters/node_modules/css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" + "@types/istanbul-lib-coverage": "*" } }, - "node_modules/critters/node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + "@types/istanbul-lib-report": "*" } }, - "node_modules/critters/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "node_modules/@types/jasmine": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.4.tgz", + "integrity": "sha512-px7OMFO/ncXxixDe1zR13V1iycqWae0MxTaw62RpFlksUi5QuNWgQJFkTQjIOvrmutJbI7Fp2Y2N1F6D2R4G6w==", + "dev": true + }, + "node_modules/@types/jasminewd2": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.13.tgz", + "integrity": "sha512-aJ3wj8tXMpBrzQ5ghIaqMisD8C3FIrcO6sDKHqFbuqAsI7yOxj0fA7MrRCPLZHIVUjERIwsMmGn/vB0UQ9u0Hg==", "dev": true, "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" + "@types/jasmine": "*" } }, - "node_modules/critters/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/karma": { + "version": "6.3.8", + "resolved": "https://registry.npmjs.org/@types/karma/-/karma-6.3.8.tgz", + "integrity": "sha512-+QGoOPhb1f6Oli8pG+hxdnGDzVhIrpsHaFSJ4UJg15Xj+QBtluKELkJY+L4Li532HmT3l5K5o1FoUZHRQeOOaQ==", "dev": true, "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" + "@types/node": "*", + "log4js": "^6.4.1" } }, - "node_modules/critters/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } + "node_modules/@types/lodash": { + "version": "4.17.7", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.7.tgz", + "integrity": "sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==", + "optional": true }, - "node_modules/critters/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" + "node_modules/@types/lodash.isequal": { + "version": "4.5.8", + "resolved": "https://registry.npmjs.org/@types/lodash.isequal/-/lodash.isequal-4.5.8.tgz", + "integrity": "sha512-uput6pg4E/tj2LGxCZo9+y27JNyB2OZuuI/T5F+ylVDYuqICLG2/ktjxx0v6GvVntAf8TvEzeQLcV0ffRirXuA==", + "optional": true, + "dependencies": { + "@types/lodash": "*" } }, - "node_modules/critters/node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true }, - "node_modules/critters/node_modules/parse5-htmlparser2-tree-adapter": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", - "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "node_modules/@types/mute-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", + "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", "dev": true, "dependencies": { - "parse5": "^6.0.1" + "@types/node": "*" } }, - "node_modules/critters/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@types/node": { + "version": "20.16.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.5.tgz", + "integrity": "sha512-VwYCweNo3ERajwy0IUlqqcyZ8/A7Zwa9ZP3MnENWcB11AejO+tLy3pu850goUW2FC/IJMdZUfKpX/yxL1gymCA==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "undici-types": "~6.19.2" } }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "node_modules/@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", "dev": true, "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" + "@types/node": "*" } }, - "node_modules/cross-spawn/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "dev": true }, - "node_modules/crypto-js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "node_modules/@types/qs": { + "version": "6.9.15", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", + "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==", "dev": true }, - "node_modules/css-blank-pseudo": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", - "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true + }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true + }, + "node_modules/@types/retry": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", + "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", "dev": true, "dependencies": { - "postcss-selector-parser": "^6.0.9" - }, - "bin": { - "css-blank-pseudo": "dist/cli.cjs" + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-index": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "dev": true, + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/wrap-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", + "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", + "dev": true + }, + "node_modules/@types/ws": { + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", + "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", + "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/type-utils": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "postcss": "^8.4" + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/css-has-pseudo": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", - "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", "dev": true, "dependencies": { - "postcss-selector-parser": "^6.0.9" - }, - "bin": { - "css-has-pseudo": "dist/cli.cjs" + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "postcss": "^8.4" + "eslint": "^8.56.0" } }, - "node_modules/css-loader": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", - "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==", + "node_modules/@typescript-eslint/parser": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", + "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", "dev": true, "dependencies": { - "icss-utils": "^5.1.0", - "postcss": "^8.4.7", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.0", - "postcss-modules-scope": "^3.0.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.3.5" + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4" }, "engines": { - "node": ">= 12.13.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/webpack" + "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "webpack": "^5.0.0" + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/css-prefers-color-scheme": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", - "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", "dev": true, - "bin": { - "css-prefers-color-scheme": "dist/cli.cjs" + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^18.18.0 || >=20.0.0" }, - "peerDependencies": { - "postcss": "^8.4" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "node_modules/@typescript-eslint/type-utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", + "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", "dev": true, "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" }, "funding": { - "url": "https://github.com/sponsors/fb55" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" + }, "engines": { - "node": ">= 6" + "node": "^18.18.0 || >=20.0.0" }, "funding": { - "url": "https://github.com/sponsors/fb55" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" } }, - "node_modules/cssdb": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.7.0.tgz", - "integrity": "sha512-1hN+I3r4VqSNQ+OmMXxYexnumbOONkSil0TWMebVXHtzYW4tRRPovUNHPHj2d4nrgOuYJ8Vs3XwvywsuwwXNNA==", + "node_modules/@typescript-eslint/types": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - } - ] + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", "dev": true, - "bin": { - "cssesc": "bin/cssesc" + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": ">=4" + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/cuint": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz", - "integrity": "sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw==", - "dev": true - }, - "node_modules/custom-event": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", - "dev": true - }, - "node_modules/cytoscape": { - "version": "3.25.0", - "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.25.0.tgz", - "integrity": "sha512-7MW3Iz57mCUo6JQCho6CmPBCbTlJr7LzyEtIkutG255HLVd4XuBg2I9BkTZLI/e4HoaOB/BiAzXuQybQ95+r9Q==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, "dependencies": { - "heap": "^0.2.6", - "lodash": "^4.17.21" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=0.10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/cytoscape-edgehandles": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/cytoscape-edgehandles/-/cytoscape-edgehandles-4.0.1.tgz", - "integrity": "sha512-uSYshkqRZ4luCxK295bEVTg46q4ZW+fwJhcIzMrtfNR7zeAnJ38Z48kUGeu5ibtXkgLbcZAg0YE4ED2dRuaePg==", + "node_modules/@typescript-eslint/utils": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.5.0.tgz", + "integrity": "sha512-6yyGYVL0e+VzGYp60wvkBHiqDWOpT63pdMV2CVG4LVDd5uR6q1qQN/7LafBZtAtNIn/mqXjsSeS5ggv/P0iECw==", + "dev": true, + "peer": true, "dependencies": { - "lodash.memoize": "^4.1.2", - "lodash.throttle": "^4.1.1" + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.5.0", + "@typescript-eslint/types": "8.5.0", + "@typescript-eslint/typescript-estree": "8.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "cytoscape": "^3.2.0" + "eslint": "^8.57.0 || ^9.0.0" } }, - "node_modules/d3-array": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", - "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.5.0.tgz", + "integrity": "sha512-06JOQ9Qgj33yvBEx6tpC8ecP9o860rsR22hWMEd12WcTRrfaFgHr2RB/CA/B+7BMhHkXT4chg2MyboGdFGawYg==", + "dev": true, + "peer": true, "dependencies": { - "internmap": "1 - 2" + "@typescript-eslint/types": "8.5.0", + "@typescript-eslint/visitor-keys": "8.5.0" }, "engines": { - "node": ">=12" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/d3-axis": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", - "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.5.0.tgz", + "integrity": "sha512-qjkormnQS5wF9pjSi6q60bKUHH44j2APxfh9TQRXK8wbYVeDYYdYJGIROL87LGZZ2gz3Rbmjc736qyL8deVtdw==", + "dev": true, + "peer": true, "engines": { - "node": ">=12" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/d3-brush": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", - "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.5.0.tgz", + "integrity": "sha512-vEG2Sf9P8BPQ+d0pxdfndw3xIXaoSjliG0/Ejk7UggByZPKXmJmw3GW5jV2gHNQNawBUyfahoSiCFVov0Ruf7Q==", + "dev": true, + "peer": true, "dependencies": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "3", - "d3-transition": "3" + "@typescript-eslint/types": "8.5.0", + "@typescript-eslint/visitor-keys": "8.5.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": ">=12" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.5.0.tgz", + "integrity": "sha512-yTPqMnbAZJNy2Xq2XU8AdtOW9tJIr+UQb64aXB9f3B1498Zx9JorVgFJcZpEc9UBuCCrdzKID2RGAMkYcDtZOw==", + "dev": true, + "peer": true, + "dependencies": { + "@typescript-eslint/types": "8.5.0", + "eslint-visitor-keys": "^3.4.3" + }, "engines": { - "node": ">=12" - } - }, - "node_modules/d3-dispatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", - "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", - "engines": { - "node": ">=12" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/d3-drag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", - "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "node_modules/@typescript-eslint/utils/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "peer": true, "dependencies": { - "d3-dispatch": "1 - 3", - "d3-selection": "3" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=12" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/d3-dsv": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", - "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "dev": true, "dependencies": { - "commander": "7", - "iconv-lite": "0.6", - "rw": "1" - }, - "bin": { - "csv2json": "bin/dsv2json.js", - "csv2tsv": "bin/dsv2dsv.js", - "dsv2dsv": "bin/dsv2dsv.js", - "dsv2json": "bin/dsv2json.js", - "json2csv": "bin/json2dsv.js", - "json2dsv": "bin/json2dsv.js", - "json2tsv": "bin/json2dsv.js", - "tsv2csv": "bin/dsv2dsv.js", - "tsv2json": "bin/dsv2json.js" + "@typescript-eslint/types": "7.18.0", + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": ">=12" + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/d3-dsv/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/@vitejs/plugin-basic-ssl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.1.0.tgz", + "integrity": "sha512-wO4Dk/rm8u7RNhOf95ZzcEmC9rYOncYgvq4z3duaJrCgjN8BxAnDVyndanfcJZ0O6XZzHz6Q0hTimxTg8Y9g/A==", + "dev": true, "engines": { - "node": ">= 10" + "node": ">=14.6.0" + }, + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" } }, - "node_modules/d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", - "engines": { - "node": ">=12" + "node_modules/@webassemblyjs/ast": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" } }, - "node_modules/d3-format": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", - "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", - "engines": { - "node": ">=12" + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" } }, - "node_modules/d3-hierarchy": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", - "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", - "engines": { - "node": ">=12" + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.12.1" } }, - "node_modules/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "dev": true, "dependencies": { - "d3-color": "1 - 3" - }, - "engines": { - "node": ">=12" + "@xtuc/ieee754": "^1.2.0" } }, - "node_modules/d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", - "engines": { - "node": ">=12" + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" } }, - "node_modules/d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", + "dev": true, "dependencies": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" - }, - "engines": { - "node": ">=12" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" } }, - "node_modules/d3-selection": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", - "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", - "engines": { - "node": ">=12" + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, - "node_modules/d3-shape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", - "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", + "dev": true, "dependencies": { - "d3-path": "^3.1.0" - }, - "engines": { - "node": ">=12" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" } }, - "node_modules/d3-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", - "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", + "dev": true, "dependencies": { - "d3-array": "2 - 3" - }, - "engines": { - "node": ">=12" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, - "node_modules/d3-time-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", + "dev": true, "dependencies": { - "d3-time": "1 - 3" - }, - "engines": { - "node": ">=12" + "@webassemblyjs/ast": "1.12.1", + "@xtuc/long": "4.2.2" } }, - "node_modules/d3-timer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "node_modules/@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "optional": true, "engines": { - "node": ">=12" + "node": ">=10.0.0" } }, - "node_modules/d3-transition": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", - "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==" + }, + "node_modules/@yarnpkg/parsers": { + "version": "3.0.0-rc.46", + "resolved": "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.0-rc.46.tgz", + "integrity": "sha512-aiATs7pSutzda/rq8fnuPwTglyVwjM22bNnK2ZgjrpAjQHSSl3lztd2f9evst1W/qnC58DRz7T7QndUDumAR4Q==", "dependencies": { - "d3-color": "1 - 3", - "d3-dispatch": "1 - 3", - "d3-ease": "1 - 3", - "d3-interpolate": "1 - 3", - "d3-timer": "1 - 3" + "js-yaml": "^3.10.0", + "tslib": "^2.4.0" }, "engines": { - "node": ">=12" - }, - "peerDependencies": { - "d3-selection": "2 - 3" + "node": ">=14.15.0" } }, - "node_modules/d3-zoom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", - "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "node_modules/@yarnpkg/parsers/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dependencies": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "2 - 3", - "d3-transition": "2 - 3" - }, - "engines": { - "node": ">=12" + "sprintf-js": "~1.0.2" } }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "dev": true, + "node_modules/@yarnpkg/parsers/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dependencies": { - "assert-plus": "^1.0.0" + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, - "engines": { - "node": ">=0.10" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/data-view-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "node_modules/@yarnpkg/parsers/node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/@zkochan/js-yaml": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@zkochan/js-yaml/-/js-yaml-0.0.7.tgz", + "integrity": "sha512-nrUSn7hzt7J6JWgWGz78ZYI8wj+gdIJdk0Ynjpp8l+trkn58Uqsf6RYrYkEK+3X18EX+TNdtJI0WxAtc+L84SQ==", + "dependencies": { + "argparse": "^2.0.1" }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/data-view-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.6" } }, - "node_modules/data-view-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "dev": true, - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.4.0" } }, - "node_modules/date-format": { - "version": "4.0.14", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", - "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", "dev": true, - "engines": { - "node": ">=4.0" + "peerDependencies": { + "acorn": "^8" } }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/decache": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/decache/-/decache-4.6.2.tgz", - "integrity": "sha512-2LPqkLeu8XWHU8qNCS3kcF6sCcb5zIzvWaAHYSvPfwhdd7mHuah29NssMzrTYyHN4F5oFy2ko9OBYxegtU0FEw==", + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", "dev": true, "dependencies": { - "callsite": "^1.0.0" + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" } }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "node_modules/address": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", + "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">= 10.0.0" } }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "node_modules/adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, "engines": { - "node": ">=0.10" + "node": ">=8.9" } }, - "node_modules/deep-equal": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", - "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", + "node_modules/adjust-sourcemap-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, "dependencies": { - "is-arguments": "^1.1.1", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.5.1" + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8.9.0" } }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "node_modules/adm-zip": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", + "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=12.0" } }, - "node_modules/default-gateway": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", - "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dev": true, "dependencies": { - "execa": "^5.0.0" + "debug": "^4.3.4" }, "engines": { - "node": ">= 10" + "node": ">= 14" } }, - "node_modules/default-require-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", - "integrity": "sha512-B0n2zDIXpzLzKeoEozorDSa1cHc1t0NjmxP0zuAxbizNU2MBqYJJKYXrrFdKuQliojXynrxgd7l4ahfg/+aA5g==", + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, "dependencies": { - "strip-bom": "^3.0.0" + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "dependencies": { - "clone": "^1.0.2" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "dev": true, "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" + "ajv": "^8.0.0" }, - "engines": { - "node": ">= 0.4" + "peerDependencies": { + "ajv": "^8.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "dev": true, - "engines": { - "node": ">=8" + "peerDependenciesMeta": { + "ajv": { + "optional": true + } } }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" + "fast-deep-equal": "^3.1.3" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "ajv": "^8.8.2" } }, - "node_modules/del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha512-Z4fzpbIRjOu7lO5jCETSWoqUDVe0IPOlfugBsF6suen2LKDlVb4QZpKEM9P+buNJ4KI1eN7I083w/pbKUpsrWQ==", + "node_modules/angular-in-memory-web-api": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/angular-in-memory-web-api/-/angular-in-memory-web-api-0.18.0.tgz", + "integrity": "sha512-Eqkr9+x3d7K4dmn6Qs3ZVAfqBDPZN0N7Qel5i8eU/pe5r44J/pfxlNW+1LC2Sb2PdENEdvFzC8wx8qly5+kQyQ==", "dev": true, "dependencies": { - "globby": "^5.0.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "rimraf": "^2.2.8" + "tslib": "^2.3.0" }, - "engines": { - "node": ">=0.10.0" + "peerDependencies": { + "@angular/common": "^18.0.0", + "@angular/core": "^18.0.0", + "rxjs": "^6.5.3 || ^7.4.0" } }, - "node_modules/del/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, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "engines": { + "node": ">=6" } }, - "node_modules/del/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", + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": 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" + "type-fest": "^0.21.3" }, "engines": { - "node": "*" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/del/node_modules/globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha512-HJRTIH2EeH44ka+LWig+EqT2ONSYpVlNfx6pyd592/VF1TbfljJ7elwie7oSwcViLGqOdWocSdu2txwBF9bjmQ==", + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", "dev": true, - "dependencies": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" } }, - "node_modules/del/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, - "dependencies": { - "brace-expansion": "^1.1.7" - }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "engines": { - "node": "*" + "node": ">=8" } }, - "node_modules/del/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "dependencies": { - "glob": "^7.1.3" + "color-convert": "^1.9.0" }, - "bin": { - "rimraf": "bin.js" + "engines": { + "node": ">=4" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, "engines": { - "node": ">=0.4.0" + "node": ">= 8" } }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "dev": true - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { - "node": ">= 0.8" + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/dependency-graph": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", - "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", + "node_modules/apache-crypt": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/apache-crypt/-/apache-crypt-1.2.6.tgz", + "integrity": "sha512-072WetlM4blL8PREJVeY+WHiUh1R5VNt2HfceGS8aKqttPHcmqE5pkKuXPz/ULmJOFkc8Hw3kfKl6vy7Qka6DA==", "dev": true, + "dependencies": { + "unix-crypt-td-js": "^1.1.4" + }, "engines": { - "node": ">= 0.6.0" + "node": ">=8" } }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "node_modules/apache-md5": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/apache-md5/-/apache-md5-1.1.8.tgz", + "integrity": "sha512-FCAJojipPn0bXjuEpjOOOMN8FZDkxfWWp4JGN9mifU2IhxvKyXZYqpzPHdnTSUpmPDy+tsslB6Z1g+Vg6nVbYA==", "dev": true, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">=8" } }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "dev": true - }, - "node_modules/dfa": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", - "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==", + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true }, - "node_modules/di": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", - "dev": true + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, - "node_modules/diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, - "engines": { - "node": ">=0.3.1" + "dependencies": { + "dequal": "^2.0.3" } }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", "dev": true, "dependencies": { - "path-type": "^4.0.0" + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, "engines": { "node": ">=8" } }, - "node_modules/dns-packet": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", - "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", "dev": true, "dependencies": { - "@leichtgewicht/ip-codec": "^2.0.1" + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" }, "engines": { - "node": ">=6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/dom-serialize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==", + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "custom-event": "~1.0.0", - "ent": "~2.2.0", - "extend": "^3.0.0", - "void-elements": "^2.0.0" + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" } }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "node_modules/axios": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] + "engines": { + "node": ">= 0.4" + } }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "node_modules/babel-loader": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.3.tgz", + "integrity": "sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==", "dev": true, "dependencies": { - "domelementtype": "^2.3.0" + "find-cache-dir": "^4.0.0", + "schema-utils": "^4.0.0" }, "engines": { - "node": ">= 4" + "node": ">= 14.15.0" }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" + "peerDependencies": { + "@babel/core": "^7.12.0", + "webpack": ">=5" } }, - "node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "node_modules/babel-loader/node_modules/find-cache-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", + "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", "dev": true, "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" + "common-path-prefix": "^3.0.0", + "pkg-dir": "^7.0.0" + }, + "engines": { + "node": ">=14.16" }, "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/dot": { - "version": "2.0.0-beta.1", - "resolved": "https://registry.npmjs.org/dot/-/dot-2.0.0-beta.1.tgz", - "integrity": "sha512-kxM7fSnNQTXOmaeGuBSXM8O3fEsBb7XSDBllkGbRwa0lJSJTxxDE/4eSNGLKZUmlFw0f1vJ5qSV2BljrgQtgIA==", - "dev": true - }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": true - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "node_modules/babel-loader/node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", "dev": true, "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "dev": true - }, - "node_modules/electron-to-chromium": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.2.tgz", - "integrity": "sha512-kc4r3U3V3WLaaZqThjYz/Y6z8tJe+7K0bbjUVo3i+LWIypVdMx5nXCkwRe6SWbY6ILqLdc1rKcKmr3HoH7wjSQ==", - "dev": true - }, - "node_modules/element-resize-detector": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/element-resize-detector/-/element-resize-detector-1.2.4.tgz", - "integrity": "sha512-Fl5Ftk6WwXE0wqCgNoseKWndjzZlDCwuPTcoVZfCP9R3EHQF8qUtr3YUPNETegRBOKqQKPW3n4kiIWngGi8tKg==", + "node_modules/babel-loader/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, "dependencies": { - "batch-processor": "1.0.0" + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/emitter-component": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.2.tgz", - "integrity": "sha512-QdXO3nXOzZB4pAjM0n6ZE+R9/+kPpECA/XSELIcc54NeYVnBqIk+4DFiBgK+8QbV3mdvTG6nedl7dTYgO+5wDw==", + "node_modules/babel-loader/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "node_modules/babel-loader/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", "dev": true, + "dependencies": { + "p-limit": "^4.0.0" + }, "engines": { - "node": ">= 4" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/babel-loader/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", "dev": true, "engines": { - "node": ">= 0.8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "node_modules/babel-loader/node_modules/pkg-dir": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", + "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", "dev": true, - "optional": true, "dependencies": { - "iconv-lite": "^0.6.2" + "find-up": "^6.3.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "node_modules/babel-loader/node_modules/yocto-queue": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", + "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", "dev": true, - "dependencies": { - "once": "^1.4.0" + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/engine.io": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz", - "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==", + "node_modules/babel-plugin-const-enum": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-const-enum/-/babel-plugin-const-enum-1.2.0.tgz", + "integrity": "sha512-o1m/6iyyFnp9MRsK1dHF3bneqyf3AlM2q3A/YbgQr2pCat6B6XJVDv2TXqzfY2RYUi4mak6WAksSBPlyYGx9dg==", "dev": true, "dependencies": { - "@types/cookie": "^0.4.1", - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.4.1", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.17.1" + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-typescript": "^7.3.3", + "@babel/traverse": "^7.16.0" }, - "engines": { - "node": ">=10.2.0" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/engine.io-parser": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz", - "integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==", + "node_modules/babel-plugin-macros": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", + "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", "dev": true, - "engines": { - "node": ">=10.0.0" + "dependencies": { + "@babel/runtime": "^7.7.2", + "cosmiconfig": "^6.0.0", + "resolve": "^1.12.0" } }, - "node_modules/enhanced-resolve": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", - "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", + "node_modules/babel-plugin-macros/node_modules/cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", "dev": true, "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" }, "engines": { - "node": ">=10.13.0" + "node": ">=8" } }, - "node_modules/ent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==", - "dev": true - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", + "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", "dev": true, - "engines": { - "node": ">=0.12" + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.6.2", + "semver": "^6.3.1" }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "engines": { - "node": ">=6" + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "dev": true + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", + "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.2", + "core-js-compat": "^3.38.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } }, - "node_modules/errno": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", - "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", + "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", "dev": true, - "optional": true, "dependencies": { - "prr": "~1.0.1" + "@babel/helper-define-polyfill-provider": "^0.6.2" }, - "bin": { - "errno": "cli.js" + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "node_modules/babel-plugin-transform-typescript-metadata": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-typescript-metadata/-/babel-plugin-transform-typescript-metadata-0.3.2.tgz", + "integrity": "sha512-mWEvCQTgXQf48yDqgN7CH50waTyYBeP2Lpqx4nNWab9sxEpdXVeKgfj1qYI2/TgUPQtNFZ85i3PemRtnXVYYJg==", "dev": true, "dependencies": { - "is-arrayish": "^0.2.1" + "@babel/helper-plugin-utils": "^7.0.0" } }, - "node_modules/es-abstract": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true, + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", "dev": true, "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", - "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" + "safe-buffer": "5.1.2" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8" } }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "node_modules/basic-auth/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true + }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==", + "dev": true + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.4" - }, "engines": { - "node": ">= 0.4" + "node": "*" } }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "node_modules/billboard.js": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/billboard.js/-/billboard.js-3.13.0.tgz", + "integrity": "sha512-zTvDlCRxaxZtgIpook29V87AsVG97CYRmqJJGNwyJS2cT2g43vIhBOksmpsUmF00hppqqinTjiBjE3qE+48mZQ==", + "dependencies": { + "@types/d3-selection": "^3.0.10", + "@types/d3-transition": "^3.0.8", + "d3-axis": "^3.0.0", + "d3-brush": "^3.0.0", + "d3-drag": "^3.0.0", + "d3-dsv": "^3.0.1", + "d3-ease": "^3.0.1", + "d3-hierarchy": "^3.1.2", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-selection": "^3.0.0", + "d3-shape": "^3.2.0", + "d3-time-format": "^4.1.0", + "d3-transition": "^3.0.1", + "d3-zoom": "^3.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, "engines": { - "node": ">= 0.4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", - "dev": true + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } }, - "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", "dev": true, "dependencies": { - "es-errors": "^1.3.0" + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "node_modules/body-parser/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": { - "get-intrinsic": "^1.2.4", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" }, "engines": { - "node": ">= 0.4" + "node": ">=0.10.0" } }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/body-parser/node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dev": true, "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "side-channel": "^1.0.6" }, "engines": { - "node": ">= 0.4" + "node": ">=0.6" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", - "dev": true - }, - "node_modules/es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", + "node_modules/bonjour-service": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", + "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", "dev": true, "dependencies": { - "es6-promise": "^4.0.3" + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" } }, - "node_modules/es6-shim": { - "version": "0.35.8", - "resolved": "https://registry.npmjs.org/es6-shim/-/es6-shim-0.35.8.tgz", - "integrity": "sha512-Twf7I2v4/1tLoIXMT8HlqaBSS5H2wQTs2wx3MNYCI8K1R1/clXyCazrcVCPm/FuO9cyV8+leEaZOWD5C253NDg==", + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true }, - "node_modules/esbuild": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.5.tgz", - "integrity": "sha512-VSf6S1QVqvxfIsSKb3UKr3VhUCis7wgDbtF4Vd9z84UJr05/Sp2fRKmzC+CSPG/dNAPPJZ0BTBLTT1Fhd6N9Gg==", + "node_modules/bootstrap.native": { + "version": "5.0.13", + "resolved": "https://registry.npmjs.org/bootstrap.native/-/bootstrap.native-5.0.13.tgz", + "integrity": "sha512-SiiTxaK3LjuOjPaXEnDBQNY3w0t28Qdx6I8drortuFg6Ch3q6cWoOxlFHThcGOPewziVarQAA4WPE00GFQmbWQ==", "dev": true, - "hasInstallScript": true, - "optional": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" + "dependencies": { + "@thednp/event-listener": "^2.0.4", + "@thednp/shorty": "^2.0.0" }, - "optionalDependencies": { - "@esbuild/linux-loong64": "0.15.5", - "esbuild-android-64": "0.15.5", - "esbuild-android-arm64": "0.15.5", - "esbuild-darwin-64": "0.15.5", - "esbuild-darwin-arm64": "0.15.5", - "esbuild-freebsd-64": "0.15.5", - "esbuild-freebsd-arm64": "0.15.5", - "esbuild-linux-32": "0.15.5", - "esbuild-linux-64": "0.15.5", - "esbuild-linux-arm": "0.15.5", - "esbuild-linux-arm64": "0.15.5", - "esbuild-linux-mips64le": "0.15.5", - "esbuild-linux-ppc64le": "0.15.5", - "esbuild-linux-riscv64": "0.15.5", - "esbuild-linux-s390x": "0.15.5", - "esbuild-netbsd-64": "0.15.5", - "esbuild-openbsd-64": "0.15.5", - "esbuild-sunos-64": "0.15.5", - "esbuild-windows-32": "0.15.5", - "esbuild-windows-64": "0.15.5", - "esbuild-windows-arm64": "0.15.5" - } - }, - "node_modules/esbuild-android-64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.5.tgz", - "integrity": "sha512-dYPPkiGNskvZqmIK29OPxolyY3tp+c47+Fsc2WYSOVjEPWNCHNyqhtFqQadcXMJDQt8eN0NMDukbyQgFcHquXg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], "engines": { - "node": ">=12" + "node": ">=16", + "pnpm": ">=8.6.0" } }, - "node_modules/esbuild-android-arm64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.5.tgz", - "integrity": "sha512-YyEkaQl08ze3cBzI/4Cm1S+rVh8HMOpCdq8B78JLbNFHhzi4NixVN93xDrHZLztlocEYqi45rHHCgA8kZFidFg==", - "cpu": [ - "arm64" - ], + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "fill-range": "^7.1.1" + }, "engines": { - "node": ">=12" + "node": ">=8" } }, - "node_modules/esbuild-darwin-64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.5.tgz", - "integrity": "sha512-Cr0iIqnWKx3ZTvDUAzG0H/u9dWjLE4c2gTtRLz4pqOBGjfjqdcZSfAObFzKTInLLSmD0ZV1I/mshhPoYSBMMCQ==", - "cpu": [ - "x64" - ], + "node_modules/brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" + "dependencies": { + "base64-js": "^1.1.2" } }, - "node_modules/esbuild-darwin-arm64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.5.tgz", - "integrity": "sha512-WIfQkocGtFrz7vCu44ypY5YmiFXpsxvz2xqwe688jFfSVCnUsCn2qkEVDo7gT8EpsLOz1J/OmqjExePL1dr1Kg==", - "cpu": [ - "arm64" - ], + "node_modules/browserslist": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", + "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", "dev": true, - "optional": true, - "os": [ - "darwin" + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } ], + "dependencies": { + "caniuse-lite": "^1.0.30001646", + "electron-to-chromium": "^1.5.4", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" + }, + "bin": { + "browserslist": "cli.js" + }, "engines": { - "node": ">=12" + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/esbuild-freebsd-64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.5.tgz", - "integrity": "sha512-M5/EfzV2RsMd/wqwR18CELcenZ8+fFxQAAEO7TJKDmP3knhWSbD72ILzrXFMMwshlPAS1ShCZ90jsxkm+8FlaA==", - "cpu": [ - "x64" - ], + "node_modules/btoa": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", + "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", "dev": true, - "optional": true, - "os": [ - "freebsd" - ], + "bin": { + "btoa": "bin/btoa.js" + }, "engines": { - "node": ">=12" + "node": ">= 0.4.0" } }, - "node_modules/esbuild-freebsd-arm64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.5.tgz", - "integrity": "sha512-2JQQ5Qs9J0440F/n/aUBNvY6lTo4XP/4lt1TwDfHuo0DY3w5++anw+jTjfouLzbJmFFiwmX7SmUhMnysocx96w==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } ], - "engines": { - "node": ">=12" + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" } }, - "node_modules/esbuild-linux-32": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.5.tgz", - "integrity": "sha512-gO9vNnIN0FTUGjvTFucIXtBSr1Woymmx/aHQtuU+2OllGU6YFLs99960UD4Dib1kFovVgs59MTXwpFdVoSMZoQ==", - "cpu": [ - "ia32" - ], + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", "dev": true, - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=12" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/esbuild-linux-64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.5.tgz", - "integrity": "sha512-ne0GFdNLsm4veXbTnYAWjbx3shpNKZJUd6XpNbKNUZaNllDZfYQt0/zRqOg0sc7O8GQ+PjSMv9IpIEULXVTVmg==", - "cpu": [ - "x64" - ], + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "run-applescript": "^7.0.0" + }, "engines": { - "node": ">=12" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/esbuild-linux-arm": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.5.tgz", - "integrity": "sha512-wvAoHEN+gJ/22gnvhZnS/+2H14HyAxM07m59RSLn3iXrQsdS518jnEWRBnJz3fR6BJa+VUTo0NxYjGaNt7RA7Q==", - "cpu": [ - "arm" - ], + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=12" + "node": ">= 0.8" } }, - "node_modules/esbuild-linux-arm64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.5.tgz", - "integrity": "sha512-7EgFyP2zjO065XTfdCxiXVEk+f83RQ1JsryN1X/VSX2li9rnHAt2swRbpoz5Vlrl6qjHrCmq5b6yxD13z6RheA==", - "cpu": [ - "arm64" - ], + "node_modules/cacache": { + "version": "18.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.4.tgz", + "integrity": "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, "engines": { - "node": ">=12" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/esbuild-linux-mips64le": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.5.tgz", - "integrity": "sha512-KdnSkHxWrJ6Y40ABu+ipTZeRhFtc8dowGyFsZY5prsmMSr1ZTG9zQawguN4/tunJ0wy3+kD54GaGwdcpwWAvZQ==", - "cpu": [ - "mips64el" - ], + "node_modules/cacache/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/cache-content-type": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-content-type/-/cache-content-type-1.0.1.tgz", + "integrity": "sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "mime-types": "^2.1.18", + "ylru": "^1.2.0" + }, "engines": { - "node": ">=12" + "node": ">= 6.0.0" } }, - "node_modules/esbuild-linux-ppc64le": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.5.tgz", - "integrity": "sha512-QdRHGeZ2ykl5P0KRmfGBZIHmqcwIsUKWmmpZTOq573jRWwmpfRmS7xOhmDHBj9pxv+6qRMH8tLr2fe+ZKQvCYw==", - "cpu": [ - "ppc64" - ], + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, "engines": { - "node": ">=12" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esbuild-linux-riscv64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.5.tgz", - "integrity": "sha512-p+WE6RX+jNILsf+exR29DwgV6B73khEQV0qWUbzxaycxawZ8NE0wA6HnnTxbiw5f4Gx9sJDUBemh9v49lKOORA==", - "cpu": [ - "riscv64" - ], + "node_modules/callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==", "dev": true, - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=12" + "node": "*" } }, - "node_modules/esbuild-linux-s390x": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.5.tgz", - "integrity": "sha512-J2ngOB4cNzmqLHh6TYMM/ips8aoZIuzxJnDdWutBw5482jGXiOzsPoEF4j2WJ2mGnm7FBCO4StGcwzOgic70JQ==", - "cpu": [ - "s390x" - ], + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=12" + "node": ">=6" } }, - "node_modules/esbuild-netbsd-64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.5.tgz", - "integrity": "sha512-MmKUYGDizYjFia0Rwt8oOgmiFH7zaYlsoQ3tIOfPxOqLssAsEgG0MUdRDm5lliqjiuoog8LyDu9srQk5YwWF3w==", - "cpu": [ - "x64" - ], + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" } }, - "node_modules/esbuild-openbsd-64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.5.tgz", - "integrity": "sha512-2mMFfkLk3oPWfopA9Plj4hyhqHNuGyp5KQyTT9Rc8hFd8wAn5ZrbJg+gNcLMo2yzf8Uiu0RT6G9B15YN9WQyMA==", - "cpu": [ - "x64" - ], + "node_modules/caniuse-lite": { + "version": "1.0.30001660", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz", + "integrity": "sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==", "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] }, - "node_modules/esbuild-sunos-64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.5.tgz", - "integrity": "sha512-2sIzhMUfLNoD+rdmV6AacilCHSxZIoGAU2oT7XmJ0lXcZWnCvCtObvO6D4puxX9YRE97GodciRGDLBaiC6x1SA==", - "cpu": [ - "x64" - ], + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, - "optional": true, - "os": [ - "sunos" - ], + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, "engines": { - "node": ">=12" + "node": ">=4" } }, - "node_modules/esbuild-wasm": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.15.5.tgz", - "integrity": "sha512-lTJOEKekN/4JI/eOEq0wLcx53co2N6vaT/XjBz46D1tvIVoUEyM0o2K6txW6gEotf31szFD/J1PbxmnbkGlK9A==", - "dev": true, - "bin": { - "esbuild": "bin/esbuild" + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/cheerio": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz", + "integrity": "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==", + "dev": true, + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "encoding-sniffer": "^0.2.0", + "htmlparser2": "^9.1.0", + "parse5": "^7.1.2", + "parse5-htmlparser2-tree-adapter": "^7.0.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^6.19.5", + "whatwg-mimetype": "^4.0.0" }, "engines": { - "node": ">=12" + "node": ">=18.17" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" } }, - "node_modules/esbuild-windows-32": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.5.tgz", - "integrity": "sha512-e+duNED9UBop7Vnlap6XKedA/53lIi12xv2ebeNS4gFmu7aKyTrok7DPIZyU5w/ftHD4MUDs5PJUkQPP9xJRzg==", - "cpu": [ - "ia32" - ], + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/esbuild-windows-64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.5.tgz", - "integrity": "sha512-v+PjvNtSASHOjPDMIai9Yi+aP+Vwox+3WVdg2JB8N9aivJ7lyhp4NVU+J0MV2OkWFPnVO8AE/7xH+72ibUUEnw==", - "cpu": [ - "x64" - ], + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, "engines": { - "node": ">=12" + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/esbuild-windows-arm64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.5.tgz", - "integrity": "sha512-Yz8w/D8CUPYstvVQujByu6mlf48lKmXkq6bkeSZZxTA626efQOJb26aDGLzmFWx6eg/FwrXgt6SZs9V8Pwy/aA==", - "cpu": [ - "arm64" - ], + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "dev": true, - "optional": true, - "os": [ - "win32" - ], "engines": { - "node": ">=12" + "node": ">=10" } }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "dev": true, "engines": { - "node": ">=6" + "node": ">=6.0" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "dev": true - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], "engines": { - "node": ">=0.8.0" + "node": ">=8" } }, - "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==", + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, "engines": { - "node": ">=8.0.0" + "node": ">=6" } }, - "node_modules/eslint-scope/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "dev": true, + "dependencies": { + "restore-cursor": "^5.0.0" + }, "engines": { - "node": ">=4.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, "engines": { - "node": ">=4" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", "dev": true, "dependencies": { - "estraverse": "^5.2.0" + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" }, "engines": { - "node": ">=4.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/cli-truncate/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "engines": { - "node": ">=4.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/estree-walker": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", - "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "node_modules/cli-truncate/node_modules/emoji-regex": { + "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 }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "node_modules/cli-truncate/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "node_modules/cli-truncate/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, "engines": { - "node": ">= 0.6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/event-stream": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-4.0.1.tgz", - "integrity": "sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==", + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", "dev": true, - "dependencies": { - "duplexer": "^0.1.1", - "from": "^0.1.7", - "map-stream": "0.0.7", - "pause-stream": "^0.0.11", - "split": "^1.0.1", - "stream-combiner": "^0.2.2", - "through": "^2.3.8" + "engines": { + "node": ">= 12" } }, - "node_modules/eventemitter-asyncresource": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz", - "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==", - "dev": true - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, "engines": { - "node": ">=0.8.x" + "node": ">=12" } }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, + "node_modules/cliui/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, "engines": { - "node": ">= 0.8.0" + "node": ">=7.0.0" } }, - "node_modules/exponential-backoff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", - "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", - "dev": true + "node_modules/cliui/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", - "dev": true, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.2", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.6.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">= 0.10.0" + "node": ">=8" } }, - "node_modules/express/node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "dev": true, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, "engines": { - "node": ">= 0.6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/express/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/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "engines": { + "node": ">=0.8" } }, - "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==", + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "dev": true, "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">=6" } }, - "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==", + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/code-block-writer": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.2.tgz", + "integrity": "sha512-XfXzAGiStXSmCIwrkdfvc7FS5Dtj8yelCtyOf2p2skCAfvLd6zu0rGzuS9NSCO3bq1JKpFZ7tbKdKlcd5occQA==", "dev": true }, - "node_modules/express/node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" + "color-name": "1.1.3" } }, - "node_modules/express/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, - "node_modules/express/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "dev": true, - "engines": { - "node": ">= 0.8" + "bin": { + "color-support": "bin.js" } }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", "dev": true }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "dev": true, - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, "engines": { - "node": ">=4" + "node": ">=0.1.90" } }, - "node_modules/external-editor/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "node_modules/columnify": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.6.0.tgz", + "integrity": "sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==", "dev": true, "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "strip-ansi": "^6.0.1", + "wcwidth": "^1.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8.0.0" } }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "dev": true, - "engines": [ - "node >=0.6.0" - ] - }, - "node_modules/fancy-log": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-2.0.0.tgz", - "integrity": "sha512-9CzxZbACXMUXW13tS0tI8XsGGmxWzO2DmYrGuBJOJ8k8q2K7hwfJA5qHjuPPe8wtsco33YR9wc+Rlr5wYFvhSA==", - "dev": true, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dependencies": { - "color-support": "^1.1.3" + "delayed-stream": "~1.0.0" }, "engines": { - "node": ">=10.13.0" + "node": ">= 0.8" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, "engines": { - "node": ">=8.6.0" + "node": ">=18" } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "node_modules/common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", "dev": true }, - "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true }, - "node_modules/faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", "dev": true, "dependencies": { - "websocket-driver": ">=0.5.1" + "mime-db": ">= 1.43.0 < 2" }, "engines": { - "node": ">=0.8.0" + "node": ">= 0.6" } }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", "dev": true, "dependencies": { - "escape-string-regexp": "^1.0.5" + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.8.0" } }, - "node_modules/file-saver": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", - "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" - }, - "node_modules/fileset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", - "integrity": "sha512-UxowFKnAFIwtmSxgKjWAVgjE3Fk7MQJT0ZIyl0NwIFZTrx4913rLaonGJ84V+x/2+w/pe4ULHRns+GZPs1TVuw==", + "node_modules/compression/node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", "dev": true, - "dependencies": { - "glob": "^7.0.3", - "minimatch": "^3.0.3" + "engines": { + "node": ">= 0.8" } }, - "node_modules/fileset/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==", + "node_modules/compression/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": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "ms": "2.0.0" } }, - "node_modules/fileset/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, - "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/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true }, - "node_modules/fileset/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, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", "dev": true, "dependencies": { - "to-regex-range": "^5.0.1" + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" }, "engines": { - "node": ">=8" + "node": ">= 0.10.0" } }, - "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", "dev": true, - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, "engines": { - "node": ">= 0.8" + "node": ">=0.8" } }, - "node_modules/finalhandler/node_modules/debug": { + "node_modules/connect/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", @@ -9374,1683 +9490,1771 @@ "ms": "2.0.0" } }, - "node_modules/finalhandler/node_modules/ms": { + "node_modules/connect/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "node_modules/find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dev": true, "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" + "safe-buffer": "5.2.1" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + "node": ">= 0.6" } }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": ">= 0.6" } }, - "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true }, - "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } + "node": ">= 0.6" } }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, + "node_modules/cookies": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.9.1.tgz", + "integrity": "sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==", "dev": true, "dependencies": { - "is-callable": "^1.1.3" + "depd": "~2.0.0", + "keygrip": "~1.1.0" + }, + "engines": { + "node": ">= 0.8" } }, - "node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "node_modules/copy-and-watch": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/copy-and-watch/-/copy-and-watch-0.1.8.tgz", + "integrity": "sha512-Prw3k4Za+C/m/OutNtjy1+7Fq+JTiryrFc5JiR0wRrYQ+yPUnsXV8DNPna7plzEcmNbm8x87fqegp9+ogNqKNQ==", "dev": true, "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" + "chokidar": "3.6.0", + "colors": "1.4.0", + "glob": "10.3.12", + "glob-parent": "6.0.2" }, - "engines": { - "node": ">=14" + "bin": { + "copy-and-watch": "bin/copy-and-watch" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">=10" } }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "node_modules/copy-and-watch/node_modules/glob": { + "version": "10.3.12", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", + "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.6", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.10.2" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, "engines": { - "node": ">=14" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "node_modules/copy-and-watch/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, "engines": { - "node": "*" + "node": ">=10.13.0" } }, - "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "node_modules/copy-and-watch/node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", "dev": true, "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" + "@isaacs/cliui": "^8.0.2" }, "engines": { - "node": ">= 0.12" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "dev": true, - "engines": { - "node": ">= 0.6" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/fraction.js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", - "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "node_modules/copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", "dev": true, - "engines": { - "node": "*" + "dependencies": { + "is-what": "^3.14.1" }, "funding": { - "type": "patreon", - "url": "https://www.patreon.com/infusion" + "url": "https://github.com/sponsors/mesqueeb" } }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "node_modules/copy-webpack-plugin": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz", + "integrity": "sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA==", "dev": true, + "dependencies": { + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.1", + "globby": "^14.0.0", + "normalize-path": "^3.0.0", + "schema-utils": "^4.2.0", + "serialize-javascript": "^6.0.2" + }, "engines": { - "node": ">= 0.6" + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" } }, - "node_modules/from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", - "dev": true - }, - "node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "node_modules/copy-webpack-plugin/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "is-glob": "^4.0.3" }, "engines": { - "node": ">=14.14" + "node": ">=10.13.0" } }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "node_modules/copy-webpack-plugin/node_modules/globby": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz", + "integrity": "sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==", "dev": true, "dependencies": { - "minipass": "^3.0.0" + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.2", + "ignore": "^5.2.4", + "path-type": "^5.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.1.0" }, "engines": { - "node": ">= 8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/fs-monkey": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", - "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", - "dev": true - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "node_modules/copy-webpack-plugin/node_modules/path-type": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", + "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, + "node": ">=12" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "node_modules/copy-webpack-plugin/node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" - }, "engines": { - "node": ">= 0.4" + "node": ">=14.16" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, + "node_modules/core-js": { + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz", + "integrity": "sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==", + "hasInstallScript": true, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/core-js" } }, - "node_modules/gauge": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", - "deprecated": "This package is no longer supported.", + "node_modules/core-js-compat": { + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz", + "integrity": "sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==", "dev": true, "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" + "browserslist": "^4.23.3" }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", "dev": true, + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, "engines": { - "node": ">=6.9.0" + "node": ">= 0.10" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "node_modules/corser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", + "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", "dev": true, "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">= 0.4.0" } }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "dev": true, "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" }, "engines": { - "node": ">= 0.4" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/critters": { + "version": "0.0.24", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.24.tgz", + "integrity": "sha512-Oyqew0FGM0wYUSNqR0L6AteO5MpMoUU0rhKRieXeiKs+PmRTxiJMyaunYB2KF6fQ3dzChXKCpbFOEJx3OQ1v/Q==", "dev": true, - "engines": { - "node": ">=8.0.0" + "dependencies": { + "chalk": "^4.1.0", + "css-select": "^5.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.2", + "htmlparser2": "^8.0.2", + "postcss": "^8.4.23", + "postcss-media-query-parser": "^0.2.3" } }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "node_modules/critters/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "node_modules/critters/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { - "call-bind": "^1.0.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/glob": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", - "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "node_modules/critters/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "color-name": "~1.1.4" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=7.0.0" } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/critters/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/critters/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, "engines": { - "node": ">= 6" + "node": ">=8" } }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "node_modules/critters/node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", "dev": true, - "engines": { - "node": ">=4" + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" } }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "node_modules/critters/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" + "has-flag": "^4.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/globby": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", - "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", + "node_modules/cron-parser": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz", + "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==", "dev": true, "dependencies": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.3.0", - "ignore": "^5.2.4", - "merge2": "^1.4.1", - "slash": "^4.0.0" + "luxon": "^3.2.1" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12.0.0" } }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.3" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 8" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", "dev": true }, - "node_modules/hammerjs": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", - "integrity": "sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==", + "node_modules/css-declaration-sorter": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz", + "integrity": "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==", "dev": true, "engines": { - "node": ">=0.8.0" + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "postcss": "^8.0.9" } }, - "node_modules/handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", - "dev": true - }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "node_modules/css-loader": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz", + "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==", "dev": true, "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" }, "engines": { - "node": ">=0.4.7" + "node": ">= 18.12.0" }, - "optionalDependencies": { - "uglify-js": "^3.1.4" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.27.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } } }, - "node_modules/handlebars/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/css-minimizer-webpack-plugin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-5.0.1.tgz", + "integrity": "sha512-3caImjKFQkS+ws1TGcFn0V1HyDJFq1Euy589JlD6/3rV2kj+w7r5G9WDMgSHvpvXHNZ2calVypZWuEDQd9wfLg==", "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "cssnano": "^6.0.1", + "jest-worker": "^29.4.3", + "postcss": "^8.4.24", + "schema-utils": "^4.0.1", + "serialize-javascript": "^6.0.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@parcel/css": { + "optional": true + }, + "@swc/css": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "lightningcss": { + "optional": true + } } }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", "dev": true, - "engines": { - "node": ">=4" + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", "dev": true, "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" }, "engines": { - "node": ">=6" + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, - "node_modules/har-validator/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", "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" + "engines": { + "node": ">= 6" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/har-validator/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/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true, - "dependencies": { - "function-bind": "^1.1.1" + "bin": { + "cssesc": "bin/cssesc" }, "engines": { - "node": ">= 0.4.0" + "node": ">=4" } }, - "node_modules/has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "node_modules/cssnano": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.1.2.tgz", + "integrity": "sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==", "dev": true, "dependencies": { - "ansi-regex": "^2.0.0" + "cssnano-preset-default": "^6.1.2", + "lilconfig": "^3.1.1" }, "engines": { - "node": ">=0.10.0" + "node": "^14 || ^16 || >=18.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/cssnano-preset-default": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz", + "integrity": "sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==", + "dev": true, + "dependencies": { + "browserslist": "^4.23.0", + "css-declaration-sorter": "^7.2.0", + "cssnano-utils": "^4.0.2", + "postcss-calc": "^9.0.1", + "postcss-colormin": "^6.1.0", + "postcss-convert-values": "^6.1.0", + "postcss-discard-comments": "^6.0.2", + "postcss-discard-duplicates": "^6.0.3", + "postcss-discard-empty": "^6.0.3", + "postcss-discard-overridden": "^6.0.2", + "postcss-merge-longhand": "^6.0.5", + "postcss-merge-rules": "^6.1.1", + "postcss-minify-font-values": "^6.1.0", + "postcss-minify-gradients": "^6.0.3", + "postcss-minify-params": "^6.1.0", + "postcss-minify-selectors": "^6.0.4", + "postcss-normalize-charset": "^6.0.2", + "postcss-normalize-display-values": "^6.0.2", + "postcss-normalize-positions": "^6.0.2", + "postcss-normalize-repeat-style": "^6.0.2", + "postcss-normalize-string": "^6.0.2", + "postcss-normalize-timing-functions": "^6.0.2", + "postcss-normalize-unicode": "^6.1.0", + "postcss-normalize-url": "^6.0.2", + "postcss-normalize-whitespace": "^6.0.2", + "postcss-ordered-values": "^6.0.2", + "postcss-reduce-initial": "^6.1.0", + "postcss-reduce-transforms": "^6.0.2", + "postcss-svgo": "^6.0.3", + "postcss-unique-selectors": "^6.0.4" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, - "node_modules/has-ansi/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "node_modules/cssnano-utils": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.2.tgz", + "integrity": "sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" } }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", "dev": true, + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, "engines": { - "node": ">=4" + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "dev": true + }, + "node_modules/custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", + "dev": true + }, + "node_modules/cytoscape": { + "version": "3.30.2", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.30.2.tgz", + "integrity": "sha512-oICxQsjW8uSaRmn4UK/jkczKOqTrVqt5/1WL0POiJUT2EKNc9STM4hYFHv917yu55aTBMFNRzymlJhVAiWPCxw==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cytoscape-edgehandles": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cytoscape-edgehandles/-/cytoscape-edgehandles-4.0.1.tgz", + "integrity": "sha512-uSYshkqRZ4luCxK295bEVTg46q4ZW+fwJhcIzMrtfNR7zeAnJ38Z48kUGeu5ibtXkgLbcZAg0YE4ED2dRuaePg==", "dependencies": { - "es-define-property": "^1.0.0" + "lodash.memoize": "^4.1.2", + "lodash.throttle": "^4.1.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "cytoscape": "^3.2.0" } }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "dev": true, - "engines": { - "node": ">= 0.4" + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dependencies": { + "internmap": "1 - 2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=12" } }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=12" } }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", "dependencies": { - "has-symbols": "^1.0.3" + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=12" } }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "dev": true + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", "dependencies": { - "function-bind": "^1.1.2" + "d3-dispatch": "1 - 3", + "d3-selection": "3" }, "engines": { - "node": ">= 0.4" + "node": ">=12" } }, - "node_modules/hdr-histogram-js": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz", - "integrity": "sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g==", - "dev": true, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", "dependencies": { - "@assemblyscript/loader": "^0.10.1", - "base64-js": "^1.2.0", - "pako": "^1.0.3" + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" } }, - "node_modules/hdr-histogram-percentiles-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz", - "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==", - "dev": true + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } }, - "node_modules/heap": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz", - "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==" + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } }, - "node_modules/hosted-git-info": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.2.1.tgz", - "integrity": "sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==", - "dev": true, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", "dependencies": { - "lru-cache": "^7.5.1" + "d3-color": "1 - 3" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=12" } }, - "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", "engines": { "node": ">=12" } }, - "node_modules/hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", - "dev": true, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", "dependencies": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" } }, - "node_modules/hpack.js/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" } }, - "node_modules/hpack.js/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", "dependencies": { - "safe-buffer": "~5.1.0" + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" } }, - "node_modules/html-entities": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", - "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/mdevils" - }, - { - "type": "patreon", - "url": "https://patreon.com/mdevils" - } - ] - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" } }, - "node_modules/http-auth": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/http-auth/-/http-auth-4.1.9.tgz", - "integrity": "sha512-kvPYxNGc9EKGTXvOMnTBQw2RZfuiSihK/mLw/a4pbtRueTE45S55Lw/3k5CktIf7Ak0veMKEIteDj4YkNmCzmQ==", - "dev": true, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", "dependencies": { - "apache-crypt": "^1.1.2", - "apache-md5": "^1.0.6", - "bcryptjs": "^2.4.3", - "uuid": "^8.3.2" + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" } }, - "node_modules/http-auth-connect": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/http-auth-connect/-/http-auth-connect-1.0.6.tgz", - "integrity": "sha512-yaO0QSCPqGCjPrl3qEEHjJP+lwZ6gMpXLuCBE06eWwcXomkI5TARtu0kxf9teFuBj6iaV3Ybr15jaWUvbzNzHw==", - "dev": true, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "dev": true - }, - "node_modules/http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", - "dev": true - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", "dev": true, "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, "engines": { - "node": ">= 0.8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/http-parser-js": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", - "dev": true - }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", "dev": true, "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" }, "engines": { - "node": ">=8.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "node_modules/date-format": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", + "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", "dev": true, - "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, "engines": { - "node": ">= 6" + "node": ">=4.0" } }, - "node_modules/http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "dependencies": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" + "ms": "^2.1.3" }, "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@types/express": "^4.17.13" + "node": ">=6.0" }, "peerDependenciesMeta": { - "@types/express": { + "supports-color": { "optional": true } } }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "node_modules/decache": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/decache/-/decache-4.6.2.tgz", + "integrity": "sha512-2LPqkLeu8XWHU8qNCS3kcF6sCcb5zIzvWaAHYSvPfwhdd7mHuah29NssMzrTYyHN4F5oFy2ko9OBYxegtU0FEw==", "dev": true, "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, + "callsite": "^1.0.0" + } + }, + "node_modules/deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==", + "dev": true + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" + "node": ">=0.10.0" } }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "node_modules/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", "dev": true, "dependencies": { - "agent-base": "6", - "debug": "4" + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" }, "engines": { - "node": ">= 6" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", "dev": true, "engines": { - "node": ">=10.17.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", "dev": true, "dependencies": { - "ms": "^2.0.0" + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" } }, - "node_modules/i18next": { - "version": "23.12.2", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.12.2.tgz", - "integrity": "sha512-XIeh5V+bi8SJSWGL3jqbTEBW5oD6rbP5L+E7dVQh1MNTxxYef0x15rhJVcRb7oiuq4jLtgy2SD8eFlf6P2cmqg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://locize.com" - }, - { - "type": "individual", - "url": "https://locize.com/i18next.html" - }, - { - "type": "individual", - "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" - } - ], - "dependencies": { - "@babel/runtime": "^7.23.2" - } - }, - "node_modules/i18next/node_modules/@babel/runtime": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz", - "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==", + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, "dependencies": { - "regenerator-runtime": "^0.14.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/i18next/node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "node": ">= 0.4" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", "dev": true, "engines": { - "node": "^10 || ^12 || >= 14" + "node": ">=12" }, - "peerDependencies": { - "postcss": "^8.1.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ieee754": { + "node_modules/define-properties": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/ignore-walk": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-5.0.1.tgz", - "integrity": "sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, "dependencies": { - "minimatch": "^5.0.1" + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/image-size": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", - "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", - "dev": true, - "optional": true, - "bin": { - "image-size": "bin/image-size.js" - }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "engines": { - "node": ">=0.10.0" + "node": ">=0.4.0" } }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", - "dev": true - }, - "node_modules/immutable": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.2.tgz", - "integrity": "sha512-oGXzbEDem9OOpDWZu88jGiYCvIsLHMvGw+8OXlpsvTFvIQplQbjg1B1cvKg8f7Hoch6+NGjpPsH1Fr+Mc2D1aA==", + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", "dev": true }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.8" } }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "node_modules/dependency-graph": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-1.0.0.tgz", + "integrity": "sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg==", "dev": true, "engines": { "node": ">=4" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "dev": true, "engines": { - "node": ">=0.8.19" + "node": ">=6" } }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", "dev": true, "engines": { "node": ">=8" } }, - "node_modules/infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "dev": true }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "node_modules/detect-port": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.6.1.tgz", + "integrity": "sha512-CmnVc+Hek2egPx1PeTFVta2W78xy2K/9Rkf6cC4T59S50tVnzKj+tnx5mmx5lwvCkujZ4uRrpRSuV+IVs3f90Q==", "dev": true, "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "address": "^1.0.1", + "debug": "4" + }, + "bin": { + "detect": "bin/detect-port.js", + "detect-port": "bin/detect-port.js" + }, + "engines": { + "node": ">= 4.0.0" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "node_modules/dfa": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", + "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==", "dev": true }, - "node_modules/ini": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.0.tgz", - "integrity": "sha512-TxYQaeNW/N8ymDvwAxPyRbhMBtnEwuvaTYpOQkFx1nSeusgezHniEc/l35Vo4iCq/mMiTJbpD7oYxN98hFlfmw==", - "dev": true, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } + "node_modules/di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", + "dev": true }, - "node_modules/injection-js": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/injection-js/-/injection-js-2.4.0.tgz", - "integrity": "sha512-6jiJt0tCAo9zjHbcwLiPL+IuNe9SQ6a9g0PEzafThW3fOQi0mrmiJGBJvDD6tmhPh8cQHIQtCOrJuBfQME4kPA==", + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true, - "dependencies": { - "tslib": "^2.0.0" + "engines": { + "node": ">=0.3.1" } }, - "node_modules/inquirer": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", - "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", - "dev": true, - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^7.0.0" - }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "engines": { - "node": ">=12.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/inquirer/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "path-type": "^4.0.0" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/inquirer/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@leichtgewicht/ip-codec": "^2.0.1" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=6" } }, - "node_modules/inquirer/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "esutils": "^2.0.2" }, "engines": { - "node": ">=7.0.0" + "node": ">=6.0.0" } }, - "node_modules/inquirer/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/inquirer/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "node_modules/dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==", "dev": true, "dependencies": { - "tslib": "^2.1.0" + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" } }, - "node_modules/inquirer/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, - "node_modules/internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", "dev": true, "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" + "domelementtype": "^2.3.0" }, "engines": { - "node": ">= 0.4" - } - }, - "node_modules/internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", - "engines": { - "node": ">=12" + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, - "node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", "dev": true, "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" }, - "engines": { - "node": ">= 12" + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/ip-address/node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "dev": true - }, - "node_modules/ip-address/node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "node_modules/dot": { + "version": "2.0.0-beta.1", + "resolved": "https://registry.npmjs.org/dot/-/dot-2.0.0-beta.1.tgz", + "integrity": "sha512-kxM7fSnNQTXOmaeGuBSXM8O3fEsBb7XSDBllkGbRwa0lJSJTxxDE/4eSNGLKZUmlFw0f1vJ5qSV2BljrgQtgIA==", "dev": true }, - "node_modules/ipaddr.js": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", - "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", "engines": { - "node": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://dotenvx.com" } }, - "node_modules/is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", - "dev": true, + "node_modules/dotenv-expand": { + "version": "11.0.6", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.6.tgz", + "integrity": "sha512-8NHi73otpWsZGBSZwwknTXS5pqMOrk9+Ssrna8xCaxkzEpU9OTf9R5ArQGVw03//Zmk9MOwLPng9WwndvpAJ5g==", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" + "dotenv": "^16.4.4" }, "engines": { - "node": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://dotenvx.com" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dev": true, "dependencies": { - "binary-extensions": "^2.0.0" + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/is-boolean-object": { + "node_modules/electron-to-chromium": { + "version": "1.5.18", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.18.tgz", + "integrity": "sha512-1OfuVACu+zKlmjsNdcJuVQuVE61sZOLbNM4JAQ1Rvh6EOj0/EUKhMJjRH73InPlXSh8HIJk1cVZ8pyOV/FMdUQ==", + "dev": true + }, + "node_modules/emitter-component": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.2.tgz", + "integrity": "sha512-QdXO3nXOzZB4pAjM0n6ZE+R9/+kPpECA/XSELIcc54NeYVnBqIk+4DFiBgK+8QbV3mdvTG6nedl7dTYgO+5wDw==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-builtin-module": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", - "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", "dev": true, - "dependencies": { - "builtin-modules": "^3.3.0" - }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 4" } }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "dev": true, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8" } }, - "node_modules/is-core-module": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", - "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "dev": true, + "optional": true, "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "iconv-lite": "^0.6.2" } }, - "node_modules/is-data-view": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "node_modules/encoding-sniffer": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz", + "integrity": "sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==", "dev": true, "dependencies": { - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" } }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/engine.io": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz", + "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=10.2.0" } }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", "dev": true, - "bin": { - "is-docker": "cli.js" - }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=10.0.0" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "node_modules/engine.io/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/enhanced-resolve": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, "engines": { - "node": ">=8" + "node": ">=10.13.0" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", "dependencies": { - "is-extglob": "^2.1.1" + "ansi-colors": "^4.1.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8.6" } }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "node_modules/ent": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.1.tgz", + "integrity": "sha512-QHuXVeZx9d+tIQAz/XztU0ZwZf2Agg9CcXcgE1rurqvdBeDBrpSwjl8/6XUqMg7tw2Y7uAdKb2sRv+bSEFqQ5A==", "dev": true, + "dependencies": { + "punycode": "^1.4.1" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" } }, - "node_modules/is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "dev": true - }, - "node_modules/is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", - "dev": true - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "dev": true, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "devOptional": true, "engines": { - "node": ">= 0.4" + "node": ">=0.12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "dev": true, "engines": { - "node": ">=0.12.0" + "node": ">=6" } }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, "engines": { - "node": ">= 0.4" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha512-cnS56eR9SPAscL77ik76ATVqoPARTqPIVkMDVxRaWH06zT+6+CzIroYRJ0VVvm0Z1zfAvxvz9i/D3Ppjaqt5Nw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true }, - "node_modules/is-path-in-cwd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", "dev": true, + "optional": true, "dependencies": { - "is-path-inside": "^1.0.0" + "prr": "~1.0.1" }, - "engines": { - "node": ">=0.10.0" + "bin": { + "errno": "cli.js" } }, - "node_modules/is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha512-qhsCR/Esx4U4hg/9I19OVUAJkGWtjRYHMRgUMZE2TDdj+Ag+kttZanLupfddNyglzz50cUlmWzUaI37GDfNx/g==", + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, "dependencies": { - "path-is-inside": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" + "is-arrayish": "^0.2.1" } }, - "node_modules/is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "node_modules/es-abstract": { + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.3", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.13", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.15" + }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", "dev": true, "dependencies": { - "isobject": "^3.0.1" + "get-intrinsic": "^1.2.4" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "node_modules/es-module-lexer": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", + "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "dev": true + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", "dev": true, "dependencies": { - "call-bind": "^1.0.7" + "es-errors": "^1.3.0" }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" + "node_modules/es-set-tostringtag": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">= 0.4" } }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -11059,148 +11263,212 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "node_modules/es6-shim": { + "version": "0.35.8", + "resolved": "https://registry.npmjs.org/es6-shim/-/es6-shim-0.35.8.tgz", + "integrity": "sha512-Twf7I2v4/1tLoIXMT8HlqaBSS5H2wQTs2wx3MNYCI8K1R1/clXyCazrcVCPm/FuO9cyV8+leEaZOWD5C253NDg==", + "dev": true + }, + "node_modules/esbuild": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.0.tgz", + "integrity": "sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==", "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" }, "engines": { - "node": ">= 0.4" + "node": ">=18" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.0", + "@esbuild/android-arm": "0.23.0", + "@esbuild/android-arm64": "0.23.0", + "@esbuild/android-x64": "0.23.0", + "@esbuild/darwin-arm64": "0.23.0", + "@esbuild/darwin-x64": "0.23.0", + "@esbuild/freebsd-arm64": "0.23.0", + "@esbuild/freebsd-x64": "0.23.0", + "@esbuild/linux-arm": "0.23.0", + "@esbuild/linux-arm64": "0.23.0", + "@esbuild/linux-ia32": "0.23.0", + "@esbuild/linux-loong64": "0.23.0", + "@esbuild/linux-mips64el": "0.23.0", + "@esbuild/linux-ppc64": "0.23.0", + "@esbuild/linux-riscv64": "0.23.0", + "@esbuild/linux-s390x": "0.23.0", + "@esbuild/linux-x64": "0.23.0", + "@esbuild/netbsd-x64": "0.23.0", + "@esbuild/openbsd-arm64": "0.23.0", + "@esbuild/openbsd-x64": "0.23.0", + "@esbuild/sunos-x64": "0.23.0", + "@esbuild/win32-arm64": "0.23.0", + "@esbuild/win32-ia32": "0.23.0", + "@esbuild/win32-x64": "0.23.0" } }, - "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "node_modules/esbuild-wasm": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.23.0.tgz", + "integrity": "sha512-6jP8UmWy6R6TUUV8bMuC3ZyZ6lZKI56x0tkxyCIqWwRRJ/DgeQKneh/Oid5EoGoPFLrGNkz47ZEtWAYuiY/u9g==", "dev": true, - "dependencies": { - "which-typed-array": "^1.1.14" + "bin": { + "esbuild": "bin/esbuild" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "dev": true }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.8.0" } }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "node_modules/eslint": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2" + "@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", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://opencollective.com/eslint" } }, - "node_modules/is-what": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", - "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", - "dev": true + "node_modules/eslint-config-prettier": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz", + "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "node_modules/eslint-scope": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz", + "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==", "dev": true, "dependencies": { - "is-docker": "^2.0.0" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/isbinaryfile": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", - "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "engines": { - "node": ">= 8.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/gjtorikian/" + "url": "https://opencollective.com/eslint" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "node_modules/eslint/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, - "engines": { - "node": ">=0.10.0" + "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/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "dev": true - }, - "node_modules/istanbul-api": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.1.7.tgz", - "integrity": "sha512-LYTOa2UrYFyJ/aSczZi/6lBykVMjCCvUmT64gOe+jPZFy4w6FYfPGqFT2IiQ2BxVHHDOvCD7qrIXb0EOh4uGWw==", + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "async": "^2.6.2", - "compare-versions": "^3.4.0", - "fileset": "^2.0.3", - "istanbul-lib-coverage": "^2.0.5", - "istanbul-lib-hook": "^2.0.7", - "istanbul-lib-instrument": "^3.3.0", - "istanbul-lib-report": "^2.0.8", - "istanbul-lib-source-maps": "^3.0.6", - "istanbul-reports": "^2.2.5", - "js-yaml": "^3.13.1", - "make-dir": "^2.1.0", - "minimatch": "^3.0.4", - "once": "^1.4.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/istanbul-api/node_modules/brace-expansion": { + "node_modules/eslint/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==", @@ -11210,791 +11478,858 @@ "concat-map": "0.0.1" } }, - "node_modules/istanbul-api/node_modules/istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/istanbul-api/node_modules/istanbul-lib-instrument": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", - "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "@babel/generator": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", - "istanbul-lib-coverage": "^2.0.5", - "semver": "^6.0.0" + "color-name": "~1.1.4" }, "engines": { - "node": ">=6" + "node": ">=7.0.0" } }, - "node_modules/istanbul-api/node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/istanbul-api/node_modules/make-dir/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, - "bin": { - "semver": "bin/semver" + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/istanbul-api/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "is-glob": "^4.0.3" }, "engines": { - "node": "*" + "node": ">=10.13.0" } }, - "node_modules/istanbul-api/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "node_modules/eslint/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, "engines": { - "node": ">=6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/istanbul-api/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { "node": ">=8" } }, - "node_modules/istanbul-lib-hook": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz", - "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==", + "node_modules/eslint/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/eslint/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, "dependencies": { - "append-transform": "^1.0.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=6" + "node": "*" } }, - "node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" + "has-flag": "^4.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, - "bin": { - "semver": "bin/semver.js" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/istanbul-lib-report": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", - "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "supports-color": "^6.1.0" + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": ">=6" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/istanbul-lib-report/node_modules/istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, "engines": { - "node": ">=6" + "node": ">=4" } }, - "node_modules/istanbul-lib-report/node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" + "estraverse": "^5.1.0" }, "engines": { - "node": ">=6" + "node": ">=0.10" } }, - "node_modules/istanbul-lib-report/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, "engines": { - "node": ">=6" + "node": ">=4.0" } }, - "node_modules/istanbul-lib-report/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "bin": { - "semver": "bin/semver" + "engines": { + "node": ">=4.0" } }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", - "source-map": "^0.6.1" - }, "engines": { - "node": ">=6" + "node": ">= 0.6" } }, - "node_modules/istanbul-lib-source-maps/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==", + "node_modules/event-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-4.0.1.tgz", + "integrity": "sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "duplexer": "^0.1.1", + "from": "^0.1.7", + "map-stream": "0.0.7", + "pause-stream": "^0.0.11", + "split": "^1.0.1", + "stream-combiner": "^0.2.2", + "through": "^2.3.8" } }, - "node_modules/istanbul-lib-source-maps/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, - "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/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true }, - "node_modules/istanbul-lib-source-maps/node_modules/istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, "engines": { - "node": ">=6" + "node": ">=0.8.x" } }, - "node_modules/istanbul-lib-source-maps/node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" }, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/istanbul-lib-source-maps/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/execa/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "mimic-fn": "^2.1.0" }, - "engines": { - "node": "*" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true, "engines": { "node": ">=6" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "dependencies": { - "glob": "^7.1.3" }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/istanbul-lib-source-maps/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true }, - "node_modules/istanbul-reports": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz", - "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==", + "node_modules/expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", "dev": true, "dependencies": { - "html-escaper": "^2.0.0" + "homedir-polyfill": "^1.0.1" }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/jackspeak": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.2.tgz", - "integrity": "sha512-mgNtVv4vUuaKA97yxUHoA3+FkuhtxkjdXEWOyB/N76fjy0FjezEt34oy3epBtvCvS+7DyKwqCFWx/oJLV5+kCg==", + "node_modules/exponential-backoff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", + "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", + "dev": true + }, + "node_modules/express": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.20.0.tgz", + "integrity": "sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==", "dev": true, "dependencies": { - "@isaacs/cliui": "^8.0.2" + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.10", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "node": ">= 0.10.0" } }, - "node_modules/jasmine": { - "version": "3.99.0", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.99.0.tgz", - "integrity": "sha512-YIThBuHzaIIcjxeuLmPD40SjxkEcc8i//sGMDKCgkRMVgIwRJf5qyExtlJpQeh7pkeoBSOe6lQEdg+/9uKg9mw==", + "node_modules/express/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": { - "glob": "^7.1.6", - "jasmine-core": "~3.99.0" + "ms": "2.0.0" + } + }, + "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==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" }, - "bin": { - "jasmine": "bin/jasmine.js" + "engines": { + "node": ">= 0.8" } }, - "node_modules/jasmine-core": { - "version": "3.99.1", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.99.1.tgz", - "integrity": "sha512-Hu1dmuoGcZ7AfyynN3LsfruwMbxMALMka+YtZeGoLuDEySVmVAPaonkNoBRIw/ectu8b9tVQCJNgp4a4knp+tg==", + "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 }, - "node_modules/jasmine-spec-reporter": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-5.0.2.tgz", - "integrity": "sha512-6gP1LbVgJ+d7PKksQBc2H0oDGNRQI3gKUsWlswKaQ2fif9X5gzhQcgM5+kiJGCQVurOG09jqNhk7payggyp5+g==", + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", "dev": true, "dependencies": { - "colors": "1.4.0" + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" } }, - "node_modules/jasmine/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==", + "node_modules/external-editor/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/jasmine/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", + "node_modules/external-editor/node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": 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" + "os-tmpdir": "~1.0.2" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=0.6.0" } }, - "node_modules/jasmine/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/fancy-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-2.0.0.tgz", + "integrity": "sha512-9CzxZbACXMUXW13tS0tI8XsGGmxWzO2DmYrGuBJOJ8k8q2K7hwfJA5qHjuPPe8wtsco33YR9wc+Rlr5wYFvhSA==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "color-support": "^1.1.3" }, "engines": { - "node": "*" + "node": ">=10.13.0" } }, - "node_modules/jasminewd2": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz", - "integrity": "sha512-Rn0nZe4rfDhzA63Al3ZGh0E+JTmM6ESZYXJGKuqKGZObsAB9fwXPD03GjtIEvJBDOhN94T5MzbwZSqzFHSQPzg==", + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, "engines": { - "node": ">= 6.9.x" + "node": ">=8.6.0" } }, - "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fast-uri": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", + "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" + "reusify": "^1.0.4" } }, - "node_modules/jest-worker/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", "dev": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, "engines": { - "node": ">=8" + "node": ">=0.8.0" } }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", "dependencies": { - "has-flag": "^4.0.0" + "escape-string-regexp": "^1.0.5" }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "flat-cache": "^3.0.4" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true + "node_modules/file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==", + "optional": true, + "peer": true }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", "dev": true, - "bin": { - "jsesc": "bin/jsesc" + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=4" + "node": ">=10" } }, - "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/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, - "bin": { - "json5": "lib/cli.js" + "dependencies": { + "to-regex-range": "^5.0.1" }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/jsonc-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz", - "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==", - "dev": true - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", "dev": true, "dependencies": { - "universalify": "^2.0.0" + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "engines": { + "node": ">= 0.8" } }, - "node_modules/jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "node_modules/finalhandler/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, - "engines": [ - "node >= 0.2.0" - ] + "dependencies": { + "ms": "2.0.0" + } }, - "node_modules/jsonschema": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz", - "integrity": "sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==", + "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": "*" + "node": ">= 0.8" } }, - "node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/finalhandler/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", "dev": true, "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" + "ee-first": "1.1.1" }, "engines": { - "node": ">=0.6.0" + "node": ">= 0.8" } }, - "node_modules/jszip": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "node_modules/finalhandler/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", "dev": true, - "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" + "engines": { + "node": ">= 0.6" } }, - "node_modules/jszip/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", "dev": true, "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" } }, - "node_modules/jszip/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "node_modules/find-file-up": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/find-file-up/-/find-file-up-2.0.1.tgz", + "integrity": "sha512-qVdaUhYO39zmh28/JLQM5CoYN9byEOKEH4qfa8K1eNV17W0UUMJ9WgbR/hHFH+t5rcl+6RTb5UC7ck/I+uRkpQ==", "dev": true, "dependencies": { - "safe-buffer": "~5.1.0" + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/karma": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.3.tgz", - "integrity": "sha512-LuucC/RE92tJ8mlCwqEoRWXP38UMAqpnq98vktmS9SznSoUPPUJQbc91dHcxcunROvfQjdORVA/YFviH+Xci9Q==", + "node_modules/find-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-pkg/-/find-pkg-2.0.0.tgz", + "integrity": "sha512-WgZ+nKbELDa6N3i/9nrHeNznm+lY3z4YfhDDWgW+5P0pdmMj26bxaxU11ookgY3NyP9GC7HvZ9etp0jRFqGEeQ==", "dev": true, "dependencies": { - "@colors/colors": "1.5.0", - "body-parser": "^1.19.0", - "braces": "^3.0.2", - "chokidar": "^3.5.1", - "connect": "^3.7.0", - "di": "^0.0.1", - "dom-serialize": "^2.2.1", - "glob": "^7.1.7", - "graceful-fs": "^4.2.6", - "http-proxy": "^1.18.1", - "isbinaryfile": "^4.0.8", - "lodash": "^4.17.21", - "log4js": "^6.4.1", - "mime": "^2.5.2", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.5", - "qjobs": "^1.2.0", - "range-parser": "^1.2.1", - "rimraf": "^3.0.2", - "socket.io": "^4.7.2", - "source-map": "^0.6.1", - "tmp": "^0.2.1", - "ua-parser-js": "^0.7.30", - "yargs": "^16.1.1" - }, - "bin": { - "karma": "bin/karma" + "find-file-up": "^2.0.1" }, "engines": { - "node": ">= 10" + "node": ">=8" } }, - "node_modules/karma-chrome-launcher": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.1.tgz", - "integrity": "sha512-hsIglcq1vtboGPAN+DGCISCFOxW+ZVnIqhDQcCMqqCp+4dmJ0Qpq5QAjkbA0X2L9Mi6OBkHi2Srrbmm7pUKkzQ==", + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { - "which": "^1.2.1" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/karma-cli": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/karma-cli/-/karma-cli-2.0.0.tgz", - "integrity": "sha512-1Kb28UILg1ZsfqQmeELbPzuEb5C6GZJfVIk0qOr8LNYQuYWmAaqP16WpbpKEjhejDrDYyYOwwJXSZO6u7q5Pvw==", + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "dependencies": { - "resolve": "^1.3.3" - }, - "bin": { - "karma": "bin/karma" + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" }, "engines": { - "node": ">= 6" + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/karma-coverage-istanbul-reporter": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-2.0.6.tgz", - "integrity": "sha512-WFh77RI8bMIKdOvI/1/IBmgnM+Q7NOLhnwG91QJrM8lW+CIXCjTzhhUsT/svLvAkLmR10uWY4RyYbHMLkTglvg==", - "dev": true, - "dependencies": { - "istanbul-api": "^2.1.6", - "minimatch": "^3.0.4" + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } } }, - "node_modules/karma-coverage-istanbul-reporter/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==", + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "is-callable": "^1.1.3" } }, - "node_modules/karma-coverage-istanbul-reporter/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" }, "engines": { - "node": "*" - } - }, - "node_modules/karma-ie-launcher": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/karma-ie-launcher/-/karma-ie-launcher-1.0.0.tgz", - "integrity": "sha512-ts71ke8pHvw6qdRtq0+7VY3ANLoZuUNNkA8abRaWV13QRPNm7TtSOqyszjHUtuwOWKcsSz4tbUtrNICrQC+SXQ==", - "dev": true, - "dependencies": { - "lodash": "^4.6.1" + "node": ">=14" }, - "peerDependencies": { - "karma": ">=0.9" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/karma-jasmine": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-4.0.2.tgz", - "integrity": "sha512-ggi84RMNQffSDmWSyyt4zxzh2CQGwsxvYYsprgyR1j8ikzIduEdOlcLvXjZGwXG/0j41KUXOWsUCBfbEHPWP9g==", + "node_modules/fork-ts-checker-webpack-plugin": { + "version": "7.2.13", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-7.2.13.tgz", + "integrity": "sha512-fR3WRkOb4bQdWB/y7ssDUlVdrclvwtyCUIHCfivAoYxq9dF7XfrDKbMdZIfwJ7hxIAqkYSGeU7lLJE6xrxIBdg==", "dev": true, "dependencies": { - "jasmine-core": "^3.6.0" + "@babel/code-frame": "^7.16.7", + "chalk": "^4.1.2", + "chokidar": "^3.5.3", + "cosmiconfig": "^7.0.1", + "deepmerge": "^4.2.2", + "fs-extra": "^10.0.0", + "memfs": "^3.4.1", + "minimatch": "^3.0.4", + "node-abort-controller": "^3.0.1", + "schema-utils": "^3.1.1", + "semver": "^7.3.5", + "tapable": "^2.2.1" }, "engines": { - "node": ">= 10" + "node": ">=12.13.0", + "yarn": ">=1.0.0" }, "peerDependencies": { - "karma": "*" + "typescript": ">3.6.0", + "vue-template-compiler": "*", + "webpack": "^5.11.0" + }, + "peerDependenciesMeta": { + "vue-template-compiler": { + "optional": true + } } }, - "node_modules/karma-jasmine-html-reporter": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.7.0.tgz", - "integrity": "sha512-pzum1TL7j90DTE86eFt48/s12hqwQuiD+e5aXx2Dc9wDEn2LfGq6RoAxEZZjFiN0RDSCOnosEKRZWxbQ+iMpQQ==", - "dev": true, - "peerDependencies": { - "jasmine-core": ">=3.8", - "karma": ">=0.9", - "karma-jasmine": ">=1.1" - } - }, - "node_modules/karma-junit-reporter": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/karma-junit-reporter/-/karma-junit-reporter-1.2.0.tgz", - "integrity": "sha512-FeuLOKlXNtJhIQK3oQASbO5QOib762CEHV8+L9wwTQpiZJgp7xKg3sNno66rL5bQPV2soG6fJdAFWqqnMJuh2w==", + "node_modules/fork-ts-checker-webpack-plugin/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": { - "path-is-absolute": "^1.0.0", - "xmlbuilder": "8.2.2" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, - "peerDependencies": { - "karma": ">=0.9" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/karma-source-map-support": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", - "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", + "node_modules/fork-ts-checker-webpack-plugin/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, - "dependencies": { - "source-map-support": "^0.5.5" + "peerDependencies": { + "ajv": "^6.9.1" } }, - "node_modules/karma-viewport": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/karma-viewport/-/karma-viewport-1.0.9.tgz", - "integrity": "sha512-E1xVe66vBQtI66TGOtZMzV5nf6BW5tW4TQVUqPK+oakVLdsG/ZUG688tGK0lL1q0t7nfQD1dwLD8Z9Guu/RVdg==", + "node_modules/fork-ts-checker-webpack-plugin/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "@types/karma": "^6.3.3", - "jsonschema": "^1.4.0" + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/karma/node_modules/brace-expansion": { + "node_modules/fork-ts-checker-webpack-plugin/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==", @@ -12004,1630 +12339,1383 @@ "concat-map": "0.0.1" } }, - "node_modules/karma/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", + "node_modules/fork-ts-checker-webpack-plugin/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": 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" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "*" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/karma/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/fork-ts-checker-webpack-plugin/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "color-name": "~1.1.4" }, "engines": { - "node": "*" - } - }, - "node_modules/karma/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" + "node": ">=7.0.0" } }, - "node_modules/karma/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "node_modules/fork-ts-checker-webpack-plugin/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, - "node_modules/karma/node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", "dev": true, "dependencies": { - "rimraf": "^3.0.0" + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" }, "engines": { - "node": ">=8.17.0" + "node": ">=10" } }, - "node_modules/karma/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=12" } }, - "node_modules/karma/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "node_modules/fork-ts-checker-webpack-plugin/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/keycharm": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/keycharm/-/keycharm-0.2.0.tgz", - "integrity": "sha512-i/XBRTiLqRConPKioy2oq45vbv04e8x59b0mnsIRQM+7Ec/8BC7UcL5pnC4FMeGb8KwG7q4wOMw7CtNZf5tiIg==", + "node_modules/fork-ts-checker-webpack-plugin/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/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/klona": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", - "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/less": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", - "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==", + "node_modules/fork-ts-checker-webpack-plugin/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, "dependencies": { - "copy-anything": "^2.0.1", - "parse-node-version": "^1.0.1", - "tslib": "^2.3.0" - }, - "bin": { - "lessc": "bin/lessc" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=6" - }, - "optionalDependencies": { - "errno": "^0.1.1", - "graceful-fs": "^4.1.2", - "image-size": "~0.5.0", - "make-dir": "^2.1.0", - "mime": "^1.4.1", - "needle": "^3.1.0", - "source-map": "~0.6.0" + "node": "*" } }, - "node_modules/less-loader": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.0.0.tgz", - "integrity": "sha512-9+LOWWjuoectIEx3zrfN83NAGxSUB5pWEabbbidVQVgZhN+wN68pOvuyirVlH1IK4VT1f3TmlyvAnCXh8O5KEw==", + "node_modules/fork-ts-checker-webpack-plugin/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": { - "klona": "^2.0.4" + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" }, "engines": { - "node": ">= 14.15.0" + "node": ">= 10.13.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "less": "^3.5.0 || ^4.0.0", - "webpack": "^5.0.0" } }, - "node_modules/less/node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "node_modules/fork-ts-checker-webpack-plugin/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "optional": true, "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/less/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, - "optional": true, - "bin": { - "mime": "cli.js" + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" }, "engines": { - "node": ">=4" + "node": ">= 6" } }, - "node_modules/less/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "dev": true, - "optional": true, "engines": { - "node": ">=6" + "node": ">= 0.6" } }, - "node_modules/less/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", "dev": true, - "optional": true, - "bin": { - "semver": "bin/semver" + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" } }, - "node_modules/less/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "dev": true, - "optional": true, "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, - "node_modules/license-webpack-plugin": { + "node_modules/from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", + "dev": true + }, + "node_modules/front-matter": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", - "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==", - "dev": true, + "resolved": "https://registry.npmjs.org/front-matter/-/front-matter-4.0.2.tgz", + "integrity": "sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==", "dependencies": { - "webpack-sources": "^3.0.0" - }, - "peerDependenciesMeta": { - "webpack": { - "optional": true - }, - "webpack-sources": { - "optional": true - } + "js-yaml": "^3.13.1" } }, - "node_modules/lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "dev": true, + "node_modules/front-matter/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dependencies": { - "immediate": "~3.0.5" + "sprintf-js": "~1.0.2" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true, - "engines": { - "node": ">=6.11.5" + "node_modules/front-matter/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/loader-utils": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", - "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", - "dev": true, + "node_modules/front-matter/node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, "engines": { - "node": ">= 12.13.0" + "node": ">=14.14" } }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "node_modules/fs-minipass": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", + "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", "dev": true, "dependencies": { - "p-locate": "^4.1.0" + "minipass": "^7.0.3" }, "engines": { - "node": ">=8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "node_modules/fs-monkey": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", + "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", "dev": true }, - "node_modules/lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "optional": true - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" - }, - "node_modules/lodash.throttle": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", - "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==" + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=10" - }, + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/log-symbols/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/log-symbols/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, "engines": { - "node": ">=7.0.0" + "node": ">=6.9.0" } }, - "node_modules/log-symbols/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/log-symbols/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "engines": { - "node": ">=8" + "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/log-symbols/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/get-east-asian-width": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", + "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log4js": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", - "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dev": true, "dependencies": { - "date-format": "^4.0.14", - "debug": "^4.3.4", - "flatted": "^3.2.7", - "rfdc": "^1.3.0", - "streamroller": "^3.1.5" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" }, "engines": { - "node": ">=8.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/loglevel": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz", - "integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==", + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, "engines": { - "node": ">= 0.6.0" + "node": ">=10" }, "funding": { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/loglevel" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/loglevel-plugin-prefix": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/loglevel-plugin-prefix/-/loglevel-plugin-prefix-0.8.4.tgz", - "integrity": "sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g==", - "dev": true - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "node_modules/get-symbol-description": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", "dev": true, "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true - }, - "node_modules/macos-release": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.1.tgz", - "integrity": "sha512-DXqXhEM7gW59OjZO8NIjBCz9AQ1BEMrfiOAl4AYByHCtVHRF4KoGNO8mqQeM8lRCtQe/UnJ4imO/d2HdkKsd+A==", - "dev": true, + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" + }, "engines": { - "node": ">=6" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/magic-string": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", - "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "dependencies": { - "sourcemap-codec": "^1.4.8" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": ">=12" + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "dependencies": { - "semver": "^6.0.0" + "is-glob": "^4.0.1" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" + "node": ">= 6" } }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true }, - "node_modules/make-fetch-happen": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz", - "integrity": "sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==", + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^17.0.0", - "http-cache-semantics": "^4.1.1", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^5.0.0", - "minipass-fetch": "^3.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^10.0.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/make-fetch-happen/node_modules/@npmcli/fs": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.0.tgz", - "integrity": "sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==", - "dev": true, - "dependencies": { - "semver": "^7.3.5" + "node": ">=16 || 14 >=14.17" }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/make-fetch-happen/node_modules/cacache": { - "version": "17.1.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.1.3.tgz", - "integrity": "sha512-jAdjGxmPxZh0IipMdR7fK/4sDSrHMLUV0+GvVUsjwyGNKHsh79kW/otg+GkbXwl6Uzvy9wsvHOX4nUoWldeZMg==", + "node_modules/global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", "dev": true, "dependencies": { - "@npmcli/fs": "^3.1.0", - "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^7.7.1", - "minipass": "^5.0.0", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/make-fetch-happen/node_modules/fs-minipass": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.2.tgz", - "integrity": "sha512-2GAfyfoaCDRrM6jaOS3UsBts8yJ55VioXdWcOL7dK9zdAuKT71+WBA4ifnNYqVjYv+4SsPxjK0JT4yIIn4cA/g==", + "node_modules/global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", "dev": true, "dependencies": { - "minipass": "^5.0.0" + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/make-fetch-happen/node_modules/glob": { - "version": "10.3.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.3.tgz", - "integrity": "sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw==", + "node_modules/global-prefix/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" + "isexe": "^2.0.0" }, "bin": { - "glob": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "which": "bin/which" } }, - "node_modules/make-fetch-happen/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, "engines": { - "node": ">=12" + "node": ">=4" } }, - "node_modules/make-fetch-happen/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "define-properties": "^1.2.1", + "gopd": "^1.0.1" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/make-fetch-happen/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, - "engines": { - "node": ">=8" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/make-fetch-happen/node_modules/ssri": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.4.tgz", - "integrity": "sha512-12+IR2CB2C28MMAw0Ncqwj5QbTcs0nGIhgJzYWzDkb21vWmfNI83KS4f3Ci6GI98WreIfG7o9UXp3C0qbpA8nQ==", + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "dependencies": { - "minipass": "^5.0.0" + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-fetch-happen/node_modules/unique-filename": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", - "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dev": true, "dependencies": { - "unique-slug": "^4.0.0" + "get-intrinsic": "^1.1.3" }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/make-fetch-happen/node_modules/unique-slug": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", - "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/hammerjs": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", + "integrity": "sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==", "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4" - }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.8.0" } }, - "node_modules/map-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", - "integrity": "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==", + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "dev": true }, - "node_modules/marked": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/marked/-/marked-7.0.3.tgz", - "integrity": "sha512-ev2uM40p0zQ/GbvqotfKcSWEa59fJwluGZj5dcaUOwDRrB1F3dncdXy8NWUApk4fi8atU3kTBOwjyjZ0ud0dxw==", + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, "bin": { - "marked": "bin/marked.js" + "handlebars": "bin/handlebars" }, "engines": { - "node": ">= 16" + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" } }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "node_modules/handlebars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "engines": { - "node": ">= 0.6" + "node": ">=0.10.0" } }, - "node_modules/memfs": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", - "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", - "dev": true, - "dependencies": { - "fs-monkey": "^1.0.4" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", - "dev": true - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true, - "engines": { - "node": ">= 8" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "engines": { - "node": ">= 0.6" + "node": ">=4" } }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true, - "bin": { - "mime": "cli.js" + "es-define-property": "^1.0.0" }, - "engines": { - "node": ">=4.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "dev": true, "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "dependencies": { - "mime-db": "1.52.0" + "node": ">= 0.4" }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mini-css-extract-plugin": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz", - "integrity": "sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==", + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true, - "dependencies": { - "schema-utils": "^4.0.0" - }, "engines": { - "node": ">= 12.13.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" + "has-symbols": "^1.0.3" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "node_modules/minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "function-bind": "^1.1.2" }, "engines": { - "node": ">=10" + "node": ">= 0.4" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "he": "bin/he" } }, - "node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "node_modules/homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", "dev": true, "dependencies": { - "yallist": "^4.0.0" + "parse-passwd": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", "dev": true, "dependencies": { - "minipass": "^3.0.0" + "lru-cache": "^10.0.1" }, "engines": { - "node": ">= 8" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/minipass-fetch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.3.tgz", - "integrity": "sha512-n5ITsTkDqYkYJZjcRWzZt9qnZKCT7nKCosJhHoj7S7zD+BP4jVbWs+odsniw5TA3E0sLomhTKOKjF86wf11PuQ==", + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", "dev": true, "dependencies": { - "minipass": "^5.0.0", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" } }, - "node_modules/minipass-fetch/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, - "engines": { - "node": ">=8" - } + "node_modules/hpack.js/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true }, - "node_modules/minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "node_modules/minipass-json-stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz", - "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==", + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "dependencies": { - "jsonparse": "^1.3.1", - "minipass": "^3.0.0" + "safe-buffer": "~5.1.0" } }, - "node_modules/minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", "dev": true, "dependencies": { - "minipass": "^3.0.0" + "whatwg-encoding": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/minipass-sized": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "node_modules/html-encoding-sniffer/node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", "dev": true, "dependencies": { - "minipass": "^3.0.0" + "iconv-lite": "0.6.3" }, "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/minipass/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "node_modules/html-entities": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ] + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, + "node_modules/http-assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz", + "integrity": "sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==", "dev": true, "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" + "deep-equal": "~1.0.1", + "http-errors": "~1.8.0" }, "engines": { - "node": ">= 8" + "node": ">= 0.8" } }, - "node_modules/minizlib/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "node_modules/http-assert/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "node_modules/http-assert/node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" }, "engines": { - "node": ">=10" + "node": ">= 0.6" } }, - "node_modules/moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "node_modules/http-assert/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, "engines": { - "node": "*" + "node": ">= 0.6" } }, - "node_modules/moment-timezone": { - "version": "0.5.43", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz", - "integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==", + "node_modules/http-auth": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/http-auth/-/http-auth-4.1.9.tgz", + "integrity": "sha512-kvPYxNGc9EKGTXvOMnTBQw2RZfuiSihK/mLw/a4pbtRueTE45S55Lw/3k5CktIf7Ak0veMKEIteDj4YkNmCzmQ==", + "dev": true, "dependencies": { - "moment": "^2.29.4" + "apache-crypt": "^1.1.2", + "apache-md5": "^1.0.6", + "bcryptjs": "^2.4.3", + "uuid": "^8.3.2" }, "engines": { - "node": "*" + "node": ">=8" } }, - "node_modules/morgan": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", - "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "node_modules/http-auth-connect": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/http-auth-connect/-/http-auth-connect-1.0.6.tgz", + "integrity": "sha512-yaO0QSCPqGCjPrl3qEEHjJP+lwZ6gMpXLuCBE06eWwcXomkI5TARtu0kxf9teFuBj6iaV3Ybr15jaWUvbzNzHw==", "dev": true, - "dependencies": { - "basic-auth": "~2.0.1", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-finished": "~2.3.0", - "on-headers": "~1.0.2" - }, "engines": { - "node": ">= 0.8.0" + "node": ">=8" } }, - "node_modules/morgan/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/http-auth/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, - "dependencies": { - "ms": "2.0.0" + "bin": { + "uuid": "dist/bin/uuid" } }, - "node_modules/morgan/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", "dev": true }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", "dev": true }, - "node_modules/multicast-dns": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", - "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dev": true, "dependencies": { - "dns-packet": "^5.2.2", - "thunky": "^1.0.2" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, - "bin": { - "multicast-dns": "cli.js" + "engines": { + "node": ">= 0.8" } }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", "dev": true }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": ">=8.0.0" } }, - "node_modules/needle": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/needle/-/needle-3.2.0.tgz", - "integrity": "sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==", + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, - "optional": true, "dependencies": { - "debug": "^3.2.6", - "iconv-lite": "^0.6.3", - "sax": "^1.2.4" - }, - "bin": { - "needle": "bin/needle" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": ">= 4.4.x" + "node": ">= 14" } }, - "node_modules/needle/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/http-proxy-middleware": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.0.tgz", + "integrity": "sha512-36AV1fIaI2cWRzHo+rbcxhe3M3jUDCNzc4D5zRl57sEWRAxdXYtw7FSQKYY6PDKssiAKjLYypbssHk+xs/kMXw==", "dev": true, - "optional": true, "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true, + "@types/http-proxy": "^1.17.10", + "debug": "^4.3.4", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.5" + }, "engines": { - "node": ">= 0.6" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "node_modules/ng-mocks": { - "version": "14.11.0", - "resolved": "https://registry.npmjs.org/ng-mocks/-/ng-mocks-14.11.0.tgz", - "integrity": "sha512-6h0TafPogU7iEbWKGQt5npfEtI7IjThsqqnDboMIZ4AJyUY7VHUmhFa39Zkd2e4oOLDLb/6sVnfDIaWCp3oFgQ==", + "node_modules/http-server": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", + "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/satanTime" + "dependencies": { + "basic-auth": "^2.0.1", + "chalk": "^4.1.2", + "corser": "^2.0.1", + "he": "^1.2.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy": "^1.18.1", + "mime": "^1.6.0", + "minimist": "^1.2.6", + "opener": "^1.5.1", + "portfinder": "^1.0.28", + "secure-compare": "3.0.1", + "union": "~0.5.0", + "url-join": "^4.0.1" }, - "peerDependencies": { - "@angular/common": "5.0.0-alpha - 5 || 6.0.0-alpha - 6 || 7.0.0-alpha - 7 || 8.0.0-alpha - 8 || 9.0.0-alpha - 9 || 10.0.0-alpha - 10 || 11.0.0-alpha - 11 || 12.0.0-alpha - 12 || 13.0.0-alpha - 13 || 14.0.0-alpha - 14 || 15.0.0-alpha - 15 || 16.0.0-alpha - 16", - "@angular/core": "5.0.0-alpha - 5 || 6.0.0-alpha - 6 || 7.0.0-alpha - 7 || 8.0.0-alpha - 8 || 9.0.0-alpha - 9 || 10.0.0-alpha - 10 || 11.0.0-alpha - 11 || 12.0.0-alpha - 12 || 13.0.0-alpha - 13 || 14.0.0-alpha - 14 || 15.0.0-alpha - 15 || 16.0.0-alpha - 16", - "@angular/forms": "5.0.0-alpha - 5 || 6.0.0-alpha - 6 || 7.0.0-alpha - 7 || 8.0.0-alpha - 8 || 9.0.0-alpha - 9 || 10.0.0-alpha - 10 || 11.0.0-alpha - 11 || 12.0.0-alpha - 12 || 13.0.0-alpha - 13 || 14.0.0-alpha - 14 || 15.0.0-alpha - 15 || 16.0.0-alpha - 16", - "@angular/platform-browser": "5.0.0-alpha - 5 || 6.0.0-alpha - 6 || 7.0.0-alpha - 7 || 8.0.0-alpha - 8 || 9.0.0-alpha - 9 || 10.0.0-alpha - 10 || 11.0.0-alpha - 11 || 12.0.0-alpha - 12 || 13.0.0-alpha - 13 || 14.0.0-alpha - 14 || 15.0.0-alpha - 15 || 16.0.0-alpha - 16" + "bin": { + "http-server": "bin/http-server" + }, + "engines": { + "node": ">=12" } }, - "node_modules/ng-packagr": { - "version": "14.2.2", - "resolved": "https://registry.npmjs.org/ng-packagr/-/ng-packagr-14.2.2.tgz", - "integrity": "sha512-AqwHcMM6x+JkCHT++IsbulnTdyoXcC2Cr4tbPamuieacc77+fFbB195hdcqEFwsKX5410cymx/ZUyHird9rxlg==", + "node_modules/http-server/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "@rollup/plugin-json": "^4.1.0", - "@rollup/plugin-node-resolve": "^13.1.3", - "ajv": "^8.10.0", - "ansi-colors": "^4.1.1", - "browserslist": "^4.20.0", - "cacache": "^16.0.0", - "chokidar": "^3.5.3", - "commander": "^9.0.0", - "dependency-graph": "^0.11.0", - "esbuild-wasm": "^0.15.0", - "find-cache-dir": "^3.3.2", - "glob": "^8.0.0", - "injection-js": "^2.4.0", - "jsonc-parser": "^3.0.0", - "less": "^4.1.2", - "ora": "^5.1.0", - "postcss": "^8.4.8", - "postcss-preset-env": "^7.4.2", - "postcss-url": "^10.1.3", - "rollup": "^2.70.0", - "rollup-plugin-sourcemaps": "^0.6.3", - "rxjs": "^7.5.5", - "sass": "^1.49.9", - "stylus": "^0.59.0" - }, - "bin": { - "ng-packagr": "cli/main.js" + "color-convert": "^2.0.1" }, "engines": { - "node": "^14.15.0 || >=16.10.0" - }, - "optionalDependencies": { - "esbuild": "^0.15.0" + "node": ">=8" }, - "peerDependencies": { - "@angular/compiler-cli": "^14.0.0 || ^14.0.0-next || ^14.2.0-next", - "tslib": "^2.3.0", - "typescript": ">=4.6.2 <4.9" + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/ng-packagr/node_modules/commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "node_modules/http-server/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, "engines": { - "node": "^12.20.0 || >=14" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/ng-packagr/node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "node_modules/http-server/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/ngx-logger": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/ngx-logger/-/ngx-logger-4.3.3.tgz", - "integrity": "sha512-LvHBt0OWIyjwVroecgxmZVDH+9lCYYd3gSiup9wNgtK0a3f+D+h2LCb8p9RI16wqCVFqYe/QChqJA8PGJzZTcw==", - "dependencies": { - "tslib": "^2.0.0", - "vlq": "^1.0.0" + "color-name": "~1.1.4" }, - "peerDependencies": { - "@angular/common": ">6.0.0", - "@angular/core": ">6.0.0", - "rxjs": ">6.0.0" - } - }, - "node_modules/nice-napi": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", - "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "!win32" - ], - "dependencies": { - "node-addon-api": "^3.0.0", - "node-gyp-build": "^4.2.2" + "engines": { + "node": ">=7.0.0" } }, - "node_modules/node-addon-api": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", - "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", - "dev": true, - "optional": true + "node_modules/http-server/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, - "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "node_modules/http-server/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": ">= 6.13.0" + "node": ">=8" } }, - "node_modules/node-gyp": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.0.tgz", - "integrity": "sha512-dMXsYP6gc9rRbejLXmTbVRYjAHw7ppswsKyMxuxJxxOHzluIO1rGp9TOQgjFJ+2MCqcOcQTOPB/8Xwhr+7s4Eg==", + "node_modules/http-server/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "env-paths": "^2.2.0", - "exponential-backoff": "^3.1.1", - "glob": "^7.1.4", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^11.0.3", - "nopt": "^6.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" + "has-flag": "^4.0.0" }, "engines": { - "node": "^12.13 || ^14.13 || >=16" - } - }, - "node_modules/node-gyp-build": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", - "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", - "dev": true, - "optional": true, - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/node-gyp/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, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "node": ">=8" } }, - "node_modules/node-gyp/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", + "node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", "dev": 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" + "agent-base": "^7.0.2", + "debug": "4" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 14" } }, - "node_modules/node-gyp/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, "engines": { - "node": "*" + "node": ">=10.17.0" } }, - "node_modules/node-gyp/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "node_modules/hyperdyperid": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", + "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, "engines": { - "node": ">= 8" + "node": ">=10.18" } }, - "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", - "dev": true - }, - "node_modules/nopt": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", - "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "node_modules/i18next": { + "version": "23.15.1", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.15.1.tgz", + "integrity": "sha512-wB4abZ3uK7EWodYisHl/asf8UYEhrI/vj/8aoSsrj/ZDxj4/UXPOa1KvFt1Fq5hkUHquNqwFlDprmjZ8iySgYA==", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], "dependencies": { - "abbrev": "^1.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "@babel/runtime": "^7.23.2" } }, - "node_modules/normalize-package-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-4.0.1.tgz", - "integrity": "sha512-EBk5QKKuocMJhB3BILuKhmaPjI8vNRSpIfO9woLC6NyHVkKKdVEdAO1mrT0ZfxNR1lKwCcTkuZfmGIFdizZ8Pg==", - "dev": true, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dependencies": { - "hosted-git-info": "^5.0.0", - "is-core-module": "^2.8.1", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" } }, - "node_modules/npm-bundled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", - "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", - "dev": true, - "dependencies": { - "npm-normalize-package-bin": "^1.0.1" - } + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "node_modules/npm-install-checks": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-5.0.0.tgz", - "integrity": "sha512-65lUsMI8ztHCxFz5ckCEC44DRvEGdZX5usQFriauxHEwt7upv1FKaQEmAtU0YnOAdwuNWCmk64xYiQABNrEyLA==", - "dev": true, - "dependencies": { - "semver": "^7.1.1" - }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 4" } }, - "node_modules/npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", - "dev": true - }, - "node_modules/npm-package-arg": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-9.1.0.tgz", - "integrity": "sha512-4J0GL+u2Nh6OnhvUKXRr2ZMG4lR8qtLp+kv7UiV00Y+nGiSxtttCyIRHCt5L5BNkXQld/RceYItau3MDOoGiBw==", + "node_modules/ignore-walk": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.5.tgz", + "integrity": "sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==", "dev": true, "dependencies": { - "hosted-git-info": "^5.0.0", - "proc-log": "^2.0.1", - "semver": "^7.3.5", - "validate-npm-package-name": "^4.0.0" + "minimatch": "^9.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm-packlist": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-5.1.3.tgz", - "integrity": "sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg==", + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", "dev": true, - "dependencies": { - "glob": "^8.0.1", - "ignore-walk": "^5.0.1", - "npm-bundled": "^2.0.0", - "npm-normalize-package-bin": "^2.0.0" - }, + "optional": true, "bin": { - "npm-packlist": "bin/index.js" + "image-size": "bin/image-size.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.10.0" } }, - "node_modules/npm-packlist/node_modules/npm-bundled": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-2.0.1.tgz", - "integrity": "sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==", + "node_modules/immutable": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", + "dev": true + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "dependencies": { - "npm-normalize-package-bin": "^2.0.0" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm-packlist/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", - "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.8.19" } }, - "node_modules/npm-pick-manifest": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-7.0.1.tgz", - "integrity": "sha512-IA8+tuv8KujbsbLQvselW2XQgmXWS47t3CB0ZrzsRZ82DbDfkcFunOaPm4X7qNuhMfq+FmV7hQT4iFVpHqV7mg==", + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true, - "dependencies": { - "npm-install-checks": "^5.0.0", - "npm-normalize-package-bin": "^1.0.1", - "npm-package-arg": "^9.0.0", - "semver": "^7.3.5" - }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=8" } }, - "node_modules/npm-registry-fetch": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-13.3.1.tgz", - "integrity": "sha512-eukJPi++DKRTjSBRcDZSDDsGqRK3ehbxfFUcgaRd0Yp6kRwOwh2WVn0r+8rMB4nnuzvAk6rQVzl6K5CkYOmnvw==", + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, "dependencies": { - "make-fetch-happen": "^10.0.6", - "minipass": "^3.1.6", - "minipass-fetch": "^2.0.3", - "minipass-json-stream": "^1.0.1", - "minizlib": "^2.1.2", - "npm-package-arg": "^9.0.1", - "proc-log": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "once": "^1.3.0", + "wrappy": "1" } }, - "node_modules/npm-registry-fetch/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", "dev": true, "engines": { - "node": ">=12" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm-registry-fetch/node_modules/make-fetch-happen": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", - "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", + "node_modules/injection-js": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/injection-js/-/injection-js-2.4.0.tgz", + "integrity": "sha512-6jiJt0tCAo9zjHbcwLiPL+IuNe9SQ6a9g0PEzafThW3fOQi0mrmiJGBJvDD6tmhPh8cQHIQtCOrJuBfQME4kPA==", "dev": true, "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^16.1.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^2.0.3", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^9.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "tslib": "^2.0.0" } }, - "node_modules/npm-registry-fetch/node_modules/minipass-fetch": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", - "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", + "node_modules/internal-slot": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", "dev": true, "dependencies": { - "minipass": "^3.1.6", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" + "node": ">= 0.4" } }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", "dev": true, "dependencies": { - "path-key": "^3.0.0" + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" }, "engines": { - "node": ">=8" + "node": ">= 12" } }, - "node_modules/npmlog": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", - "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", - "deprecated": "This package is no longer supported.", + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "dev": true, - "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 0.10" } }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "node_modules/is-array-buffer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", "dev": true, "dependencies": { - "boolbase": "^1.0.0" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "dev": true, - "engines": { - "node": "*" + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -13635,15 +13723,26 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object-is": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", - "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1" + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -13651,25 +13750,28 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", "dev": true, "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" + "is-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -13678,185 +13780,184 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true - }, - "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dev": true, "dependencies": { - "ee-first": "1.1.1" + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", "dev": true, + "bin": { + "is-docker": "cli.js" + }, "engines": { - "node": ">= 0.8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, - "dependencies": { - "wrappy": "1" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, "engines": { - "node": ">=6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", "dev": true, "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/opencollective-postinstall": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", - "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, - "bin": { - "opencollective-postinstall": "index.js" + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", "dev": true, "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" }, "engines": { - "node": ">=10" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ora/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/ora/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "dev": true + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ora/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/is-network-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.1.0.tgz", + "integrity": "sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==", "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, "engines": { - "node": ">=7.0.0" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ora/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/ora/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, "engines": { - "node": ">=8" + "node": ">=0.12.0" } }, - "node_modules/ora/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "has-tostringtag": "^1.0.0" }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, "engines": { "node": ">=8" } }, - "node_modules/os-name": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz", - "integrity": "sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==", + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", "dev": true, - "dependencies": { - "macos-release": "^2.5.0", - "windows-release": "^4.0.0" - }, "engines": { "node": ">=10" }, @@ -13864,1161 +13965,996 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, "dependencies": { - "p-try": "^2.0.0" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=6" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "node_modules/is-shared-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "dev": true, "dependencies": { - "p-limit": "^2.2.0" + "call-bind": "^1.0.7" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "dev": true, "dependencies": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/p-retry/node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, "engines": { - "node": ">= 4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", "dev": true, + "dependencies": { + "which-typed-array": "^1.1.14" + }, "engines": { - "node": ">=6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", - "dev": true + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/pacote": { - "version": "13.6.2", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-13.6.2.tgz", - "integrity": "sha512-Gu8fU3GsvOPkak2CkbojR7vjs3k3P9cA6uazKTHdsdV0gpCEQq2opelnEv30KRQWgVzP5Vd/5umjcedma3MKtg==", + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "dev": true, "dependencies": { - "@npmcli/git": "^3.0.0", - "@npmcli/installed-package-contents": "^1.0.7", - "@npmcli/promise-spawn": "^3.0.0", - "@npmcli/run-script": "^4.1.0", - "cacache": "^16.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "infer-owner": "^1.0.4", - "minipass": "^3.1.6", - "mkdirp": "^1.0.4", - "npm-package-arg": "^9.0.0", - "npm-packlist": "^5.1.0", - "npm-pick-manifest": "^7.0.0", - "npm-registry-fetch": "^13.0.1", - "proc-log": "^2.0.0", - "promise-retry": "^2.0.1", - "read-package-json": "^5.0.0", - "read-package-json-fast": "^2.0.3", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", - "tar": "^6.1.11" - }, - "bin": { - "pacote": "lib/bin.js" + "call-bind": "^1.0.2" }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "node_modules/is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", "dev": true }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" + "is-inside-container": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/parse-node-version": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", - "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", "dev": true, "engines": { - "node": ">= 0.10" + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" } }, - "node_modules/parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", - "optional": true + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true }, - "node_modules/parse5-html-rewriting-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz", - "integrity": "sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg==", + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true, - "dependencies": { - "parse5": "^6.0.1", - "parse5-sax-parser": "^6.0.1" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/parse5-html-rewriting-stream/node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", - "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "node_modules/isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", "dev": true, - "dependencies": { - "domhandler": "^5.0.2", - "parse5": "^7.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" + "peerDependencies": { + "ws": "*" } }, - "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, - "dependencies": { - "entities": "^4.4.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" + "engines": { + "node": ">=8" } }, - "node_modules/parse5-sax-parser": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz", - "integrity": "sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==", + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, "dependencies": { - "parse5": "^6.0.1" + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" } }, - "node_modules/parse5-sax-parser/node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, "engines": { - "node": ">= 0.8" + "node": ">=10" } }, - "node_modules/path-browserify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", - "dev": true - }, - "node_modules/path-exists": { + "node_modules/istanbul-lib-report/node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", - "dev": true - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, "engines": { "node": ">=8" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "node_modules/istanbul-lib-source-maps": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", "dev": true, "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" }, "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=6" } }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true + "node_modules/istanbul-lib-source-maps/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, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } }, - "node_modules/path-scurry/node_modules/minipass": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.2.tgz", - "integrity": "sha512-eL79dXrE1q9dBbDCLg7xfn/vl7MS4F1gvJAgjJrQli/jbQWdUttuVawphqpffoIYfRdq78LHx6GP4bU/EQ2ATA==", + "node_modules/istanbul-lib-source-maps/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, + "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": ">=16 || 14 >=14.17" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", - "dev": true - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "node_modules/istanbul-lib-source-maps/node_modules/istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", "dev": true, "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "node_modules/istanbul-lib-source-maps/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", "dev": true, "dependencies": { - "through": "~2.3" + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" } }, - "node_modules/pdfmake": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/pdfmake/-/pdfmake-0.2.10.tgz", - "integrity": "sha512-doipFnmE1UHSk+Z3wfQuVweVQqx2pE/Ns2G5gCqZmWwqjDj+mZHnZYH/ryXWoIfD+iVdZUAutgI/VHkTCN+Xrw==", + "node_modules/istanbul-lib-source-maps/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, "dependencies": { - "@foliojs-fork/linebreak": "^1.1.1", - "@foliojs-fork/pdfkit": "^0.14.0", - "iconv-lite": "^0.6.3", - "xmldoc": "^1.1.2" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=12" + "node": "*" } }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "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==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/istanbul-lib-source-maps/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true, "engines": { - "node": ">=8.6" + "node": ">=6" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "bin": { + "rimraf": "bin.js" } }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "node_modules/istanbul-lib-source-maps/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "engines": { - "node": ">=0.10.0" + "bin": { + "semver": "bin/semver" } }, - "node_modules/pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, "dependencies": { - "pinkie": "^2.0.0" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/piscina": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.2.0.tgz", - "integrity": "sha512-yn/jMdHRw+q2ZJhFhyqsmANcbF6V2QwmD84c6xRau+QpQOmtrBCoRGdvTfeuFDYXB5W2m6MfLkjkvQa9lUSmIA==", + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, "dependencies": { - "eventemitter-asyncresource": "^1.0.0", - "hdr-histogram-js": "^2.0.1", - "hdr-histogram-percentiles-obj": "^3.0.0" + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" }, "optionalDependencies": { - "nice-napi": "^1.0.2" + "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", "dev": true, "dependencies": { - "find-up": "^4.0.0" + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/png-js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz", - "integrity": "sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g==", - "dev": true - }, - "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "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" - } - ], - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "bin": { + "jake": "bin/cli.js" }, "engines": { - "node": "^10 || ^12 || >=14" + "node": ">=10" } }, - "node_modules/postcss-attribute-case-insensitive": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", - "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", + "node_modules/jake/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "postcss-selector-parser": "^6.0.10" + "color-convert": "^2.0.1" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": ">=8" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/postcss-clamp": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", - "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", + "node_modules/jake/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, "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=7.6.0" - }, - "peerDependencies": { - "postcss": "^8.4.6" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/postcss-color-functional-notation": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", - "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", + "node_modules/jake/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { - "postcss-value-parser": "^4.2.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/postcss-color-hex-alpha": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", - "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", + "node_modules/jake/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "postcss-value-parser": "^4.2.0" + "color-name": "~1.1.4" }, "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" + "node": ">=7.0.0" } }, - "node_modules/postcss-color-rebeccapurple": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", - "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", + "node_modules/jake/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jake/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">=8" } }, - "node_modules/postcss-custom-media": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", - "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", + "node_modules/jake/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, "dependencies": { - "postcss-value-parser": "^4.2.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.3" + "node": "*" } }, - "node_modules/postcss-custom-properties": { - "version": "12.1.11", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.11.tgz", - "integrity": "sha512-0IDJYhgU8xDv1KY6+VgUwuQkVtmYzRwu+dMjnmdMafXYv86SWqfxkc7qdDvWS38vsjaEtv8e0vGOUQrAiMBLpQ==", + "node_modules/jake/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "postcss-value-parser": "^4.2.0" + "has-flag": "^4.0.0" }, "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">=8" } }, - "node_modules/postcss-custom-selectors": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", - "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", + "node_modules/jasmine": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-5.3.0.tgz", + "integrity": "sha512-Vrv5VWTXVZ/5xcNawlYCmE24pOaZu3KduLr9iAaENoMJ8W8Ryvhfpw2cf3rI4Unc2ajvu2t4tCKjS72TnraBGQ==", "dev": true, "dependencies": { - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" + "glob": "^10.2.2", + "jasmine-core": "~5.3.0" }, - "peerDependencies": { - "postcss": "^8.3" + "bin": { + "jasmine": "bin/jasmine.js" } }, - "node_modules/postcss-dir-pseudo-class": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", - "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", + "node_modules/jasmine-core": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.3.0.tgz", + "integrity": "sha512-zsOmeBKESky4toybvWEikRiZ0jHoBEu79wNArLfMdSnlLMZx3Xcp6CSm2sUcYyoJC+Uyj8LBJap/MUbVSfJ27g==", + "dev": true + }, + "node_modules/jasmine-spec-reporter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-7.0.0.tgz", + "integrity": "sha512-OtC7JRasiTcjsaCBPtMO0Tl8glCejM4J4/dNuOJdA8lBjz4PmWjYQ6pzb0uzpBNAWJMDudYuj9OdXJWqM2QTJg==", "dev": true, "dependencies": { - "postcss-selector-parser": "^6.0.10" + "colors": "1.4.0" + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/postcss-double-position-gradients": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", - "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", - "dev": true, + "node_modules/jest-diff/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" + "color-convert": "^2.0.1" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": ">=8" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/postcss-env-function": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", - "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", - "dev": true, + "node_modules/jest-diff/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dependencies": { - "postcss-value-parser": "^4.2.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": ">=10" }, - "peerDependencies": { - "postcss": "^8.4" + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/postcss-focus-visible": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", - "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", - "dev": true, + "node_modules/jest-diff/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dependencies": { - "postcss-selector-parser": "^6.0.9" + "color-name": "~1.1.4" }, "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" + "node": ">=7.0.0" } }, - "node_modules/postcss-focus-within": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", - "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.9" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-font-variant": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", - "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", - "dev": true, - "peerDependencies": { - "postcss": "^8.1.0" - } + "node_modules/jest-diff/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/postcss-gap-properties": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", - "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", - "dev": true, + "node_modules/jest-diff/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">=8" } }, - "node_modules/postcss-image-set-function": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", - "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", - "dev": true, + "node_modules/jest-diff/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dependencies": { - "postcss-value-parser": "^4.2.0" + "has-flag": "^4.0.0" }, "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">=8" } }, - "node_modules/postcss-import": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.0.0.tgz", - "integrity": "sha512-Y20shPQ07RitgBGv2zvkEAu9bqvrD77C9axhj/aA1BQj4czape2MdClCExvB27EwYEJdGgKZBpKanb0t1rK2Kg==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/postcss-initial": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", - "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, - "peerDependencies": { - "postcss": "^8.0.0" + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/postcss-lab-function": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", - "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", + "node_modules/jest-util/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" + "color-convert": "^2.0.1" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": ">=8" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/postcss-loader": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.0.1.tgz", - "integrity": "sha512-VRviFEyYlLjctSM93gAZtcJJ/iSkPZ79zWbN/1fSH+NisBByEiVLqpdVDrPLVSi8DX0oJo12kL/GppTBdKVXiQ==", + "node_modules/jest-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { - "cosmiconfig": "^7.0.0", - "klona": "^2.0.5", - "semver": "^7.3.7" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">= 14.15.0" + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "postcss": "^7.0.0 || ^8.0.1", - "webpack": "^5.0.0" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/postcss-loader/node_modules/cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "node_modules/jest-util/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" + "color-name": "~1.1.4" }, "engines": { - "node": ">=10" + "node": ">=7.0.0" } }, - "node_modules/postcss-logical": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", - "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", - "dev": true, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } + "node_modules/jest-util/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, - "node_modules/postcss-media-minmax": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", - "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", + "node_modules/jest-util/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "postcss": "^8.1.0" + "node": ">=8" } }, - "node_modules/postcss-modules-extract-imports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "node_modules/jest-util/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { - "node": "^10 || ^12 || >= 14" + "node": ">=8.6" }, - "peerDependencies": { - "postcss": "^8.1.0" + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz", - "integrity": "sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==", + "node_modules/jest-util/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" + "has-flag": "^4.0.0" }, "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" + "node": ">=8" } }, - "node_modules/postcss-modules-scope": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", - "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, "dependencies": { - "postcss-selector-parser": "^6.0.4" + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" }, "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/postcss-modules-values": { + "node_modules/jest-worker/node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "dependencies": { - "icss-utils": "^5.0.0" - }, "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" + "node": ">=8" } }, - "node_modules/postcss-nesting": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.2.0.tgz", - "integrity": "sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==", + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "dependencies": { - "@csstools/selector-specificity": "^2.0.0", - "postcss-selector-parser": "^6.0.10" + "has-flag": "^4.0.0" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/postcss-opacity-percentage": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.3.tgz", - "integrity": "sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==", + "node_modules/jiti": { + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", "dev": true, - "funding": [ - { - "type": "kofi", - "url": "https://ko-fi.com/mrcgrtz" - }, - { - "type": "liberapay", - "url": "https://liberapay.com/mrcgrtz" - } - ], - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.2" + "bin": { + "jiti": "bin/jiti.js" } }, - "node_modules/postcss-overflow-shorthand": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", - "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" + "argparse": "^2.0.1" }, - "peerDependencies": { - "postcss": "^8.2" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/postcss-page-break": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", - "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", - "dev": true, - "peerDependencies": { - "postcss": "^8" - } + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true }, - "node_modules/postcss-place": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", - "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-preset-env": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.0.tgz", - "integrity": "sha512-leqiqLOellpLKfbHkD06E04P6d9ZQ24mat6hu4NSqun7WG0UhspHR5Myiv/510qouCjoo4+YJtNOqg5xHaFnCA==", - "dev": true, - "dependencies": { - "@csstools/postcss-cascade-layers": "^1.0.5", - "@csstools/postcss-color-function": "^1.1.1", - "@csstools/postcss-font-format-keywords": "^1.0.1", - "@csstools/postcss-hwb-function": "^1.0.2", - "@csstools/postcss-ic-unit": "^1.0.1", - "@csstools/postcss-is-pseudo-class": "^2.0.7", - "@csstools/postcss-nested-calc": "^1.0.0", - "@csstools/postcss-normalize-display-values": "^1.0.1", - "@csstools/postcss-oklab-function": "^1.1.1", - "@csstools/postcss-progressive-custom-properties": "^1.3.0", - "@csstools/postcss-stepped-value-functions": "^1.0.1", - "@csstools/postcss-text-decoration-shorthand": "^1.0.0", - "@csstools/postcss-trigonometric-functions": "^1.0.2", - "@csstools/postcss-unset-value": "^1.0.2", - "autoprefixer": "^10.4.8", - "browserslist": "^4.21.3", - "css-blank-pseudo": "^3.0.3", - "css-has-pseudo": "^3.0.4", - "css-prefers-color-scheme": "^6.0.3", - "cssdb": "^7.0.0", - "postcss-attribute-case-insensitive": "^5.0.2", - "postcss-clamp": "^4.1.0", - "postcss-color-functional-notation": "^4.2.4", - "postcss-color-hex-alpha": "^8.0.4", - "postcss-color-rebeccapurple": "^7.1.1", - "postcss-custom-media": "^8.0.2", - "postcss-custom-properties": "^12.1.8", - "postcss-custom-selectors": "^6.0.3", - "postcss-dir-pseudo-class": "^6.0.5", - "postcss-double-position-gradients": "^3.1.2", - "postcss-env-function": "^4.0.6", - "postcss-focus-visible": "^6.0.4", - "postcss-focus-within": "^5.0.4", - "postcss-font-variant": "^5.0.0", - "postcss-gap-properties": "^3.0.5", - "postcss-image-set-function": "^4.0.7", - "postcss-initial": "^4.0.1", - "postcss-lab-function": "^4.2.1", - "postcss-logical": "^5.0.4", - "postcss-media-minmax": "^5.0.0", - "postcss-nesting": "^10.1.10", - "postcss-opacity-percentage": "^1.1.2", - "postcss-overflow-shorthand": "^3.0.4", - "postcss-page-break": "^3.0.4", - "postcss-place": "^7.0.5", - "postcss-pseudo-class-any-link": "^7.1.6", - "postcss-replace-overflow-wrap": "^4.0.0", - "postcss-selector-not": "^6.0.1", - "postcss-value-parser": "^4.2.0" + "bin": { + "jsesc": "bin/jsesc" }, "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": ">=4" } }, - "node_modules/postcss-pseudo-class-any-link": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", - "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", + "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/postcss-replace-overflow-wrap": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", - "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", - "dev": true, - "peerDependencies": { - "postcss": "^8.0.3" + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" } }, - "node_modules/postcss-selector-not": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", - "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", - "dev": true, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dependencies": { - "postcss-selector-parser": "^6.0.10" + "universalify": "^2.0.0" }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/jsonschema": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz", + "integrity": "sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==", + "dev": true, "engines": { - "node": "^12 || ^14 || >=16" + "node": "*" + } + }, + "node_modules/karma": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.4.tgz", + "integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==", + "dev": true, + "dependencies": { + "@colors/colors": "1.5.0", + "body-parser": "^1.19.0", + "braces": "^3.0.2", + "chokidar": "^3.5.1", + "connect": "^3.7.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.1", + "glob": "^7.1.7", + "graceful-fs": "^4.2.6", + "http-proxy": "^1.18.1", + "isbinaryfile": "^4.0.8", + "lodash": "^4.17.21", + "log4js": "^6.4.1", + "mime": "^2.5.2", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.5", + "qjobs": "^1.2.0", + "range-parser": "^1.2.1", + "rimraf": "^3.0.2", + "socket.io": "^4.7.2", + "source-map": "^0.6.1", + "tmp": "^0.2.1", + "ua-parser-js": "^0.7.30", + "yargs": "^16.1.1" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" + "bin": { + "karma": "bin/karma" }, - "peerDependencies": { - "postcss": "^8.2" + "engines": { + "node": ">= 10" } }, - "node_modules/postcss-selector-parser": { - "version": "6.0.13", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", - "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "node_modules/karma-chrome-launcher": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.2.0.tgz", + "integrity": "sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q==", "dev": true, "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" + "which": "^1.2.1" + } + }, + "node_modules/karma-chrome-launcher/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" }, - "engines": { - "node": ">=4" + "bin": { + "which": "bin/which" } }, - "node_modules/postcss-url": { - "version": "10.1.3", - "resolved": "https://registry.npmjs.org/postcss-url/-/postcss-url-10.1.3.tgz", - "integrity": "sha512-FUzyxfI5l2tKmXdYc6VTu3TWZsInayEKPbiyW+P6vmmIrrb4I6CGX0BFoewgYHLK+oIL5FECEK02REYRpBvUCw==", + "node_modules/karma-cli": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/karma-cli/-/karma-cli-2.0.0.tgz", + "integrity": "sha512-1Kb28UILg1ZsfqQmeELbPzuEb5C6GZJfVIk0qOr8LNYQuYWmAaqP16WpbpKEjhejDrDYyYOwwJXSZO6u7q5Pvw==", "dev": true, "dependencies": { - "make-dir": "~3.1.0", - "mime": "~2.5.2", - "minimatch": "~3.0.4", - "xxhashjs": "~0.2.2" + "resolve": "^1.3.3" + }, + "bin": { + "karma": "bin/karma" }, "engines": { - "node": ">=10" + "node": ">= 6" + } + }, + "node_modules/karma-coverage-istanbul-reporter": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-3.0.3.tgz", + "integrity": "sha512-wE4VFhG/QZv2Y4CdAYWDbMmcAHeS926ZIji4z+FkB2aF/EposRb6DP6G5ncT/wXhqUfAb/d7kZrNKPonbvsATw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^3.0.6", + "istanbul-reports": "^3.0.2", + "minimatch": "^3.0.4" }, - "peerDependencies": { - "postcss": "^8.0.0" + "funding": { + "url": "https://github.com/sponsors/mattlewis92" } }, - "node_modules/postcss-url/node_modules/brace-expansion": { + "node_modules/karma-coverage-istanbul-reporter/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==", @@ -15028,22 +14964,10 @@ "concat-map": "0.0.1" } }, - "node_modules/postcss-url/node_modules/mime": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", - "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", - "dev": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/postcss-url/node_modules/minimatch": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", - "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "node_modules/karma-coverage-istanbul-reporter/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, "dependencies": { "brace-expansion": "^1.1.7" @@ -15052,136 +14976,101 @@ "node": "*" } }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true - }, - "node_modules/postinstall-build": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postinstall-build/-/postinstall-build-5.0.3.tgz", - "integrity": "sha512-vPvPe8TKgp4FLgY3+DfxCE5PIfoXBK2lyLfNCxsRbDsV6vS4oU5RG/IWxrblMn6heagbnMED3MemUQllQ2bQUg==", - "deprecated": "postinstall-build's behavior is now built into npm! You should migrate off of postinstall-build and use the new `prepare` lifecycle script with npm 5.0.0 or greater.", - "dev": true, - "bin": { - "postinstall-build": "cli.js" - } - }, - "node_modules/pretty-bytes": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "node_modules/karma-ie-launcher": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/karma-ie-launcher/-/karma-ie-launcher-1.0.0.tgz", + "integrity": "sha512-ts71ke8pHvw6qdRtq0+7VY3ANLoZuUNNkA8abRaWV13QRPNm7TtSOqyszjHUtuwOWKcsSz4tbUtrNICrQC+SXQ==", "dev": true, - "engines": { - "node": ">=6" + "dependencies": { + "lodash": "^4.6.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "karma": ">=0.9" } }, - "node_modules/prismjs": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", - "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", + "node_modules/karma-jasmine": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-5.1.0.tgz", + "integrity": "sha512-i/zQLFrfEpRyQoJF9fsCdTMOF5c2dK7C7OmsuKg2D0YSsuZSfQDiLuaiktbuio6F2wiCsZSnSnieIQ0ant/uzQ==", "dev": true, + "dependencies": { + "jasmine-core": "^4.1.0" + }, "engines": { - "node": ">=6" + "node": ">=12" + }, + "peerDependencies": { + "karma": "^6.0.0" } }, - "node_modules/proc-log": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz", - "integrity": "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==", + "node_modules/karma-jasmine-html-reporter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-2.1.0.tgz", + "integrity": "sha512-sPQE1+nlsn6Hwb5t+HHwyy0A1FNCVKuL1192b+XNauMYWThz2kweiBVW1DqloRpVvZIJkIoHVB7XRpK78n1xbQ==", "dev": true, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "peerDependencies": { + "jasmine-core": "^4.0.0 || ^5.0.0", + "karma": "^6.0.0", + "karma-jasmine": "^5.0.0" } }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "node_modules/karma-jasmine/node_modules/jasmine-core": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.6.1.tgz", + "integrity": "sha512-VYz/BjjmC3klLJlLwA4Kw8ytk0zDSmbbDLNs794VnWmkcCB7I9aAL/D48VNQtmITyPvea2C3jdUMfc3kAoy0PQ==", "dev": true }, - "node_modules/promise-retry": { + "node_modules/karma-junit-reporter": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "resolved": "https://registry.npmjs.org/karma-junit-reporter/-/karma-junit-reporter-2.0.1.tgz", + "integrity": "sha512-VtcGfE0JE4OE1wn0LK8xxDKaTP7slN8DO3I+4xg6gAi1IoAHAXOJ1V9G/y45Xg6sxdxPOR3THCFtDlAfBo9Afw==", "dev": true, "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" + "path-is-absolute": "^1.0.0", + "xmlbuilder": "12.0.0" }, "engines": { - "node": ">=10" + "node": ">= 8" + }, + "peerDependencies": { + "karma": ">=0.9" } }, - "node_modules/propagating-hammerjs": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/propagating-hammerjs/-/propagating-hammerjs-1.5.0.tgz", - "integrity": "sha512-3PUXWmomwutoZfydC+lJwK1bKCh6sK6jZGB31RUX6+4EXzsbkDZrK4/sVR7gBrvJaEIwpTVyxQUAd29FKkmVdw==", + "node_modules/karma-source-map-support": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", + "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", "dev": true, "dependencies": { - "hammerjs": "^2.0.8" - } - }, - "node_modules/protractor": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/protractor/-/protractor-7.0.0.tgz", - "integrity": "sha512-UqkFjivi4GcvUQYzqGYNe0mLzfn5jiLmO8w9nMhQoJRLhy2grJonpga2IWhI6yJO30LibWXJJtA4MOIZD2GgZw==", - "deprecated": "We have news to share - Protractor is deprecated and will reach end-of-life by Summer 2023. To learn more and find out about other options please refer to this post on the Angular blog. Thank you for using and contributing to Protractor. https://goo.gle/state-of-e2e-in-angular", - "dev": true, - "dependencies": { - "@types/q": "^0.0.32", - "@types/selenium-webdriver": "^3.0.0", - "blocking-proxy": "^1.0.0", - "browserstack": "^1.5.1", - "chalk": "^1.1.3", - "glob": "^7.0.3", - "jasmine": "2.8.0", - "jasminewd2": "^2.1.0", - "q": "1.4.1", - "saucelabs": "^1.5.0", - "selenium-webdriver": "3.6.0", - "source-map-support": "~0.4.0", - "webdriver-js-extender": "2.1.0", - "webdriver-manager": "^12.1.7", - "yargs": "^15.3.1" - }, - "bin": { - "protractor": "bin/protractor", - "webdriver-manager": "bin/webdriver-manager" - }, - "engines": { - "node": ">=10.13.x" + "source-map-support": "^0.5.5" } }, - "node_modules/protractor/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "node_modules/karma-viewport": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/karma-viewport/-/karma-viewport-1.0.9.tgz", + "integrity": "sha512-E1xVe66vBQtI66TGOtZMzV5nf6BW5tW4TQVUqPK+oakVLdsG/ZUG688tGK0lL1q0t7nfQD1dwLD8Z9Guu/RVdg==", "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "@types/karma": "^6.3.3", + "jsonschema": "^1.4.0" } }, - "node_modules/protractor/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "node_modules/karma/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/protractor/node_modules/brace-expansion": { + "node_modules/karma/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==", @@ -15191,55 +15080,18 @@ "concat-map": "0.0.1" } }, - "node_modules/protractor/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "node_modules/karma/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, - "node_modules/protractor/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/protractor/node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/protractor/node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/protractor/node_modules/color-convert": { + "node_modules/karma/node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", @@ -15251,13 +15103,19 @@ "node": ">=7.0.0" } }, - "node_modules/protractor/node_modules/color-name": { + "node_modules/karma/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/protractor/node_modules/glob": { + "node_modules/karma/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/karma/node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", @@ -15278,27 +15136,28 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/protractor/node_modules/jasmine": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz", - "integrity": "sha512-KbdGQTf5jbZgltoHs31XGiChAPumMSY64OZMWLNYnEnMfG5uwGBhffePwuskexjT+/Jea/gU3qAU8344hNohSw==", + "node_modules/karma/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, - "dependencies": { - "exit": "^0.1.2", - "glob": "^7.0.6", - "jasmine-core": "~2.8.0" - }, - "bin": { - "jasmine": "bin/jasmine.js" + "engines": { + "node": ">=8" } }, - "node_modules/protractor/node_modules/jasmine-core": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", - "integrity": "sha512-SNkOkS+/jMZvLhuSx1fjhcNWUC/KG6oVyFUGkSBEr9n1axSNduWU8GlI7suaHXr4yxjet6KjrUZxUTE5WzzWwQ==", - "dev": true + "node_modules/karma/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } }, - "node_modules/protractor/node_modules/minimatch": { + "node_modules/karma/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", @@ -15310,49 +15169,33 @@ "node": "*" } }, - "node_modules/protractor/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "node_modules/karma/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/protractor/node_modules/source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", - "dev": true, - "dependencies": { - "source-map": "^0.5.6" - } - }, - "node_modules/protractor/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "node_modules/karma/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { - "ansi-regex": "^2.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/protractor/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", - "dev": true, - "engines": { - "node": ">=0.8.0" + "node": ">=8" } }, - "node_modules/protractor/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "node_modules/karma/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "dependencies": { "ansi-styles": "^4.0.0", @@ -15360,17917 +15203,8041 @@ "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/protractor/node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/protractor/node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/karma/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=10" } }, - "node_modules/protractor/node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/karma/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/protractor/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "node_modules/keycharm": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/keycharm/-/keycharm-0.2.0.tgz", + "integrity": "sha512-i/XBRTiLqRConPKioy2oq45vbv04e8x59b0mnsIRQM+7Ec/8BC7UcL5pnC4FMeGb8KwG7q4wOMw7CtNZf5tiIg==", "dev": true }, - "node_modules/protractor/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "node_modules/keygrip": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", + "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", "dev": true, "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" + "tsscmp": "1.0.6" }, "engines": { - "node": ">=8" + "node": ">= 0.6" } }, - "node_modules/protractor/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" + "json-buffer": "3.0.1" } }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, "engines": { - "node": ">= 0.10" + "node": ">=0.10.0" } }, - "node_modules/proxy-addr/node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", "dev": true, "engines": { - "node": ">= 0.10" + "node": ">= 8" } }, - "node_modules/prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "node_modules/koa": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/koa/-/koa-2.15.3.tgz", + "integrity": "sha512-j/8tY9j5t+GVMLeioLaxweJiKUayFhlGqNTzf2ZGwL0ZCQijd2RLHK0SLW5Tsko8YyyqCZC2cojIb0/s62qTAg==", "dev": true, - "optional": true - }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dependencies": { + "accepts": "^1.3.5", + "cache-content-type": "^1.0.0", + "content-disposition": "~0.5.2", + "content-type": "^1.0.4", + "cookies": "~0.9.0", + "debug": "^4.3.2", + "delegates": "^1.0.0", + "depd": "^2.0.0", + "destroy": "^1.0.4", + "encodeurl": "^1.0.2", + "escape-html": "^1.0.3", + "fresh": "~0.5.2", + "http-assert": "^1.3.0", + "http-errors": "^1.6.3", + "is-generator-function": "^1.0.7", + "koa-compose": "^4.1.0", + "koa-convert": "^2.0.0", + "on-finished": "^2.3.0", + "only": "~0.0.2", + "parseurl": "^1.3.2", + "statuses": "^1.5.0", + "type-is": "^1.6.16", + "vary": "^1.1.2" + }, + "engines": { + "node": "^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4" + } + }, + "node_modules/koa-compose": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz", + "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==", "dev": true }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "node_modules/koa-convert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/koa-convert/-/koa-convert-2.0.0.tgz", + "integrity": "sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA==", "dev": true, "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "co": "^4.6.0", + "koa-compose": "^4.1.0" + }, + "engines": { + "node": ">= 10" } }, - "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "node_modules/koa/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": ">=6" + "node": ">= 0.8" } }, - "node_modules/q": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", - "integrity": "sha512-/CdEdaw49VZVmyIDGUQKDDT53c7qBkO6g5CefWz91Ae+l4+cRtcDYwMTXh6me4O8TMldeGHG3N2Bl84V78Ywbg==", - "deprecated": "You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\n\n(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)", + "node_modules/koa/node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" + "node": ">= 0.6" } }, - "node_modules/qjobs": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "node_modules/koa/node_modules/http-errors/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", "dev": true, "engines": { - "node": ">=0.9" + "node": ">= 0.6" } }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "node_modules/koa/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", "dev": true, - "dependencies": { - "side-channel": "^1.0.4" - }, "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.6" } }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "node_modules/launch-editor": { + "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, "dependencies": { - "safe-buffer": "^5.1.0" + "picocolors": "^1.0.0", + "shell-quote": "^1.8.1" } }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "node_modules/less": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/less/-/less-4.2.0.tgz", + "integrity": "sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==", "dev": true, + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^2.3.0" + }, + "bin": { + "lessc": "bin/lessc" + }, "engines": { - "node": ">= 0.6" + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "source-map": "~0.6.0" } }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "node_modules/less-loader": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-12.2.0.tgz", + "integrity": "sha512-MYUxjSQSBUQmowc0l5nPieOYwMzGPUaTzB6inNW/bdPEG9zOL3eAAD1Qw5ZxSPk7we5dMojHwNODYMV1hq4EVg==", "dev": true, - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, "engines": { - "node": ">= 0.8" + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "less": "^3.5.0 || ^4.0.0", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } } }, - "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "node_modules/less/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", "dev": true, + "optional": true, "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "pify": "^4.0.1", + "semver": "^5.6.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "node_modules/less/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true, - "dependencies": { - "pify": "^2.3.0" + "optional": true, + "engines": { + "node": ">=6" } }, - "node_modules/read-package-json": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-5.0.2.tgz", - "integrity": "sha512-BSzugrt4kQ/Z0krro8zhTwV1Kd79ue25IhNN/VtHFy1mG/6Tluyi+msc0UpwaoQzxSHa28mntAjIZY6kEgfR9Q==", - "deprecated": "This package is no longer supported. Please use @npmcli/package-json instead.", + "node_modules/less/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/less/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "dependencies": { - "glob": "^8.0.1", - "json-parse-even-better-errors": "^2.3.1", - "normalize-package-data": "^4.0.0", - "npm-normalize-package-bin": "^2.0.0" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 0.8.0" } }, - "node_modules/read-package-json-fast": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz", - "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==", + "node_modules/license-webpack-plugin": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", + "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==", "dev": true, "dependencies": { - "json-parse-even-better-errors": "^2.3.0", - "npm-normalize-package-bin": "^1.0.1" + "webpack-sources": "^3.0.0" }, - "engines": { - "node": ">=10" + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-sources": { + "optional": true + } } }, - "node_modules/read-package-json/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", - "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", + "node_modules/lilconfig": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, - "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==", + "node_modules/lines-and-columns": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.3.tgz", + "integrity": "sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/listr2": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.4.tgz", + "integrity": "sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==", "dev": true, "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" }, "engines": { - "node": ">= 6" + "node": ">=18.0.0" } }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "node_modules/listr2/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, - "dependencies": { - "picomatch": "^2.2.1" + "engines": { + "node": ">=12" }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/listr2/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, "engines": { - "node": ">=8.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/reflect-metadata": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "node_modules/listr2/node_modules/emoji-regex": { + "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 }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "node_modules/listr2/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", "dev": true }, - "node_modules/regenerate-unicode-properties": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", - "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "node_modules/listr2/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "dependencies": { - "regenerate": "^1.4.2" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=4" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", - "dev": true - }, - "node_modules/regenerator-transform": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", - "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.8.4" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/regex-parser": { - "version": "2.2.11", - "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", - "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==", - "dev": true - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "node_modules/listr2/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.6", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", "dev": true, "dependencies": { - "@babel/regjsgen": "^0.8.0", - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=4" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "node_modules/lmdb": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.0.13.tgz", + "integrity": "sha512-UGe+BbaSUQtAMZobTb4nHvFMrmvuAQKSeaqAX2meTEQjfsbpl5sxdHD8T72OnwD4GU9uwNhYXIVe4QGs8N9Zyw==", "dev": true, + "hasInstallScript": true, "dependencies": { - "jsesc": "~0.5.0" + "msgpackr": "^1.10.2", + "node-addon-api": "^6.1.0", + "node-gyp-build-optional-packages": "5.2.2", + "ordered-binary": "^1.4.1", + "weak-lru-cache": "^1.2.2" }, "bin": { - "regjsparser": "bin/parser" + "download-lmdb-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@lmdb/lmdb-darwin-arm64": "3.0.13", + "@lmdb/lmdb-darwin-x64": "3.0.13", + "@lmdb/lmdb-linux-arm": "3.0.13", + "@lmdb/lmdb-linux-arm64": "3.0.13", + "@lmdb/lmdb-linux-x64": "3.0.13", + "@lmdb/lmdb-win32-x64": "3.0.13" } }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true, - "bin": { - "jsesc": "bin/jsesc" - } - }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "dev": true, - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, "engines": { - "node": ">= 6" + "node": ">=6.11.5" } }, - "node_modules/request/node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "node_modules/loader-utils": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz", + "integrity": "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==", "dev": true, "engines": { - "node": ">=0.6" + "node": ">= 12.13.0" } }, - "node_modules/request/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "dev": true, - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "node_modules/lodash.clamp": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/lodash.clamp/-/lodash.clamp-4.0.3.tgz", + "integrity": "sha512-HvzRFWjtcguTW7yd8NJBshuNaCa8aqNFtnswdT7f/cMd/1YKy5Zzoq4W/Oxvnx9l7aeY258uSdDfM793+eLsVg==", + "optional": true, + "peer": true + }, + "node_modules/lodash.clonedeepwith": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeepwith/-/lodash.clonedeepwith-4.5.0.tgz", + "integrity": "sha512-QRBRSxhbtsX1nc0baxSkkK5WlVTTm/s48DSukcGcWZwIyI8Zz+lB+kFiELJXtzfH4Aj6kMWQ1VWW4U5uUDgZMA==", "dev": true }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "devOptional": true + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "optional": true + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dev": true, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" }, - "bin": { - "resolve": "bin/resolve" + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/resolve-url-loader": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", - "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", - "dev": true, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dependencies": { - "adjust-sourcemap-loader": "^4.0.0", - "convert-source-map": "^1.7.0", - "loader-utils": "^2.0.0", - "postcss": "^8.2.14", - "source-map": "0.6.1" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=12" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/resolve-url-loader/node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" + "color-name": "~1.1.4" }, "engines": { - "node": ">=8.9.0" + "node": ">=7.0.0" } }, - "node_modules/resolve-url-loader/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" + "has-flag": "^4.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", "dev": true, + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, "engines": { - "node": ">= 4" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "node_modules/log-update/node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", "dev": true, + "dependencies": { + "environment": "^1.0.0" + }, "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", - "dev": true - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" + "engines": { + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/rimraf/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==", + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/rimraf/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", + "node_modules/log-update/node_modules/emoji-regex": { + "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 + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", "dev": 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" + "get-east-asian-width": "^1.0.0" }, "engines": { - "node": "*" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" }, "engines": { - "node": "*" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/rollup": { - "version": "2.79.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", - "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "node_modules/log-update/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, - "bin": { - "rollup": "dist/bin/rollup" + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=10.0.0" + "node": ">=18" }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/rollup-plugin-sourcemaps": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/rollup-plugin-sourcemaps/-/rollup-plugin-sourcemaps-0.6.3.tgz", - "integrity": "sha512-paFu+nT1xvuO1tPFYXGe+XnQvg4Hjqv/eIhG8i5EspfYYPBKL57X7iVbfv55aNVASg3dzWvES9dmWsL2KhfByw==", + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "dependencies": { - "@rollup/pluginutils": "^3.0.9", - "source-map-resolve": "^0.6.0" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=10.0.0" + "node": ">=12" }, - "peerDependencies": { - "@types/node": ">=10.0.0", - "rollup": ">=0.31.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", "dev": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, "engines": { - "node": ">=0.12.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "node_modules/log4js": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", + "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rw": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" - }, - "node_modules/rxjs": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", - "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==", "dependencies": { - "tslib": "^1.9.0" + "date-format": "^4.0.14", + "debug": "^4.3.4", + "flatted": "^3.2.7", + "rfdc": "^1.3.0", + "streamroller": "^3.1.5" }, "engines": { - "npm": ">=2.0.0" + "node": ">=8.0" } }, - "node_modules/rxjs/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/safe-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "node_modules/loglevel": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz", + "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==", "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, "engines": { - "node": ">=0.4" + "node": ">= 0.6.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" } }, - "node_modules/safe-array-concat/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "node_modules/loglevel-plugin-prefix": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/loglevel-plugin-prefix/-/loglevel-plugin-prefix-0.8.4.tgz", + "integrity": "sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g==", "dev": true }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "node_modules/long-timeout": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz", + "integrity": "sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==", "dev": true }, - "node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-regex": "^1.1.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "yallist": "^3.0.2" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true }, - "node_modules/sass": { - "version": "1.54.4", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.54.4.tgz", - "integrity": "sha512-3tmF16yvnBwtlPrNBHw/H907j8MlOX8aTBnlNX1yrKx24RKcJGPyLhFUwkoKBKesR3unP93/2z14Ll8NicwQUA==", + "node_modules/luxon": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", "dev": true, - "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": ">=12.0.0" + "node": ">=12" } }, - "node_modules/sass-loader": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.0.2.tgz", - "integrity": "sha512-BbiqbVmbfJaWVeOOAu2o7DhYWtcNmTfvroVgFXa6k2hHheMxNAeDHLNoDy/Q5aoaVlz0LH+MbMktKwm9vN/j8Q==", + "node_modules/macos-release": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.1.tgz", + "integrity": "sha512-DXqXhEM7gW59OjZO8NIjBCz9AQ1BEMrfiOAl4AYByHCtVHRF4KoGNO8mqQeM8lRCtQe/UnJ4imO/d2HdkKsd+A==", "dev": true, - "dependencies": { - "klona": "^2.0.4", - "neo-async": "^2.6.2" - }, "engines": { - "node": ">= 14.15.0" + "node": ">=6" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "fibers": ">= 3.1.0", - "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", - "sass": "^1.3.0", - "sass-embedded": "*", - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "fibers": { - "optional": true - }, - "node-sass": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - } + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/saucelabs": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz", - "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==", + "node_modules/magic-string": { + "version": "0.30.11", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", "dev": true, "dependencies": { - "https-proxy-agent": "^2.2.1" - }, - "engines": { - "node": "*" + "@jridgewell/sourcemap-codec": "^1.5.0" } }, - "node_modules/saucelabs/node_modules/agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, "dependencies": { - "es6-promisify": "^5.0.0" + "semver": "^6.0.0" }, "engines": { - "node": ">= 4.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/saucelabs/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "dependencies": { - "ms": "^2.1.1" + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/saucelabs/node_modules/https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/make-fetch-happen": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz", + "integrity": "sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==", "dev": true, "dependencies": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", + "http-cache-semantics": "^4.1.1", + "is-lambda": "^1.0.1", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1", + "ssri": "^10.0.0" }, "engines": { - "node": ">= 4.5.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "node_modules/map-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", + "integrity": "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==", "dev": true }, - "node_modules/schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "node_modules/marked": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-7.0.3.tgz", + "integrity": "sha512-ev2uM40p0zQ/GbvqotfKcSWEa59fJwluGZj5dcaUOwDRrB1F3dncdXy8NWUApk4fi8atU3kTBOwjyjZ0ud0dxw==", "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" + "bin": { + "marked": "bin/marked.js" }, "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">= 16" } }, - "node_modules/schema-utils/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/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true }, - "node_modules/schema-utils/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==", + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" + "engines": { + "node": ">= 0.6" } }, - "node_modules/schema-utils/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/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "dev": true, + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } }, - "node_modules/select-hose": { + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, - "node_modules/selenium-webdriver": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", - "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==", + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "dependencies": { - "jszip": "^3.1.3", - "rimraf": "^2.5.4", - "tmp": "0.0.30", - "xml2js": "^0.4.17" + "braces": "^3.0.3", + "picomatch": "^2.3.1" }, "engines": { - "node": ">= 6.9.0" + "node": ">=8.6" } }, - "node_modules/selenium-webdriver/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==", + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/selenium-webdriver/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", + "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/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "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" + "mime-db": "1.52.0" }, "engines": { - "node": "*" + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "engines": { + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/selenium-webdriver/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/mini-css-extract-plugin": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz", + "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "schema-utils": "^4.0.0", + "tapable": "^2.2.1" }, "engines": { - "node": "*" + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" } }, - "node_modules/selenium-webdriver/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dependencies": { - "glob": "^7.1.3" + "brace-expansion": "^2.0.1" }, - "bin": { - "rimraf": "bin.js" + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" } }, - "node_modules/selenium-webdriver/node_modules/tmp": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", - "integrity": "sha512-HXdTB7lvMwcb55XFfrTM8CPr/IYREk4hVBFaQ4b/6nInrluSL86hfHm7vu0luYKCfyBZp2trCjpc8caC3vVM3w==", + "node_modules/minipass-collect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", + "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", "dev": true, "dependencies": { - "os-tmpdir": "~1.0.1" + "minipass": "^7.0.3" }, "engines": { - "node": ">=0.4.0" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/selfsigned": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", - "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", + "node_modules/minipass-fetch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", + "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", "dev": true, "dependencies": { - "@types/node-forge": "^1.3.0", - "node-forge": "^1" + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" }, "engines": { - "node": ">=10" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" } }, - "node_modules/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", "dev": true, "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "minipass": "^3.0.0" }, "engines": { - "node": ">=10" + "node": ">= 8" } }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, "dependencies": { "yallist": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/semver/node_modules/yallist": { + "node_modules/minipass-flush/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", "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" + "minipass": "^3.0.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">=8" } }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, "dependencies": { - "ms": "2.0.0" + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "node_modules/send/node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", "dev": true, - "bin": { - "mime": "cli.js" + "dependencies": { + "minipass": "^3.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/send/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/send/node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, "dependencies": { - "ee-first": "1.1.1" + "yallist": "^4.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">=8" } }, - "node_modules/send/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } + "node_modules/minipass-sized/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, - "node_modules/serialize-javascript": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", - "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "dev": true, "dependencies": { - "randombytes": "^2.1.0" + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, "dependencies": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" + "yallist": "^4.0.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">=8" } }, - "node_modules/serve-index/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "dependencies": { - "ms": "2.0.0" + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" } }, - "node_modules/serve-index/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "dev": true, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", "engines": { - "node": ">= 0.6" + "node": "*" } }, - "node_modules/serve-index/node_modules/http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", - "dev": true, + "node_modules/moment-timezone": { + "version": "0.5.45", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.45.tgz", + "integrity": "sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==", "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" + "moment": "^2.29.4" }, "engines": { - "node": ">= 0.6" + "node": "*" } }, - "node_modules/serve-index/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", - "dev": true - }, - "node_modules/serve-index/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/serve-index/node_modules/setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - }, - "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "node_modules/morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", "dev": true, "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" }, "engines": { "node": ">= 0.8.0" } }, - "node_modules/set-blocking": { + "node_modules/morgan/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/morgan/node_modules/ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "node_modules/morgan/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", "dev": true, "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" + "ee-first": "1.1.1" }, "engines": { - "node": ">= 0.4" + "node": ">= 0.8" } }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "node_modules/mrmime": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", "dev": true, - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, "engines": { - "node": ">= 0.4" + "node": ">=10" } }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "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/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true + "node_modules/msgpackr": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.0.tgz", + "integrity": "sha512-I8qXuuALqJe5laEBYoFykChhSXLikZmUhccjGsPuSJ/7uPip2TJ7lwdIQwWSAi0jGZDXv4WOP8Qg65QZRuXxXw==", + "dev": true, + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" + } }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "node_modules/msgpackr-extract": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz", + "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", "dev": true, + "hasInstallScript": true, + "optional": true, "dependencies": { - "kind-of": "^6.0.2" + "node-gyp-build-optional-packages": "5.2.2" }, - "engines": { - "node": ">=8" + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" } }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "node_modules/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", "dev": true, "dependencies": { - "shebang-regex": "^3.0.0" + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" }, - "engines": { - "node": ">=8" + "bin": { + "multicast-dns": "cli.js" } }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "node_modules/mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", "dev": true, "engines": { - "node": ">=8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "node_modules/needle": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", + "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", "dev": true, - "engines": { - "node": ">=12" + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" } }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "dev": true, "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" + "node": ">= 0.6" } }, - "node_modules/smooth-scrollbar": { - "version": "8.8.4", - "resolved": "https://registry.npmjs.org/smooth-scrollbar/-/smooth-scrollbar-8.8.4.tgz", - "integrity": "sha512-Wn2Q/iE+PE1ef4EfhFMhgaIMfSrjJl2LalRu0hBWSrw5nZKGsUIjWFWHnLB/Lpx7N+a1pJH6+wkQzBr+5nWPUg==", - "dependencies": { - "core-js": "^3.6.4", - "tslib": "^1.10.0" - } + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true }, - "node_modules/smooth-scrollbar/node_modules/core-js": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.0.tgz", - "integrity": "sha512-rd4rYZNlF3WuoYuRIDEmbR/ga9CeuWX9U05umAvgrrZoHY4Z++cp/xwPQMvUpBB4Ag6J8KfD80G0zwCyaSxDww==", - "hasInstallScript": true, + "node_modules/ng-mocks": { + "version": "14.13.1", + "resolved": "https://registry.npmjs.org/ng-mocks/-/ng-mocks-14.13.1.tgz", + "integrity": "sha512-eyfnjXeC108SqVD09i/cBwCpKkK0JjBoAg8jp7oQS2HS081K3WJTttFpgLGeLDYKmZsZ6nYpI+HHNQ3OksaJ7A==", + "dev": true, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" + "url": "https://github.com/sponsors/help-me-mom" + }, + "peerDependencies": { + "@angular/common": "5.0.0-alpha - 5 || 6.0.0-alpha - 6 || 7.0.0-alpha - 7 || 8.0.0-alpha - 8 || 9.0.0-alpha - 9 || 10.0.0-alpha - 10 || 11.0.0-alpha - 11 || 12.0.0-alpha - 12 || 13.0.0-alpha - 13 || 14.0.0-alpha - 14 || 15.0.0-alpha - 15 || 16.0.0-alpha - 16 || 17.0.0-alpha - 17 || 18.0.0-alpha - 18", + "@angular/core": "5.0.0-alpha - 5 || 6.0.0-alpha - 6 || 7.0.0-alpha - 7 || 8.0.0-alpha - 8 || 9.0.0-alpha - 9 || 10.0.0-alpha - 10 || 11.0.0-alpha - 11 || 12.0.0-alpha - 12 || 13.0.0-alpha - 13 || 14.0.0-alpha - 14 || 15.0.0-alpha - 15 || 16.0.0-alpha - 16 || 17.0.0-alpha - 17 || 18.0.0-alpha - 18", + "@angular/forms": "5.0.0-alpha - 5 || 6.0.0-alpha - 6 || 7.0.0-alpha - 7 || 8.0.0-alpha - 8 || 9.0.0-alpha - 9 || 10.0.0-alpha - 10 || 11.0.0-alpha - 11 || 12.0.0-alpha - 12 || 13.0.0-alpha - 13 || 14.0.0-alpha - 14 || 15.0.0-alpha - 15 || 16.0.0-alpha - 16 || 17.0.0-alpha - 17 || 18.0.0-alpha - 18", + "@angular/platform-browser": "5.0.0-alpha - 5 || 6.0.0-alpha - 6 || 7.0.0-alpha - 7 || 8.0.0-alpha - 8 || 9.0.0-alpha - 9 || 10.0.0-alpha - 10 || 11.0.0-alpha - 11 || 12.0.0-alpha - 12 || 13.0.0-alpha - 13 || 14.0.0-alpha - 14 || 15.0.0-alpha - 15 || 16.0.0-alpha - 16 || 17.0.0-alpha - 17 || 18.0.0-alpha - 18" } }, - "node_modules/smooth-scrollbar/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/socket.io": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.2.tgz", - "integrity": "sha512-bvKVS29/I5fl2FGLNHuXlQaUH/BlzX1IN6S+NKLNZpBsPZIDH+90eQmCs2Railn4YUiww4SzUedJ6+uzwFnKLw==", + "node_modules/ng-packagr": { + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/ng-packagr/-/ng-packagr-18.2.1.tgz", + "integrity": "sha512-dy9ZDpZb3QpAz+Y/m8VAu7ctr2VrnRU3gmQwJagnNybVJtCsKn3lZA3IW7Z7GTLoG5IALSPouiCgiB/C8ozv7w==", "dev": true, "dependencies": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "cors": "~2.8.5", - "debug": "~4.3.2", - "engine.io": "~6.5.2", - "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.4" + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-node-resolve": "^15.2.3", + "@rollup/wasm-node": "^4.18.0", + "ajv": "^8.12.0", + "ansi-colors": "^4.1.3", + "browserslist": "^4.22.1", + "cacache": "^18.0.0", + "chokidar": "^3.5.3", + "commander": "^12.0.0", + "convert-source-map": "^2.0.0", + "dependency-graph": "^1.0.0", + "esbuild": "^0.23.0", + "fast-glob": "^3.3.1", + "find-cache-dir": "^3.3.2", + "injection-js": "^2.4.0", + "jsonc-parser": "^3.2.0", + "less": "^4.2.0", + "ora": "^5.1.0", + "piscina": "^4.4.0", + "postcss": "^8.4.31", + "rxjs": "^7.8.1", + "sass": "^1.69.5" + }, + "bin": { + "ng-packagr": "cli/main.js" }, "engines": { - "node": ">=10.2.0" + "node": "^18.19.1 || >=20.11.1" + }, + "optionalDependencies": { + "rollup": "^4.18.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^18.0.0 || ^18.2.0-next.0", + "tailwindcss": "^2.0.0 || ^3.0.0", + "tslib": "^2.3.0", + "typescript": ">=5.4 <5.6" + }, + "peerDependenciesMeta": { + "tailwindcss": { + "optional": true + } } }, - "node_modules/socket.io-adapter": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", - "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", - "dev": true, + "node_modules/ng-packagr/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/ng-recaptcha-2": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/ng-recaptcha-2/-/ng-recaptcha-2-14.0.0.tgz", + "integrity": "sha512-LTl9kmMD//hQrrduc72onBZu+atghgJcEXzh8CenSvuNTDcHJPTjsuRwl83lG/3CXoeXkp+deHdZJJQGhyOE+A==", "dependencies": { - "debug": "~4.3.4", - "ws": "~8.17.1" + "@types/grecaptcha": "^3.0.7", + "tslib": "^2.2.0" + }, + "peerDependencies": { + "@angular/core": "^18.0.1" } }, - "node_modules/socket.io-parser": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", - "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", - "dev": true, + "node_modules/ngx-logger": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/ngx-logger/-/ngx-logger-5.0.12.tgz", + "integrity": "sha512-4kTtPvxQoV2ka6pigtvkbtaLKpMYWqZm7Slu0YQVcwzBKoVR2K+oLmMVcA50S6kCxkZXq7iKcrXUKR2vhMXPqQ==", "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" + "tslib": "^2.3.0", + "vlq": "^1.0.0" }, - "engines": { - "node": ">=10.0.0" + "peerDependencies": { + "rxjs": ">6.0.0" } }, - "node_modules/sockjs": { - "version": "0.3.24", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", - "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "node_modules/nice-napi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", + "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "!win32" + ], "dependencies": { - "faye-websocket": "^0.11.3", - "uuid": "^8.3.2", - "websocket-driver": "^0.7.4" + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.2" } }, - "node_modules/socks": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", - "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "node_modules/nice-napi/node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "dev": true, + "optional": true + }, + "node_modules/node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", + "dev": true + }, + "node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "dev": true + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", "dev": true, - "dependencies": { - "ip-address": "^9.0.5", - "smart-buffer": "^4.2.0" - }, "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" + "node": ">= 6.13.0" } }, - "node_modules/socks-proxy-agent": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", - "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "node_modules/node-gyp": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.2.0.tgz", + "integrity": "sha512-sp3FonBAaFe4aYTcFdZUn2NYkbP7xroPGYvQmP4Nl5PxamznItBnNCgjrVTKrEfQynInMsJvZrdmqUnysCJ8rw==", "dev": true, "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^13.0.0", + "nopt": "^7.0.0", + "proc-log": "^4.1.0", + "semver": "^7.3.5", + "tar": "^6.2.1", + "which": "^4.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" }, "engines": { - "node": ">= 10" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "node_modules/node-gyp-build": { + "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, - "engines": { - "node": ">= 8" + "optional": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" } }, - "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "dev": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "node_modules/node-gyp/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=16" } }, - "node_modules/source-map-loader": { + "node_modules/node-gyp/node_modules/which": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-4.0.0.tgz", - "integrity": "sha512-i3KVgM3+QPAHNbGavK+VBq03YoJl24m9JWNbLgsjTj8aJzXG9M61bantBTNBt7CNwY2FYf+RJRYJ3pzalKjIrw==", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dev": true, "dependencies": { - "abab": "^2.0.6", - "iconv-lite": "^0.6.3", - "source-map-js": "^1.0.2" + "isexe": "^3.1.1" }, - "engines": { - "node": ">= 14.15.0" + "bin": { + "node-which": "bin/which.js" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/node-machine-id": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/node-machine-id/-/node-machine-id-1.1.12.tgz", + "integrity": "sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==" + }, + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true + }, + "node_modules/node-schedule": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-2.1.1.tgz", + "integrity": "sha512-OXdegQq03OmXEjt2hZP33W2YPs/E5BcFQks46+G2gAxs4gHOIVD1u7EqlYLYSKsaIpyKCK9Gbk0ta1/gjRSMRQ==", + "dev": true, + "dependencies": { + "cron-parser": "^4.2.0", + "long-timeout": "0.1.1", + "sorted-array-functions": "^1.3.0" }, - "peerDependencies": { - "webpack": "^5.72.1" + "engines": { + "node": ">=6" } }, - "node_modules/source-map-resolve": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", - "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", - "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", "dev": true, "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0" + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", "dev": true, "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "deprecated": "Please use @jridgewell/sourcemap-codec instead", - "dev": true - }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "node_modules/spdx-expression-parse": { + "node_modules/npm-bundled": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.1.tgz", + "integrity": "sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==", "dev": true, "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/spdx-license-ids": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", - "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", - "dev": true - }, - "node_modules/spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "node_modules/npm-install-checks": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.3.0.tgz", + "integrity": "sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==", "dev": true, "dependencies": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" + "semver": "^7.1.1" }, "engines": { - "node": ">=6.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "node_modules/npm-normalize-package-bin": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", + "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", "dev": true, - "dependencies": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/split": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "node_modules/npm-package-arg": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.3.tgz", + "integrity": "sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==", "dev": true, "dependencies": { - "through": "2" + "hosted-git-info": "^7.0.0", + "proc-log": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" }, "engines": { - "node": "*" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "node_modules/sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "node_modules/npm-packlist": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-8.0.2.tgz", + "integrity": "sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==", "dev": true, "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" + "ignore-walk": "^6.0.4" }, "engines": { - "node": ">=0.10.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/ssri": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", - "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", + "node_modules/npm-pick-manifest": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-9.1.0.tgz", + "integrity": "sha512-nkc+3pIIhqHVQr085X9d2JzPzLyjzQS96zbruppqC9aZRm/x8xx6xhI98gHtsfELP2bE+loHq8ZaHFHhe+NauA==", "dev": true, "dependencies": { - "minipass": "^3.1.1" + "npm-install-checks": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "npm-package-arg": "^11.0.0", + "semver": "^7.3.5" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/stream-combiner": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", - "integrity": "sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==", - "dev": true, - "dependencies": { - "duplexer": "~0.1.1", - "through": "~2.3.4" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/streamroller": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", - "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", + "node_modules/npm-registry-fetch": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-17.1.0.tgz", + "integrity": "sha512-5+bKQRH0J1xG1uZ1zMNvxW0VEyoNWgJpY9UDuluPFLKDfJ9u2JmmjmTJV1srBGQOROfdBMiVvnH2Zvpbm+xkVA==", "dev": true, "dependencies": { - "date-format": "^4.0.14", - "debug": "^4.3.4", - "fs-extra": "^8.1.0" + "@npmcli/redact": "^2.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^13.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minizlib": "^2.1.2", + "npm-package-arg": "^11.0.0", + "proc-log": "^4.0.0" }, "engines": { - "node": ">=8.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/streamroller/node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "path-key": "^3.0.0" }, "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/streamroller/node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "dev": true, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/streamroller/node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" + "node": ">=8" } }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", "dev": true, "dependencies": { - "safe-buffer": "~5.2.0" + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" } }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" + "node_modules/nx": { + "version": "19.7.0", + "resolved": "https://registry.npmjs.org/nx/-/nx-19.7.0.tgz", + "integrity": "sha512-SZOnoCqPl8yJyPWt721INotFcgRlmBmGKUvXJ+wwnLidNKoUbX1RICIAceWkpZwaVZ2c/fqKYqjXLtGEblyZng==", + "hasInstallScript": true, + "dependencies": { + "@napi-rs/wasm-runtime": "0.2.4", + "@nrwl/tao": "19.7.0", + "@yarnpkg/lockfile": "^1.1.0", + "@yarnpkg/parsers": "3.0.0-rc.46", + "@zkochan/js-yaml": "0.0.7", + "axios": "^1.7.4", + "chalk": "^4.1.0", + "cli-cursor": "3.1.0", + "cli-spinners": "2.6.1", + "cliui": "^8.0.1", + "dotenv": "~16.4.5", + "dotenv-expand": "~11.0.6", + "enquirer": "~2.3.6", + "figures": "3.2.0", + "flat": "^5.0.2", + "front-matter": "^4.0.2", + "fs-extra": "^11.1.0", + "ignore": "^5.0.4", + "jest-diff": "^29.4.1", + "jsonc-parser": "3.2.0", + "lines-and-columns": "2.0.3", + "minimatch": "9.0.3", + "node-machine-id": "1.1.12", + "npm-run-path": "^4.0.1", + "open": "^8.4.0", + "ora": "5.3.0", + "semver": "^7.5.3", + "string-width": "^4.2.3", + "strong-log-transformer": "^2.1.0", + "tar-stream": "~2.2.0", + "tmp": "~0.2.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0", + "yargs": "^17.6.2", + "yargs-parser": "21.1.1" + }, + "bin": { + "nx": "bin/nx.js", + "nx-cloud": "bin/nx-cloud.js" + }, + "optionalDependencies": { + "@nx/nx-darwin-arm64": "19.7.0", + "@nx/nx-darwin-x64": "19.7.0", + "@nx/nx-freebsd-x64": "19.7.0", + "@nx/nx-linux-arm-gnueabihf": "19.7.0", + "@nx/nx-linux-arm64-gnu": "19.7.0", + "@nx/nx-linux-arm64-musl": "19.7.0", + "@nx/nx-linux-x64-gnu": "19.7.0", + "@nx/nx-linux-x64-musl": "19.7.0", + "@nx/nx-win32-arm64-msvc": "19.7.0", + "@nx/nx-win32-x64-msvc": "19.7.0" + }, + "peerDependencies": { + "@swc-node/register": "^1.8.0", + "@swc/core": "^1.3.85" + }, + "peerDependenciesMeta": { + "@swc-node/register": { + "optional": true }, - { - "type": "consulting", - "url": "https://feross.org/support" + "@swc/core": { + "optional": true } - ] + } }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, + "node_modules/nx/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "color-convert": "^2.0.1" }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/string.prototype.trim": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", - "dev": true, + "node_modules/nx/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/string.prototype.trimend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", - "dev": true, + "node_modules/nx/node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "restore-cursor": "^3.1.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8" } }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, + "node_modules/nx/node_modules/cli-spinners": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", "engines": { - "node": ">= 0.4" + "node": ">=6" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, + "node_modules/nx/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dependencies": { - "ansi-regex": "^5.0.1" + "color-name": "~1.1.4" }, "engines": { - "node": ">=8" + "node": ">=7.0.0" } }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } + "node_modules/nx/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/strip-final-newline": { + "node_modules/nx/node_modules/define-lazy-prop": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", "engines": { - "node": ">=6" + "node": ">=8" } }, - "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, - "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" - }, + "node_modules/nx/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/nx/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "engines": { - "node": "*" - }, - "funding": { - "url": "https://opencollective.com/stylus" + "node": ">=8" } }, - "node_modules/stylus-loader": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-7.0.0.tgz", - "integrity": "sha512-WTbtLrNfOfLgzTaR9Lj/BPhQroKk/LC1hfTXSUbrxmxgfUo3Y3LpmKRVA2R1XbjvTAvOfaian9vOyfv1z99E+A==", - "dev": true, - "dependencies": { - "fast-glob": "^3.2.11", - "klona": "^2.0.5", - "normalize-path": "^3.0.0" + "node_modules/nx/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "bin": { + "is-docker": "cli.js" }, "engines": { - "node": ">= 14.15.0" + "node": ">=8" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "stylus": ">=0.52.4", - "webpack": "^5.0.0" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "node_modules/nx/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" } }, - "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, + "node_modules/nx/node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "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" + "is-docker": "^2.0.0" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=8" } }, - "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, + "node_modules/nx/node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==" + }, + "node_modules/nx/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dependencies": { - "brace-expansion": "^1.1.7" + "mimic-fn": "^2.1.0" }, "engines": { - "node": "*" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, + "node_modules/nx/node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", "dependencies": { - "has-flag": "^3.0.0" + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" }, "engines": { - "node": ">=4" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/svg-pan-zoom": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/svg-pan-zoom/-/svg-pan-zoom-3.6.1.tgz", - "integrity": "sha512-JaKkGHHfGvRrcMPdJWkssLBeWqM+Isg/a09H7kgNNajT1cX5AztDTNs+C8UzpCxjCTRrG34WbquwaovZbmSk9g==", - "dev": true - }, - "node_modules/swagger-ui-dist": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.12.0.tgz", - "integrity": "sha512-B0Iy2ueXtbByE6OOyHTi3lFQkpPi/L7kFOKFeKTr44za7dJIELa9kzaca6GkndCgpK1QTjArnoXG+aUy0XQp1w==" - }, - "node_modules/symbol-observable": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", - "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", - "dev": true, - "engines": { - "node": ">=0.10" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/systemjs": { - "version": "0.21.6", - "resolved": "https://registry.npmjs.org/systemjs/-/systemjs-0.21.6.tgz", - "integrity": "sha512-R+5S9eV9vcQgWOoS4D87joZ4xkFJHb19ZsyKY07D1+VBDE9bwYcU+KXE0r5XlDA8mFoJGyuWDbfrNoh90JsA8g==" - }, - "node_modules/systemjs-plugin-babel": { - "version": "0.0.25", - "resolved": "https://registry.npmjs.org/systemjs-plugin-babel/-/systemjs-plugin-babel-0.0.25.tgz", - "integrity": "sha512-RMKSizWWlw4+IpDB385ugxn7Owd9W+HEtjYDQ6yO1FpsnER/vk6FbXRweUF+mvRi6EHgk8vDdUdtui7ReDwX3w==" - }, - "node_modules/tablesort": { + "node_modules/nx/node_modules/ora": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/tablesort/-/tablesort-5.3.0.tgz", - "integrity": "sha512-WkfcZBHsp47gVH9CBHG0ZXopriG01IA87arGrchvIe868d4RiXVvoYPS1zMq9IdW05kBs5iGsqxTABqLyWonbg==", - "dev": true - }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "dev": true, + "resolved": "https://registry.npmjs.org/ora/-/ora-5.3.0.tgz", + "integrity": "sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==", "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" + "bl": "^4.0.3", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "log-symbols": "^4.0.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, + "node_modules/nx/node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, "engines": { "node": ">=8" } }, - "node_modules/tar/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "node_modules/nx/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, - "node_modules/terser": { - "version": "5.19.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.2.tgz", - "integrity": "sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==", - "dev": true, + "node_modules/nx/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.9", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", - "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", - "dev": true, + "node_modules/nx/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.17", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.16.8" + "has-flag": "^4.0.0" }, "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } + "node": ">=8" } }, - "node_modules/terser-webpack-plugin/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/terser-webpack-plugin/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/terser-webpack-plugin/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/terser-webpack-plugin/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/terser/node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude/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, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/test-exclude/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, - "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/test-exclude/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, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true - }, - "node_modules/thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", - "dev": true - }, - "node_modules/tiny-inflate": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", - "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", - "dev": true - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", - "dev": true, - "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tough-cookie/node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/traverse": { - "version": "0.6.9", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.9.tgz", - "integrity": "sha512-7bBrcF+/LQzSgFmT0X5YclVqQxtv7TDJ1f8Wj7ibBu/U6BMLeOpUxuZjV7rMc44UtKxlnMFigdhFAIszSX1DMg==", - "dev": true, - "dependencies": { - "gopd": "^1.0.1", - "typedarray.prototype.slice": "^1.0.3", - "which-typed-array": "^1.1.15" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true, - "bin": { - "tree-kill": "cli.js" - } - }, - "node_modules/ts-morph": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-22.0.0.tgz", - "integrity": "sha512-M9MqFGZREyeb5fTl6gNHKZLqBQA0TjA1lea+CR48R8EBTDuWrNqW6ccC5QvjNR4s6wDumD3LTCjOFSp9iwlzaw==", - "dev": true, - "dependencies": { - "@ts-morph/common": "~0.23.0", - "code-block-writer": "^13.0.1" - } - }, - "node_modules/ts-node": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", - "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, - "dependencies": { - "arrify": "^1.0.0", - "buffer-from": "^1.1.0", - "diff": "^3.1.0", - "make-error": "^1.1.1", - "minimist": "^1.2.0", - "mkdirp": "^0.5.1", - "source-map-support": "^0.5.6", - "yn": "^2.0.0" - }, - "bin": { - "ts-node": "dist/bin.js" - }, "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/ts-node/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/tslib": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", - "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" - }, - "node_modules/tslint": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", - "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", - "deprecated": "TSLint has been deprecated in favor of ESLint. Please see https://github.com/palantir/tslint/issues/4534 for more information.", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^4.0.1", - "glob": "^7.1.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.3", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.13.0", - "tsutils": "^2.29.0" - }, - "bin": { - "tslint": "bin/tslint" - }, - "engines": { - "node": ">=4.8.0" - }, - "peerDependencies": { - "typescript": ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev || >= 4.0.0-dev" - } - }, - "node_modules/tslint/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, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/tslint/node_modules/builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/tslint/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/tslint/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/tslint/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, - "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/tslint/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, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tslint/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/tslint/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/tslint/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "peerDependencies": { - "typescript": ">=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-assert": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz", - "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==", - "dev": true - }, - "node_modules/typedarray.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typedarray.prototype.slice/-/typedarray.prototype.slice-1.0.3.tgz", - "integrity": "sha512-8WbVAQAUlENo1q3c3zZYuy5k9VzBQvp8AX9WOtbvyWlLM1v5JaSRmjubLjzHF4JFtptjH/5c/i95yaElvcjC0A==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-errors": "^1.3.0", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-offset": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typemoq": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/typemoq/-/typemoq-2.1.0.tgz", - "integrity": "sha512-DtRNLb7x8yCTv/KHlwes+NI+aGb4Vl1iPC63Hhtcvk1DpxSAZzKWQv0RQFY0jX2Uqj0SDBNl8Na4e6MV6TNDgw==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "circular-json": "^0.3.1", - "lodash": "^4.17.4", - "postinstall-build": "^5.0.1" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/typescript": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", - "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/ua-parser-js": { - "version": "0.7.35", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.35.tgz", - "integrity": "sha512-veRf7dawaj9xaWEu9HoTVn5Pggtc/qj+kqTOFvNiN1l0YdxwC1kvel57UCjThjGa3BHBihE8/UJAHI+uQHmd/g==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - } - ], - "engines": { - "node": "*" - } - }, - "node_modules/uglify-js": { - "version": "3.17.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", - "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", - "dev": true, - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dev": true, - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-properties": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", - "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", - "dev": true, - "dependencies": { - "base64-js": "^1.3.0", - "unicode-trie": "^2.0.0" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-trie": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", - "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", - "dev": true, - "dependencies": { - "pako": "^0.2.5", - "tiny-inflate": "^1.0.0" - } - }, - "node_modules/unicode-trie/node_modules/pako": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", - "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", - "dev": true - }, - "node_modules/unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "dev": true, - "dependencies": { - "unique-slug": "^2.0.0" - } - }, - "node_modules/unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4" - } - }, - "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/unix-crypt-td-js": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/unix-crypt-td-js/-/unix-crypt-td-js-1.1.4.tgz", - "integrity": "sha512-8rMeVYWSIyccIJscb9NdCfZKSRBKYTeVnwmiRYT2ulE3qd1RaDQ0xQDP+rI3ccIWbhu/zuo5cgN8z73belNZgw==", - "dev": true - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", - "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "node_modules/url-polyfill": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/url-polyfill/-/url-polyfill-1.1.12.tgz", - "integrity": "sha512-mYFmBHCapZjtcNHW0MDq9967t+z4Dmg5CJ0KqysK3+ZbyoNOWQHksGCTWwDhxGXllkWlOc10Xfko6v4a3ucM6A==" - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "dev": true, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/validate-npm-package-name": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz", - "integrity": "sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==", - "dev": true, - "dependencies": { - "builtins": "^5.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/vis": { - "version": "4.21.0-EOL", - "resolved": "https://registry.npmjs.org/vis/-/vis-4.21.0-EOL.tgz", - "integrity": "sha512-JVS1mywKg5S88XbkDJPfCb3n+vlg5fMA8Ae2hzs3KHAwD4ryM5qwlbFZ6ReDfY8te7I4NLCpuCoywJQEehvJlQ==", - "deprecated": "Please consider using https://github.com/visjs", - "dev": true, - "dependencies": { - "emitter-component": "^1.1.1", - "hammerjs": "^2.0.8", - "keycharm": "^0.2.0", - "moment": "^2.18.1", - "propagating-hammerjs": "^1.4.6" - } - }, - "node_modules/vlq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz", - "integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==" - }, - "node_modules/void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", - "dev": true, - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "dev": true, - "dependencies": { - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/web-animations-js": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/web-animations-js/-/web-animations-js-2.3.2.tgz", - "integrity": "sha512-TOMFWtQdxzjWp8qx4DAraTWTsdhxVSiWa6NkPFSaPtZ1diKUxTn4yTix73A1euG1WbSOMMPcY51cnjTIHrGtDA==" - }, - "node_modules/webdriver-js-extender": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz", - "integrity": "sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==", - "dev": true, - "dependencies": { - "@types/selenium-webdriver": "^3.0.0", - "selenium-webdriver": "^3.0.1" - }, - "engines": { - "node": ">=6.9.x" - } - }, - "node_modules/webdriver-manager": { - "version": "12.1.9", - "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.9.tgz", - "integrity": "sha512-Yl113uKm8z4m/KMUVWHq1Sjtla2uxEBtx2Ue3AmIlnlPAKloDn/Lvmy6pqWCUersVISpdMeVpAaGbNnvMuT2LQ==", - "dev": true, - "dependencies": { - "adm-zip": "^0.5.2", - "chalk": "^1.1.1", - "del": "^2.2.0", - "glob": "^7.0.3", - "ini": "^1.3.4", - "minimist": "^1.2.0", - "q": "^1.4.1", - "request": "^2.87.0", - "rimraf": "^2.5.2", - "semver": "^5.3.0", - "xml2js": "^0.4.17" - }, - "bin": { - "webdriver-manager": "bin/webdriver-manager" - }, - "engines": { - "node": ">=6.9.x" - } - }, - "node_modules/webdriver-manager/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webdriver-manager/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webdriver-manager/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, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/webdriver-manager/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", - "dev": true, - "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webdriver-manager/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, - "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/webdriver-manager/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "node_modules/webdriver-manager/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, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/webdriver-manager/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/webdriver-manager/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/webdriver-manager/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webdriver-manager/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/webpack": { - "version": "5.76.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz", - "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==", - "dev": true, - "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "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.1.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-dev-middleware": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", - "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", - "dev": true, - "dependencies": { - "colorette": "^2.0.10", - "memfs": "^3.4.3", - "mime-types": "^2.1.31", - "range-parser": "^1.2.1", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/webpack-dev-middleware/node_modules/schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/webpack-dev-server": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.0.tgz", - "integrity": "sha512-L5S4Q2zT57SK7tazgzjMiSMBdsw+rGYIX27MgPgx7LDhWO0lViPrHKoLS7jo5In06PWYAhlYu3PbyoC6yAThbw==", - "dev": true, - "dependencies": { - "@types/bonjour": "^3.5.9", - "@types/connect-history-api-fallback": "^1.3.5", - "@types/express": "^4.17.13", - "@types/serve-index": "^1.9.1", - "@types/serve-static": "^1.13.10", - "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.1", - "ansi-html-community": "^0.0.8", - "bonjour-service": "^1.0.11", - "chokidar": "^3.5.3", - "colorette": "^2.0.10", - "compression": "^1.7.4", - "connect-history-api-fallback": "^2.0.0", - "default-gateway": "^6.0.3", - "express": "^4.17.3", - "graceful-fs": "^4.2.6", - "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.3", - "ipaddr.js": "^2.0.1", - "open": "^8.0.9", - "p-retry": "^4.5.0", - "rimraf": "^3.0.2", - "schema-utils": "^4.0.0", - "selfsigned": "^2.0.1", - "serve-index": "^1.9.1", - "sockjs": "^0.3.24", - "spdy": "^4.0.2", - "webpack-dev-middleware": "^5.3.1", - "ws": "^8.4.2" - }, - "bin": { - "webpack-dev-server": "bin/webpack-dev-server.js" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.37.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-dev-server/node_modules/schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/webpack-merge": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", - "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", - "dev": true, - "dependencies": { - "clone-deep": "^4.0.1", - "wildcard": "^2.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "dev": true, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack-subresource-integrity": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz", - "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==", - "dev": true, - "dependencies": { - "typed-assert": "^1.0.8" - }, - "engines": { - "node": ">= 12" - }, - "peerDependencies": { - "html-webpack-plugin": ">= 5.0.0-beta.1 < 6", - "webpack": "^5.12.0" - }, - "peerDependenciesMeta": { - "html-webpack-plugin": { - "optional": true - } - } - }, - "node_modules/webpack/node_modules/@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", - "dev": true - }, - "node_modules/webpack/node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/webpack/node_modules/acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", - "dev": true, - "peerDependencies": { - "acorn": "^8" - } - }, - "node_modules/webpack/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/webpack/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/webpack/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/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/websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "dev": true, - "dependencies": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/whatwg-fetch": { - "version": "3.6.17", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.17.tgz", - "integrity": "sha512-c4ghIvG6th0eudYwKZY5keb81wtFz9/WeAHAoy8+r18kcWlitUIrmGFQ2rWEl4UCKUilD3zCLHOIPheHx5ypRQ==" - }, - "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "dev": true - }, - "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dev": true, - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "node_modules/wildcard": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", - "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", - "dev": true - }, - "node_modules/windows-release": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz", - "integrity": "sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==", - "dev": true, - "dependencies": { - "execa": "^4.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/windows-release/node_modules/execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/windows-release/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/windows-release/node_modules/human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true, - "engines": { - "node": ">=8.12.0" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xml2js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", - "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", - "dev": true, - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/xml2js/node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/xmlbuilder": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz", - "integrity": "sha512-eKRAFz04jghooy8muekqzo8uCSVNeyRedbuJrp0fovbLIi7wlsYtdUn3vBAAPq2Y3/0xMz2WMEUQ8yhVVO9Stw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/xmldoc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/xmldoc/-/xmldoc-1.3.0.tgz", - "integrity": "sha512-y7IRWW6PvEnYQZNZFMRLNJw+p3pezM4nKYPfr15g4OOW9i8VpeydycFuipE2297OvZnh3jSb2pxOt9QpkZUVng==", - "dev": true, - "dependencies": { - "sax": "^1.2.4" - } - }, - "node_modules/xxhashjs": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz", - "integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==", - "dev": true, - "dependencies": { - "cuint": "^0.2.2" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/yargs": { - "version": "17.5.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", - "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/yn": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", - "integrity": "sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/zepto": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/zepto/-/zepto-1.2.0.tgz", - "integrity": "sha512-C1x6lfvBICFTQIMgbt3JqMOno3VOtkWat/xEakLTOurskYIHPmzJrzd1e8BnmtdDVJlGuk5D+FxyCA8MPmkIyA==", - "dev": true - }, - "node_modules/zone.js": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.8.tgz", - "integrity": "sha512-82bctBg2hKcEJ21humWIkXRlLBBmrc3nN7DFh5LGGhcyycO2S7FN8NmdvlcKaGFDNVL4/9kFLmwmInTavdJERA==", - "dependencies": { - "tslib": "^2.3.0" - } - } - }, - "dependencies": { - "@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 - }, - "@aduh95/viz.js": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@aduh95/viz.js/-/viz.js-3.4.0.tgz", - "integrity": "sha512-KI2nVf9JdwWCXqK6RVf+9/096G7VWN4Z84mnynlyZKao2xQENW8WNEjLmvdlxS5X8PNWXFC1zqwm7tveOXw/4A==", - "dev": true - }, - "@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@angular-devkit/architect": { - "version": "0.1402.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1402.12.tgz", - "integrity": "sha512-LuK26pyaqyClEbY0n4/WIh3irUuA8wwmMmEj8uW4boziuJWv7U42lJJRF3VwkchiyOIp8qiKg995K6IoeXkWgA==", - "dev": true, - "requires": { - "@angular-devkit/core": "14.2.12", - "rxjs": "6.6.7" - }, - "dependencies": { - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "@angular-devkit/build-angular": { - "version": "14.2.13", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-14.2.13.tgz", - "integrity": "sha512-FJZKQ3xYFvEJ807sxVy4bCVyGU2NMl3UUPNfLIdIdzwwDEP9tx/cc+c4VtVPEZZfU8jVenu8XOvL6L0vpjt3yg==", - "dev": true, - "requires": { - "@ampproject/remapping": "2.2.0", - "@angular-devkit/architect": "0.1402.13", - "@angular-devkit/build-webpack": "0.1402.13", - "@angular-devkit/core": "14.2.13", - "@babel/core": "7.18.10", - "@babel/generator": "7.18.12", - "@babel/helper-annotate-as-pure": "7.18.6", - "@babel/plugin-proposal-async-generator-functions": "7.18.10", - "@babel/plugin-transform-async-to-generator": "7.18.6", - "@babel/plugin-transform-runtime": "7.18.10", - "@babel/preset-env": "7.18.10", - "@babel/runtime": "7.18.9", - "@babel/template": "7.18.10", - "@discoveryjs/json-ext": "0.5.7", - "@ngtools/webpack": "14.2.13", - "ansi-colors": "4.1.3", - "babel-loader": "8.2.5", - "babel-plugin-istanbul": "6.1.1", - "browserslist": "^4.9.1", - "cacache": "16.1.2", - "copy-webpack-plugin": "11.0.0", - "critters": "0.0.16", - "css-loader": "6.7.1", - "esbuild": "0.15.5", - "esbuild-wasm": "0.15.5", - "glob": "8.0.3", - "https-proxy-agent": "5.0.1", - "inquirer": "8.2.4", - "jsonc-parser": "3.1.0", - "karma-source-map-support": "1.4.0", - "less": "4.1.3", - "less-loader": "11.0.0", - "license-webpack-plugin": "4.0.2", - "loader-utils": "3.2.1", - "mini-css-extract-plugin": "2.6.1", - "minimatch": "5.1.0", - "open": "8.4.0", - "ora": "5.4.1", - "parse5-html-rewriting-stream": "6.0.1", - "piscina": "3.2.0", - "postcss": "8.4.31", - "postcss-import": "15.0.0", - "postcss-loader": "7.0.1", - "postcss-preset-env": "7.8.0", - "regenerator-runtime": "0.13.9", - "resolve-url-loader": "5.0.0", - "rxjs": "6.6.7", - "sass": "1.54.4", - "sass-loader": "13.0.2", - "semver": "7.5.3", - "source-map-loader": "4.0.0", - "source-map-support": "0.5.21", - "stylus": "0.59.0", - "stylus-loader": "7.0.0", - "terser": "5.14.2", - "text-table": "0.2.0", - "tree-kill": "1.2.2", - "tslib": "2.4.0", - "webpack": "5.76.1", - "webpack-dev-middleware": "5.3.4", - "webpack-dev-server": "4.11.0", - "webpack-merge": "5.8.0", - "webpack-subresource-integrity": "5.1.0" - }, - "dependencies": { - "@angular-devkit/architect": { - "version": "0.1402.13", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1402.13.tgz", - "integrity": "sha512-n0ISBuvkZHoOpAzuAZql1TU9VLHUE9e/a9g4VNOPHewjMzpN02VqeGKvJfOCKtzkCs6gVssIlILm2/SXxkIFxQ==", - "dev": true, - "requires": { - "@angular-devkit/core": "14.2.13", - "rxjs": "6.6.7" - } - }, - "@angular-devkit/core": { - "version": "14.2.13", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.13.tgz", - "integrity": "sha512-aIefeZcbjghQg/V6U9CTLtyB5fXDJ63KwYqVYkWP+i0XriS5A9puFgq2u/OVsWxAfYvqpDqp5AdQ0g0bi3CAsA==", - "dev": true, - "requires": { - "ajv": "8.11.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.1.0", - "rxjs": "6.6.7", - "source-map": "0.7.4" - } - }, - "acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "dev": true - }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "terser": { - "version": "5.14.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", - "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", - "dev": true, - "requires": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - } - }, - "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", - "dev": true - } - } - }, - "@angular-devkit/build-webpack": { - "version": "0.1402.13", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1402.13.tgz", - "integrity": "sha512-K27aJmuw86ZOdiu5PoGeGDJ2v7g2ZCK0bGwc8jzkjTLRfvd4FRKIIZumGv3hbQ3vQRLikiU6WMDRTFyCZky/EA==", - "dev": true, - "requires": { - "@angular-devkit/architect": "0.1402.13", - "rxjs": "6.6.7" - }, - "dependencies": { - "@angular-devkit/architect": { - "version": "0.1402.13", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1402.13.tgz", - "integrity": "sha512-n0ISBuvkZHoOpAzuAZql1TU9VLHUE9e/a9g4VNOPHewjMzpN02VqeGKvJfOCKtzkCs6gVssIlILm2/SXxkIFxQ==", - "dev": true, - "requires": { - "@angular-devkit/core": "14.2.13", - "rxjs": "6.6.7" - } - }, - "@angular-devkit/core": { - "version": "14.2.13", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.13.tgz", - "integrity": "sha512-aIefeZcbjghQg/V6U9CTLtyB5fXDJ63KwYqVYkWP+i0XriS5A9puFgq2u/OVsWxAfYvqpDqp5AdQ0g0bi3CAsA==", - "dev": true, - "requires": { - "ajv": "8.11.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.1.0", - "rxjs": "6.6.7", - "source-map": "0.7.4" - } - }, - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "@angular-devkit/core": { - "version": "14.2.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.12.tgz", - "integrity": "sha512-tg1+deEZdm3fgk2BQ6y7tujciL6qhtN5Ums266lX//kAZeZ4nNNXTBT+oY5xgfjvmLbW+xKg0XZrAS0oIRKY5g==", - "dev": true, - "requires": { - "ajv": "8.11.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.1.0", - "rxjs": "6.6.7", - "source-map": "0.7.4" - }, - "dependencies": { - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "@angular-devkit/schematics": { - "version": "14.2.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-14.2.12.tgz", - "integrity": "sha512-MN5yGR+SSSPPBBVMf4cifDJn9u0IYvxiHst+HWokH2AkBYy+vB1x8jYES2l1wkiISD7nvjTixfqX+Y95oMBoLg==", - "dev": true, - "requires": { - "@angular-devkit/core": "14.2.12", - "jsonc-parser": "3.1.0", - "magic-string": "0.26.2", - "ora": "5.4.1", - "rxjs": "6.6.7" - }, - "dependencies": { - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "@angular/animations": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-14.3.0.tgz", - "integrity": "sha512-QoBcIKy1ZiU+4qJsAh5Ls20BupWiXiZzKb0s6L9/dntPt5Msr4Ao289XR2P6O1L+kTsCprH9Kt41zyGQ/bkRqg==", - "requires": { - "tslib": "^2.3.0" - } - }, - "@angular/cdk": { - "version": "14.2.7", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-14.2.7.tgz", - "integrity": "sha512-/tEsYaUbDSnfEmKVvAMramIptmhI67O+9STjOV0i+74XR2NospeK0fkbywIANu1n3w6AHGMotvRWJrjmbCElFg==", - "requires": { - "parse5": "^5.0.0", - "tslib": "^2.3.0" - } - }, - "@angular/cli": { - "version": "14.2.12", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-14.2.12.tgz", - "integrity": "sha512-G/785b6jIIX7J+zS8RHaCT1OYzqANw5hJlnLf8tLgmaadLMVNQvIrvHTYtmD86pbqCYyVDLoMxefxRIwMHJuqw==", - "dev": true, - "requires": { - "@angular-devkit/architect": "0.1402.12", - "@angular-devkit/core": "14.2.12", - "@angular-devkit/schematics": "14.2.12", - "@schematics/angular": "14.2.12", - "@yarnpkg/lockfile": "1.1.0", - "ansi-colors": "4.1.3", - "debug": "4.3.4", - "ini": "3.0.0", - "inquirer": "8.2.4", - "jsonc-parser": "3.1.0", - "npm-package-arg": "9.1.0", - "npm-pick-manifest": "7.0.1", - "open": "8.4.0", - "ora": "5.4.1", - "pacote": "13.6.2", - "resolve": "1.22.1", - "semver": "7.5.3", - "symbol-observable": "4.0.0", - "uuid": "8.3.2", - "yargs": "17.5.1" - } - }, - "@angular/common": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-14.3.0.tgz", - "integrity": "sha512-pV9oyG3JhGWeQ+TFB0Qub6a1VZWMNZ6/7zEopvYivdqa5yDLLDSBRWb6P80RuONXyGnM1pa7l5nYopX+r/23GQ==", - "requires": { - "tslib": "^2.3.0" - } - }, - "@angular/compiler": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-14.3.0.tgz", - "integrity": "sha512-E15Rh0t3vA+bctbKnBCaDmLvc3ix+ZBt6yFZmhZalReQ+KpOlvOJv+L9oiFEgg+rYVl2QdvN7US1fvT0PqswLw==", - "requires": { - "tslib": "^2.3.0" - } - }, - "@angular/compiler-cli": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-14.3.0.tgz", - "integrity": "sha512-eoKpKdQ2X6axMgzcPUMZVYl3bIlTMzMeTo5V29No4BzgiUB+QoOTYGNJZkGRyqTNpwD9uSBJvmT2vG9+eC4ghQ==", - "dev": true, - "requires": { - "@babel/core": "^7.17.2", - "chokidar": "^3.0.0", - "convert-source-map": "^1.5.1", - "dependency-graph": "^0.11.0", - "magic-string": "^0.26.0", - "reflect-metadata": "^0.1.2", - "semver": "^7.0.0", - "sourcemap-codec": "^1.4.8", - "tslib": "^2.3.0", - "yargs": "^17.2.1" - } - }, - "@angular/core": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-14.3.0.tgz", - "integrity": "sha512-wYiwItc0Uyn4FWZ/OAx/Ubp2/WrD3EgUJ476y1XI7yATGPF8n9Ld5iCXT08HOvc4eBcYlDfh90kTXR6/MfhzdQ==", - "requires": { - "tslib": "^2.3.0" - } - }, - "@angular/forms": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-14.3.0.tgz", - "integrity": "sha512-fBZZC2UFMom2AZPjGQzROPXFWO6kvCsPDKctjJwClVC8PuMrkm+RRyiYRdBbt2qxWHEqOZM2OCQo73xUyZOYHw==", - "requires": { - "tslib": "^2.3.0" - } - }, - "@angular/language-service": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-14.3.0.tgz", - "integrity": "sha512-Sij3OQzj1UGs1O8H9PxVAY/o27+oqZwQRnib66rsWvtbIBTjHp4FV3dTs5iVcr62GGv4V4Mff/2I82NP10GPQg==", - "dev": true - }, - "@angular/material": { - "version": "14.2.7", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-14.2.7.tgz", - "integrity": "sha512-WXHh8pEStpgkXZJmYOg2cI8BSHkV82ET4XTJCNPdveumaCn1UYnaNzsXD13kw5z+zmy8CufhFEzdXTrv/yt7KQ==", - "requires": { - "tslib": "^2.3.0" - } - }, - "@angular/material-moment-adapter": { - "version": "14.2.7", - "resolved": "https://registry.npmjs.org/@angular/material-moment-adapter/-/material-moment-adapter-14.2.7.tgz", - "integrity": "sha512-uadhugTqgETxCTis4da/TpYfIUQDQSfGAxgH1n93eFNhGdW+aof3T4uKcbZ5d18RzU+lgO6RELuPnWW2PPlmCA==", - "requires": { - "tslib": "^2.3.0" - } - }, - "@angular/platform-browser": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-14.3.0.tgz", - "integrity": "sha512-w9Y3740UmTz44T0Egvc+4QV9sEbO61L+aRHbpkLTJdlEGzHByZvxJmJyBYmdqeyTPwc/Zpy7c02frlpfAlyB7A==", - "requires": { - "tslib": "^2.3.0" - } - }, - "@angular/platform-browser-dynamic": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-14.3.0.tgz", - "integrity": "sha512-rneZiMrIiYRhrkQvdL40E2ErKRn4Zdo6EtjBM9pAmWeyoM8oMnOZb9gz5vhrkNWg06kVMVg0yKqluP5How7j3A==", - "requires": { - "tslib": "^2.3.0" - } - }, - "@angular/router": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-14.3.0.tgz", - "integrity": "sha512-uip0V7w7k7xyxxpTPbr7EuMnYLj3FzJrwkLVJSEw3TMMGHt5VU5t4BBa9veGZOta2C205XFrTAHnp8mD+XYY1w==", - "requires": { - "tslib": "^2.3.0" - } - }, - "@assemblyscript/loader": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", - "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==", - "dev": true - }, - "@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.24.7", - "picocolors": "^1.0.0" - } - }, - "@babel/compat-data": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.0.tgz", - "integrity": "sha512-P4fwKI2mjEb3ZU5cnMJzvRsRKGBUcs8jvxIoRmr6ufAY9Xk2Bz7JubRTTivkw55c7WQJfTECeqYVa+HZ0FzREg==", - "dev": true - }, - "@babel/core": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", - "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.10", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helpers": "^7.18.9", - "@babel/parser": "^7.18.10", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.10", - "@babel/types": "^7.18.10", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.18.12", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz", - "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==", - "dev": true, - "requires": { - "@babel/types": "^7.18.10", - "@jridgewell/gen-mapping": "^0.3.2", - "jsesc": "^2.5.1" - }, - "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - } - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", - "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.24.7.tgz", - "integrity": "sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==", - "dev": true, - "requires": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.8.tgz", - "integrity": "sha512-oU+UoqCHdp+nWVDkpldqIQL/i/bvAv53tRqLG/s+cOXxe66zOYLU7ar/Xs3LdmBihrUMEUhwu6dMZwbNOYDwvw==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.24.8", - "@babel/helper-validator-option": "^7.24.8", - "browserslist": "^4.23.1", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.0.tgz", - "integrity": "sha512-GYM6BxeQsETc9mnct+nIIpf63SAyzvyYN7UB/IlTyd+MBg06afFGp0mIeUqGyWgS2mxad6vqbMrHVlaL3m70sQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.8", - "@babel/helper-optimise-call-expression": "^7.24.7", - "@babel/helper-replace-supers": "^7.25.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/traverse": "^7.25.0", - "semver": "^6.3.1" - }, - "dependencies": { - "@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", - "dev": true, - "requires": { - "@babel/types": "^7.24.7" - } - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.0.tgz", - "integrity": "sha512-q0T+dknZS+L5LDazIP+02gEZITG5unzvb6yIjcmj5i0eFrs5ToBV2m2JGH4EsE/gtP8ygEGLGApBgRIZkTm7zg==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "regexpu-core": "^5.3.1", - "semver": "^6.3.1" - }, - "dependencies": { - "@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", - "dev": true, - "requires": { - "@babel/types": "^7.24.7" - } - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-define-polyfill-provider": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", - "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-plugin-utils": "^7.16.7", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", - "dev": true - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.8.tgz", - "integrity": "sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA==", - "dev": true, - "requires": { - "@babel/traverse": "^7.24.8", - "@babel/types": "^7.24.8" - } - }, - "@babel/helper-module-imports": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", - "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", - "dev": true, - "requires": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - } - }, - "@babel/helper-module-transforms": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.0.tgz", - "integrity": "sha512-bIkOa2ZJYn7FHnepzr5iX9Kmz8FjIz4UKzJ9zhX3dnYuVW0xul9RuR3skBfoLu+FPTQw90EHW9rJsSZhyLQ3fQ==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7", - "@babel/traverse": "^7.25.0" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.7.tgz", - "integrity": "sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==", - "dev": true, - "requires": { - "@babel/types": "^7.24.7" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", - "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", - "dev": true - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.0.tgz", - "integrity": "sha512-NhavI2eWEIz/H9dbrG0TuOicDhNexze43i5z7lEqwYm0WEZVTwnPpA0EafUTP7+6/W79HWIP2cTe3Z5NiSTVpw==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-wrap-function": "^7.25.0", - "@babel/traverse": "^7.25.0" - }, - "dependencies": { - "@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", - "dev": true, - "requires": { - "@babel/types": "^7.24.7" - } - } - } - }, - "@babel/helper-replace-supers": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.0.tgz", - "integrity": "sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.24.8", - "@babel/helper-optimise-call-expression": "^7.24.7", - "@babel/traverse": "^7.25.0" - } - }, - "@babel/helper-simple-access": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", - "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", - "dev": true, - "requires": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - } - }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz", - "integrity": "sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==", - "dev": true, - "requires": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - } - }, - "@babel/helper-string-parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", - "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", - "dev": true - }, - "@babel/helper-wrap-function": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.0.tgz", - "integrity": "sha512-s6Q1ebqutSiZnEjaofc/UKDyC4SbzV5n5SrA2Gq8UawLycr3i04f1dX4OzoQVnexm6aOCh37SQNYlJ/8Ku+PMQ==", - "dev": true, - "requires": { - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.0", - "@babel/types": "^7.25.0" - }, - "dependencies": { - "@babel/template": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", - "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.25.0", - "@babel/types": "^7.25.0" - } - } - } - }, - "@babel/helpers": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", - "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", - "dev": true, - "requires": { - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.0" - }, - "dependencies": { - "@babel/template": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", - "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.25.0", - "@babel/types": "^7.25.0" - } - } - } - }, - "@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - } - }, - "@babel/parser": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.0.tgz", - "integrity": "sha512-CzdIU9jdP0dg7HdyB+bHvDJGagUv+qtzZt5rYCWwW6tITNqV9odjp6Qu41gkG0ca5UfdDUWrKkiAnHHdGRnOrA==", - "dev": true - }, - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.0.tgz", - "integrity": "sha512-dG0aApncVQwAUJa8tP1VHTnmU67BeIQvKafd3raEx315H54FfkZSz3B/TT+33ZQAjatGJA79gZqTtqL5QZUKXw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/traverse": "^7.25.0" - } - }, - "@babel/plugin-bugfix-safari-class-field-initializer-scope": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.0.tgz", - "integrity": "sha512-Bm4bH2qsX880b/3ziJ8KD711LT7z4u8CFudmjqle65AZj/HNUFhEf90dqYv6O86buWvSBmeQDjv0Tn2aF/bIBA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.8" - } - }, - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.0.tgz", - "integrity": "sha512-lXwdNZtTmeVOOFtwM/WDe7yg1PL8sYhRk/XH0FzbR2HDQ0xC+EnQ/JHeoMYSavtU115tnUk0q9CDyq8si+LMAA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.8" - } - }, - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.7.tgz", - "integrity": "sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.7" - } - }, - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.0.tgz", - "integrity": "sha512-tggFrk1AIShG/RUQbEwt2Tr/E+ObkfwrPjR6BjbRvsx24+PSjK8zrq0GWPNCjo8qpRx4DuJzlcvWJqlm+0h3kw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/traverse": "^7.25.0" - } - }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz", - "integrity": "sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-remap-async-to-generator": "^7.18.9", - "@babel/plugin-syntax-async-generators": "^7.8.4" - } - }, - "@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-proposal-class-static-block": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", - "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - } - }, - "@babel/plugin-proposal-dynamic-import": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", - "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - } - }, - "@babel/plugin-proposal-export-namespace-from": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", - "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - } - }, - "@babel/plugin-proposal-json-strings": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", - "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-json-strings": "^7.8.3" - } - }, - "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", - "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - } - }, - "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", - "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - } - }, - "@babel/plugin-proposal-numeric-separator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", - "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", - "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.20.7" - } - }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", - "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - } - }, - "@babel/plugin-proposal-optional-chaining": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", - "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - } - }, - "@babel/plugin-proposal-private-methods": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", - "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", - "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", - "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-import-assertions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.7.tgz", - "integrity": "sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-syntax-import-attributes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.7.tgz", - "integrity": "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-unicode-sets-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", - "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.7.tgz", - "integrity": "sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-async-generator-functions": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.0.tgz", - "integrity": "sha512-uaIi2FdqzjpAMvVqvB51S42oC2JEVgh0LDsGfZVDysWE8LrJtQC2jvKmOqEYThKyB7bDEb7BP1GYWDm7tABA0Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-remap-async-to-generator": "^7.25.0", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/traverse": "^7.25.0" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", - "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-remap-async-to-generator": "^7.18.6" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.7.tgz", - "integrity": "sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.0.tgz", - "integrity": "sha512-yBQjYoOjXlFv9nlXb3f1casSHOZkWr29NX+zChVanLg5Nc157CrbEX9D7hxxtTpuFy7Q0YzmmWfJxzvps4kXrQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.8" - } - }, - "@babel/plugin-transform-class-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.7.tgz", - "integrity": "sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-class-static-block": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.7.tgz", - "integrity": "sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.0.tgz", - "integrity": "sha512-xyi6qjr/fYU304fiRwFbekzkqVJZ6A7hOjWZd+89FVcBqPV3S9Wuozz82xdpLspckeaafntbzglaW4pqpzvtSw==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.8", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-replace-supers": "^7.25.0", - "@babel/traverse": "^7.25.0", - "globals": "^11.1.0" - }, - "dependencies": { - "@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", - "dev": true, - "requires": { - "@babel/types": "^7.24.7" - } - } - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz", - "integrity": "sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/template": "^7.24.7" - }, - "dependencies": { - "@babel/template": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", - "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.25.0", - "@babel/types": "^7.25.0" - } - } - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.8.tgz", - "integrity": "sha512-36e87mfY8TnRxc7yc6M9g9gOB7rKgSahqkIKwLpz4Ppk2+zC2Cy1is0uwtuSG6AE4zlTOUa+7JGz9jCJGLqQFQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.8" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.7.tgz", - "integrity": "sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.7.tgz", - "integrity": "sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.0.tgz", - "integrity": "sha512-YLpb4LlYSc3sCUa35un84poXoraOiQucUTTu8X1j18JV+gNa8E0nyUf/CjZ171IRGr4jEguF+vzJU66QZhn29g==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.25.0", - "@babel/helper-plugin-utils": "^7.24.8" - } - }, - "@babel/plugin-transform-dynamic-import": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz", - "integrity": "sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.7.tgz", - "integrity": "sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ==", - "dev": true, - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-export-namespace-from": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.7.tgz", - "integrity": "sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.7.tgz", - "integrity": "sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.25.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.1.tgz", - "integrity": "sha512-TVVJVdW9RKMNgJJlLtHsKDTydjZAbwIsn6ySBPQaEAUU5+gVvlJt/9nRmqVbsV/IBanRjzWoaAQKLoamWVOUuA==", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.24.8", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/traverse": "^7.25.1" - } - }, - "@babel/plugin-transform-json-strings": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.7.tgz", - "integrity": "sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-json-strings": "^7.8.3" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.7.tgz", - "integrity": "sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-logical-assignment-operators": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.7.tgz", - "integrity": "sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.7.tgz", - "integrity": "sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.7.tgz", - "integrity": "sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.8.tgz", - "integrity": "sha512-WHsk9H8XxRs3JXKWFiqtQebdh9b/pTk4EgueygFzYlTKAg0Ud985mSevdNjdXdFBATSKVJGQXP1tv6aGbssLKA==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.24.8", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-simple-access": "^7.24.7" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.0.tgz", - "integrity": "sha512-YPJfjQPDXxyQWg/0+jHKj1llnY5f/R6a0p/vP4lPymxLu7Lvl4k2WMitqi08yxwQcCVUUdG9LCUj4TNEgAp3Jw==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.25.0", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "@babel/traverse": "^7.25.0" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.7.tgz", - "integrity": "sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.24.7.tgz", - "integrity": "sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.7.tgz", - "integrity": "sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.7.tgz", - "integrity": "sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - } - }, - "@babel/plugin-transform-numeric-separator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.7.tgz", - "integrity": "sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - } - }, - "@babel/plugin-transform-object-rest-spread": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.7.tgz", - "integrity": "sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q==", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.24.7" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.7.tgz", - "integrity": "sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7" - } - }, - "@babel/plugin-transform-optional-catch-binding": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.7.tgz", - "integrity": "sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - } - }, - "@babel/plugin-transform-optional-chaining": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.8.tgz", - "integrity": "sha512-5cTOLSMs9eypEy8JUVvIKOu6NgvbJMnpG62VpIHrTmROdQ+L5mDAaI40g25k5vXti55JWNX5jCkq3HZxXBQANw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.7.tgz", - "integrity": "sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-private-methods": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.7.tgz", - "integrity": "sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-private-property-in-object": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.7.tgz", - "integrity": "sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - }, - "dependencies": { - "@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", - "dev": true, - "requires": { - "@babel/types": "^7.24.7" - } - } - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.7.tgz", - "integrity": "sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.7.tgz", - "integrity": "sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "regenerator-transform": "^0.15.2" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.7.tgz", - "integrity": "sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-runtime": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.10.tgz", - "integrity": "sha512-q5mMeYAdfEbpBAgzl7tBre/la3LeCxmDO1+wMXRdPWbcoMjR3GiXlCLk7JBZVVye0bqTGNMbt0yYVXX1B1jEWQ==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.9", - "babel-plugin-polyfill-corejs2": "^0.3.2", - "babel-plugin-polyfill-corejs3": "^0.5.3", - "babel-plugin-polyfill-regenerator": "^0.4.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.7.tgz", - "integrity": "sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.7.tgz", - "integrity": "sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.7.tgz", - "integrity": "sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.7.tgz", - "integrity": "sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.8.tgz", - "integrity": "sha512-adNTUpDCVnmAE58VEqKlAA6ZBlNkMnWD0ZcW76lyNFN3MJniyGFZfNwERVk8Ap56MCnXztmDr19T4mPTztcuaw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.8" - } - }, - "@babel/plugin-transform-unicode-escapes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.7.tgz", - "integrity": "sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-unicode-property-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.7.tgz", - "integrity": "sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.7.tgz", - "integrity": "sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/plugin-transform-unicode-sets-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.7.tgz", - "integrity": "sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - } - }, - "@babel/preset-env": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.10.tgz", - "integrity": "sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.18.8", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-async-generator-functions": "^7.18.10", - "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-class-static-block": "^7.18.6", - "@babel/plugin-proposal-dynamic-import": "^7.18.6", - "@babel/plugin-proposal-export-namespace-from": "^7.18.9", - "@babel/plugin-proposal-json-strings": "^7.18.6", - "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", - "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.18.9", - "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", - "@babel/plugin-proposal-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-private-methods": "^7.18.6", - "@babel/plugin-proposal-private-property-in-object": "^7.18.6", - "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.18.6", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-transform-arrow-functions": "^7.18.6", - "@babel/plugin-transform-async-to-generator": "^7.18.6", - "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.18.9", - "@babel/plugin-transform-classes": "^7.18.9", - "@babel/plugin-transform-computed-properties": "^7.18.9", - "@babel/plugin-transform-destructuring": "^7.18.9", - "@babel/plugin-transform-dotall-regex": "^7.18.6", - "@babel/plugin-transform-duplicate-keys": "^7.18.9", - "@babel/plugin-transform-exponentiation-operator": "^7.18.6", - "@babel/plugin-transform-for-of": "^7.18.8", - "@babel/plugin-transform-function-name": "^7.18.9", - "@babel/plugin-transform-literals": "^7.18.9", - "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.18.6", - "@babel/plugin-transform-modules-commonjs": "^7.18.6", - "@babel/plugin-transform-modules-systemjs": "^7.18.9", - "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.18.6", - "@babel/plugin-transform-new-target": "^7.18.6", - "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.18.8", - "@babel/plugin-transform-property-literals": "^7.18.6", - "@babel/plugin-transform-regenerator": "^7.18.6", - "@babel/plugin-transform-reserved-words": "^7.18.6", - "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.18.9", - "@babel/plugin-transform-sticky-regex": "^7.18.6", - "@babel/plugin-transform-template-literals": "^7.18.9", - "@babel/plugin-transform-typeof-symbol": "^7.18.9", - "@babel/plugin-transform-unicode-escapes": "^7.18.10", - "@babel/plugin-transform-unicode-regex": "^7.18.6", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.18.10", - "babel-plugin-polyfill-corejs2": "^0.3.2", - "babel-plugin-polyfill-corejs3": "^0.5.3", - "babel-plugin-polyfill-regenerator": "^0.4.0", - "core-js-compat": "^3.22.1", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/preset-modules": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6.tgz", - "integrity": "sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", - "dev": true - }, - "@babel/runtime": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", - "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", - "dev": true, - "requires": { - "regenerator-runtime": "^0.13.4" - } - }, - "@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" - } - }, - "@babel/traverse": { - "version": "7.25.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.1.tgz", - "integrity": "sha512-LrHHoWq08ZpmmFqBAzN+hUdWwy5zt7FGa/hVwMcOqW6OVtwqaoD5utfuGYU87JYxdZgLUvktAsn37j/sYR9siA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.0", - "@babel/parser": "^7.25.0", - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.0", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "dependencies": { - "@babel/generator": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", - "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", - "dev": true, - "requires": { - "@babel/types": "^7.25.0", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" - } - }, - "@babel/template": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", - "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.25.0", - "@babel/types": "^7.25.0" - } - }, - "@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - } - } - } - }, - "@babel/types": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.0.tgz", - "integrity": "sha512-LcnxQSsd9aXOIgmmSpvZ/1yo46ra2ESYyqLcryaBZOghxy5qqOBjvCWP5JfkI8yl9rlxRgdLTTMCQQRcN2hdCg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" - } - }, - "@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "dev": true - }, - "@compodoc/compodoc": { - "version": "1.1.25", - "resolved": "https://registry.npmjs.org/@compodoc/compodoc/-/compodoc-1.1.25.tgz", - "integrity": "sha512-MsTEv6S0JGkdXc8pFp3yB/r8Lw49YenD0TCXyIVAmQhWNDtGWi4m2TGz02hdiKAlTJ1McQJFuyXWiItTQtje0A==", - "dev": true, - "requires": { - "@angular-devkit/schematics": "18.0.1", - "@babel/core": "^7.24.6", - "@babel/plugin-transform-private-methods": "^7.24.6", - "@babel/preset-env": "^7.24.6", - "@compodoc/live-server": "^1.2.3", - "@compodoc/ngd-transformer": "^2.1.3", - "bootstrap.native": "^5.0.12", - "chalk": "4.1.2", - "cheerio": "^1.0.0-rc.12", - "chokidar": "^3.6.0", - "colors": "1.4.0", - "commander": "^12.1.0", - "cosmiconfig": "^9.0.0", - "decache": "^4.6.2", - "es6-shim": "^0.35.8", - "fancy-log": "^2.0.0", - "fast-glob": "^3.3.2", - "fs-extra": "^11.2.0", - "glob": "^10.4.1", - "handlebars": "^4.7.8", - "html-entities": "^2.5.2", - "i18next": "^23.11.5", - "json5": "^2.2.3", - "lodash": "^4.17.21", - "loglevel": "^1.9.1", - "loglevel-plugin-prefix": "^0.8.4", - "lunr": "^2.3.9", - "marked": "7.0.3", - "minimist": "^1.2.8", - "opencollective-postinstall": "^2.0.3", - "os-name": "4.0.1", - "pdfmake": "^0.2.10", - "prismjs": "^1.29.0", - "semver": "^7.6.2", - "svg-pan-zoom": "^3.6.1", - "tablesort": "^5.3.0", - "traverse": "^0.6.9", - "ts-morph": "^22.0.0", - "uuid": "^9.0.1", - "vis": "^4.21.0-EOL", - "zepto": "^1.2.0" - }, - "dependencies": { - "@angular-devkit/core": { - "version": "18.0.1", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.0.1.tgz", - "integrity": "sha512-91eKZoObs+wRgwssw81Y/94Nvixj0WqJkNusBAg+gAfZTCEeJoGGZJkRK8wrONbM79C3Bx8lN/TfSIPRbjnfOQ==", - "dev": true, - "requires": { - "ajv": "8.13.0", - "ajv-formats": "3.0.1", - "jsonc-parser": "3.2.1", - "picomatch": "4.0.2", - "rxjs": "7.8.1", - "source-map": "0.7.4" - } - }, - "@angular-devkit/schematics": { - "version": "18.0.1", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.0.1.tgz", - "integrity": "sha512-AKcEGa3fIgyXT6XTQZWEJZzgmcqlB89fcF7JFOuz4rgQfRmnE2xFw37lKE6ZclCOSiEoffAvgrL8acjdPI1ouw==", - "dev": true, - "requires": { - "@angular-devkit/core": "18.0.1", - "jsonc-parser": "3.2.1", - "magic-string": "0.30.10", - "ora": "5.4.1", - "rxjs": "7.8.1" - } - }, - "@babel/core": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.9.tgz", - "integrity": "sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.9", - "@babel/helper-compilation-targets": "^7.24.8", - "@babel/helper-module-transforms": "^7.24.9", - "@babel/helpers": "^7.24.8", - "@babel/parser": "^7.24.8", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.8", - "@babel/types": "^7.24.9", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", - "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", - "dev": true, - "requires": { - "@babel/types": "^7.25.0", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" - } - }, - "@babel/helper-define-polyfill-provider": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", - "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" - } - }, - "@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", - "dev": true, - "requires": {} - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz", - "integrity": "sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7" - } - }, - "@babel/preset-env": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.0.tgz", - "integrity": "sha512-vYAA8PrCOeZfG4D87hmw1KJ1BPubghXP1e2MacRFwECGNKL76dkA38JEwYllbvQCpf/kLxsTtir0b8MtxKoVCw==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.25.0", - "@babel/helper-compilation-targets": "^7.24.8", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-validator-option": "^7.24.8", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.0", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.0", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.0", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.0", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.24.7", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.24.7", - "@babel/plugin-transform-async-generator-functions": "^7.25.0", - "@babel/plugin-transform-async-to-generator": "^7.24.7", - "@babel/plugin-transform-block-scoped-functions": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.25.0", - "@babel/plugin-transform-class-properties": "^7.24.7", - "@babel/plugin-transform-class-static-block": "^7.24.7", - "@babel/plugin-transform-classes": "^7.25.0", - "@babel/plugin-transform-computed-properties": "^7.24.7", - "@babel/plugin-transform-destructuring": "^7.24.8", - "@babel/plugin-transform-dotall-regex": "^7.24.7", - "@babel/plugin-transform-duplicate-keys": "^7.24.7", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.0", - "@babel/plugin-transform-dynamic-import": "^7.24.7", - "@babel/plugin-transform-exponentiation-operator": "^7.24.7", - "@babel/plugin-transform-export-namespace-from": "^7.24.7", - "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.25.0", - "@babel/plugin-transform-json-strings": "^7.24.7", - "@babel/plugin-transform-literals": "^7.24.7", - "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", - "@babel/plugin-transform-member-expression-literals": "^7.24.7", - "@babel/plugin-transform-modules-amd": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.8", - "@babel/plugin-transform-modules-systemjs": "^7.25.0", - "@babel/plugin-transform-modules-umd": "^7.24.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", - "@babel/plugin-transform-new-target": "^7.24.7", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", - "@babel/plugin-transform-numeric-separator": "^7.24.7", - "@babel/plugin-transform-object-rest-spread": "^7.24.7", - "@babel/plugin-transform-object-super": "^7.24.7", - "@babel/plugin-transform-optional-catch-binding": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.8", - "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.24.7", - "@babel/plugin-transform-private-property-in-object": "^7.24.7", - "@babel/plugin-transform-property-literals": "^7.24.7", - "@babel/plugin-transform-regenerator": "^7.24.7", - "@babel/plugin-transform-reserved-words": "^7.24.7", - "@babel/plugin-transform-shorthand-properties": "^7.24.7", - "@babel/plugin-transform-spread": "^7.24.7", - "@babel/plugin-transform-sticky-regex": "^7.24.7", - "@babel/plugin-transform-template-literals": "^7.24.7", - "@babel/plugin-transform-typeof-symbol": "^7.24.8", - "@babel/plugin-transform-unicode-escapes": "^7.24.7", - "@babel/plugin-transform-unicode-property-regex": "^7.24.7", - "@babel/plugin-transform-unicode-regex": "^7.24.7", - "@babel/plugin-transform-unicode-sets-regex": "^7.24.7", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.4", - "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.37.1", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/preset-modules": { - "version": "0.1.6-no-external-plugins", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", - "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "@babel/template": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", - "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.25.0", - "@babel/types": "^7.25.0" - } - }, - "@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "ajv": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", - "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" - } - }, - "ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "dev": true, - "requires": { - "ajv": "^8.0.0" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "argparse": { - "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 - }, - "babel-plugin-polyfill-corejs2": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", - "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.6.2", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "babel-plugin-polyfill-corejs3": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.4.tgz", - "integrity": "sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.6.1", - "core-js-compat": "^3.36.1" - } - }, - "babel-plugin-polyfill-regenerator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", - "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.6.2" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "cosmiconfig": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", - "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", - "dev": true, - "requires": { - "env-paths": "^2.2.1", - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0" - } - }, - "glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "requires": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "requires": { - "@isaacs/cliui": "^8.0.2", - "@pkgjs/parseargs": "^0.11.0" - } - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "jsonc-parser": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", - "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", - "dev": true - }, - "magic-string": { - "version": "0.30.10", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", - "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", - "dev": true, - "requires": { - "@jridgewell/sourcemap-codec": "^1.4.15" - } - }, - "minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - }, - "minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true - }, - "picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true - }, - "rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "requires": { - "tslib": "^2.1.0" - } - }, - "semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", - "dev": true, - "optional": true, - "peer": true - }, - "uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "dev": true - } - } - }, - "@compodoc/live-server": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@compodoc/live-server/-/live-server-1.2.3.tgz", - "integrity": "sha512-hDmntVCyjjaxuJzPzBx68orNZ7TW4BtHWMnXlIVn5dqhK7vuFF/11hspO1cMmc+2QTYgqde1TBcb3127S7Zrow==", - "dev": true, - "requires": { - "chokidar": "^3.5.2", - "colors": "1.4.0", - "connect": "^3.7.0", - "cors": "latest", - "event-stream": "4.0.1", - "faye-websocket": "0.11.x", - "http-auth": "4.1.9", - "http-auth-connect": "^1.0.5", - "morgan": "^1.10.0", - "object-assign": "latest", - "open": "8.4.0", - "proxy-middleware": "latest", - "send": "latest", - "serve-index": "^1.9.1" - }, - "dependencies": { - "proxy-middleware": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/proxy-middleware/-/proxy-middleware-0.15.0.tgz", - "integrity": "sha512-EGCG8SeoIRVMhsqHQUdDigB2i7qU7fCsWASwn54+nPutYO8n4q6EiwMzyfWlC+dzRFExP+kvcnDFdBDHoZBU7Q==", - "dev": true - } - } - }, - "@compodoc/ngd-core": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@compodoc/ngd-core/-/ngd-core-2.1.1.tgz", - "integrity": "sha512-Z+wE6wWZYVnudRYg6qunDlyh3Orw39Ib66Gvrz5kX5u7So+iu3tr6sQJdqH6yGS3hAjig5avlfhWLlgsb6/x1Q==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.3", - "fancy-log": "^2.0.0", - "typescript": "^5.0.4" - }, - "dependencies": { - "typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", - "dev": true - } - } - }, - "@compodoc/ngd-transformer": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@compodoc/ngd-transformer/-/ngd-transformer-2.1.3.tgz", - "integrity": "sha512-oWxJza7CpWR8/FeWYfE6j+jgncnGBsTWnZLt5rD2GUpsGSQTuGrsFPnmbbaVLgRS5QIVWBJYke7QFBr/7qVMWg==", - "dev": true, - "requires": { - "@aduh95/viz.js": "3.4.0", - "@compodoc/ngd-core": "~2.1.1", - "dot": "^2.0.0-beta.1", - "fs-extra": "^11.1.1" - } - }, - "@csstools/postcss-cascade-layers": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz", - "integrity": "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==", - "dev": true, - "requires": { - "@csstools/selector-specificity": "^2.0.2", - "postcss-selector-parser": "^6.0.10" - } - }, - "@csstools/postcss-color-function": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", - "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", - "dev": true, - "requires": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - } - }, - "@csstools/postcss-font-format-keywords": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", - "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "@csstools/postcss-hwb-function": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", - "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "@csstools/postcss-ic-unit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", - "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", - "dev": true, - "requires": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - } - }, - "@csstools/postcss-is-pseudo-class": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", - "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", - "dev": true, - "requires": { - "@csstools/selector-specificity": "^2.0.0", - "postcss-selector-parser": "^6.0.10" - } - }, - "@csstools/postcss-nested-calc": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz", - "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "@csstools/postcss-normalize-display-values": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", - "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "@csstools/postcss-oklab-function": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", - "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", - "dev": true, - "requires": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - } - }, - "@csstools/postcss-progressive-custom-properties": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", - "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "@csstools/postcss-stepped-value-functions": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", - "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "@csstools/postcss-text-decoration-shorthand": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz", - "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "@csstools/postcss-trigonometric-functions": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", - "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "@csstools/postcss-unset-value": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", - "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", - "dev": true, - "requires": {} - }, - "@csstools/selector-specificity": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz", - "integrity": "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==", - "dev": true, - "requires": {} - }, - "@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", - "dev": true - }, - "@elemental-ui/cadence-icon": { - "version": "file:imx-modules\\elemental-ui_cadence-icon.tgz", - "integrity": "sha512-NQjPGHdJxemkw1qzlPaAV+DWLF+biAAa3/4kZxM3CzllA+SNivECMpkyDMcKqBCXLmcys6pxvSo88dTpgILREw==" - }, - "@elemental-ui/core": { - "version": "file:imx-modules\\elemental-ui_core.tgz", - "integrity": "sha512-d7TqH//IKajEOF2yuJGntTy78I5YmpmNb/j9SpCccXfVq6ym96sxtiYuyh4lVT3CUkKdzeVZCok75w/RLlo+VQ==", - "requires": { - "tslib": "^2.0.0" - } - }, - "@esbuild/linux-loong64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.5.tgz", - "integrity": "sha512-UHkDFCfSGTuXq08oQltXxSZmH1TXyWsL+4QhZDWvvLl6mEJQqk3u7/wq1LjhrrAXYIllaTtRSzUXl4Olkf2J8A==", - "dev": true, - "optional": true - }, - "@foliojs-fork/fontkit": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@foliojs-fork/fontkit/-/fontkit-1.9.2.tgz", - "integrity": "sha512-IfB5EiIb+GZk+77TRB86AHroVaqfq8JRFlUbz0WEwsInyCG0epX2tCPOy+UfaWPju30DeVoUAXfzWXmhn753KA==", - "dev": true, - "requires": { - "@foliojs-fork/restructure": "^2.0.2", - "brotli": "^1.2.0", - "clone": "^1.0.4", - "deep-equal": "^1.0.0", - "dfa": "^1.2.0", - "tiny-inflate": "^1.0.2", - "unicode-properties": "^1.2.2", - "unicode-trie": "^2.0.0" - } - }, - "@foliojs-fork/linebreak": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@foliojs-fork/linebreak/-/linebreak-1.1.2.tgz", - "integrity": "sha512-ZPohpxxbuKNE0l/5iBJnOAfUaMACwvUIKCvqtWGKIMv1lPYoNjYXRfhi9FeeV9McBkBLxsMFWTVVhHJA8cyzvg==", - "dev": true, - "requires": { - "base64-js": "1.3.1", - "unicode-trie": "^2.0.0" - }, - "dependencies": { - "base64-js": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", - "dev": true - } - } - }, - "@foliojs-fork/pdfkit": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@foliojs-fork/pdfkit/-/pdfkit-0.14.0.tgz", - "integrity": "sha512-nMOiQAv6id89MT3tVTCgc7HxD5ZMANwio2o5yvs5sexQkC0KI3BLaLakpsrHmFfeGFAhqPmZATZGbJGXTUebpg==", - "dev": true, - "requires": { - "@foliojs-fork/fontkit": "^1.9.1", - "@foliojs-fork/linebreak": "^1.1.1", - "crypto-js": "^4.2.0", - "png-js": "^1.0.0" - } - }, - "@foliojs-fork/restructure": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@foliojs-fork/restructure/-/restructure-2.0.2.tgz", - "integrity": "sha512-59SgoZ3EXbkfSX7b63tsou/SDGzwUEK6MuB5sKqgVK1/XE0fxmpsOb9DQI8LXW3KfGnAjImCGhhEb7uPPAUVNA==", - "dev": true - }, - "@gar/promisify": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", - "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", - "dev": true - }, - "@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "requires": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true - }, - "ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "requires": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - } - }, - "string-width-cjs": { - "version": "npm:string-width@4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } - } - }, - "strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" - } - }, - "strip-ansi-cjs": { - "version": "npm:strip-ansi@6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - } - } - }, - "wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "requires": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - } - }, - "wrap-ansi-cjs": { - "version": "npm:wrap-ansi@7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } - } - } - } - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true - }, - "@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true - }, - "@jridgewell/source-map": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", - "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - } - } - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "@leichtgewicht/ip-codec": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", - "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", - "dev": true - }, - "@ngtools/webpack": { - "version": "14.2.13", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-14.2.13.tgz", - "integrity": "sha512-RQx/rGX7K/+R55x1R6Ax1JzyeHi8cW11dEXpzHWipyuSpusQLUN53F02eMB4VTakXsL3mFNWWy4bX3/LSq8/9w==", - "dev": true, - "requires": {} - }, - "@ngx-translate/core": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-11.0.1.tgz", - "integrity": "sha512-nBCa1ZD9fAUY/3eskP3Lql2fNg8OMrYIej1/5GRsfcutx9tG/5fZLCv9m6UCw1aS+u4uK/vXjv1ctG/FdMvaWg==", - "requires": { - "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } - } - }, - "@ngx-translate/http-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@ngx-translate/http-loader/-/http-loader-4.0.0.tgz", - "integrity": "sha512-x8LumqydWD7eX9yQTAVeoCM9gFUIGVTUjZqbxdAUavAA3qVnk9wCQux7iHLPXpydl8vyQmLoPQR+fFU+DUDOMA==", - "requires": { - "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@npmcli/fs": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", - "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", - "dev": true, - "requires": { - "@gar/promisify": "^1.1.3", - "semver": "^7.3.5" - } - }, - "@npmcli/git": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-3.0.2.tgz", - "integrity": "sha512-CAcd08y3DWBJqJDpfuVL0uijlq5oaXaOJEKHKc4wqrjd00gkvTZB+nFuLn+doOOKddaQS9JfqtNoFCO2LCvA3w==", - "dev": true, - "requires": { - "@npmcli/promise-spawn": "^3.0.0", - "lru-cache": "^7.4.4", - "mkdirp": "^1.0.4", - "npm-pick-manifest": "^7.0.0", - "proc-log": "^2.0.0", - "promise-inflight": "^1.0.1", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^2.0.2" - }, - "dependencies": { - "lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "@npmcli/installed-package-contents": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz", - "integrity": "sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==", - "dev": true, - "requires": { - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "@npmcli/move-file": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", - "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", - "dev": true, - "requires": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - } - }, - "@npmcli/node-gyp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-2.0.0.tgz", - "integrity": "sha512-doNI35wIe3bBaEgrlPfdJPaCpUR89pJWep4Hq3aRdh6gKazIVWfs0jHttvSSoq47ZXgC7h73kDsUl8AoIQUB+A==", - "dev": true - }, - "@npmcli/promise-spawn": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-3.0.0.tgz", - "integrity": "sha512-s9SgS+p3a9Eohe68cSI3fi+hpcZUmXq5P7w0kMlAsWVtR7XbK3ptkZqKT2cK1zLDObJ3sR+8P59sJE0w/KTL1g==", - "dev": true, - "requires": { - "infer-owner": "^1.0.4" - } - }, - "@npmcli/run-script": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-4.2.1.tgz", - "integrity": "sha512-7dqywvVudPSrRCW5nTHpHgeWnbBtz8cFkOuKrecm6ih+oO9ciydhWt6OF7HlqupRRmB8Q/gECVdB9LMfToJbRg==", - "dev": true, - "requires": { - "@npmcli/node-gyp": "^2.0.0", - "@npmcli/promise-spawn": "^3.0.0", - "node-gyp": "^9.0.0", - "read-package-json-fast": "^2.0.3", - "which": "^2.0.2" - }, - "dependencies": { - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "optional": true - }, - "@rollup/plugin-json": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-4.1.0.tgz", - "integrity": "sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==", - "dev": true, - "requires": { - "@rollup/pluginutils": "^3.0.8" - } - }, - "@rollup/plugin-node-resolve": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.3.0.tgz", - "integrity": "sha512-Lus8rbUo1eEcnS4yTFKLZrVumLPY+YayBdWXgFSHYhTT2iJbMhoaaBL3xl5NCdeRytErGr8tZ0L71BMRmnlwSw==", - "dev": true, - "requires": { - "@rollup/pluginutils": "^3.1.0", - "@types/resolve": "1.17.1", - "deepmerge": "^4.2.2", - "is-builtin-module": "^3.1.0", - "is-module": "^1.0.0", - "resolve": "^1.19.0" - } - }, - "@rollup/pluginutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", - "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", - "dev": true, - "requires": { - "@types/estree": "0.0.39", - "estree-walker": "^1.0.1", - "picomatch": "^2.2.2" - } - }, - "@schematics/angular": { - "version": "14.2.12", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-14.2.12.tgz", - "integrity": "sha512-nCxoFzH/uh5vqhaAjAfQIBgGuCdV3RMjdSwD0VQ+GFiFvTe8rqFyDl+qpNCgETz4LwmGHb5HNjDH9+VyVLgfZQ==", - "dev": true, - "requires": { - "@angular-devkit/core": "14.2.12", - "@angular-devkit/schematics": "14.2.12", - "jsonc-parser": "3.1.0" - } - }, - "@socket.io/component-emitter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", - "dev": true - }, - "@thednp/event-listener": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@thednp/event-listener/-/event-listener-2.0.5.tgz", - "integrity": "sha512-Zns+CFEAIKIEyqmuBZ3K2DSvk5IppaWcioghxLZPMrzkV034aOA38lP7NIKSxkeu0Eqd4UPxC06FksO6Pb/tmA==", - "dev": true - }, - "@thednp/shorty": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@thednp/shorty/-/shorty-2.0.3.tgz", - "integrity": "sha512-ngKP9/wQxM6JPDFjO6ak8lSz38ZA6cIFQy3gZbZM3xgUqArBr+VG9aoSoLHHEuaObyd9q9Jq/T0Wez7qrck0Gw==", - "dev": true - }, - "@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "dev": true - }, - "@ts-morph/common": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.23.0.tgz", - "integrity": "sha512-m7Lllj9n/S6sOkCkRftpM7L24uvmfXQFedlW/4hENcuJH1HHm9u5EgxZb9uVjQSCGrbBWBkOGgcTxNg36r6ywA==", - "dev": true, - "requires": { - "fast-glob": "^3.3.2", - "minimatch": "^9.0.3", - "mkdirp": "^3.0.1", - "path-browserify": "^1.0.1" - }, - "dependencies": { - "minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - }, - "mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "dev": true - } - } - }, - "@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "dev": true, - "requires": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "@types/bonjour": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", - "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/connect-history-api-fallback": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", - "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", - "dev": true, - "requires": { - "@types/express-serve-static-core": "*", - "@types/node": "*" - } - }, - "@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", - "dev": true - }, - "@types/cors": { - "version": "2.8.13", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", - "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/cytoscape": { - "version": "3.19.10", - "resolved": "https://registry.npmjs.org/@types/cytoscape/-/cytoscape-3.19.10.tgz", - "integrity": "sha512-PLsKQcsUd05nz4PYyulIhjkLnlq9oD2WYpswrWOjoqtFZEuuBje0f9fi2zTG5/yfTf5+Gpllf/MPcFmfDzZ24w==" - }, - "@types/cytoscape-edgehandles": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/cytoscape-edgehandles/-/cytoscape-edgehandles-4.0.0.tgz", - "integrity": "sha512-150ohT1VxeS6fa414KLvv6B85CdHAFLI/rInFdCrzhOBgSwStSNonu0rypthliq9AxDjZ+wPOI5hB7VJOyu0wQ==", - "requires": { - "@types/cytoscape": "*" - } - }, - "@types/d3-hierarchy": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", - "integrity": "sha512-9hjRTVoZjRFR6xo8igAJyNXQyPX6Aq++Nhb5ebrUF414dv4jr2MitM2fWiOY475wa3Za7TOS2Gh9fmqEhLTt0A==", - "dev": true - }, - "@types/d3-scale": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.3.tgz", - "integrity": "sha512-PATBiMCpvHJSMtZAMEhc2WyL+hnzarKzI6wAHYjhsonjWJYGq5BXTzQjv4l8m2jO183/4wZ90rKvSeT7o72xNQ==", - "dev": true, - "requires": { - "@types/d3-time": "*" - } - }, - "@types/d3-selection": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.5.tgz", - "integrity": "sha512-xCB0z3Hi8eFIqyja3vW8iV01+OHGYR2di/+e+AiOcXIOrY82lcvWW8Ke1DYE/EUVMsBl4Db9RppSBS3X1U6J0w==" - }, - "@types/d3-time": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz", - "integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==", - "dev": true - }, - "@types/d3-transition": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.3.tgz", - "integrity": "sha512-/S90Od8Id1wgQNvIA8iFv9jRhCiZcGhPd2qX0bKF/PS+y0W5CrXKgIiELd2CvG1mlQrWK/qlYh3VxicqG1ZvgA==", - "requires": { - "@types/d3-selection": "*" - } - }, - "@types/eslint": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.1.tgz", - "integrity": "sha512-XpNDc4Z5Tb4x+SW1MriMVeIsMoONHCkWFMkR/aPJbzEsxqHy+4Glu/BqTdPrApfDeMaXbtNh6bseNgl5KaWrSg==", - "dev": true, - "requires": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", - "dev": true, - "requires": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "@types/estree": { - "version": "0.0.39", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", - "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", - "dev": true - }, - "@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "dev": true, - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "@types/express-serve-static-core": { - "version": "4.19.5", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz", - "integrity": "sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==", - "dev": true, - "requires": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "dev": true - }, - "@types/http-proxy": { - "version": "1.17.14", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", - "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/jasmine": { - "version": "3.6.11", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.6.11.tgz", - "integrity": "sha512-S6pvzQDvMZHrkBz2Mcn/8Du7cpr76PlRJBAoHnSDNbulULsH5dp0Gns+WRyNX5LHejz/ljxK4/vIHK/caHt6SQ==", - "dev": true - }, - "@types/jasminewd2": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.10.tgz", - "integrity": "sha512-J7mDz7ovjwjc+Y9rR9rY53hFWKATcIkrr9DwQWmOas4/pnIPJTXawnzjwpHm3RSxz/e3ZVUvQ7cRbd5UQLo10g==", - "dev": true, - "requires": { - "@types/jasmine": "*" - } - }, - "@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", - "dev": true - }, - "@types/karma": { - "version": "6.3.4", - "resolved": "https://registry.npmjs.org/@types/karma/-/karma-6.3.4.tgz", - "integrity": "sha512-aefuFcs4e4NAOi1Ue4AP9fR2TQv45NFpdaLXdg+3FxDDndk/4T6LoHRTmKtRVqkwtQPh5Ntc3CxsoOgzldAAfg==", - "dev": true, - "requires": { - "@types/node": "*", - "log4js": "^6.4.1" - } - }, - "@types/lodash": { - "version": "4.14.196", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.196.tgz", - "integrity": "sha512-22y3o88f4a94mKljsZcanlNWPzO0uBsBdzLAngf2tp533LzZcQzb6+eZPJ+vCTt+bqF2XnvT9gejTLsAcJAJyQ==", - "optional": true - }, - "@types/lodash.isequal": { - "version": "4.5.6", - "resolved": "https://registry.npmjs.org/@types/lodash.isequal/-/lodash.isequal-4.5.6.tgz", - "integrity": "sha512-Ww4UGSe3DmtvLLJm2F16hDwEQSv7U0Rr8SujLUA2wHI2D2dm8kPu6Et+/y303LfjTIwSBKXB/YTUcAKpem/XEg==", - "optional": true, - "requires": { - "@types/lodash": "*" - } - }, - "@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true - }, - "@types/node": { - "version": "16.18.104", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.104.tgz", - "integrity": "sha512-OF3keVCbfPlkzxnnDBUZJn1RiCJzKeadjiW0xTEb0G1SUJ5gDVb3qnzZr2T4uIFvsbKJbXy1v2DN7e2zaEY7jQ==", - "dev": true - }, - "@types/node-forge": { - "version": "1.3.11", - "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", - "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true - }, - "@types/q": { - "version": "0.0.32", - "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", - "integrity": "sha512-qYi3YV9inU/REEfxwVcGZzbS3KG/Xs90lv0Pr+lDtuVjBPGd1A+eciXzVSaRvLify132BfcvhvEjeVahrUl0Ug==", - "dev": true - }, - "@types/qs": { - "version": "6.9.15", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", - "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==", - "dev": true - }, - "@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true - }, - "@types/resolve": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", - "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", - "dev": true - }, - "@types/selenium-webdriver": { - "version": "3.0.22", - "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.22.tgz", - "integrity": "sha512-Nh76NUqvfsZHG5ot5gMlHNNHQvbRvv5UpM4FH3K1HuUGeq4scNlRoKVKSOP/EGIYHhJ2IUXyQc+38jvZLxfB2Q==", - "dev": true - }, - "@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "dev": true, - "requires": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "@types/serve-index": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", - "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", - "dev": true, - "requires": { - "@types/express": "*" - } - }, - "@types/serve-static": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", - "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", - "dev": true, - "requires": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" - } - }, - "@types/sockjs": { - "version": "0.3.36", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", - "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/systemjs": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@types/systemjs/-/systemjs-6.13.1.tgz", - "integrity": "sha512-Jxo2/uif1WpkabfyvWpFmPWFPDdwKUmyL7xWzjtxNALEu2pgce+eISjbf0Vr+SsK/D9savO5kTRcf+COLK5eiQ==" - }, - "@types/ws": { - "version": "8.5.12", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", - "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", - "dev": true, - "requires": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" - } - }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", - "dev": true - }, - "@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", - "dev": true - }, - "@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", - "dev": true - }, - "@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", - "dev": true, - "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", - "dev": true - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" - } - }, - "@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", - "dev": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", - "dev": true, - "requires": { - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", - "dev": true - }, - "@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "@xmldom/xmldom": { - "version": "0.8.10", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", - "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", - "optional": true - }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, - "@yarnpkg/lockfile": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", - "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", - "dev": true - }, - "abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "dev": true - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, - "accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, - "requires": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - } - }, - "adjust-sourcemap-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", - "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", - "dev": true, - "requires": { - "loader-utils": "^2.0.0", - "regex-parser": "^2.2.11" - }, - "dependencies": { - "loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - } - } - }, - "adm-zip": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", - "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==", - "dev": true - }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "requires": { - "debug": "4" - } - }, - "agentkeepalive": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.3.0.tgz", - "integrity": "sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "depd": "^2.0.0", - "humanize-ms": "^1.2.1" - } - }, - "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, - "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "requires": { - "ajv": "^8.0.0" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "angular-in-memory-web-api": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/angular-in-memory-web-api/-/angular-in-memory-web-api-0.14.0.tgz", - "integrity": "sha512-8RLFBpXZONDQxYGKiheaYQQl3iydesCrhWLuzDD6AsQDcOF+HEvIuOfBdJaTWKfqyNZNWjHvXzIyT0bUIunb/A==", - "dev": true, - "requires": { - "tslib": "^2.3.0" - } - }, - "ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - } - }, - "ansi-html-community": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", - "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", - "dev": true - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "apache-crypt": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/apache-crypt/-/apache-crypt-1.2.6.tgz", - "integrity": "sha512-072WetlM4blL8PREJVeY+WHiUh1R5VNt2HfceGS8aKqttPHcmqE5pkKuXPz/ULmJOFkc8Hw3kfKl6vy7Qka6DA==", - "dev": true, - "requires": { - "unix-crypt-td-js": "^1.1.4" - } - }, - "apache-md5": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/apache-md5/-/apache-md5-1.1.8.tgz", - "integrity": "sha512-FCAJojipPn0bXjuEpjOOOMN8FZDkxfWWp4JGN9mifU2IhxvKyXZYqpzPHdnTSUpmPDy+tsslB6Z1g+Vg6nVbYA==", - "dev": true - }, - "append-transform": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", - "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", - "dev": true, - "requires": { - "default-require-extensions": "^2.0.0" - } - }, - "applicationinsights-js": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/applicationinsights-js/-/applicationinsights-js-1.0.21.tgz", - "integrity": "sha512-AUkkm8OWfCgbBuMe7kSAwUFpc1e2y+WisieQx/VgCS+BT/0AubmnGZ1yQ+zkENVriM9qArKNjLqTQp38x995wg==" - }, - "aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "dev": true - }, - "are-we-there-yet": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", - "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", - "dev": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", - "dev": true, - "requires": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" - } - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "dev": true - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", - "dev": true, - "requires": { - "array-uniq": "^1.0.1" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", - "dev": true - }, - "arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", - "dev": true, - "requires": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" - } - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", - "dev": true - }, - "asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "dev": true - }, - "async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true - }, - "autoprefixer": { - "version": "10.4.14", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", - "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", - "dev": true, - "requires": { - "browserslist": "^4.21.5", - "caniuse-lite": "^1.0.30001464", - "fraction.js": "^4.2.0", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" - } - }, - "available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dev": true, - "requires": { - "possible-typed-array-names": "^1.0.0" - } - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "dev": true - }, - "aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", - "dev": true - }, - "babel-loader": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", - "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", - "dev": true, - "requires": { - "find-cache-dir": "^3.3.1", - "loader-utils": "^2.0.0", - "make-dir": "^3.1.0", - "schema-utils": "^2.6.5" - }, - "dependencies": { - "loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - } - } - }, - "babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - } - }, - "babel-plugin-polyfill-corejs2": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", - "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.3", - "semver": "^6.1.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "babel-plugin-polyfill-corejs3": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz", - "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.2", - "core-js-compat": "^3.21.0" - } - }, - "babel-plugin-polyfill-regenerator": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", - "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.3" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true - }, - "base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "dev": true - }, - "basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "dev": true, - "requires": { - "safe-buffer": "5.1.2" - } - }, - "batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", - "dev": true - }, - "batch-processor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/batch-processor/-/batch-processor-1.0.0.tgz", - "integrity": "sha512-xoLQD8gmmR32MeuBHgH0Tzd5PuSZx71ZsbhVxOCRbgktZEPe4SQy7s9Z50uPp0F/f7iw2XmkHN2xkgbMfckMDA==" - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "bcryptjs": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", - "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==", - "dev": true - }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true - }, - "billboard.js": { - "version": "3.12.4", - "resolved": "https://registry.npmjs.org/billboard.js/-/billboard.js-3.12.4.tgz", - "integrity": "sha512-iXKDrci2UVH9J0b5Y8gRsr650bQk2TfIRycBAgVZZjdHkwmKDdTLD0D2YxbVe3GvcP60Tog2HypQV64QlAuTwQ==", - "requires": { - "@types/d3-selection": "^3.0.0", - "@types/d3-transition": "^3.0.0", - "d3-axis": "^3.0.0", - "d3-brush": "^3.0.0", - "d3-drag": "^3.0.0", - "d3-dsv": "^3.0.1", - "d3-ease": "^3.0.1", - "d3-hierarchy": "^3.1.2", - "d3-interpolate": "^3.0.1", - "d3-scale": "^4.0.2", - "d3-selection": "^3.0.0", - "d3-shape": "^3.2.0", - "d3-time-format": "^4.1.0", - "d3-transition": "^3.0.1", - "d3-zoom": "^3.0.0" - } - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "blocking-proxy": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-1.0.1.tgz", - "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", - "dev": true, - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - } - } - }, - "bonjour-service": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", - "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3", - "multicast-dns": "^7.2.5" - } - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true - }, - "bootstrap.native": { - "version": "5.0.13", - "resolved": "https://registry.npmjs.org/bootstrap.native/-/bootstrap.native-5.0.13.tgz", - "integrity": "sha512-SiiTxaK3LjuOjPaXEnDBQNY3w0t28Qdx6I8drortuFg6Ch3q6cWoOxlFHThcGOPewziVarQAA4WPE00GFQmbWQ==", - "dev": true, - "requires": { - "@thednp/event-listener": "^2.0.4", - "@thednp/shorty": "^2.0.0" - } - }, - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "requires": { - "fill-range": "^7.1.1" - } - }, - "brotli": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", - "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", - "dev": true, - "requires": { - "base64-js": "^1.1.2" - } - }, - "browserslist": { - "version": "4.23.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz", - "integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001640", - "electron-to-chromium": "^1.4.820", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.1.0" - } - }, - "browserstack": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.6.1.tgz", - "integrity": "sha512-GxtFjpIaKdbAyzHfFDKixKO8IBT7wR3NjbzrGc78nNs/Ciys9wU3/nBtsqsWv5nDSrdI5tz0peKuzCPuNXNUiw==", - "dev": true, - "requires": { - "https-proxy-agent": "^2.2.1" - }, - "dependencies": { - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "dev": true, - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - } - } - } - }, - "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", - "dev": true - }, - "builtins": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", - "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", - "dev": true, - "requires": { - "semver": "^7.0.0" - } - }, - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true - }, - "cacache": { - "version": "16.1.2", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.2.tgz", - "integrity": "sha512-Xx+xPlfCZIUHagysjjOAje9nRo8pRDczQCcXb4J2O0BLtH+xeVue6ba4y1kfJfQMAnM2mkcoMIAyOctlaRGWYA==", - "dev": true, - "requires": { - "@npmcli/fs": "^2.1.0", - "@npmcli/move-file": "^2.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "glob": "^8.0.1", - "infer-owner": "^1.0.4", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", - "tar": "^6.1.11", - "unique-filename": "^1.1.1" - }, - "dependencies": { - "lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true - } - } - }, - "call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dev": true, - "requires": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - } - }, - "callsite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==", - "dev": true - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30001643", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001643.tgz", - "integrity": "sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg==", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "cheerio": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", - "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", - "dev": true, - "requires": { - "cheerio-select": "^2.1.0", - "dom-serializer": "^2.0.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "htmlparser2": "^8.0.1", - "parse5": "^7.0.0", - "parse5-htmlparser2-tree-adapter": "^7.0.0" - }, - "dependencies": { - "parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "dev": true, - "requires": { - "entities": "^4.4.0" - } - } - } - }, - "cheerio-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", - "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", - "dev": true, - "requires": { - "boolbase": "^1.0.0", - "css-select": "^5.1.0", - "css-what": "^6.1.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1" - } - }, - "chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true - }, - "chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true - }, - "circular-json": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", - "dev": true - }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-spinners": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.0.tgz", - "integrity": "sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==", - "dev": true - }, - "cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true - }, - "clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - } - }, - "code-block-writer": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.2.tgz", - "integrity": "sha512-XfXzAGiStXSmCIwrkdfvc7FS5Dtj8yelCtyOf2p2skCAfvLd6zu0rGzuS9NSCO3bq1JKpFZ7tbKdKlcd5occQA==", - "dev": true - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true - }, - "colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "dev": true - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true - }, - "compare-versions": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", - "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", - "dev": true - }, - "compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dev": true, - "requires": { - "mime-db": ">= 1.43.0 < 2" - } - }, - "compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "dev": true, - "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "dependencies": { - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - } - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "connect": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", - "dev": true, - "requires": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - } - } - }, - "connect-history-api-fallback": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", - "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", - "dev": true - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "dev": true - }, - "content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dev": true, - "requires": { - "safe-buffer": "5.2.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } - } - }, - "content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "dev": true - }, - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "dev": true - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "dev": true - }, - "copy-and-watch": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/copy-and-watch/-/copy-and-watch-0.1.6.tgz", - "integrity": "sha512-lTdQK2V/7T3fsRXwiG9CORZMtiTgjIW1SMUdWctV1CagV4bzXgjXz0WS+/zpeotZ27RJvoPeR21T4pZEnUzAUQ==", - "dev": true, - "requires": { - "chokidar": "3.5.2", - "colors": "1.4.0", - "glob": "7.2.0", - "glob-parent": "6.0.2" - }, - "dependencies": { - "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, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "copy-anything": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", - "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", - "dev": true, - "requires": { - "is-what": "^3.14.1" - } - }, - "copy-webpack-plugin": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", - "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", - "dev": true, - "requires": { - "fast-glob": "^3.2.11", - "glob-parent": "^6.0.1", - "globby": "^13.1.1", - "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0" - }, - "dependencies": { - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - } - } - } - }, - "core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" - }, - "core-js-compat": { - "version": "3.37.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.1.tgz", - "integrity": "sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==", - "dev": true, - "requires": { - "browserslist": "^4.23.0" - } - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true - }, - "cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dev": true, - "requires": { - "object-assign": "^4", - "vary": "^1" - } - }, - "critters": { - "version": "0.0.16", - "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.16.tgz", - "integrity": "sha512-JwjgmO6i3y6RWtLYmXwO5jMd+maZt8Tnfu7VVISmEWyQqfLpB8soBswf8/2bu6SBXxtKA68Al3c+qIG1ApT68A==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "css-select": "^4.2.0", - "parse5": "^6.0.1", - "parse5-htmlparser2-tree-adapter": "^6.0.1", - "postcss": "8.4.31", - "pretty-bytes": "^5.3.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "dev": true, - "requires": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - } - }, - "dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - } - }, - "domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dev": true, - "requires": { - "domelementtype": "^2.2.0" - } - }, - "domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "requires": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - } - }, - "entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, - "parse5-htmlparser2-tree-adapter": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", - "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", - "dev": true, - "requires": { - "parse5": "^6.0.1" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "dependencies": { - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "crypto-js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", - "dev": true - }, - "css-blank-pseudo": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", - "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.9" - } - }, - "css-has-pseudo": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", - "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.9" - } - }, - "css-loader": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", - "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==", - "dev": true, - "requires": { - "icss-utils": "^5.1.0", - "postcss": "8.4.31", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.0", - "postcss-modules-scope": "^3.0.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.3.5" - } - }, - "css-prefers-color-scheme": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", - "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", - "dev": true, - "requires": {} - }, - "css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "dev": true, - "requires": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - } - }, - "css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "dev": true - }, - "cssdb": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.7.0.tgz", - "integrity": "sha512-1hN+I3r4VqSNQ+OmMXxYexnumbOONkSil0TWMebVXHtzYW4tRRPovUNHPHj2d4nrgOuYJ8Vs3XwvywsuwwXNNA==", - "dev": true - }, - "cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true - }, - "cuint": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz", - "integrity": "sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw==", - "dev": true - }, - "custom-event": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", - "dev": true - }, - "cytoscape": { - "version": "3.25.0", - "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.25.0.tgz", - "integrity": "sha512-7MW3Iz57mCUo6JQCho6CmPBCbTlJr7LzyEtIkutG255HLVd4XuBg2I9BkTZLI/e4HoaOB/BiAzXuQybQ95+r9Q==", - "requires": { - "heap": "^0.2.6", - "lodash": "^4.17.21" - } - }, - "cytoscape-edgehandles": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/cytoscape-edgehandles/-/cytoscape-edgehandles-4.0.1.tgz", - "integrity": "sha512-uSYshkqRZ4luCxK295bEVTg46q4ZW+fwJhcIzMrtfNR7zeAnJ38Z48kUGeu5ibtXkgLbcZAg0YE4ED2dRuaePg==", - "requires": { - "lodash.memoize": "^4.1.2", - "lodash.throttle": "^4.1.1" - } - }, - "d3-array": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", - "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", - "requires": { - "internmap": "1 - 2" - } - }, - "d3-axis": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", - "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==" - }, - "d3-brush": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", - "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", - "requires": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "3", - "d3-transition": "3" - } - }, - "d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==" - }, - "d3-dispatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", - "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==" - }, - "d3-drag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", - "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", - "requires": { - "d3-dispatch": "1 - 3", - "d3-selection": "3" - } - }, - "d3-dsv": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", - "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", - "requires": { - "commander": "7", - "iconv-lite": "0.6", - "rw": "1" - }, - "dependencies": { - "commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" - } - } - }, - "d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==" - }, - "d3-format": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", - "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==" - }, - "d3-hierarchy": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", - "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==" - }, - "d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", - "requires": { - "d3-color": "1 - 3" - } - }, - "d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==" - }, - "d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "requires": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" - } - }, - "d3-selection": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", - "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==" - }, - "d3-shape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", - "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", - "requires": { - "d3-path": "^3.1.0" - } - }, - "d3-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", - "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", - "requires": { - "d3-array": "2 - 3" - } - }, - "d3-time-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", - "requires": { - "d3-time": "1 - 3" - } - }, - "d3-timer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==" - }, - "d3-transition": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", - "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", - "requires": { - "d3-color": "1 - 3", - "d3-dispatch": "1 - 3", - "d3-ease": "1 - 3", - "d3-interpolate": "1 - 3", - "d3-timer": "1 - 3" - } - }, - "d3-zoom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", - "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", - "requires": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "2 - 3", - "d3-transition": "2 - 3" - } - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "data-view-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", - "dev": true, - "requires": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - } - }, - "data-view-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - } - }, - "data-view-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", - "dev": true, - "requires": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - } - }, - "date-format": { - "version": "4.0.14", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", - "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", - "dev": true - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "decache": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/decache/-/decache-4.6.2.tgz", - "integrity": "sha512-2LPqkLeu8XWHU8qNCS3kcF6sCcb5zIzvWaAHYSvPfwhdd7mHuah29NssMzrTYyHN4F5oFy2ko9OBYxegtU0FEw==", - "dev": true, - "requires": { - "callsite": "^1.0.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true - }, - "decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", - "dev": true - }, - "deep-equal": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", - "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", - "dev": true, - "requires": { - "is-arguments": "^1.1.1", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.5.1" - } - }, - "deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true - }, - "default-gateway": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", - "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", - "dev": true, - "requires": { - "execa": "^5.0.0" - } - }, - "default-require-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", - "integrity": "sha512-B0n2zDIXpzLzKeoEozorDSa1cHc1t0NjmxP0zuAxbizNU2MBqYJJKYXrrFdKuQliojXynrxgd7l4ahfg/+aA5g==", - "dev": true, - "requires": { - "strip-bom": "^3.0.0" - } - }, - "defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "requires": { - "clone": "^1.0.2" - } - }, - "define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, - "requires": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - } - }, - "define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "dev": true - }, - "define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "requires": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - } - }, - "del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha512-Z4fzpbIRjOu7lO5jCETSWoqUDVe0IPOlfugBsF6suen2LKDlVb4QZpKEM9P+buNJ4KI1eN7I083w/pbKUpsrWQ==", - "dev": true, - "requires": { - "globby": "^5.0.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "rimraf": "^2.2.8" - }, - "dependencies": { - "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, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "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" - } - }, - "globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha512-HJRTIH2EeH44ka+LWig+EqT2ONSYpVlNfx6pyd592/VF1TbfljJ7elwie7oSwcViLGqOdWocSdu2txwBF9bjmQ==", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "dev": true - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true - }, - "dependency-graph": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", - "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", - "dev": true - }, - "destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dev": true - }, - "detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "dev": true - }, - "dfa": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", - "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==", - "dev": true - }, - "di": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", - "dev": true - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "dns-packet": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", - "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", - "dev": true, - "requires": { - "@leichtgewicht/ip-codec": "^2.0.1" - } - }, - "dom-serialize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==", - "dev": true, - "requires": { - "custom-event": "~1.0.0", - "ent": "~2.2.0", - "extend": "^3.0.0", - "void-elements": "^2.0.0" - } - }, - "dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dev": true, - "requires": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - } - }, - "domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "dev": true - }, - "domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dev": true, - "requires": { - "domelementtype": "^2.3.0" - } - }, - "domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "dev": true, - "requires": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - } - }, - "dot": { - "version": "2.0.0-beta.1", - "resolved": "https://registry.npmjs.org/dot/-/dot-2.0.0-beta.1.tgz", - "integrity": "sha512-kxM7fSnNQTXOmaeGuBSXM8O3fEsBb7XSDBllkGbRwa0lJSJTxxDE/4eSNGLKZUmlFw0f1vJ5qSV2BljrgQtgIA==", - "dev": true - }, - "duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": true - }, - "eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "dev": true - }, - "electron-to-chromium": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.2.tgz", - "integrity": "sha512-kc4r3U3V3WLaaZqThjYz/Y6z8tJe+7K0bbjUVo3i+LWIypVdMx5nXCkwRe6SWbY6ILqLdc1rKcKmr3HoH7wjSQ==", - "dev": true - }, - "element-resize-detector": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/element-resize-detector/-/element-resize-detector-1.2.4.tgz", - "integrity": "sha512-Fl5Ftk6WwXE0wqCgNoseKWndjzZlDCwuPTcoVZfCP9R3EHQF8qUtr3YUPNETegRBOKqQKPW3n4kiIWngGi8tKg==", - "requires": { - "batch-processor": "1.0.0" - } - }, - "emitter-component": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.2.tgz", - "integrity": "sha512-QdXO3nXOzZB4pAjM0n6ZE+R9/+kPpECA/XSELIcc54NeYVnBqIk+4DFiBgK+8QbV3mdvTG6nedl7dTYgO+5wDw==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true - }, - "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 - }, - "encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "dev": true, - "optional": true, - "requires": { - "iconv-lite": "^0.6.2" - } - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "engine.io": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz", - "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==", - "dev": true, - "requires": { - "@types/cookie": "^0.4.1", - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.4.1", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.17.1" - } - }, - "engine.io-parser": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz", - "integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==", - "dev": true - }, - "enhanced-resolve": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", - "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - } - }, - "ent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==", - "dev": true - }, - "entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true - }, - "env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "dev": true - }, - "err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "dev": true - }, - "errno": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", - "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", - "dev": true, - "optional": true, - "requires": { - "prr": "~1.0.1" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", - "dev": true, - "requires": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", - "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" - } - }, - "es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dev": true, - "requires": { - "get-intrinsic": "^1.2.4" - } - }, - "es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true - }, - "es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", - "dev": true - }, - "es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", - "dev": true, - "requires": { - "es-errors": "^1.3.0" - } - }, - "es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", - "dev": true, - "requires": { - "get-intrinsic": "^1.2.4", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", - "dev": true - }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", - "dev": true, - "requires": { - "es6-promise": "^4.0.3" - } - }, - "es6-shim": { - "version": "0.35.8", - "resolved": "https://registry.npmjs.org/es6-shim/-/es6-shim-0.35.8.tgz", - "integrity": "sha512-Twf7I2v4/1tLoIXMT8HlqaBSS5H2wQTs2wx3MNYCI8K1R1/clXyCazrcVCPm/FuO9cyV8+leEaZOWD5C253NDg==", - "dev": true - }, - "esbuild": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.5.tgz", - "integrity": "sha512-VSf6S1QVqvxfIsSKb3UKr3VhUCis7wgDbtF4Vd9z84UJr05/Sp2fRKmzC+CSPG/dNAPPJZ0BTBLTT1Fhd6N9Gg==", - "dev": true, - "optional": true, - "requires": { - "@esbuild/linux-loong64": "0.15.5", - "esbuild-android-64": "0.15.5", - "esbuild-android-arm64": "0.15.5", - "esbuild-darwin-64": "0.15.5", - "esbuild-darwin-arm64": "0.15.5", - "esbuild-freebsd-64": "0.15.5", - "esbuild-freebsd-arm64": "0.15.5", - "esbuild-linux-32": "0.15.5", - "esbuild-linux-64": "0.15.5", - "esbuild-linux-arm": "0.15.5", - "esbuild-linux-arm64": "0.15.5", - "esbuild-linux-mips64le": "0.15.5", - "esbuild-linux-ppc64le": "0.15.5", - "esbuild-linux-riscv64": "0.15.5", - "esbuild-linux-s390x": "0.15.5", - "esbuild-netbsd-64": "0.15.5", - "esbuild-openbsd-64": "0.15.5", - "esbuild-sunos-64": "0.15.5", - "esbuild-windows-32": "0.15.5", - "esbuild-windows-64": "0.15.5", - "esbuild-windows-arm64": "0.15.5" - } - }, - "esbuild-android-64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.5.tgz", - "integrity": "sha512-dYPPkiGNskvZqmIK29OPxolyY3tp+c47+Fsc2WYSOVjEPWNCHNyqhtFqQadcXMJDQt8eN0NMDukbyQgFcHquXg==", - "dev": true, - "optional": true - }, - "esbuild-android-arm64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.5.tgz", - "integrity": "sha512-YyEkaQl08ze3cBzI/4Cm1S+rVh8HMOpCdq8B78JLbNFHhzi4NixVN93xDrHZLztlocEYqi45rHHCgA8kZFidFg==", - "dev": true, - "optional": true - }, - "esbuild-darwin-64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.5.tgz", - "integrity": "sha512-Cr0iIqnWKx3ZTvDUAzG0H/u9dWjLE4c2gTtRLz4pqOBGjfjqdcZSfAObFzKTInLLSmD0ZV1I/mshhPoYSBMMCQ==", - "dev": true, - "optional": true - }, - "esbuild-darwin-arm64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.5.tgz", - "integrity": "sha512-WIfQkocGtFrz7vCu44ypY5YmiFXpsxvz2xqwe688jFfSVCnUsCn2qkEVDo7gT8EpsLOz1J/OmqjExePL1dr1Kg==", - "dev": true, - "optional": true - }, - "esbuild-freebsd-64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.5.tgz", - "integrity": "sha512-M5/EfzV2RsMd/wqwR18CELcenZ8+fFxQAAEO7TJKDmP3knhWSbD72ILzrXFMMwshlPAS1ShCZ90jsxkm+8FlaA==", - "dev": true, - "optional": true - }, - "esbuild-freebsd-arm64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.5.tgz", - "integrity": "sha512-2JQQ5Qs9J0440F/n/aUBNvY6lTo4XP/4lt1TwDfHuo0DY3w5++anw+jTjfouLzbJmFFiwmX7SmUhMnysocx96w==", - "dev": true, - "optional": true - }, - "esbuild-linux-32": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.5.tgz", - "integrity": "sha512-gO9vNnIN0FTUGjvTFucIXtBSr1Woymmx/aHQtuU+2OllGU6YFLs99960UD4Dib1kFovVgs59MTXwpFdVoSMZoQ==", - "dev": true, - "optional": true - }, - "esbuild-linux-64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.5.tgz", - "integrity": "sha512-ne0GFdNLsm4veXbTnYAWjbx3shpNKZJUd6XpNbKNUZaNllDZfYQt0/zRqOg0sc7O8GQ+PjSMv9IpIEULXVTVmg==", - "dev": true, - "optional": true - }, - "esbuild-linux-arm": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.5.tgz", - "integrity": "sha512-wvAoHEN+gJ/22gnvhZnS/+2H14HyAxM07m59RSLn3iXrQsdS518jnEWRBnJz3fR6BJa+VUTo0NxYjGaNt7RA7Q==", - "dev": true, - "optional": true - }, - "esbuild-linux-arm64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.5.tgz", - "integrity": "sha512-7EgFyP2zjO065XTfdCxiXVEk+f83RQ1JsryN1X/VSX2li9rnHAt2swRbpoz5Vlrl6qjHrCmq5b6yxD13z6RheA==", - "dev": true, - "optional": true - }, - "esbuild-linux-mips64le": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.5.tgz", - "integrity": "sha512-KdnSkHxWrJ6Y40ABu+ipTZeRhFtc8dowGyFsZY5prsmMSr1ZTG9zQawguN4/tunJ0wy3+kD54GaGwdcpwWAvZQ==", - "dev": true, - "optional": true - }, - "esbuild-linux-ppc64le": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.5.tgz", - "integrity": "sha512-QdRHGeZ2ykl5P0KRmfGBZIHmqcwIsUKWmmpZTOq573jRWwmpfRmS7xOhmDHBj9pxv+6qRMH8tLr2fe+ZKQvCYw==", - "dev": true, - "optional": true - }, - "esbuild-linux-riscv64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.5.tgz", - "integrity": "sha512-p+WE6RX+jNILsf+exR29DwgV6B73khEQV0qWUbzxaycxawZ8NE0wA6HnnTxbiw5f4Gx9sJDUBemh9v49lKOORA==", - "dev": true, - "optional": true - }, - "esbuild-linux-s390x": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.5.tgz", - "integrity": "sha512-J2ngOB4cNzmqLHh6TYMM/ips8aoZIuzxJnDdWutBw5482jGXiOzsPoEF4j2WJ2mGnm7FBCO4StGcwzOgic70JQ==", - "dev": true, - "optional": true + "node": ">=0.10.0" + } }, - "esbuild-netbsd-64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.5.tgz", - "integrity": "sha512-MmKUYGDizYjFia0Rwt8oOgmiFH7zaYlsoQ3tIOfPxOqLssAsEgG0MUdRDm5lliqjiuoog8LyDu9srQk5YwWF3w==", + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", "dev": true, - "optional": true + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "esbuild-openbsd-64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.5.tgz", - "integrity": "sha512-2mMFfkLk3oPWfopA9Plj4hyhqHNuGyp5KQyTT9Rc8hFd8wAn5ZrbJg+gNcLMo2yzf8Uiu0RT6G9B15YN9WQyMA==", + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, - "optional": true + "engines": { + "node": ">= 0.4" + } }, - "esbuild-sunos-64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.5.tgz", - "integrity": "sha512-2sIzhMUfLNoD+rdmV6AacilCHSxZIoGAU2oT7XmJ0lXcZWnCvCtObvO6D4puxX9YRE97GodciRGDLBaiC6x1SA==", + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dev": true, - "optional": true + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "esbuild-wasm": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.15.5.tgz", - "integrity": "sha512-lTJOEKekN/4JI/eOEq0wLcx53co2N6vaT/XjBz46D1tvIVoUEyM0o2K6txW6gEotf31szFD/J1PbxmnbkGlK9A==", + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", "dev": true }, - "esbuild-windows-32": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.5.tgz", - "integrity": "sha512-e+duNED9UBop7Vnlap6XKedA/53lIi12xv2ebeNS4gFmu7aKyTrok7DPIZyU5w/ftHD4MUDs5PJUkQPP9xJRzg==", - "dev": true, - "optional": true - }, - "esbuild-windows-64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.5.tgz", - "integrity": "sha512-v+PjvNtSASHOjPDMIai9Yi+aP+Vwox+3WVdg2JB8N9aivJ7lyhp4NVU+J0MV2OkWFPnVO8AE/7xH+72ibUUEnw==", + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dev": true, - "optional": true + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } }, - "esbuild-windows-arm64": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.5.tgz", - "integrity": "sha512-Yz8w/D8CUPYstvVQujByu6mlf48lKmXkq6bkeSZZxTA626efQOJb26aDGLzmFWx6eg/FwrXgt6SZs9V8Pwy/aA==", + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", "dev": true, - "optional": true - }, - "escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "dev": true - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "dev": true + "engines": { + "node": ">= 0.8" + } }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } }, - "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==", + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, "dependencies": { - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - } + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "node_modules/only": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz", + "integrity": "sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==", "dev": true }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "node_modules/open": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz", + "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "estree-walker": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", - "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", - "dev": true - }, - "esutils": { + "node_modules/opencollective-postinstall": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "dev": true - }, - "event-stream": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-4.0.1.tgz", - "integrity": "sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", "dev": true, - "requires": { - "duplexer": "^0.1.1", - "from": "^0.1.7", - "map-stream": "0.0.7", - "pause-stream": "^0.0.11", - "split": "^1.0.1", - "stream-combiner": "^0.2.2", - "through": "^2.3.8" + "bin": { + "opencollective-postinstall": "index.js" } }, - "eventemitter-asyncresource": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz", - "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==", - "dev": true - }, - "eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, - "events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" + "bin": { + "opener": "bin/opener-bin.js" } }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true - }, - "exponential-backoff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", - "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", - "dev": true - }, - "express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, - "requires": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.2", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.6.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, "dependencies": { - "cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true - } + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" } }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "node_modules/ora/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" }, - "dependencies": { - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - } + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "dev": true - }, - "fancy-log": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-2.0.0.tgz", - "integrity": "sha512-9CzxZbACXMUXW13tS0tI8XsGGmxWzO2DmYrGuBJOJ8k8q2K7hwfJA5qHjuPPe8wtsco33YR9wc+Rlr5wYFvhSA==", + "node_modules/ora/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "requires": { - "color-support": "^1.1.3" + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "node_modules/ora/node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } }, - "fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "node_modules/ora/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "node_modules/ora/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "node_modules/ora/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "requires": { - "reusify": "^1.0.4" + "engines": { + "node": ">=8" } }, - "faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "node_modules/ora/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, - "requires": { - "websocket-driver": ">=0.5.1" + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "node_modules/ora/node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" } }, - "file-saver": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", - "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" - }, - "fileset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", - "integrity": "sha512-UxowFKnAFIwtmSxgKjWAVgjE3Fk7MQJT0ZIyl0NwIFZTrx4913rLaonGJ84V+x/2+w/pe4ULHRns+GZPs1TVuw==", - "dev": true, - "requires": { - "glob": "^7.0.3", - "minimatch": "^3.0.3" - }, - "dependencies": { - "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, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "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" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } + "node_modules/ora/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true }, - "fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "node_modules/ora/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "requires": { - "to-regex-range": "^5.0.1" + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "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==", + "dev": true + }, + "node_modules/os-name": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz", + "integrity": "sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==", "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - } + "macos-release": "^2.5.0", + "windows-release": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" + "engines": { + "node": ">=0.10.0" } }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", - "dev": true - }, - "follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", - "dev": true - }, - "for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, - "requires": { - "is-callable": "^1.1.3" + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, "dependencies": { - "signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true - } + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "dev": true - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "node_modules/p-retry": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.0.tgz", + "integrity": "sha512-JA6nkq6hKyWLLasXQXUrO4z8BUZGUt/LjlJxx8Gb2+2ntodU/SS63YZ8b0LUTbQ8ZB9iwOfhEPhg4ykKnn2KsA==", "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" + "dependencies": { + "@types/retry": "0.12.2", + "is-network-error": "^1.0.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "dev": true - }, - "fraction.js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", - "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", - "dev": true - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "dev": true - }, - "from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", - "dev": true - }, - "fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "node_modules/p-retry/node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "engines": { + "node": ">= 4" } }, - "fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, - "requires": { - "minipass": "^3.0.0" + "engines": { + "node": ">=6" } }, - "fs-monkey": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", - "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", - "dev": true - }, - "fs.realpath": { + "node_modules/package-json-from-dist": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", + "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", "dev": true }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "node_modules/pacote": { + "version": "18.0.6", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-18.0.6.tgz", + "integrity": "sha512-+eK3G27SMwsB8kLIuj4h1FUhHtwiEUo21Tw8wNjmvdlpOEr613edv+8FUsTj/4F/VN5ywGE19X18N7CC2EJk6A==", "dev": true, - "optional": true + "dependencies": { + "@npmcli/git": "^5.0.0", + "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/package-json": "^5.1.0", + "@npmcli/promise-spawn": "^7.0.0", + "@npmcli/run-script": "^8.0.0", + "cacache": "^18.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^11.0.0", + "npm-packlist": "^8.0.0", + "npm-pick-manifest": "^9.0.0", + "npm-registry-fetch": "^17.0.0", + "proc-log": "^4.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^2.2.0", + "ssri": "^10.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } }, - "function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", "dev": true }, - "function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true - }, - "gauge": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", - "dev": true, - "requires": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "node_modules/parse-json/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 }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "node_modules/parse-json/node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, - "get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", "dev": true, - "requires": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "engines": { + "node": ">= 0.10" } }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true + "node_modules/parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true + "node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "devOptional": true, + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } }, - "get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "node_modules/parse5-html-rewriting-stream": { + "version": "7.0.0", + "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, - "requires": { - "call-bind": "^1.0.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" + "dependencies": { + "entities": "^4.3.0", + "parse5": "^7.0.0", + "parse5-sax-parser": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", "dev": true, - "requires": { - "assert-plus": "^1.0.0" + "dependencies": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "glob": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", - "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/parse5-sax-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-7.0.0.tgz", + "integrity": "sha512-5A+v2SNsq8T6/mG3ahcz8ZtQ0OUFTatxPbeidoMB7tkJSGDY3tdfl4MHovtLQHkEn5CGxijNWRQHhRQ6IRpXKg==", "dev": true, - "requires": { - "is-glob": "^4.0.1" + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "dev": true, - "requires": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" + "engines": { + "node": ">= 0.8" } }, - "globby": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", - "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, - "requires": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.3.0", - "ignore": "^5.2.4", - "merge2": "^1.4.1", - "slash": "^4.0.0" + "engines": { + "node": ">=8" } }, - "gopd": { + "node_modules/path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, - "requires": { - "get-intrinsic": "^1.1.3" + "engines": { + "node": ">=0.10.0" } }, - "graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "hammerjs": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", - "integrity": "sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==", - "dev": true + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } }, - "handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, - "handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, - "requires": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4", - "wordwrap": "^1.0.0" - }, "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "node_modules/path-to-regexp": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "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 - } + "engines": { + "node": ">=8" } }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "node_modules/pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", "dev": true, - "requires": { - "function-bind": "^1.1.1" + "dependencies": { + "through": "~2.3" } }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "node_modules/pdfmake": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/pdfmake/-/pdfmake-0.2.12.tgz", + "integrity": "sha512-TFsqaG6KVtk+TWermmJNNwom3wmB/xiz07prM74KBhdM+7pz3Uwq2b0uoqhhQRn6cYUTpL8lXZY6xF011o1YcQ==", "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - }, "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true - } + "@foliojs-fork/linebreak": "^1.1.1", + "@foliojs-fork/pdfkit": "^0.14.0", + "iconv-lite": "^0.6.3", + "xmldoc": "^1.1.2" + }, + "engines": { + "node": ">=12" } }, - "has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "node_modules/picocolors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", "dev": true }, - "has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, - "requires": { - "es-define-property": "^1.0.0" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "dev": true - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true - }, - "has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true, - "requires": { - "has-symbols": "^1.0.3" + "engines": { + "node": ">=0.10.0" } }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "dev": true - }, - "hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "node_modules/piscina": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.6.1.tgz", + "integrity": "sha512-z30AwWGtQE+Apr+2WBZensP2lIvwoaMcOPkQlIEmSGMJNUvaYACylPYrQM6wSdUNJlnDVMSpLv7xTMJqlVshOA==", "dev": true, - "requires": { - "function-bind": "^1.1.2" + "optionalDependencies": { + "nice-napi": "^1.0.2" } }, - "hdr-histogram-js": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz", - "integrity": "sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g==", + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, - "requires": { - "@assemblyscript/loader": "^0.10.1", - "base64-js": "^1.2.0", - "pako": "^1.0.3" + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "hdr-histogram-percentiles-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz", - "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==", - "dev": true - }, - "heap": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz", - "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==" - }, - "hosted-git-info": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.2.1.tgz", - "integrity": "sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==", + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, - "requires": { - "lru-cache": "^7.5.1" - }, "dependencies": { - "lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true - } + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, - "requires": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" + "dependencies": { + "p-locate": "^4.1.0" }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, "dependencies": { - "readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "html-entities": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", - "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", - "dev": true + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "node_modules/png-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz", + "integrity": "sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g==", "dev": true }, - "htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "node_modules/portfinder": { + "version": "1.0.32", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", + "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", "dev": true, - "requires": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" + "dependencies": { + "async": "^2.6.4", + "debug": "^3.2.7", + "mkdirp": "^0.5.6" + }, + "engines": { + "node": ">= 0.12.0" } }, - "http-auth": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/http-auth/-/http-auth-4.1.9.tgz", - "integrity": "sha512-kvPYxNGc9EKGTXvOMnTBQw2RZfuiSihK/mLw/a4pbtRueTE45S55Lw/3k5CktIf7Ak0veMKEIteDj4YkNmCzmQ==", + "node_modules/portfinder/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "dev": true, - "requires": { - "apache-crypt": "^1.1.2", - "apache-md5": "^1.0.6", - "bcryptjs": "^2.4.3", - "uuid": "^8.3.2" + "dependencies": { + "lodash": "^4.17.14" } }, - "http-auth-connect": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/http-auth-connect/-/http-auth-connect-1.0.6.tgz", - "integrity": "sha512-yaO0QSCPqGCjPrl3qEEHjJP+lwZ6gMpXLuCBE06eWwcXomkI5TARtu0kxf9teFuBj6iaV3Ybr15jaWUvbzNzHw==", - "dev": true + "node_modules/portfinder/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } }, - "http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "dev": true + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + } }, - "http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", - "dev": true + "node_modules/postcss": { + "version": "8.4.41", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", + "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", + "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" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.1", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } }, - "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "node_modules/postcss-calc": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz", + "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==", "dev": true, - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "dependencies": { + "postcss-selector-parser": "^6.0.11", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" }, + "peerDependencies": { + "postcss": "^8.2.2" + } + }, + "node_modules/postcss-colormin": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.1.0.tgz", + "integrity": "sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==", + "dev": true, "dependencies": { - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true - } + "browserslist": "^4.23.0", + "caniuse-api": "^3.0.0", + "colord": "^2.9.3", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, - "http-parser-js": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", - "dev": true - }, - "http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "node_modules/postcss-convert-values": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz", + "integrity": "sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==", "dev": true, - "requires": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" + "dependencies": { + "browserslist": "^4.23.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, - "http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "node_modules/postcss-discard-comments": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz", + "integrity": "sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==", "dev": true, - "requires": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, - "http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "node_modules/postcss-discard-duplicates": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz", + "integrity": "sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==", "dev": true, - "requires": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "node_modules/postcss-discard-empty": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz", + "integrity": "sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==", "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, - "https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "node_modules/postcss-discard-overridden": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz", + "integrity": "sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==", "dev": true, - "requires": { - "agent-base": "6", - "debug": "4" + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, - "humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "node_modules/postcss-import": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", + "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", "dev": true, - "requires": { - "ms": "^2.0.0" + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" } }, - "i18next": { - "version": "23.12.2", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.12.2.tgz", - "integrity": "sha512-XIeh5V+bi8SJSWGL3jqbTEBW5oD6rbP5L+E7dVQh1MNTxxYef0x15rhJVcRb7oiuq4jLtgy2SD8eFlf6P2cmqg==", + "node_modules/postcss-loader": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.1.1.tgz", + "integrity": "sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==", "dev": true, - "requires": { - "@babel/runtime": "^7.23.2" - }, "dependencies": { - "@babel/runtime": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz", - "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==", - "dev": true, - "requires": { - "regenerator-runtime": "^0.14.0" - } + "cosmiconfig": "^9.0.0", + "jiti": "^1.20.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true }, - "regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true + "webpack": { + "optional": true } } }, - "iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - }, - "icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true, - "requires": {} - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "node_modules/postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", "dev": true }, - "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true - }, - "ignore-walk": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-5.0.1.tgz", - "integrity": "sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==", + "node_modules/postcss-merge-longhand": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz", + "integrity": "sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==", "dev": true, - "requires": { - "minimatch": "^5.0.1" + "dependencies": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^6.1.1" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, - "image-size": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", - "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "node_modules/postcss-merge-rules": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz", + "integrity": "sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==", "dev": true, - "optional": true - }, - "immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", - "dev": true - }, - "immutable": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.2.tgz", - "integrity": "sha512-oGXzbEDem9OOpDWZu88jGiYCvIsLHMvGw+8OXlpsvTFvIQplQbjg1B1cvKg8f7Hoch6+NGjpPsH1Fr+Mc2D1aA==", - "dev": true + "dependencies": { + "browserslist": "^4.23.0", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^4.0.2", + "postcss-selector-parser": "^6.0.16" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "node_modules/postcss-minify-font-values": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz", + "integrity": "sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==", "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, "dependencies": { - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - } + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, - "infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "dev": true + "node_modules/postcss-minify-gradients": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz", + "integrity": "sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==", + "dev": true, + "dependencies": { + "colord": "^2.9.3", + "cssnano-utils": "^4.0.2", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "node_modules/postcss-minify-params": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz", + "integrity": "sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==", "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" + "dependencies": { + "browserslist": "^4.23.0", + "cssnano-utils": "^4.0.2", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "node_modules/postcss-minify-selectors": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz", + "integrity": "sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.16" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } }, - "ini": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.0.tgz", - "integrity": "sha512-TxYQaeNW/N8ymDvwAxPyRbhMBtnEwuvaTYpOQkFx1nSeusgezHniEc/l35Vo4iCq/mMiTJbpD7oYxN98hFlfmw==", - "dev": true + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } }, - "injection-js": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/injection-js/-/injection-js-2.4.0.tgz", - "integrity": "sha512-6jiJt0tCAo9zjHbcwLiPL+IuNe9SQ6a9g0PEzafThW3fOQi0mrmiJGBJvDD6tmhPh8cQHIQtCOrJuBfQME4kPA==", + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", + "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", "dev": true, - "requires": { - "tslib": "^2.0.0" + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" } }, - "inquirer": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", - "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", + "node_modules/postcss-modules-scope": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", + "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^7.0.0" + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "requires": { - "tslib": "^2.1.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz", + "integrity": "sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==", + "dev": true, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, - "internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "node_modules/postcss-normalize-display-values": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz", + "integrity": "sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==", "dev": true, - "requires": { - "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, - "internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==" - }, - "ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "node_modules/postcss-normalize-positions": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz", + "integrity": "sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==", "dev": true, - "requires": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, "dependencies": { - "jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "dev": true - }, - "sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "dev": true - } + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, - "ipaddr.js": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", - "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", - "dev": true - }, - "is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "node_modules/postcss-normalize-repeat-style": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz", + "integrity": "sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==", "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, - "is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "node_modules/postcss-normalize-string": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz", + "integrity": "sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==", "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "node_modules/postcss-normalize-timing-functions": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz", + "integrity": "sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==", "dev": true, - "requires": { - "has-bigints": "^1.0.1" + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "node_modules/postcss-normalize-unicode": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz", + "integrity": "sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==", "dev": true, - "requires": { - "binary-extensions": "^2.0.0" + "dependencies": { + "browserslist": "^4.23.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, - "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "node_modules/postcss-normalize-url": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz", + "integrity": "sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==", "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, - "is-builtin-module": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", - "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "node_modules/postcss-normalize-whitespace": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz", + "integrity": "sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==", "dev": true, - "requires": { - "builtin-modules": "^3.3.0" + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, - "is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true - }, - "is-core-module": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", - "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "node_modules/postcss-ordered-values": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz", + "integrity": "sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==", "dev": true, - "requires": { - "has": "^1.0.3" + "dependencies": { + "cssnano-utils": "^4.0.2", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, - "is-data-view": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "node_modules/postcss-reduce-initial": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz", + "integrity": "sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==", "dev": true, - "requires": { - "is-typed-array": "^1.1.13" + "dependencies": { + "browserslist": "^4.23.0", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, - "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "node_modules/postcss-reduce-transforms": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz", + "integrity": "sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==", "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, - "requires": { - "is-extglob": "^2.1.1" + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" } }, - "is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true - }, - "is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "dev": true - }, - "is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", - "dev": true - }, - "is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true + "node_modules/postcss-svgo": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.3.tgz", + "integrity": "sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^3.2.0" + }, + "engines": { + "node": "^14 || ^16 || >= 18" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } }, - "is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "node_modules/postcss-unique-selectors": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz", + "integrity": "sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==", "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" + "dependencies": { + "postcss-selector-parser": "^6.0.16" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, - "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha512-cnS56eR9SPAscL77ik76ATVqoPARTqPIVkMDVxRaWH06zT+6+CzIroYRJ0VVvm0Z1zfAvxvz9i/D3Ppjaqt5Nw==", + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, - "is-path-in-cwd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, - "requires": { - "is-path-inside": "^1.0.0" + "engines": { + "node": ">= 0.8.0" } }, - "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha512-qhsCR/Esx4U4hg/9I19OVUAJkGWtjRYHMRgUMZE2TDdj+Ag+kttZanLupfddNyglzz50cUlmWzUaI37GDfNx/g==", + "node_modules/prettier": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", + "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", "dev": true, - "requires": { - "path-is-inside": "^1.0.1" + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", - "dev": true + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "node_modules/prismjs": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "engines": { + "node": ">=6" } }, - "is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "node_modules/proc-log": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", + "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", "dev": true, - "requires": { - "call-bind": "^1.0.7" + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "is-stream": { + "node_modules/process-nextick-args": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" } }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "node_modules/propagating-hammerjs": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/propagating-hammerjs/-/propagating-hammerjs-1.5.0.tgz", + "integrity": "sha512-3PUXWmomwutoZfydC+lJwK1bKCh6sK6jZGB31RUX6+4EXzsbkDZrK4/sVR7gBrvJaEIwpTVyxQUAd29FKkmVdw==", "dev": true, - "requires": { - "has-symbols": "^1.0.2" + "dependencies": { + "hammerjs": "^2.0.8" } }, - "is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dev": true, - "requires": { - "which-typed-array": "^1.1.14" + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" } }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, - "is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "node_modules/proxy-middleware": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/proxy-middleware/-/proxy-middleware-0.15.0.tgz", + "integrity": "sha512-EGCG8SeoIRVMhsqHQUdDigB2i7qU7fCsWASwn54+nPutYO8n4q6EiwMzyfWlC+dzRFExP+kvcnDFdBDHoZBU7Q==", "dev": true, - "requires": { - "call-bind": "^1.0.2" + "engines": { + "node": ">=0.8.0" } }, - "is-what": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", - "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", - "dev": true + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "optional": true }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, - "requires": { - "is-docker": "^2.0.0" + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "isbinaryfile": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", - "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", "dev": true }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "dev": true + "node_modules/qjobs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "dev": true, + "engines": { + "node": ">=0.9" + } }, - "istanbul-api": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.1.7.tgz", - "integrity": "sha512-LYTOa2UrYFyJ/aSczZi/6lBykVMjCCvUmT64gOe+jPZFy4w6FYfPGqFT2IiQ2BxVHHDOvCD7qrIXb0EOh4uGWw==", + "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, - "requires": { - "async": "^2.6.2", - "compare-versions": "^3.4.0", - "fileset": "^2.0.3", - "istanbul-lib-coverage": "^2.0.5", - "istanbul-lib-hook": "^2.0.7", - "istanbul-lib-instrument": "^3.3.0", - "istanbul-lib-report": "^2.0.8", - "istanbul-lib-source-maps": "^3.0.6", - "istanbul-reports": "^2.2.5", - "js-yaml": "^3.13.1", - "make-dir": "^2.1.0", - "minimatch": "^3.0.4", - "once": "^1.4.0" - }, "dependencies": { - "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, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", - "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", - "dev": true, - "requires": { - "@babel/generator": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", - "istanbul-lib-coverage": "^2.0.5", - "semver": "^6.0.0" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "dependencies": { - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - } - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true + { + "type": "patreon", + "url": "https://www.patreon.com/feross" }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true + { + "type": "consulting", + "url": "https://feross.org/support" } - } + ] }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "node_modules/rambda": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/rambda/-/rambda-9.3.0.tgz", + "integrity": "sha512-cl/7DCCKNxmsbc0dXZTJTY08rvDdzLhVfE6kPBson1fWzDapLzv0RKSzjpmAqP53fkQqAvq05gpUVHTrUNsuxg==", "dev": true }, - "istanbul-lib-hook": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz", - "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==", + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, - "requires": { - "append-transform": "^1.0.0" + "dependencies": { + "safe-buffer": "^5.1.0" } }, - "istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } + "engines": { + "node": ">= 0.6" } }, - "istanbul-lib-report": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", - "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dev": true, - "requires": { - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "supports-color": "^6.1.0" - }, "dependencies": { - "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", - "source-map": "^0.6.1" + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, "dependencies": { - "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, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "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" - } - }, - "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } + "pify": "^2.3.0" + } + }, + "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==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, - "istanbul-reports": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz", - "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==", + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, - "requires": { - "html-escaper": "^2.0.0" + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" } }, - "jackspeak": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.2.tgz", - "integrity": "sha512-mgNtVv4vUuaKA97yxUHoA3+FkuhtxkjdXEWOyB/N76fjy0FjezEt34oy3epBtvCvS+7DyKwqCFWx/oJLV5+kCg==", + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "requires": { - "@isaacs/cliui": "^8.0.2", - "@pkgjs/parseargs": "^0.11.0" + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "jasmine": { - "version": "3.99.0", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.99.0.tgz", - "integrity": "sha512-YIThBuHzaIIcjxeuLmPD40SjxkEcc8i//sGMDKCgkRMVgIwRJf5qyExtlJpQeh7pkeoBSOe6lQEdg+/9uKg9mw==", - "dev": true, - "requires": { - "glob": "^7.1.6", - "jasmine-core": "~3.99.0" - }, - "dependencies": { - "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, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "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" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "dev": true }, - "jasmine-core": { - "version": "3.99.1", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.99.1.tgz", - "integrity": "sha512-Hu1dmuoGcZ7AfyynN3LsfruwMbxMALMka+YtZeGoLuDEySVmVAPaonkNoBRIw/ectu8b9tVQCJNgp4a4knp+tg==", + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", "dev": true }, - "jasmine-spec-reporter": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-5.0.2.tgz", - "integrity": "sha512-6gP1LbVgJ+d7PKksQBc2H0oDGNRQI3gKUsWlswKaQ2fif9X5gzhQcgM5+kiJGCQVurOG09jqNhk7payggyp5+g==", + "node_modules/regenerate-unicode-properties": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", + "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", "dev": true, - "requires": { - "colors": "1.4.0" + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" } }, - "jasminewd2": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz", - "integrity": "sha512-Rn0nZe4rfDhzA63Al3ZGh0E+JTmM6ESZYXJGKuqKGZObsAB9fwXPD03GjtIEvJBDOhN94T5MzbwZSqzFHSQPzg==", + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", "dev": true }, - "jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "node_modules/regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "@babel/runtime": "^7.8.4" } }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "node_modules/regex-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.0.tgz", + "integrity": "sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg==", "dev": true }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "node_modules/regexp.prototype.flags": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "dependencies": { + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "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 - }, - "json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "node_modules/regexpu-core": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "dev": true, + "dependencies": { + "@babel/regjsgen": "^0.8.0", + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true + "node_modules/regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "dev": true, + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } }, - "jsonc-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz", - "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==", - "dev": true + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" + "engines": { + "node": ">=0.10.0" } }, - "jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", - "dev": true - }, - "jsonschema": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz", - "integrity": "sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==", + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true }, - "jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - } - }, - "jszip": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", - "dev": true, - "requires": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "karma": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.3.tgz", - "integrity": "sha512-LuucC/RE92tJ8mlCwqEoRWXP38UMAqpnq98vktmS9SznSoUPPUJQbc91dHcxcunROvfQjdORVA/YFviH+Xci9Q==", + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, - "requires": { - "@colors/colors": "1.5.0", - "body-parser": "^1.19.0", - "braces": "^3.0.2", - "chokidar": "^3.5.1", - "connect": "^3.7.0", - "di": "^0.0.1", - "dom-serialize": "^2.2.1", - "glob": "^7.1.7", - "graceful-fs": "^4.2.6", - "http-proxy": "^1.18.1", - "isbinaryfile": "^4.0.8", - "lodash": "^4.17.21", - "log4js": "^6.4.1", - "mime": "^2.5.2", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.5", - "qjobs": "^1.2.0", - "range-parser": "^1.2.1", - "rimraf": "^3.0.2", - "socket.io": "^4.7.2", - "source-map": "^0.6.1", - "tmp": "^0.2.1", - "ua-parser-js": "^0.7.30", - "yargs": "^16.1.1" - }, "dependencies": { - "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, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "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" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "requires": { - "minimist": "^1.2.6" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "requires": { - "rimraf": "^3.0.0" - } - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true - } + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "karma-chrome-launcher": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.1.tgz", - "integrity": "sha512-hsIglcq1vtboGPAN+DGCISCFOxW+ZVnIqhDQcCMqqCp+4dmJ0Qpq5QAjkbA0X2L9Mi6OBkHi2Srrbmm7pUKkzQ==", + "node_modules/resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", "dev": true, - "requires": { - "which": "^1.2.1" + "dependencies": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "karma-cli": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/karma-cli/-/karma-cli-2.0.0.tgz", - "integrity": "sha512-1Kb28UILg1ZsfqQmeELbPzuEb5C6GZJfVIk0qOr8LNYQuYWmAaqP16WpbpKEjhejDrDYyYOwwJXSZO6u7q5Pvw==", + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, - "requires": { - "resolve": "^1.3.3" + "engines": { + "node": ">=4" } }, - "karma-coverage-istanbul-reporter": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-2.0.6.tgz", - "integrity": "sha512-WFh77RI8bMIKdOvI/1/IBmgnM+Q7NOLhnwG91QJrM8lW+CIXCjTzhhUsT/svLvAkLmR10uWY4RyYbHMLkTglvg==", + "node_modules/resolve-url-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", + "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", "dev": true, - "requires": { - "istanbul-api": "^2.1.6", - "minimatch": "^3.0.4" - }, "dependencies": { - "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, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.14", + "source-map": "0.6.1" + }, + "engines": { + "node": ">=12" } }, - "karma-ie-launcher": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/karma-ie-launcher/-/karma-ie-launcher-1.0.0.tgz", - "integrity": "sha512-ts71ke8pHvw6qdRtq0+7VY3ANLoZuUNNkA8abRaWV13QRPNm7TtSOqyszjHUtuwOWKcsSz4tbUtrNICrQC+SXQ==", + "node_modules/resolve-url-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, - "requires": { - "lodash": "^4.6.1" + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" } }, - "karma-jasmine": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-4.0.2.tgz", - "integrity": "sha512-ggi84RMNQffSDmWSyyt4zxzh2CQGwsxvYYsprgyR1j8ikzIduEdOlcLvXjZGwXG/0j41KUXOWsUCBfbEHPWP9g==", + "node_modules/resolve-url-loader/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "requires": { - "jasmine-core": "^3.6.0" + "engines": { + "node": ">=0.10.0" } }, - "karma-jasmine-html-reporter": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.7.0.tgz", - "integrity": "sha512-pzum1TL7j90DTE86eFt48/s12hqwQuiD+e5aXx2Dc9wDEn2LfGq6RoAxEZZjFiN0RDSCOnosEKRZWxbQ+iMpQQ==", - "dev": true, - "requires": {} - }, - "karma-junit-reporter": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/karma-junit-reporter/-/karma-junit-reporter-1.2.0.tgz", - "integrity": "sha512-FeuLOKlXNtJhIQK3oQASbO5QOib762CEHV8+L9wwTQpiZJgp7xKg3sNno66rL5bQPV2soG6fJdAFWqqnMJuh2w==", + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", "dev": true, - "requires": { - "path-is-absolute": "^1.0.0", - "xmlbuilder": "8.2.2" + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "karma-source-map-support": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", - "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "dev": true, - "requires": { - "source-map-support": "^0.5.5" + "engines": { + "node": ">= 4" } }, - "karma-viewport": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/karma-viewport/-/karma-viewport-1.0.9.tgz", - "integrity": "sha512-E1xVe66vBQtI66TGOtZMzV5nf6BW5tW4TQVUqPK+oakVLdsG/ZUG688tGK0lL1q0t7nfQD1dwLD8Z9Guu/RVdg==", + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true, - "requires": { - "@types/karma": "^6.3.3", - "jsonschema": "^1.4.0" + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" } }, - "keycharm": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/keycharm/-/keycharm-0.2.0.tgz", - "integrity": "sha512-i/XBRTiLqRConPKioy2oq45vbv04e8x59b0mnsIRQM+7Ec/8BC7UcL5pnC4FMeGb8KwG7q4wOMw7CtNZf5tiIg==", - "dev": true - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "klona": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", - "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "dev": true }, - "less": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", - "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==", + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, - "requires": { - "copy-anything": "^2.0.1", - "errno": "^0.1.1", - "graceful-fs": "^4.1.2", - "image-size": "~0.5.0", - "make-dir": "^2.1.0", - "mime": "^1.4.1", - "needle": "^3.1.0", - "parse-node-version": "^1.0.1", - "source-map": "~0.6.0", - "tslib": "^2.3.0" - }, "dependencies": { - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "optional": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true, - "optional": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true, - "optional": true - }, - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "optional": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - } + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "less-loader": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.0.0.tgz", - "integrity": "sha512-9+LOWWjuoectIEx3zrfN83NAGxSUB5pWEabbbidVQVgZhN+wN68pOvuyirVlH1IK4VT1f3TmlyvAnCXh8O5KEw==", + "node_modules/rimraf/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, - "requires": { - "klona": "^2.0.4" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "license-webpack-plugin": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", - "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==", + "node_modules/rimraf/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, - "requires": { - "webpack-sources": "^3.0.0" + "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" } }, - "lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "node_modules/rimraf/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, - "requires": { - "immediate": "~3.0.5" + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true - }, - "loader-utils": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", - "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", - "dev": true - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "node_modules/rollup": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.20.0.tgz", + "integrity": "sha512-6rbWBChcnSGzIlXeIdNIZTopKYad8ZG8ajhl78lGRLsI2rX8IkaotQhVas2Ma+GPxJav19wrSzvRvuiv0YKzWw==", "dev": true, - "requires": { - "p-locate": "^4.1.0" + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "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", + "fsevents": "~2.3.2" } }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true - }, - "lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "optional": true - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" - }, - "lodash.throttle": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", - "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==" - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "node_modules/run-applescript": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "engines": { + "node": ">=18" }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "log4js": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", - "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, - "requires": { - "date-format": "^4.0.14", - "debug": "^4.3.4", - "flatted": "^3.2.7", - "rfdc": "^1.3.0", - "streamroller": "^3.1.5" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" } }, - "loglevel": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz", - "integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==", - "dev": true + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" }, - "loglevel-plugin-prefix": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/loglevel-plugin-prefix/-/loglevel-plugin-prefix-0.8.4.tgz", - "integrity": "sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g==", - "dev": true + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dependencies": { + "tslib": "^2.1.0" + } }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "node_modules/safe-array-concat": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", "dev": true, - "requires": { - "yallist": "^3.0.2" + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true - }, - "macos-release": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.1.tgz", - "integrity": "sha512-DXqXhEM7gW59OjZO8NIjBCz9AQ1BEMrfiOAl4AYByHCtVHRF4KoGNO8mqQeM8lRCtQe/UnJ4imO/d2HdkKsd+A==", - "dev": true + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "magic-string": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", - "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", + "node_modules/safe-regex-test": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", "dev": true, - "requires": { - "sourcemap-codec": "^1.4.8" + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-regex": "^1.1.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "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, - "requires": { - "semver": "^6.0.0" - }, "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } + "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" } }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "make-fetch-happen": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz", - "integrity": "sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==", + "node_modules/sass-loader": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.0.tgz", + "integrity": "sha512-n13Z+3rU9A177dk4888czcVFiC8CL9dii4qpXWUg3YIIgZEvi9TCFKjOQcbK0kJM7DJu9VucrZFddvNfYCPwtw==", "dev": true, - "requires": { - "agentkeepalive": "^4.2.1", - "cacache": "^17.0.0", - "http-cache-semantics": "^4.1.1", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^5.0.0", - "minipass-fetch": "^3.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^10.0.0" - }, "dependencies": { - "@npmcli/fs": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.0.tgz", - "integrity": "sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==", - "dev": true, - "requires": { - "semver": "^7.3.5" - } - }, - "cacache": { - "version": "17.1.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.1.3.tgz", - "integrity": "sha512-jAdjGxmPxZh0IipMdR7fK/4sDSrHMLUV0+GvVUsjwyGNKHsh79kW/otg+GkbXwl6Uzvy9wsvHOX4nUoWldeZMg==", - "dev": true, - "requires": { - "@npmcli/fs": "^3.1.0", - "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^7.7.1", - "minipass": "^5.0.0", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" - } - }, - "fs-minipass": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.2.tgz", - "integrity": "sha512-2GAfyfoaCDRrM6jaOS3UsBts8yJ55VioXdWcOL7dK9zdAuKT71+WBA4ifnNYqVjYv+4SsPxjK0JT4yIIn4cA/g==", - "dev": true, - "requires": { - "minipass": "^5.0.0" - } - }, - "glob": { - "version": "10.3.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.3.tgz", - "integrity": "sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw==", - "dev": true, - "requires": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - } - }, - "lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true - }, - "minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true }, - "minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true + "node-sass": { + "optional": true }, - "ssri": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.4.tgz", - "integrity": "sha512-12+IR2CB2C28MMAw0Ncqwj5QbTcs0nGIhgJzYWzDkb21vWmfNI83KS4f3Ci6GI98WreIfG7o9UXp3C0qbpA8nQ==", - "dev": true, - "requires": { - "minipass": "^5.0.0" - } + "sass": { + "optional": true }, - "unique-filename": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", - "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", - "dev": true, - "requires": { - "unique-slug": "^4.0.0" - } + "sass-embedded": { + "optional": true }, - "unique-slug": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", - "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4" - } + "webpack": { + "optional": true } } }, - "map-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", - "integrity": "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==", - "dev": true - }, - "marked": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/marked/-/marked-7.0.3.tgz", - "integrity": "sha512-ev2uM40p0zQ/GbvqotfKcSWEa59fJwluGZj5dcaUOwDRrB1F3dncdXy8NWUApk4fi8atU3kTBOwjyjZ0ud0dxw==", - "dev": true - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", "dev": true }, - "memfs": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", - "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", "dev": true, - "requires": { - "fs-monkey": "^1.0.4" + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", - "dev": true - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "dev": true - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "node_modules/schema-utils/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } } }, - "mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "node_modules/secure-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", + "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", "dev": true }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", "dev": true }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "node_modules/selfsigned": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", "dev": true, - "requires": { - "mime-db": "1.52.0" + "dependencies": { + "@types/node-forge": "^1.3.0", + "node-forge": "^1" + }, + "engines": { + "node": ">=10" } }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } }, - "mini-css-extract-plugin": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz", - "integrity": "sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==", + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "dev": true, - "requires": { - "schema-utils": "^4.0.0" - }, "dependencies": { - "schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - } - } + "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" + }, + "engines": { + "node": ">= 0.8.0" } }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "node_modules/send/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, - "requires": { - "brace-expansion": "^2.0.1" + "dependencies": { + "ms": "2.0.0" } }, - "minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - }, - "dependencies": { - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "minipass-collect": { + "node_modules/send/node_modules/encodeurl": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "dev": true, - "requires": { - "minipass": "^3.0.0" + "engines": { + "node": ">= 0.8" } }, - "minipass-fetch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.3.tgz", - "integrity": "sha512-n5ITsTkDqYkYJZjcRWzZt9qnZKCT7nKCosJhHoj7S7zD+BP4jVbWs+odsniw5TA3E0sLomhTKOKjF86wf11PuQ==", + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, - "requires": { - "encoding": "^0.1.13", - "minipass": "^5.0.0", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" - }, "dependencies": { - "minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true - } - } - }, - "minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "dev": true, - "requires": { - "minipass": "^3.0.0" + "randombytes": "^2.1.0" } }, - "minipass-json-stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz", - "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==", + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", "dev": true, - "requires": { - "jsonparse": "^1.3.1", - "minipass": "^3.0.0" + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" } }, - "minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "node_modules/serve-index/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, - "requires": { - "minipass": "^3.0.0" + "dependencies": { + "ms": "2.0.0" } }, - "minipass-sized": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", "dev": true, - "requires": { - "minipass": "^3.0.0" + "engines": { + "node": ">= 0.6" } }, - "minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", "dev": true, - "requires": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, "dependencies": { - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" } }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", "dev": true }, - "moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true }, - "moment-timezone": { - "version": "0.5.43", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz", - "integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==", - "requires": { - "moment": "^2.29.4" - } + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true }, - "morgan": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", - "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", "dev": true, - "requires": { - "basic-auth": "~2.0.1", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-finished": "~2.3.0", - "on-headers": "~1.0.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - } + "engines": { + "node": ">= 0.6" } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "multicast-dns": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", - "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "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==", "dev": true, - "requires": { - "dns-packet": "^5.2.2", - "thunky": "^1.0.2" + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, - "nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true - }, - "needle": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/needle/-/needle-3.2.0.tgz", - "integrity": "sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==", + "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, - "optional": true, - "requires": { - "debug": "^3.2.6", - "iconv-lite": "^0.6.3", - "sax": "^1.2.4" - }, "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "optional": true, - "requires": { - "ms": "^2.1.1" - } - } + "ms": "2.0.0" } }, - "negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true - }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "node_modules/serve-static/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "ng-mocks": { - "version": "14.11.0", - "resolved": "https://registry.npmjs.org/ng-mocks/-/ng-mocks-14.11.0.tgz", - "integrity": "sha512-6h0TafPogU7iEbWKGQt5npfEtI7IjThsqqnDboMIZ4AJyUY7VHUmhFa39Zkd2e4oOLDLb/6sVnfDIaWCp3oFgQ==", - "dev": true, - "requires": {} - }, - "ng-packagr": { - "version": "14.2.2", - "resolved": "https://registry.npmjs.org/ng-packagr/-/ng-packagr-14.2.2.tgz", - "integrity": "sha512-AqwHcMM6x+JkCHT++IsbulnTdyoXcC2Cr4tbPamuieacc77+fFbB195hdcqEFwsKX5410cymx/ZUyHird9rxlg==", - "dev": true, - "requires": { - "@rollup/plugin-json": "^4.1.0", - "@rollup/plugin-node-resolve": "^13.1.3", - "ajv": "^8.10.0", - "ansi-colors": "^4.1.1", - "browserslist": "^4.20.0", - "cacache": "^16.0.0", - "chokidar": "^3.5.3", - "commander": "^9.0.0", - "dependency-graph": "^0.11.0", - "esbuild": "^0.15.0", - "esbuild-wasm": "^0.15.0", - "find-cache-dir": "^3.3.2", - "glob": "^8.0.0", - "injection-js": "^2.4.0", - "jsonc-parser": "^3.0.0", - "less": "^4.1.2", - "ora": "^5.1.0", - "postcss": "8.4.31", - "postcss-preset-env": "^7.4.2", - "postcss-url": "^10.1.3", - "rollup": "^2.70.0", - "rollup-plugin-sourcemaps": "^0.6.3", - "rxjs": "^7.5.5", - "sass": "^1.49.9", - "stylus": "^0.59.0" - }, - "dependencies": { - "commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", - "dev": true - }, - "rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "requires": { - "tslib": "^2.1.0" - } - } - } - }, - "ngx-logger": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/ngx-logger/-/ngx-logger-4.3.3.tgz", - "integrity": "sha512-LvHBt0OWIyjwVroecgxmZVDH+9lCYYd3gSiup9wNgtK0a3f+D+h2LCb8p9RI16wqCVFqYe/QChqJA8PGJzZTcw==", - "requires": { - "tslib": "^2.0.0", - "vlq": "^1.0.0" - } - }, - "nice-napi": { + "node_modules/serve-static/node_modules/encodeurl": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", - "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "dev": true, - "optional": true, - "requires": { - "node-addon-api": "^3.0.0", - "node-gyp-build": "^4.2.2" + "engines": { + "node": ">= 0.8" } }, - "node-addon-api": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", - "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "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==", "dev": true, - "optional": true - }, - "node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", - "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" + }, + "engines": { + "node": ">= 0.8.0" + } }, - "node-gyp": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.0.tgz", - "integrity": "sha512-dMXsYP6gc9rRbejLXmTbVRYjAHw7ppswsKyMxuxJxxOHzluIO1rGp9TOQgjFJ+2MCqcOcQTOPB/8Xwhr+7s4Eg==", + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, - "requires": { - "env-paths": "^2.2.0", - "exponential-backoff": "^3.1.1", - "glob": "^7.1.4", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^11.0.3", - "nopt": "^6.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" - }, - "dependencies": { - "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, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "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" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" } }, - "node-gyp-build": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", - "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, - "optional": true + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } }, - "node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "dev": true }, - "nopt": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", - "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "dev": true, - "requires": { - "abbrev": "^1.0.0" + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" } }, - "normalize-package-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-4.0.1.tgz", - "integrity": "sha512-EBk5QKKuocMJhB3BILuKhmaPjI8vNRSpIfO9woLC6NyHVkKKdVEdAO1mrT0ZfxNR1lKwCcTkuZfmGIFdizZ8Pg==", + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "requires": { - "hosted-git-info": "^5.0.0", - "is-core-module": "^2.8.1", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "normalize-path": { + "node_modules/shebang-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "dev": true - }, - "npm-bundled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", - "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, - "requires": { - "npm-normalize-package-bin": "^1.0.1" + "engines": { + "node": ">=8" } }, - "npm-install-checks": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-5.0.0.tgz", - "integrity": "sha512-65lUsMI8ztHCxFz5ckCEC44DRvEGdZX5usQFriauxHEwt7upv1FKaQEmAtU0YnOAdwuNWCmk64xYiQABNrEyLA==", + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", "dev": true, - "requires": { - "semver": "^7.1.1" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", - "dev": true - }, - "npm-package-arg": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-9.1.0.tgz", - "integrity": "sha512-4J0GL+u2Nh6OnhvUKXRr2ZMG4lR8qtLp+kv7UiV00Y+nGiSxtttCyIRHCt5L5BNkXQld/RceYItau3MDOoGiBw==", + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dev": true, - "requires": { - "hosted-git-info": "^5.0.0", - "proc-log": "^2.0.1", - "semver": "^7.3.5", - "validate-npm-package-name": "^4.0.0" + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "npm-packlist": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-5.1.3.tgz", - "integrity": "sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg==", + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, - "requires": { - "glob": "^8.0.1", - "ignore-walk": "^5.0.1", - "npm-bundled": "^2.0.0", - "npm-normalize-package-bin": "^2.0.0" + "engines": { + "node": ">=14" }, - "dependencies": { - "npm-bundled": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-2.0.1.tgz", - "integrity": "sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==", - "dev": true, - "requires": { - "npm-normalize-package-bin": "^2.0.0" - } - }, - "npm-normalize-package-bin": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", - "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", - "dev": true - } + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "npm-pick-manifest": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-7.0.1.tgz", - "integrity": "sha512-IA8+tuv8KujbsbLQvselW2XQgmXWS47t3CB0ZrzsRZ82DbDfkcFunOaPm4X7qNuhMfq+FmV7hQT4iFVpHqV7mg==", + "node_modules/sigstore": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-2.3.1.tgz", + "integrity": "sha512-8G+/XDU8wNsJOQS5ysDVO0Etg9/2uA5gR9l4ZwijjlwxBcrU6RPfwi2+jJmbP+Ap1Hlp/nVAaEO4Fj22/SL2gQ==", "dev": true, - "requires": { - "npm-install-checks": "^5.0.0", - "npm-normalize-package-bin": "^1.0.1", - "npm-package-arg": "^9.0.0", - "semver": "^7.3.5" + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "@sigstore/sign": "^2.3.2", + "@sigstore/tuf": "^2.3.4", + "@sigstore/verify": "^1.2.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "npm-registry-fetch": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-13.3.1.tgz", - "integrity": "sha512-eukJPi++DKRTjSBRcDZSDDsGqRK3ehbxfFUcgaRd0Yp6kRwOwh2WVn0r+8rMB4nnuzvAk6rQVzl6K5CkYOmnvw==", + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, - "requires": { - "make-fetch-happen": "^10.0.6", - "minipass": "^3.1.6", - "minipass-fetch": "^2.0.3", - "minipass-json-stream": "^1.0.1", - "minizlib": "^2.1.2", - "npm-package-arg": "^9.0.1", - "proc-log": "^2.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true - }, - "make-fetch-happen": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", - "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", - "dev": true, - "requires": { - "agentkeepalive": "^4.2.1", - "cacache": "^16.1.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^2.0.3", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^9.0.0" - } - }, - "minipass-fetch": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", - "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", - "dev": true, - "requires": { - "encoding": "^0.1.13", - "minipass": "^3.1.6", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" - } - } + "engines": { + "node": ">=8" } }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", "dev": true, - "requires": { - "path-key": "^3.0.0" + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "npmlog": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", - "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, - "requires": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true, - "requires": { - "boolbase": "^1.0.0" + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" } }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true + "node_modules/smooth-scrollbar": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/smooth-scrollbar/-/smooth-scrollbar-8.8.1.tgz", + "integrity": "sha512-FQDRtdLTRye8LdRHohNAxh0hyo7gl8+APfA+8Qu5S38MBqR8/WqOoRVzjizH6FTdbU1qGsB7gs8LhMeFJ4Jr8g==", + "optional": true, + "peer": true, + "dependencies": { + "core-js": "^3.6.4", + "lodash.clamp": "^4.0.3", + "lodash.debounce": "^4.0.8", + "tslib": "^1.10.0" + } }, - "object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", - "dev": true + "node_modules/smooth-scrollbar/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true, + "peer": true }, - "object-is": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", - "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "node_modules/socket.io": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz", + "integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==", "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1" + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.5.2", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" } }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", "dev": true, - "requires": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" } }, - "obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", "dev": true, - "requires": { - "ee-first": "1.1.1" + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" } }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "node_modules/sockjs/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, - "requires": { - "wrappy": "1" + "bin": { + "uuid": "dist/bin/uuid" } }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "node_modules/socks": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", "dev": true, - "requires": { - "mimic-fn": "^2.1.0" + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" } }, - "open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "node_modules/socks-proxy-agent": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", + "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", "dev": true, - "requires": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" + "dependencies": { + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" } }, - "opencollective-postinstall": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", - "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "node_modules/sorted-array-functions": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz", + "integrity": "sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==", "dev": true }, - "ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "dev": true, - "requires": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "engines": { + "node": ">= 8" } }, - "os-name": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz", - "integrity": "sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==", + "node_modules/source-map-js": { + "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, - "requires": { - "macos-release": "^2.5.0", - "windows-release": "^4.0.0" + "engines": { + "node": ">=0.10.0" } }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/source-map-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-5.0.0.tgz", + "integrity": "sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA==", "dev": true, - "requires": { - "p-try": "^2.0.0" + "dependencies": { + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.72.1" } }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, - "requires": { - "p-limit": "^2.2.0" + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "requires": { - "aggregate-error": "^3.0.0" + "engines": { + "node": ">=0.10.0" } }, - "p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "dev": true, - "requires": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" - }, "dependencies": { - "retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "dev": true - } + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" } }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", "dev": true }, - "pacote": { - "version": "13.6.2", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-13.6.2.tgz", - "integrity": "sha512-Gu8fU3GsvOPkak2CkbojR7vjs3k3P9cA6uazKTHdsdV0gpCEQq2opelnEv30KRQWgVzP5Vd/5umjcedma3MKtg==", - "dev": true, - "requires": { - "@npmcli/git": "^3.0.0", - "@npmcli/installed-package-contents": "^1.0.7", - "@npmcli/promise-spawn": "^3.0.0", - "@npmcli/run-script": "^4.1.0", - "cacache": "^16.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "infer-owner": "^1.0.4", - "minipass": "^3.1.6", - "mkdirp": "^1.0.4", - "npm-package-arg": "^9.0.0", - "npm-packlist": "^5.1.0", - "npm-pick-manifest": "^7.0.0", - "npm-registry-fetch": "^13.0.1", - "proc-log": "^2.0.0", - "promise-retry": "^2.0.1", - "read-package-json": "^5.0.0", - "read-package-json-fast": "^2.0.3", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", - "tar": "^6.1.11" + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, - "pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "node_modules/spdx-license-ids": { + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", + "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", "dev": true }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", "dev": true, - "requires": { - "callsites": "^3.0.0" + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" } }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" } }, - "parse-node-version": { + "node_modules/split": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", - "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", - "dev": true + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dev": true, + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } }, - "parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", - "optional": true + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true }, - "parse5-html-rewriting-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz", - "integrity": "sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg==", + "node_modules/ssri": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", + "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", "dev": true, - "requires": { - "parse5": "^6.0.1", - "parse5-sax-parser": "^6.0.1" - }, "dependencies": { - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - } + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "parse5-htmlparser2-tree-adapter": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", - "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "dev": true, - "requires": { - "domhandler": "^5.0.2", - "parse5": "^7.0.0" - }, - "dependencies": { - "parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "dev": true, - "requires": { - "entities": "^4.4.0" - } - } + "engines": { + "node": ">= 0.8" } }, - "parse5-sax-parser": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz", - "integrity": "sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==", + "node_modules/stream-combiner": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", + "integrity": "sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==", "dev": true, - "requires": { - "parse5": "^6.0.1" - }, "dependencies": { - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - } + "duplexer": "~0.1.1", + "through": "~2.3.4" } }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true - }, - "path-browserify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", - "dev": true - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "node_modules/streamroller": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", + "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", "dev": true, - "requires": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, "dependencies": { - "lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true - }, - "minipass": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.2.tgz", - "integrity": "sha512-eL79dXrE1q9dBbDCLg7xfn/vl7MS4F1gvJAgjJrQli/jbQWdUttuVawphqpffoIYfRdq78LHx6GP4bU/EQ2ATA==", - "dev": true - } + "date-format": "^4.0.14", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + }, + "engines": { + "node": ">=8.0" } }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", - "dev": true + "node_modules/streamroller/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } }, - "path-type": { + "node_modules/streamroller/node_modules/jsonfile": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, - "requires": { - "through": "~2.3" + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "pdfmake": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/pdfmake/-/pdfmake-0.2.10.tgz", - "integrity": "sha512-doipFnmE1UHSk+Z3wfQuVweVQqx2pE/Ns2G5gCqZmWwqjDj+mZHnZYH/ryXWoIfD+iVdZUAutgI/VHkTCN+Xrw==", + "node_modules/streamroller/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, - "requires": { - "@foliojs-fork/linebreak": "^1.1.1", - "@foliojs-fork/pdfkit": "^0.14.0", - "iconv-lite": "^0.6.3", - "xmldoc": "^1.1.2" + "engines": { + "node": ">= 4.0.0" } }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "dev": true - }, - "picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "dev": true + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, - "requires": { - "pinkie": "^2.0.0" + "engines": { + "node": ">=8" } }, - "piscina": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.2.0.tgz", - "integrity": "sha512-yn/jMdHRw+q2ZJhFhyqsmANcbF6V2QwmD84c6xRau+QpQOmtrBCoRGdvTfeuFDYXB5W2m6MfLkjkvQa9lUSmIA==", + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, - "requires": { - "eventemitter-asyncresource": "^1.0.0", - "hdr-histogram-js": "^2.0.1", - "hdr-histogram-percentiles-obj": "^3.0.0", - "nice-napi": "^1.0.2" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, - "requires": { - "find-up": "^4.0.0" + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "png-js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz", - "integrity": "sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g==", - "dev": true - }, - "possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", - "dev": true - }, - "postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "node_modules/string.prototype.trim": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", "dev": true, - "requires": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "postcss-attribute-case-insensitive": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", - "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", + "node_modules/string.prototype.trimend": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.10" + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "postcss-clamp": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", - "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "postcss-color-functional-notation": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", - "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "postcss-color-hex-alpha": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", - "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "postcss-color-rebeccapurple": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", - "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" + "engines": { + "node": ">=6" } }, - "postcss-custom-media": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", - "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strong-log-transformer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz", + "integrity": "sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA==", + "dependencies": { + "duplexer": "^0.1.1", + "minimist": "^1.2.0", + "through": "^2.3.4" + }, + "bin": { + "sl-log-transformer": "bin/sl-log-transformer.js" + }, + "engines": { + "node": ">=4" } }, - "postcss-custom-properties": { - "version": "12.1.11", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.11.tgz", - "integrity": "sha512-0IDJYhgU8xDv1KY6+VgUwuQkVtmYzRwu+dMjnmdMafXYv86SWqfxkc7qdDvWS38vsjaEtv8e0vGOUQrAiMBLpQ==", + "node_modules/style-loader": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.4.tgz", + "integrity": "sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==", "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" } }, - "postcss-custom-selectors": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", - "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", + "node_modules/stylehacks": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.1.1.tgz", + "integrity": "sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==", "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.4" + "dependencies": { + "browserslist": "^4.23.0", + "postcss-selector-parser": "^6.0.16" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, - "postcss-dir-pseudo-class": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", - "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", + "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, - "requires": { - "postcss-selector-parser": "^6.0.10" + "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" } }, - "postcss-double-position-gradients": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", - "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", + "node_modules/stylus-loader": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-7.1.3.tgz", + "integrity": "sha512-TY0SKwiY7D2kMd3UxaWKSf3xHF0FFN/FAfsSqfrhxRT/koXTwffq2cgEWDkLQz7VojMu7qEEHt5TlMjkPx9UDw==", "dev": true, - "requires": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" + "dependencies": { + "fast-glob": "^3.2.12", + "normalize-path": "^3.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "stylus": ">=0.52.4", + "webpack": "^5.0.0" } }, - "postcss-env-function": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", - "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", + "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, - "requires": { - "postcss-value-parser": "^4.2.0" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "postcss-focus-visible": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", - "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", + "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, - "requires": { - "postcss-selector-parser": "^6.0.9" + "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" } }, - "postcss-focus-within": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", - "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", + "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, - "requires": { - "postcss-selector-parser": "^6.0.9" + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "postcss-font-variant": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", - "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", - "dev": true, - "requires": {} - }, - "postcss-gap-properties": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", - "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", - "dev": true, - "requires": {} + "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 }, - "postcss-image-set-function": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", - "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "postcss-import": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.0.0.tgz", - "integrity": "sha512-Y20shPQ07RitgBGv2zvkEAu9bqvrD77C9axhj/aA1BQj4czape2MdClCExvB27EwYEJdGgKZBpKanb0t1rK2Kg==", + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, - "requires": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "postcss-initial": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", - "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", - "dev": true, - "requires": {} + "node_modules/svg-pan-zoom": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/svg-pan-zoom/-/svg-pan-zoom-3.6.1.tgz", + "integrity": "sha512-JaKkGHHfGvRrcMPdJWkssLBeWqM+Isg/a09H7kgNNajT1cX5AztDTNs+C8UzpCxjCTRrG34WbquwaovZbmSk9g==", + "dev": true }, - "postcss-lab-function": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", - "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", + "node_modules/svgo": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", + "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", "dev": true, - "requires": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" } }, - "postcss-loader": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.0.1.tgz", - "integrity": "sha512-VRviFEyYlLjctSM93gAZtcJJ/iSkPZ79zWbN/1fSH+NisBByEiVLqpdVDrPLVSi8DX0oJo12kL/GppTBdKVXiQ==", + "node_modules/svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true, - "requires": { - "cosmiconfig": "^7.0.0", - "klona": "^2.0.5", - "semver": "^7.3.7" - }, - "dependencies": { - "cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", - "dev": true, - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - } - } + "engines": { + "node": ">= 10" } }, - "postcss-logical": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", - "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", - "dev": true, - "requires": {} + "node_modules/swagger-ui-dist": { + "version": "5.17.14", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.17.14.tgz", + "integrity": "sha512-CVbSfaLpstV65OnSjbXfVd6Sta3q3F7Cj/yYuvHMp1P90LztOLs6PfUnKEVAeiIVQt9u2SaPwv0LiH/OyMjHRw==" }, - "postcss-media-minmax": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", - "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", + "node_modules/symbol-observable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", "dev": true, - "requires": {} + "engines": { + "node": ">=0.10" + } }, - "postcss-modules-extract-imports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "dev": true, - "requires": {} + "node_modules/tablesort": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/tablesort/-/tablesort-5.3.0.tgz", + "integrity": "sha512-WkfcZBHsp47gVH9CBHG0ZXopriG01IA87arGrchvIe868d4RiXVvoYPS1zMq9IdW05kBs5iGsqxTABqLyWonbg==", + "dev": true }, - "postcss-modules-local-by-default": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz", - "integrity": "sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==", + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true, - "requires": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" + "engines": { + "node": ">=6" } }, - "postcss-modules-scope": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", - "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.4" + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, - "postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "dev": true, - "requires": { - "icss-utils": "^5.0.0" + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" } }, - "postcss-nesting": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.2.0.tgz", - "integrity": "sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==", + "node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "dev": true, - "requires": { - "@csstools/selector-specificity": "^2.0.0", - "postcss-selector-parser": "^6.0.10" + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" } }, - "postcss-opacity-percentage": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.3.tgz", - "integrity": "sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==", - "dev": true, - "requires": {} - }, - "postcss-overflow-shorthand": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", - "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", + "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "postcss-page-break": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", - "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", - "dev": true, - "requires": {} - }, - "postcss-place": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", - "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-preset-env": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.0.tgz", - "integrity": "sha512-leqiqLOellpLKfbHkD06E04P6d9ZQ24mat6hu4NSqun7WG0UhspHR5Myiv/510qouCjoo4+YJtNOqg5xHaFnCA==", - "dev": true, - "requires": { - "@csstools/postcss-cascade-layers": "^1.0.5", - "@csstools/postcss-color-function": "^1.1.1", - "@csstools/postcss-font-format-keywords": "^1.0.1", - "@csstools/postcss-hwb-function": "^1.0.2", - "@csstools/postcss-ic-unit": "^1.0.1", - "@csstools/postcss-is-pseudo-class": "^2.0.7", - "@csstools/postcss-nested-calc": "^1.0.0", - "@csstools/postcss-normalize-display-values": "^1.0.1", - "@csstools/postcss-oklab-function": "^1.1.1", - "@csstools/postcss-progressive-custom-properties": "^1.3.0", - "@csstools/postcss-stepped-value-functions": "^1.0.1", - "@csstools/postcss-text-decoration-shorthand": "^1.0.0", - "@csstools/postcss-trigonometric-functions": "^1.0.2", - "@csstools/postcss-unset-value": "^1.0.2", - "autoprefixer": "^10.4.8", - "browserslist": "^4.21.3", - "css-blank-pseudo": "^3.0.3", - "css-has-pseudo": "^3.0.4", - "css-prefers-color-scheme": "^6.0.3", - "cssdb": "^7.0.0", - "postcss-attribute-case-insensitive": "^5.0.2", - "postcss-clamp": "^4.1.0", - "postcss-color-functional-notation": "^4.2.4", - "postcss-color-hex-alpha": "^8.0.4", - "postcss-color-rebeccapurple": "^7.1.1", - "postcss-custom-media": "^8.0.2", - "postcss-custom-properties": "^12.1.8", - "postcss-custom-selectors": "^6.0.3", - "postcss-dir-pseudo-class": "^6.0.5", - "postcss-double-position-gradients": "^3.1.2", - "postcss-env-function": "^4.0.6", - "postcss-focus-visible": "^6.0.4", - "postcss-focus-within": "^5.0.4", - "postcss-font-variant": "^5.0.0", - "postcss-gap-properties": "^3.0.5", - "postcss-image-set-function": "^4.0.7", - "postcss-initial": "^4.0.1", - "postcss-lab-function": "^4.2.1", - "postcss-logical": "^5.0.4", - "postcss-media-minmax": "^5.0.0", - "postcss-nesting": "^10.1.10", - "postcss-opacity-percentage": "^1.1.2", - "postcss-overflow-shorthand": "^3.0.4", - "postcss-page-break": "^3.0.4", - "postcss-place": "^7.0.5", - "postcss-pseudo-class-any-link": "^7.1.6", - "postcss-replace-overflow-wrap": "^4.0.0", - "postcss-selector-not": "^6.0.1", - "postcss-value-parser": "^4.2.0" + "engines": { + "node": ">=8" } }, - "postcss-pseudo-class-any-link": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", - "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.10" + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" } }, - "postcss-replace-overflow-wrap": { + "node_modules/tar/node_modules/yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", - "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", - "dev": true, - "requires": {} - }, - "postcss-selector-not": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", - "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.10" - } + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, - "postcss-selector-parser": { - "version": "6.0.13", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", - "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "node_modules/terser": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.32.0.tgz", + "integrity": "sha512-v3Gtw3IzpBJ0ugkxEX8U0W6+TnPKRRCWGh1jC/iM/e3Ki5+qvO1L1EAZ56bZasc64aXHwRHNIQEzm6//i5cemQ==", "dev": true, - "requires": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" } }, - "postcss-url": { - "version": "10.1.3", - "resolved": "https://registry.npmjs.org/postcss-url/-/postcss-url-10.1.3.tgz", - "integrity": "sha512-FUzyxfI5l2tKmXdYc6VTu3TWZsInayEKPbiyW+P6vmmIrrb4I6CGX0BFoewgYHLK+oIL5FECEK02REYRpBvUCw==", + "node_modules/terser-webpack-plugin": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", "dev": true, - "requires": { - "make-dir": "~3.1.0", - "mime": "~2.5.2", - "minimatch": "~3.0.4", - "xxhashjs": "~0.2.2" - }, "dependencies": { - "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, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true }, - "mime": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", - "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", - "dev": true + "esbuild": { + "optional": true }, - "minimatch": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", - "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } + "uglify-js": { + "optional": true } } }, - "postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true - }, - "postinstall-build": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postinstall-build/-/postinstall-build-5.0.3.tgz", - "integrity": "sha512-vPvPe8TKgp4FLgY3+DfxCE5PIfoXBK2lyLfNCxsRbDsV6vS4oU5RG/IWxrblMn6heagbnMED3MemUQllQ2bQUg==", - "dev": true - }, - "pretty-bytes": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", - "dev": true - }, - "prismjs": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", - "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", - "dev": true - }, - "proc-log": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz", - "integrity": "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", - "dev": true - }, - "promise-retry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "node_modules/terser-webpack-plugin/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, - "requires": { - "err-code": "^2.0.2", - "retry": "^0.12.0" + "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" } }, - "propagating-hammerjs": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/propagating-hammerjs/-/propagating-hammerjs-1.5.0.tgz", - "integrity": "sha512-3PUXWmomwutoZfydC+lJwK1bKCh6sK6jZGB31RUX6+4EXzsbkDZrK4/sVR7gBrvJaEIwpTVyxQUAd29FKkmVdw==", + "node_modules/terser-webpack-plugin/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, - "requires": { - "hammerjs": "^2.0.8" + "peerDependencies": { + "ajv": "^6.9.1" } }, - "protractor": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/protractor/-/protractor-7.0.0.tgz", - "integrity": "sha512-UqkFjivi4GcvUQYzqGYNe0mLzfn5jiLmO8w9nMhQoJRLhy2grJonpga2IWhI6yJO30LibWXJJtA4MOIZD2GgZw==", - "dev": true, - "requires": { - "@types/q": "^0.0.32", - "@types/selenium-webdriver": "^3.0.0", - "blocking-proxy": "^1.0.0", - "browserstack": "^1.5.1", - "chalk": "^1.1.3", - "glob": "^7.0.3", - "jasmine": "2.8.0", - "jasminewd2": "^2.1.0", - "q": "1.4.1", - "saucelabs": "^1.5.0", - "selenium-webdriver": "3.6.0", - "source-map-support": "~0.4.0", - "webdriver-js-extender": "2.1.0", - "webdriver-manager": "^12.1.7", - "yargs": "^15.3.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", - "dev": true - }, - "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, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "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" - } - }, - "jasmine": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz", - "integrity": "sha512-KbdGQTf5jbZgltoHs31XGiChAPumMSY64OZMWLNYnEnMfG5uwGBhffePwuskexjT+/Jea/gU3qAU8344hNohSw==", - "dev": true, - "requires": { - "exit": "^0.1.2", - "glob": "^7.0.6", - "jasmine-core": "~2.8.0" - } - }, - "jasmine-core": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", - "integrity": "sha512-SNkOkS+/jMZvLhuSx1fjhcNWUC/KG6oVyFUGkSBEr9n1axSNduWU8GlI7suaHXr4yxjet6KjrUZxUTE5WzzWwQ==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "dev": true - }, - "source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", - "dev": true, - "requires": { - "source-map": "^0.5.6" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", - "dev": true - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } - } - }, - "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - } - }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } + "node_modules/terser-webpack-plugin/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" } }, - "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "node_modules/terser-webpack-plugin/node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, - "requires": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, "dependencies": { - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true - } + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" } }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", - "dev": true, - "optional": true - }, - "psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "node_modules/terser-webpack-plugin/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 }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "node_modules/terser-webpack-plugin/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, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "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" } }, - "punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true + "node_modules/terser-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } }, - "q": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", - "integrity": "sha512-/CdEdaw49VZVmyIDGUQKDDT53c7qBkO6g5CefWz91Ae+l4+cRtcDYwMTXh6me4O8TMldeGHG3N2Bl84V78Ywbg==", + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, - "qjobs": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, - "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "node_modules/thingies": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz", + "integrity": "sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==", "dev": true, - "requires": { - "side-channel": "^1.0.4" + "engines": { + "node": ">=10.18" + }, + "peerDependencies": { + "tslib": "^2" } }, - "querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "dev": true }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", "dev": true }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "engines": { + "node": ">=14.14" } }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } }, - "raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, "dependencies": { - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - } + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" } }, - "read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "node_modules/traverse": { + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.9.tgz", + "integrity": "sha512-7bBrcF+/LQzSgFmT0X5YclVqQxtv7TDJ1f8Wj7ibBu/U6BMLeOpUxuZjV7rMc44UtKxlnMFigdhFAIszSX1DMg==", "dev": true, - "requires": { - "pify": "^2.3.0" + "dependencies": { + "gopd": "^1.0.1", + "typedarray.prototype.slice": "^1.0.3", + "which-typed-array": "^1.1.15" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "read-package-json": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-5.0.2.tgz", - "integrity": "sha512-BSzugrt4kQ/Z0krro8zhTwV1Kd79ue25IhNN/VtHFy1mG/6Tluyi+msc0UpwaoQzxSHa28mntAjIZY6kEgfR9Q==", + "node_modules/tree-dump": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.2.tgz", + "integrity": "sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ==", "dev": true, - "requires": { - "glob": "^8.0.1", - "json-parse-even-better-errors": "^2.3.1", - "normalize-package-data": "^4.0.0", - "npm-normalize-package-bin": "^2.0.0" + "engines": { + "node": ">=10.0" }, - "dependencies": { - "npm-normalize-package-bin": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", - "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", - "dev": true - } + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" } }, - "read-package-json-fast": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz", - "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==", + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true, - "requires": { - "json-parse-even-better-errors": "^2.3.0", - "npm-normalize-package-bin": "^1.0.1" + "bin": { + "tree-kill": "cli.js" } }, - "readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" } }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "node_modules/ts-loader": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", + "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", "dev": true, - "requires": { - "picomatch": "^2.2.1" + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" } }, - "reflect-metadata": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", - "dev": true - }, - "regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true - }, - "regenerate-unicode-properties": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", - "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "node_modules/ts-loader/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "requires": { - "regenerate": "^1.4.2" + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", - "dev": true + "node_modules/ts-loader/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } }, - "regenerator-transform": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", - "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "node_modules/ts-loader/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "requires": { - "@babel/runtime": "^7.8.4" + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "regex-parser": { - "version": "2.2.11", - "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", - "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==", + "node_modules/ts-loader/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "regexp.prototype.flags": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "node_modules/ts-loader/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "requires": { - "call-bind": "^1.0.6", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" + "engines": { + "node": ">=8" } }, - "regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "node_modules/ts-loader/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "requires": { - "@babel/regjsgen": "^0.8.0", - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "node_modules/ts-morph": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-22.0.0.tgz", + "integrity": "sha512-M9MqFGZREyeb5fTl6gNHKZLqBQA0TjA1lea+CR48R8EBTDuWrNqW6ccC5QvjNR4s6wDumD3LTCjOFSp9iwlzaw==", "dev": true, - "requires": { - "jsesc": "~0.5.0" - }, "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", - "dev": true - } + "@ts-morph/common": "~0.23.0", + "code-block-writer": "^13.0.1" } }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "4.1.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "dev": true + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true + "@swc/wasm": { + "optional": true } } }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true - }, - "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dev": true, - "requires": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, - "resolve-url-loader": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", - "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", + "node_modules/tsconfig-paths-webpack-plugin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.0.0.tgz", + "integrity": "sha512-fw/7265mIWukrSHd0i+wSwx64kYUSAKPfxRDksjKIYTxSAp9W9/xcZVBF4Kl0eqQd5eBpAQ/oQrc5RyM/0c1GQ==", "dev": true, - "requires": { - "adjust-sourcemap-loader": "^4.0.0", - "convert-source-map": "^1.7.0", - "loader-utils": "^2.0.0", - "postcss": "8.4.31", - "source-map": "0.6.1" - }, "dependencies": { - "loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } + "chalk": "^4.1.0", + "enhanced-resolve": "^5.7.0", + "tsconfig-paths": "^4.0.0" + }, + "engines": { + "node": ">=10.13.0" } }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "node_modules/tsconfig-paths-webpack-plugin/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "node_modules/tsconfig-paths-webpack-plugin/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/tsconfig-paths-webpack-plugin/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "requires": { - "glob": "^7.1.3" - }, "dependencies": { - "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, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "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" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "rollup": { - "version": "2.79.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", - "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "node_modules/tsconfig-paths-webpack-plugin/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/tsconfig-paths-webpack-plugin/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "requires": { - "fsevents": "~2.3.2" + "engines": { + "node": ">=8" } }, - "rollup-plugin-sourcemaps": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/rollup-plugin-sourcemaps/-/rollup-plugin-sourcemaps-0.6.3.tgz", - "integrity": "sha512-paFu+nT1xvuO1tPFYXGe+XnQvg4Hjqv/eIhG8i5EspfYYPBKL57X7iVbfv55aNVASg3dzWvES9dmWsL2KhfByw==", + "node_modules/tsconfig-paths-webpack-plugin/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "requires": { - "@rollup/pluginutils": "^3.0.9", - "source-map-resolve": "^0.6.0" + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true + "node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "node_modules/tsscmp": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", + "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", "dev": true, - "requires": { - "queue-microtask": "^1.2.2" + "engines": { + "node": ">=0.6.x" } }, - "rw": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" - }, - "rxjs": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", - "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==", - "requires": { - "tslib": "^1.9.0" - }, + "node_modules/tuf-js": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-2.2.1.tgz", + "integrity": "sha512-GwIJau9XaA8nLVbUXsN3IlFi7WmQ48gBUrl3FTkkL/XLu/POhBzfmX9hd33FNMX1qAsfl6ozO1iMmW9NC8YniA==", + "dev": true, "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } + "@tufjs/models": "2.0.1", + "debug": "^4.3.4", + "make-fetch-happen": "^13.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "safe-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, - "requires": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, "dependencies": { - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - } + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" } }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, - "requires": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-regex": "^1.1.4" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sass": { - "version": "1.54.4", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.54.4.tgz", - "integrity": "sha512-3tmF16yvnBwtlPrNBHw/H907j8MlOX8aTBnlNX1yrKx24RKcJGPyLhFUwkoKBKesR3unP93/2z14Ll8NicwQUA==", + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "dev": true, - "requires": { - "chokidar": ">=3.0.0 <4.0.0", - "immutable": "^4.0.0", - "source-map-js": ">=0.6.2 <2.0.0" + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" } }, - "sass-loader": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.0.2.tgz", - "integrity": "sha512-BbiqbVmbfJaWVeOOAu2o7DhYWtcNmTfvroVgFXa6k2hHheMxNAeDHLNoDy/Q5aoaVlz0LH+MbMktKwm9vN/j8Q==", + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", "dev": true, - "requires": { - "klona": "^2.0.4", - "neo-async": "^2.6.2" + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" } }, - "saucelabs": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz", - "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==", + "node_modules/typed-array-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", "dev": true, - "requires": { - "https-proxy-agent": "^2.2.1" - }, "dependencies": { - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "dev": true, - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - } - } + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, - "schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "node_modules/typed-array-byte-offset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", "dev": true, - "requires": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" - }, "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "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, - "requires": {} - }, - "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 - } - } - }, - "select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", - "dev": true - }, - "selenium-webdriver": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", - "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==", - "dev": true, - "requires": { - "jszip": "^3.1.3", - "rimraf": "^2.5.4", - "tmp": "0.0.30", - "xml2js": "0.5.0" - }, - "dependencies": { - "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, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "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" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "tmp": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", - "integrity": "sha512-HXdTB7lvMwcb55XFfrTM8CPr/IYREk4hVBFaQ4b/6nInrluSL86hfHm7vu0luYKCfyBZp2trCjpc8caC3vVM3w==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.1" - } - } + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "selfsigned": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", - "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", + "node_modules/typed-array-length": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", "dev": true, - "requires": { - "@types/node-forge": "^1.3.0", - "node-forge": "^1" + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "node_modules/typed-assert": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz", + "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==", + "dev": true + }, + "node_modules/typedarray.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typedarray.prototype.slice/-/typedarray.prototype.slice-1.0.3.tgz", + "integrity": "sha512-8WbVAQAUlENo1q3c3zZYuy5k9VzBQvp8AX9WOtbvyWlLM1v5JaSRmjubLjzHF4JFtptjH/5c/i95yaElvcjC0A==", "dev": true, - "requires": { - "lru-cache": "^6.0.0" - }, "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-errors": "^1.3.0", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-offset": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "node_modules/typescript": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true, - "requires": { - "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" + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - } - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true - }, - "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 + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ua-parser-js": { + "version": "0.7.38", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.38.tgz", + "integrity": "sha512-fYmIy7fKTSFAhG3fuPlubeGaMoAd6r0rSnfEsO5nEY55i26KSLt9EH7PLQiiqPUhNqYIJvSkTy1oArIcXAbPbA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" }, - "on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } + { + "type": "paypal", + "url": "https://paypal.me/faisalman" }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" } + ], + "engines": { + "node": "*" } }, - "serialize-javascript": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", - "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", "dev": true, - "requires": { - "randombytes": "^2.1.0" + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" } }, - "serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, - "requires": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "dev": true - }, - "http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", - "dev": true - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - } + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "node_modules/undici": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.19.8.tgz", + "integrity": "sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==", "dev": true, - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" + "engines": { + "node": ">=18.17" } }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "dev": true }, - "set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", "dev": true, - "requires": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" + "engines": { + "node": ">=4" } }, - "set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", "dev": true, - "requires": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" } }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "dev": true + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "dev": true, + "engines": { + "node": ">=4" + } }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true + "node_modules/unicode-properties": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", + "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", + "dev": true, + "dependencies": { + "base64-js": "^1.3.0", + "unicode-trie": "^2.0.0" + } }, - "shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", "dev": true, - "requires": { - "kind-of": "^6.0.2" + "engines": { + "node": ">=4" } }, - "shebang-command": { + "node_modules/unicode-trie": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", + "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", "dev": true, - "requires": { - "shebang-regex": "^3.0.0" + "dependencies": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" } }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "node_modules/union": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", + "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", "dev": true, - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "dependencies": { + "qs": "^6.4.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "node_modules/unique-filename": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", + "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", + "dev": true, + "dependencies": { + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, - "slash": { + "node_modules/unique-slug": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "dev": true - }, - "smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "dev": true - }, - "smooth-scrollbar": { - "version": "8.8.4", - "resolved": "https://registry.npmjs.org/smooth-scrollbar/-/smooth-scrollbar-8.8.4.tgz", - "integrity": "sha512-Wn2Q/iE+PE1ef4EfhFMhgaIMfSrjJl2LalRu0hBWSrw5nZKGsUIjWFWHnLB/Lpx7N+a1pJH6+wkQzBr+5nWPUg==", - "requires": { - "core-js": "^3.6.4", - "tslib": "^1.10.0" - }, + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", + "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", + "dev": true, "dependencies": { - "core-js": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.0.tgz", - "integrity": "sha512-rd4rYZNlF3WuoYuRIDEmbR/ga9CeuWX9U05umAvgrrZoHY4Z++cp/xwPQMvUpBB4Ag6J8KfD80G0zwCyaSxDww==" - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "socket.io": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.2.tgz", - "integrity": "sha512-bvKVS29/I5fl2FGLNHuXlQaUH/BlzX1IN6S+NKLNZpBsPZIDH+90eQmCs2Railn4YUiww4SzUedJ6+uzwFnKLw==", - "dev": true, - "requires": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "cors": "~2.8.5", - "debug": "~4.3.2", - "engine.io": "~6.5.2", - "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.4" + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "engines": { + "node": ">= 10.0.0" } }, - "socket.io-adapter": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", - "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "node_modules/unix-crypt-td-js": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/unix-crypt-td-js/-/unix-crypt-td-js-1.1.4.tgz", + "integrity": "sha512-8rMeVYWSIyccIJscb9NdCfZKSRBKYTeVnwmiRYT2ulE3qd1RaDQ0xQDP+rI3ccIWbhu/zuo5cgN8z73belNZgw==", + "dev": true + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "dev": true, - "requires": { - "debug": "~4.3.4", - "ws": "~8.17.1" + "engines": { + "node": ">= 0.8" } }, - "socket.io-parser": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", - "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "node_modules/upath": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz", + "integrity": "sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==", "dev": true, - "requires": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" + "engines": { + "node": ">=4", + "yarn": "*" } }, - "sockjs": { - "version": "0.3.24", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", - "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "node_modules/update-browserslist-db": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", "dev": true, - "requires": { - "faye-websocket": "^0.11.3", - "uuid": "^8.3.2", - "websocket-driver": "^0.7.4" + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.2", + "picocolors": "^1.0.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "socks": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", - "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "requires": { - "ip-address": "^9.0.5", - "smart-buffer": "^4.2.0" + "dependencies": { + "punycode": "^2.1.0" } }, - "socks-proxy-agent": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", - "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "node_modules/uri-js/node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, - "requires": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" + "engines": { + "node": ">=6" } }, - "source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", "dev": true }, - "source-map-js": { + "node_modules/url-polyfill": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/url-polyfill/-/url-polyfill-1.1.12.tgz", + "integrity": "sha512-mYFmBHCapZjtcNHW0MDq9967t+z4Dmg5CJ0KqysK3+ZbyoNOWQHksGCTWwDhxGXllkWlOc10Xfko6v4a3ucM6A==" + }, + "node_modules/util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "dev": true + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, - "source-map-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-4.0.0.tgz", - "integrity": "sha512-i3KVgM3+QPAHNbGavK+VBq03YoJl24m9JWNbLgsjTj8aJzXG9M61bantBTNBt7CNwY2FYf+RJRYJ3pzalKjIrw==", + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "dev": true, - "requires": { - "abab": "^2.0.6", - "iconv-lite": "^0.6.3", - "source-map-js": "^1.0.2" + "engines": { + "node": ">= 0.4.0" } }, - "source-map-resolve": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", - "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", "dev": true, - "requires": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0" + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" } }, - "source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" } }, - "sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "dev": true - }, - "spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "node_modules/validate-npm-package-name": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", + "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "engines": { + "node": ">= 0.8" } }, - "spdx-license-ids": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", - "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", - "dev": true - }, - "spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "node_modules/vis": { + "version": "4.21.0-EOL", + "resolved": "https://registry.npmjs.org/vis/-/vis-4.21.0-EOL.tgz", + "integrity": "sha512-JVS1mywKg5S88XbkDJPfCb3n+vlg5fMA8Ae2hzs3KHAwD4ryM5qwlbFZ6ReDfY8te7I4NLCpuCoywJQEehvJlQ==", + "deprecated": "Please consider using https://github.com/visjs", "dev": true, - "requires": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" + "dependencies": { + "emitter-component": "^1.1.1", + "hammerjs": "^2.0.8", + "keycharm": "^0.2.0", + "moment": "^2.18.1", + "propagating-hammerjs": "^1.4.6" } }, - "spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "node_modules/vite": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.0.tgz", + "integrity": "sha512-5xokfMX0PIiwCMCMb9ZJcMyh5wbBun0zUzKib+L65vAZ8GY9ePZMXxFrHbr/Kyll2+LSCY7xtERPpxkBDKngwg==", "dev": true, - "requires": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.40", + "rollup": "^4.13.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } } }, - "split": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], "dev": true, - "requires": { - "through": "2" + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" } }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "ssri": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", - "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], "dev": true, - "requires": { - "minipass": "^3.1.1" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "dev": true - }, - "stream-combiner": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", - "integrity": "sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==", + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], "dev": true, - "requires": { - "duplexer": "~0.1.1", - "through": "~2.3.4" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "streamroller": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", - "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], "dev": true, - "requires": { - "date-format": "^4.0.14", - "debug": "^4.3.4", - "fs-extra": "^8.1.0" - }, - "dependencies": { - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - } + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" } }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" } }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" } }, - "string.prototype.trim": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" } }, - "string.prototype.trimend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], "dev": true, - "requires": { - "ansi-regex": "^5.0.1" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "stylus": { - "version": "0.59.0", - "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.59.0.tgz", - "integrity": "sha512-lQ9w/XIOH5ZHVNuNbWW8D822r+/wBSO/d6XvtyHLF7LW4KaCIDeVbvn5DF8fGCJAUCwVhVi/h6J0NUcnylUEjg==", + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], "dev": true, - "requires": { - "@adobe/css-tools": "^4.0.1", - "debug": "^4.3.2", - "glob": "^7.1.6", - "sax": "~1.2.4", - "source-map": "^0.7.3" - }, - "dependencies": { - "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, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "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" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "stylus-loader": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-7.0.0.tgz", - "integrity": "sha512-WTbtLrNfOfLgzTaR9Lj/BPhQroKk/LC1hfTXSUbrxmxgfUo3Y3LpmKRVA2R1XbjvTAvOfaian9vOyfv1z99E+A==", + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], "dev": true, - "requires": { - "fast-glob": "^3.2.11", - "klona": "^2.0.5", - "normalize-path": "^3.0.0" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], "dev": true, - "requires": { - "has-flag": "^3.0.0" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "svg-pan-zoom": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/svg-pan-zoom/-/svg-pan-zoom-3.6.1.tgz", - "integrity": "sha512-JaKkGHHfGvRrcMPdJWkssLBeWqM+Isg/a09H7kgNNajT1cX5AztDTNs+C8UzpCxjCTRrG34WbquwaovZbmSk9g==", - "dev": true - }, - "swagger-ui-dist": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.12.0.tgz", - "integrity": "sha512-B0Iy2ueXtbByE6OOyHTi3lFQkpPi/L7kFOKFeKTr44za7dJIELa9kzaca6GkndCgpK1QTjArnoXG+aUy0XQp1w==" - }, - "symbol-observable": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", - "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", - "dev": true - }, - "systemjs": { - "version": "0.21.6", - "resolved": "https://registry.npmjs.org/systemjs/-/systemjs-0.21.6.tgz", - "integrity": "sha512-R+5S9eV9vcQgWOoS4D87joZ4xkFJHb19ZsyKY07D1+VBDE9bwYcU+KXE0r5XlDA8mFoJGyuWDbfrNoh90JsA8g==" - }, - "systemjs-plugin-babel": { - "version": "0.0.25", - "resolved": "https://registry.npmjs.org/systemjs-plugin-babel/-/systemjs-plugin-babel-0.0.25.tgz", - "integrity": "sha512-RMKSizWWlw4+IpDB385ugxn7Owd9W+HEtjYDQ6yO1FpsnER/vk6FbXRweUF+mvRi6EHgk8vDdUdtui7ReDwX3w==" - }, - "tablesort": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/tablesort/-/tablesort-5.3.0.tgz", - "integrity": "sha512-WkfcZBHsp47gVH9CBHG0ZXopriG01IA87arGrchvIe868d4RiXVvoYPS1zMq9IdW05kBs5iGsqxTABqLyWonbg==", - "dev": true - }, - "tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true - }, - "tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], "dev": true, - "requires": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "dependencies": { - "minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "terser": { - "version": "5.19.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.2.tgz", - "integrity": "sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==", + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], "dev": true, - "requires": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "dependencies": { - "acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "dev": true - }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - } + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "terser-webpack-plugin": { - "version": "5.3.9", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", - "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.17", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.16.8" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "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, - "requires": {} - }, - "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 - }, - "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, - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - } + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "dependencies": { - "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, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "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" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" } }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true - }, - "thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", - "dev": true - }, - "tiny-inflate": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", - "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" } }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], "dev": true, - "requires": { - "is-number": "^7.0.0" + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" } }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true - }, - "tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], "dev": true, - "requires": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "dependencies": { - "universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true - } + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" } }, - "traverse": { - "version": "0.6.9", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.9.tgz", - "integrity": "sha512-7bBrcF+/LQzSgFmT0X5YclVqQxtv7TDJ1f8Wj7ibBu/U6BMLeOpUxuZjV7rMc44UtKxlnMFigdhFAIszSX1DMg==", + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], "dev": true, - "requires": { - "gopd": "^1.0.1", - "typedarray.prototype.slice": "^1.0.3", - "which-typed-array": "^1.1.15" + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" } }, - "tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true - }, - "ts-morph": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-22.0.0.tgz", - "integrity": "sha512-M9MqFGZREyeb5fTl6gNHKZLqBQA0TjA1lea+CR48R8EBTDuWrNqW6ccC5QvjNR4s6wDumD3LTCjOFSp9iwlzaw==", + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], "dev": true, - "requires": { - "@ts-morph/common": "~0.23.0", - "code-block-writer": "^13.0.1" + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" } }, - "ts-node": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", - "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", + "node_modules/vite/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, - "requires": { - "arrify": "^1.0.0", - "buffer-from": "^1.1.0", - "diff": "^3.1.0", - "make-error": "^1.1.1", - "minimist": "^1.2.0", - "mkdirp": "^0.5.1", - "source-map-support": "^0.5.6", - "yn": "^2.0.0" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "requires": { - "minimist": "^1.2.6" - } - } + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, - "tslib": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", - "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" + "node_modules/vlq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz", + "integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==" }, - "tslint": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", - "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", + "node_modules/void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==", "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^4.0.1", - "glob": "^7.1.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.3", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.13.0", - "tsutils": "^2.29.0" - }, - "dependencies": { - "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, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ==", - "dev": true - }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "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" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "requires": { - "minimist": "^1.2.6" - } - }, - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } + "engines": { + "node": ">=0.10.0" } }, - "tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "node_modules/watchpack": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", + "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", "dev": true, - "requires": { - "tslib": "^1.8.1" - }, "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" } }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", "dev": true, - "requires": { - "safe-buffer": "^5.0.1" + "dependencies": { + "minimalistic-assert": "^1.0.0" } }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dependencies": { + "defaults": "^1.0.3" + } }, - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "node_modules/weak-lru-cache": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", + "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==", "dev": true }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "node_modules/web-animations-js": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/web-animations-js/-/web-animations-js-2.3.2.tgz", + "integrity": "sha512-TOMFWtQdxzjWp8qx4DAraTWTsdhxVSiWa6NkPFSaPtZ1diKUxTn4yTix73A1euG1WbSOMMPcY51cnjTIHrGtDA==" + }, + "node_modules/webpack": { + "version": "5.94.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", + "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", "dev": true, - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" + "dependencies": { + "@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.1", + "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" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } } }, - "typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "node_modules/webpack-dev-middleware": { + "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, - "requires": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^4.6.0", + "mime-types": "^2.1.31", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + } } }, - "typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "node_modules/webpack-dev-middleware/node_modules/memfs": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.11.1.tgz", + "integrity": "sha512-LZcMTBAgqUUKNXZagcZxvXXfgF1bHX7Y7nQ0QyEiNbRJgE29GhgPd8Yna1VQcLlPiHt/5RFJMWYN9Uv/VPNvjQ==", "dev": true, - "requires": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "dependencies": { + "@jsonjoy.com/json-pack": "^1.0.3", + "@jsonjoy.com/util": "^1.3.0", + "tree-dump": "^1.0.1", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">= 4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" } }, - "typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "node_modules/webpack-dev-server": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.0.4.tgz", + "integrity": "sha512-dljXhUgx3HqKP2d8J/fUMvhxGhzjeNVarDLcbO/EWMSgRizDkxHQDZQaLFL5VJY9tRBj2Gz+rvCEYYvhbqPHNA==", "dev": true, - "requires": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "dependencies": { + "@types/bonjour": "^3.5.13", + "@types/connect-history-api-fallback": "^1.5.4", + "@types/express": "^4.17.21", + "@types/serve-index": "^1.9.4", + "@types/serve-static": "^1.15.5", + "@types/sockjs": "^0.3.36", + "@types/ws": "^8.5.10", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.2.1", + "chokidar": "^3.6.0", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.4.0", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.1.0", + "launch-editor": "^2.6.1", + "open": "^10.0.3", + "p-retry": "^6.2.0", + "rimraf": "^5.0.5", + "schema-utils": "^4.2.0", + "selfsigned": "^2.4.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^7.1.0", + "ws": "^8.16.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } } }, - "typed-array-length": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "node_modules/webpack-dev-server/node_modules/http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", "dev": true, - "requires": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } } }, - "typed-assert": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz", - "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==", - "dev": true - }, - "typedarray.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typedarray.prototype.slice/-/typedarray.prototype.slice-1.0.3.tgz", - "integrity": "sha512-8WbVAQAUlENo1q3c3zZYuy5k9VzBQvp8AX9WOtbvyWlLM1v5JaSRmjubLjzHF4JFtptjH/5c/i95yaElvcjC0A==", + "node_modules/webpack-dev-server/node_modules/ipaddr.js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-errors": "^1.3.0", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-offset": "^1.0.2" + "engines": { + "node": ">= 10" } }, - "typemoq": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/typemoq/-/typemoq-2.1.0.tgz", - "integrity": "sha512-DtRNLb7x8yCTv/KHlwes+NI+aGb4Vl1iPC63Hhtcvk1DpxSAZzKWQv0RQFY0jX2Uqj0SDBNl8Na4e6MV6TNDgw==", + "node_modules/webpack-dev-server/node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", "dev": true, - "requires": { - "circular-json": "^0.3.1", - "lodash": "^4.17.4", - "postinstall-build": "^5.0.1" + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "typescript": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", - "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", - "dev": true - }, - "ua-parser-js": { - "version": "0.7.35", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.35.tgz", - "integrity": "sha512-veRf7dawaj9xaWEu9HoTVn5Pggtc/qj+kqTOFvNiN1l0YdxwC1kvel57UCjThjGa3BHBihE8/UJAHI+uQHmd/g==", - "dev": true - }, - "uglify-js": { - "version": "3.17.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", - "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "node_modules/webpack-merge": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", + "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", "dev": true, - "optional": true + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.1" + }, + "engines": { + "node": ">=18.0.0" + } }, - "unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "node_modules/webpack-node-externals": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz", + "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==", "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" + "engines": { + "node": ">=6" } }, - "unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", - "dev": true - }, - "unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true, - "requires": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" + "engines": { + "node": ">=10.13.0" } }, - "unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", - "dev": true - }, - "unicode-properties": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", - "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", + "node_modules/webpack-subresource-integrity": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz", + "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==", "dev": true, - "requires": { - "base64-js": "^1.3.0", - "unicode-trie": "^2.0.0" + "dependencies": { + "typed-assert": "^1.0.8" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "html-webpack-plugin": ">= 5.0.0-beta.1 < 6", + "webpack": "^5.12.0" + }, + "peerDependenciesMeta": { + "html-webpack-plugin": { + "optional": true + } } }, - "unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "dev": true - }, - "unicode-trie": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", - "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", + "node_modules/webpack/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, - "requires": { - "pako": "^0.2.5", - "tiny-inflate": "^1.0.0" - }, "dependencies": { - "pako": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", - "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", - "dev": true - } + "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" } }, - "unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "node_modules/webpack/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, - "requires": { - "unique-slug": "^2.0.0" + "peerDependencies": { + "ajv": "^6.9.1" } }, - "unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "node_modules/webpack/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, - "requires": { - "imurmurhash": "^0.1.4" + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" } }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true + "node_modules/webpack/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" + } }, - "unix-crypt-td-js": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/unix-crypt-td-js/-/unix-crypt-td-js-1.1.4.tgz", - "integrity": "sha512-8rMeVYWSIyccIJscb9NdCfZKSRBKYTeVnwmiRYT2ulE3qd1RaDQ0xQDP+rI3ccIWbhu/zuo5cgN8z73belNZgw==", + "node_modules/webpack/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 }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "node_modules/webpack/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 }, - "update-browserslist-db": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", - "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", + "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, - "requires": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" + "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" } }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", "dev": true, - "requires": { - "punycode": "^2.1.0" + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" } }, - "url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", "dev": true, - "requires": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" + "engines": { + "node": ">=0.8.0" } }, - "url-polyfill": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/url-polyfill/-/url-polyfill-1.1.12.tgz", - "integrity": "sha512-mYFmBHCapZjtcNHW0MDq9967t+z4Dmg5CJ0KqysK3+ZbyoNOWQHksGCTWwDhxGXllkWlOc10Xfko6v4a3ucM6A==" - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "dev": true - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" } }, - "validate-npm-package-name": { + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==" + }, + "node_modules/whatwg-mimetype": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz", - "integrity": "sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", "dev": true, - "requires": { - "builtins": "^5.0.0" + "engines": { + "node": ">=18" } }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "dev": true - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "vis": { - "version": "4.21.0-EOL", - "resolved": "https://registry.npmjs.org/vis/-/vis-4.21.0-EOL.tgz", - "integrity": "sha512-JVS1mywKg5S88XbkDJPfCb3n+vlg5fMA8Ae2hzs3KHAwD4ryM5qwlbFZ6ReDfY8te7I4NLCpuCoywJQEehvJlQ==", + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "dev": true, - "requires": { - "emitter-component": "^1.1.1", - "hammerjs": "^2.0.8", - "keycharm": "^0.2.0", - "moment": "^2.18.1", - "propagating-hammerjs": "^1.4.6" + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "vlq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz", - "integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==" + "node_modules/which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "void-elements": { + "node_modules/wildcard": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", "dev": true }, - "watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "node_modules/windows-release": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz", + "integrity": "sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==", "dev": true, - "requires": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" + "dependencies": { + "execa": "^4.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "node_modules/windows-release/node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", "dev": true, - "requires": { - "minimalistic-assert": "^1.0.0" + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "node_modules/windows-release/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, - "requires": { - "defaults": "^1.0.3" + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "web-animations-js": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/web-animations-js/-/web-animations-js-2.3.2.tgz", - "integrity": "sha512-TOMFWtQdxzjWp8qx4DAraTWTsdhxVSiWa6NkPFSaPtZ1diKUxTn4yTix73A1euG1WbSOMMPcY51cnjTIHrGtDA==" - }, - "webdriver-js-extender": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz", - "integrity": "sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==", + "node_modules/windows-release/node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", "dev": true, - "requires": { - "@types/selenium-webdriver": "^3.0.0", - "selenium-webdriver": "^3.0.1" + "engines": { + "node": ">=8.12.0" } }, - "webdriver-manager": { - "version": "12.1.9", - "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.9.tgz", - "integrity": "sha512-Yl113uKm8z4m/KMUVWHq1Sjtla2uxEBtx2Ue3AmIlnlPAKloDn/Lvmy6pqWCUersVISpdMeVpAaGbNnvMuT2LQ==", + "node_modules/windows-release/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, - "requires": { - "adm-zip": "^0.5.2", - "chalk": "^1.1.1", - "del": "^2.2.0", - "glob": "^7.0.3", - "ini": "^1.3.4", - "minimist": "^1.2.0", - "q": "^1.4.1", - "request": "^2.87.0", - "rimraf": "^2.5.2", - "semver": "^5.3.0", - "xml2js": "0.5.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", - "dev": true - }, - "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, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "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" - } - }, - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", - "dev": true - } + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "webpack": { - "version": "5.76.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz", - "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==", + "node_modules/windows-release/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, - "requires": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "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.1.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" - }, - "dependencies": { - "@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", - "dev": true - }, - "acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "dev": true - }, - "acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", - "dev": true, - "requires": {} - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "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, - "requires": {} - }, - "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 - }, - "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, - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - } + "engines": { + "node": ">=0.10.0" } }, - "webpack-dev-middleware": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", - "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, - "requires": { - "colorette": "^2.0.10", - "memfs": "^3.4.3", - "mime-types": "^2.1.31", - "range-parser": "^1.2.1", - "schema-utils": "^4.0.0" - }, "dependencies": { - "schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - } - } - } - }, - "webpack-dev-server": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.0.tgz", - "integrity": "sha512-L5S4Q2zT57SK7tazgzjMiSMBdsw+rGYIX27MgPgx7LDhWO0lViPrHKoLS7jo5In06PWYAhlYu3PbyoC6yAThbw==", - "dev": true, - "requires": { - "@types/bonjour": "^3.5.9", - "@types/connect-history-api-fallback": "^1.3.5", - "@types/express": "^4.17.13", - "@types/serve-index": "^1.9.1", - "@types/serve-static": "^1.13.10", - "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.1", - "ansi-html-community": "^0.0.8", - "bonjour-service": "^1.0.11", - "chokidar": "^3.5.3", - "colorette": "^2.0.10", - "compression": "^1.7.4", - "connect-history-api-fallback": "^2.0.0", - "default-gateway": "^6.0.3", - "express": "^4.17.3", - "graceful-fs": "^4.2.6", - "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.3", - "ipaddr.js": "^2.0.1", - "open": "^8.0.9", - "p-retry": "^4.5.0", - "rimraf": "^3.0.2", - "schema-utils": "^4.0.0", - "selfsigned": "^2.0.1", - "serve-index": "^1.9.1", - "sockjs": "^0.3.24", - "spdy": "^4.0.2", - "webpack-dev-middleware": "5.3.4", - "ws": "^8.4.2" - }, - "dependencies": { - "schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - } - } + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" } }, - "webpack-merge": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", - "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "requires": { - "clone-deep": "^4.0.1", - "wildcard": "^2.0.0" + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "dev": true - }, - "webpack-subresource-integrity": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz", - "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==", + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "requires": { - "typed-assert": "^1.0.8" + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "requires": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "whatwg-fetch": { - "version": "3.6.17", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.17.tgz", - "integrity": "sha512-c4ghIvG6th0eudYwKZY5keb81wtFz9/WeAHAoy8+r18kcWlitUIrmGFQ2rWEl4UCKUilD3zCLHOIPheHx5ypRQ==" + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, - "requires": { - "isexe": "^2.0.0" + "engines": { + "node": ">=8" } }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "dev": true - }, - "which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "requires": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.2" + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "requires": { - "string-width": "^1.0.2 || 2 || 3 || 4" + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "wildcard": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", - "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "windows-release": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz", - "integrity": "sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==", + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, - "requires": { - "execa": "^4.0.2" - }, - "dependencies": { - "execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - } - }, - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true - } + "engines": { + "node": ">=8" } }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "wrappy": { + "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, - "ws": { + "node_modules/ws": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "dev": true, - "requires": {} - }, - "xml2js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", - "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", - "dev": true, - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" + "engines": { + "node": ">=10.0.0" }, - "dependencies": { - "xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "dev": true + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true } } }, - "xmlbuilder": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz", - "integrity": "sha512-eKRAFz04jghooy8muekqzo8uCSVNeyRedbuJrp0fovbLIi7wlsYtdUn3vBAAPq2Y3/0xMz2WMEUQ8yhVVO9Stw==", - "dev": true + "node_modules/xmlbuilder": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-12.0.0.tgz", + "integrity": "sha512-lMo8DJ8u6JRWp0/Y4XLa/atVDr75H9litKlb2E5j3V3MesoL50EBgZDWoLT3F/LztVnG67GjPXLZpqcky/UMnQ==", + "dev": true, + "engines": { + "node": ">=6.0" + } }, - "xmldoc": { + "node_modules/xmldoc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/xmldoc/-/xmldoc-1.3.0.tgz", "integrity": "sha512-y7IRWW6PvEnYQZNZFMRLNJw+p3pezM4nKYPfr15g4OOW9i8VpeydycFuipE2297OvZnh3jSb2pxOt9QpkZUVng==", "dev": true, - "requires": { + "dependencies": { "sax": "^1.2.4" } }, - "xxhashjs": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz", - "integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==", - "dev": true, - "requires": { - "cuint": "^0.2.2" - } - }, - "y18n": { + "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true + "engines": { + "node": ">=10" + } }, - "yallist": { + "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, - "yaml": { + "node_modules/yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true - }, - "yargs": { - "version": "17.5.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", - "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", "dev": true, - "requires": { - "cliui": "^7.0.2", + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" } }, - "yargs-parser": { + "node_modules/yargs-parser": { "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true + "engines": { + "node": ">=12" + } }, - "yn": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", - "integrity": "sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==", - "dev": true + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/yargs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ylru": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ylru/-/ylru-1.4.0.tgz", + "integrity": "sha512-2OQsPNEmBCvXuFlIni/a+Rn+R2pHW9INm0BxXJ4hVDA8TirqMj+J/Rp9ItLatT/5pZqWwefVrTQcHpixsxnVlA==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "zepto": { + "node_modules/zepto": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/zepto/-/zepto-1.2.0.tgz", "integrity": "sha512-C1x6lfvBICFTQIMgbt3JqMOno3VOtkWat/xEakLTOurskYIHPmzJrzd1e8BnmtdDVJlGuk5D+FxyCA8MPmkIyA==", "dev": true }, - "zone.js": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.8.tgz", - "integrity": "sha512-82bctBg2hKcEJ21humWIkXRlLBBmrc3nN7DFh5LGGhcyycO2S7FN8NmdvlcKaGFDNVL4/9kFLmwmInTavdJERA==", - "requires": { - "tslib": "^2.3.0" - } + "node_modules/zone.js": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.10.tgz", + "integrity": "sha512-YGAhaO7J5ywOXW6InXNlLmfU194F8lVgu7bRntUF3TiG8Y3nBK0x1UJJuHUP/e8IyihkjCYqhCScpSwnlaSRkQ==" } } } diff --git a/imxweb/package.json b/imxweb/package.json index 94a2b7bdc..4e41d4f18 100644 --- a/imxweb/package.json +++ b/imxweb/package.json @@ -1,135 +1,148 @@ { "name": "imxweb", - "version": "9.2.1", + "version": "9.3.0", "scripts": { "ng": "ng", "start": "ng serve --configuration development", "start:remote-dev": "ng serve -c remote-dev", "start:remote-qs": "ng serve -c remote-qs", + "pre-build": "node prebuild.js", "build": "ng build", + "nx:build": "npx nx build", "build:watch": "ng build --watch --configuration development", "build:watch:dynamic": "ng build --watch --configuration dynamic", - "build:app": "ng build --aot --configuration production", - "build:app:debug": "ng build --configuration production --source-map true", - "build:lib": "ng build --configuration production", - "build:lib:debug": "ng build", + "build:watch:dynamic-ops": "ng build --watch --configuration dynamic-ops", + "build:watch:dynamic-pwd": "ng build --watch --configuration dynamic-pwd", + "nx:build-all": "npx nx run-many -t build", + "build:production": "npx nx build --configuration production", + "postinstall": "node install-local-packages.js && node remove-local-package-locks.js", "test": "ng test --code-coverage --browsers Chrome", - "test-ie": "ng test --browsers IE --watch=false", "test-ci": "ng test --code-coverage --browsers ChromeHeadless --watch=false", + "nx:test-ci": "npx nx run-many -t test --code-coverage --browsers ChromeHeadless --watch=false", "lint": "ng lint", "lint:fix": "ng lint --fix=true", "e2e": "ng e2e", - "doc:aob": "cd projects/aob && compodoc -p tsconfig.lib.json", - "doc:qbm": "cd projects/qbm && compodoc -p tsconfig.lib.json", - "doc:qbm-landingpage": "cd projects/qbm-app-landingpage && compodoc -p tsconfig.app.json", - "doc:qer": "cd projects/qer && compodoc -p tsconfig.lib.json", - "doc:qer-opssupport": "cd projects/qer-app-operationssupport && compodoc -p tsconfig.app.json", - "doc:qer-portal": "cd projects/qer-app-portal && compodoc -p tsconfig.app.json", - "postinstall": "node remove-local-package-locks.js" + "doc": "node build-docs.js" + }, + "scriptsComments": { + "start": "Runs a development server for on localhost:4200. e.g npm run start ", + "pre-build": "Runs the prebuild.js script against to move lib files to expected locations. Run automatically within nx:build. e.g npm run pre-build ", + "build": "Directly uses the angular compiler to build a single project. e.g npm run build ", + "nx:build": "Uses Nx to build all dependencies before the specified project. e.g npm run nx:build ", + "build:watch": "Directly uses the angular compiler to build-watch a single project. e.g", + "build:watch:dynamic": "Portal: Directly uses the angular packagr to build-watch and place a single dynamic module.", + "build:watch:dynamic-ops": "OpsWeb: Directly uses the angular packagr to build-watch and place a single dynamic module.", + "build:watch:dynamic-pwd": "PwdWeb: Directly uses the angular packagr to build-watch and place a single dynamic module.", + "build:production": "Use Nx to build a production ready project.", + "postinstall": "Lifecycle script ran after install. Runs the install-local-packages.js to ask user if remote or local api packages should be used. Then runs the remove-local-package-locks.js to clean the package-lock.json of workspace-specific packages. ", + "test": "Tests a single project. e.g. npm run test ", + "nx:test-ci": "Tests all projects in workspace.", + "doc": "Runs the build-doc.js script against to apply compodoc to any given project. e.g. npm run doc " }, "private": true, "dependencies": { - "@angular/animations": "^14.2.12", - "@angular/cdk": "^14.2.7", - "@angular/common": "^14.2.12", - "@angular/compiler": "^14.2.12", - "@angular/core": "^14.2.12", - "@angular/forms": "^14.2.12", - "@angular/material": "^14.2.7", - "@angular/material-moment-adapter": "^14.2.7", - "@angular/platform-browser": "^14.2.12", - "@angular/platform-browser-dynamic": "^14.2.12", - "@angular/router": "^14.2.12", - "@elemental-ui/cadence-icon": "file:imx-modules/elemental-ui_cadence-icon.tgz", - "@elemental-ui/core": "file:imx-modules/elemental-ui_core.tgz", - "@ngx-translate/core": "^11.0.1", - "@ngx-translate/http-loader": "^4.0.0", - "@types/cytoscape": "^3.19.9", - "@types/cytoscape-edgehandles": "^4.0.0", - "@types/systemjs": "^6.1.1", - "applicationinsights-js": "^1.0.21", - "billboard.js": "^3.11.3", - "core-js": "^2.6.9", - "cytoscape": "^3.23.0", + "@angular/animations": "^18.2.2", + "@angular/cdk": "^18.2.2", + "@angular/common": "^18.2.2", + "@angular/compiler": "^18.2.2", + "@angular/core": "^18.2.2", + "@angular/forms": "^18.2.2", + "@angular/material": "^18.2.2", + "@angular/material-moment-adapter": "^18.2.2", + "@angular/platform-browser": "^18.2.2", + "@angular/platform-browser-dynamic": "^18.2.2", + "@angular/router": "^18.2.2", + "@ngx-translate/core": "^15.0.0", + "@ngx-translate/http-loader": "^8.0.0", + "billboard.js": "^3.12.4", + "core-js": "^3.37.1", + "cytoscape": "^3.29.2", "cytoscape-edgehandles": "^4.0.1", "d3-hierarchy": "^3.1.2", "d3-scale": "^4.0.2", - "element-resize-detector": "^1.1.13", - "file-saver": "^2.0.2", - "moment-timezone": "^0.5.38", - "ngx-logger": "^4.1.2", - "rxjs": "~6.5.4", - "smooth-scrollbar": "^8.5.1", - "swagger-ui-dist": "~4.12.0", - "systemjs": "^0.21.6", - "systemjs-plugin-babel": "0.0.25", - "tslib": "^2.0.0", + "fs-extra": "^11.2.0", + "moment-timezone": "^0.5.45", + "ng-recaptcha-2": "^14.0.0", + "ngx-logger": "^5.0.12", + "nx": "^19.7.0", + "rxjs": "^7.8.1", + "swagger-ui-dist": "~5.17.14", + "tslib": "^2.6.3", "url-polyfill": "^1.1.12", "web-animations-js": "^2.3.2", - "whatwg-fetch": "^3.0.0", - "zone.js": "~0.11.5" + "whatwg-fetch": "^3.6.20", + "zone.js": "~0.14.7" }, "devDependencies": { - "@angular-devkit/build-angular": "^14.2.12", - "@angular/cli": "^14.2.12", - "@angular/compiler-cli": "^14.2.12", - "@angular/language-service": "^14.2.12", - "@compodoc/compodoc": "^1.1.10", - "@types/d3-hierarchy": "^3.1.0", - "@types/d3-scale": "^4.0.2", - "@types/jasmine": "~3.6.0", - "@types/jasminewd2": "^2.0.10", - "@types/node": "^16.11.7", - "angular-in-memory-web-api": "^0.14.0", - "copy-and-watch": "^0.1.5", - "jasmine": "^3.4.0", - "jasmine-core": "^3.8.0", - "jasmine-spec-reporter": "~5.0.0", + "@angular-devkit/build-angular": "^18.2.2", + "@angular-devkit/core": "^18.2.2", + "@angular-devkit/schematics": "^18.2.2", + "@angular-eslint/builder": "^18.2.2", + "@angular-eslint/eslint-plugin": "^18.2.2", + "@angular-eslint/eslint-plugin-template": "^18.2.2", + "@angular-eslint/schematics": "^18.2.2", + "@angular-eslint/template-parser": "^18.2.2", + "@angular-eslint/utils": "^18.2.2", + "@angular/cli": "^18.2.2", + "@angular/compiler-cli": "^18.2.2", + "@angular/language-service": "^18.2.2", + "@compodoc/compodoc": "1.1.25", + "@nx/angular": "^19.7.0", + "@nx/js": "^19.7.0", + "@nx/workspace": "^19.7.0", + "@schematics/angular": "^18.2.2", + "@types/cytoscape": "^3.21.4", + "@types/cytoscape-edgehandles": "^4.0.4", + "@types/d3-hierarchy": "^3.1.7", + "@types/d3-scale": "^4.0.8", + "@types/jasmine": "~5.1.4", + "@types/jasminewd2": "^2.0.13", + "@types/node": "^20.14.9", + "@typescript-eslint/eslint-plugin": "^7.14.1", + "@typescript-eslint/parser": "^7.14.1", + "angular-in-memory-web-api": "^0.18.0", + "copy-and-watch": "^0.1.8", + "eslint": "^8.44.0", + "eslint-config-prettier": "^8.8.0", + "jasmine": "^5.1.0", + "jasmine-core": "^5.1.2", + "jasmine-spec-reporter": "~7.0.0", "karma": "^6.4.3", - "karma-chrome-launcher": "~3.1.0", + "karma-chrome-launcher": "~3.2.0", "karma-cli": "^2.0.0", - "karma-coverage-istanbul-reporter": "~2.0.1", + "karma-coverage-istanbul-reporter": "~3.0.3", "karma-ie-launcher": "^1.0.0", - "karma-jasmine": "~4.0.0", - "karma-jasmine-html-reporter": "^1.5.0", - "karma-junit-reporter": "^1.2.0", - "karma-viewport": "^1.0.4", - "ng-mocks": "^14.5.2", - "ng-packagr": "^14.2.2", - "protractor": "^7.0.0", - "terser": "^5.15.1", - "ts-node": "~7.0.0", - "tslint": "~6.1.0", - "typemoq": "^2.1.0", - "typescript": "~4.6.4" + "karma-jasmine": "~5.1.0", + "karma-jasmine-html-reporter": "^2.1.0", + "karma-junit-reporter": "^2.0.1", + "karma-viewport": "^1.0.9", + "ng-mocks": "^14.13.0", + "ng-packagr": "^18.2.1", + "prettier": "3.3.2", + "terser": "^5.31.1", + "ts-node": "^10.9.2", + "typescript": "^5.4.5" }, "optionalDependencies": { - "imx-api-aad": "file:imx-modules/imx-api-aad.tgz", - "imx-api-aob": "file:imx-modules/imx-api-aob.tgz", - "imx-api-apc": "file:imx-modules/imx-api-apc.tgz", - "imx-api-att": "file:imx-modules/imx-api-att.tgz", - "imx-api-cpl": "file:imx-modules/imx-api-cpl.tgz", - "imx-api-dpr": "file:imx-modules/imx-api-dpr.tgz", - "imx-api-hds": "file:imx-modules/imx-api-hds.tgz", - "imx-api-o3e": "file:imx-modules/imx-api-o3e.tgz", - "imx-api-o3t": "file:imx-modules/imx-api-o3t.tgz", - "imx-api-olg": "file:imx-modules/imx-api-olg.tgz", - "imx-api-pol": "file:imx-modules/imx-api-pol.tgz", - "imx-api-qbm": "file:imx-modules/imx-api-qbm.tgz", - "imx-api-qer": "file:imx-modules/imx-api-qer.tgz", - "imx-api-rmb": "file:imx-modules/imx-api-rmb.tgz", - "imx-api-rms": "file:imx-modules/imx-api-rms.tgz", - "imx-api-rps": "file:imx-modules/imx-api-rps.tgz", - "imx-api-sac": "file:imx-modules/imx-api-sac.tgz", - "imx-api-tsb": "file:imx-modules/imx-api-tsb.tgz", - "imx-api-uci": "file:imx-modules/imx-api-uci.tgz", - "imx-qbm-dbts": "file:imx-modules/imx-qbm-dbts.tgz" - }, - - "overrides": { - "postcss": "8.4.31", - "tough-cookie": "4.1.3", - "webpack-dev-middleware": "5.3.4", - "xml2js": "0.5.0" + "@elemental-ui/cadence-icon": "^3.1.101", + "@elemental-ui/core": "^18.0.1", + "@imx-modules/imx-api-aad": "~9.3.0", + "@imx-modules/imx-api-aob": "~9.3.0", + "@imx-modules/imx-api-apc": "~9.3.0", + "@imx-modules/imx-api-att": "~9.3.0", + "@imx-modules/imx-api-cpl": "~9.3.0", + "@imx-modules/imx-api-dpr": "~9.3.0", + "@imx-modules/imx-api-hds": "~9.3.0", + "@imx-modules/imx-api-olg": "~9.3.0", + "@imx-modules/imx-api-pol": "~9.3.0", + "@imx-modules/imx-api-qbm": "~9.3.0", + "@imx-modules/imx-api-qer": "~9.3.0", + "@imx-modules/imx-api-rmb": "~9.3.0", + "@imx-modules/imx-api-rms": "~9.3.0", + "@imx-modules/imx-api-rps": "~9.3.0", + "@imx-modules/imx-api-sac": "~9.3.0", + "@imx-modules/imx-api-tsb": "~9.3.0", + "@imx-modules/imx-api-uci": "~9.3.0", + "@imx-modules/imx-qbm-dbts": "~9.3.0" } } diff --git a/imxweb/prebuild.js b/imxweb/prebuild.js new file mode 100644 index 000000000..7e37a1961 --- /dev/null +++ b/imxweb/prebuild.js @@ -0,0 +1,48 @@ +// Script to handle moving plugin libraries to their respective application +// Use: node prebuild.js +// must correspond to the physical path i.e qer-app-portal + +if (process.argv.length < 3) +{ + console.error("No application given. Please provide an application name as an argument.") + process.exit(1) +} + +const fse = require('fs-extra'); +const path = require('path'); +const app = process.argv[2]; + + +const modules = getDependencies(app); +setupFiles(modules); + + +function getDependencies(app = "") { + const appProject = path.join('projects', app, 'project.json') + + if (!fse.existsSync(appProject)) { + console.error('The application given does not lead to a valid project file'); + process.exit(1); + } + const project = fse.readJSONSync(appProject); + if (!project.targets.prebuild.dependsOn[0].projects) { + console.error('The application project does not have the structure targets -> prebuild -> dependsOn -> [projects]'); + process.exit(1); + } + return project.targets.prebuild.dependsOn[0].projects; +} + +function setupFiles(modules = []) { + modules.forEach(module => { + let src = path.join('dist', module); + let dest = path.join('html', app, module); + if (fse.existsSync(src)) { + console.log(`Copying ${module} from ${src} folder to ${dest}...`); + fse.copySync(src, dest); + console.log('Finished!') + } + else { + console.warn(`No ${module} artifact exists, skipping...`) + } + }) +} diff --git a/imxweb/projects/aad/.compodocrc.json b/imxweb/projects/aad/.compodocrc.json index c4e734351..1a3d7a581 100644 --- a/imxweb/projects/aad/.compodocrc.json +++ b/imxweb/projects/aad/.compodocrc.json @@ -1,6 +1,6 @@ { "name": "IMX Web - AAD Library", - "output": "../../documentation/v92/aad", + "output": "../../documentation/v93/aad", "theme": "material", "assetsFolder": "../../compodoc/assets", "customLogo": "../../compodoc/assets/images/oneidentity-logo.png", diff --git a/imxweb/projects/aad/.eslintrc.json b/imxweb/projects/aad/.eslintrc.json new file mode 100644 index 000000000..cf18944aa --- /dev/null +++ b/imxweb/projects/aad/.eslintrc.json @@ -0,0 +1,35 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*", "**/*.spec.ts"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": ["imxweb/projects/aad/tsconfig.lib.json", "imxweb/projects/aad/tsconfig.spec.json"], + "createDefaultProgram": true + }, + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "imx", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "imx", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "rules": {} + } + ] +} diff --git a/imxweb/projects/aad/imx-plugin-config.json b/imxweb/projects/aad/imx-plugin-config.json index df6c9882f..4e64a27f0 100644 --- a/imxweb/projects/aad/imx-plugin-config.json +++ b/imxweb/projects/aad/imx-plugin-config.json @@ -1,8 +1,8 @@ { - "qer-app-portal": [ - { - "Container": "aad", - "Name": "AadConfigModule" - } - ] -} \ No newline at end of file + "qer-app-portal": [ + { + "Container": "aad", + "Name": "AadConfigModule" + } + ] +} diff --git a/imxweb/projects/aad/karma.conf.js b/imxweb/projects/aad/karma.conf.js index 462789f9e..33e53c95d 100644 --- a/imxweb/projects/aad/karma.conf.js +++ b/imxweb/projects/aad/karma.conf.js @@ -15,7 +15,7 @@ module.exports = function (config) { require('karma-junit-reporter'), ], client: { - clearContext: false // leave Jasmine Spec Runner output visible in browser + clearContext: false, // leave Jasmine Spec Runner output visible in browser }, coverageIstanbulReporter: { dir: require('path').join(__dirname, 'results'), @@ -23,11 +23,11 @@ module.exports = function (config) { fixWebpackSourcePaths: true, 'report-config': { html: { - subdir: 'coverage-html' + subdir: 'coverage-html', }, }, thresholds: { - lines: 40 + lines: 40, }, }, junitReporter: { @@ -37,13 +37,14 @@ module.exports = function (config) { port: 9876, captureTimeout: 210000, browserDisconnectTolerance: 3, - browserDisconnectTimeout : 210000, - browserNoActivityTimeout : 210000, + browserDisconnectTimeout: 210000, + browserNoActivityTimeout: 210000, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], singleRun: false, - restartOnFileChange: true + failOnEmptyTestSuite: false, + restartOnFileChange: true, }); }; diff --git a/imxweb/projects/aad/ng-package.dynamic.json b/imxweb/projects/aad/ng-package.dynamic.json index d0faa79d0..450ad5c74 100644 --- a/imxweb/projects/aad/ng-package.dynamic.json +++ b/imxweb/projects/aad/ng-package.dynamic.json @@ -16,12 +16,11 @@ "@elemental-ui/core", "@ngx-translate/core", "@ngx-translate/http-loader", - "applicationinsights-js", "billboard.js" ], - "dest": "../../html/aad", + "dest": "../../html/qer-app-portal/aad", "lib": { "entryFile": "src/public_api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] } } diff --git a/imxweb/projects/aad/ng-package.json b/imxweb/projects/aad/ng-package.json index 41b245deb..e347c89b9 100644 --- a/imxweb/projects/aad/ng-package.json +++ b/imxweb/projects/aad/ng-package.json @@ -4,6 +4,6 @@ "dest": "../../dist/aad", "lib": { "entryFile": "src/public_api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] } } diff --git a/imxweb/projects/aad/package.json b/imxweb/projects/aad/package.json index df0bfba60..1921185ca 100644 --- a/imxweb/projects/aad/package.json +++ b/imxweb/projects/aad/package.json @@ -1,6 +1,6 @@ { "name": "aad", - "version": "9.2.1", + "version": "9.3.0", "private": true, "bundledDependencies": [ "imx-api-aad" diff --git a/imxweb/projects/aad/project.json b/imxweb/projects/aad/project.json new file mode 100644 index 000000000..117338a92 --- /dev/null +++ b/imxweb/projects/aad/project.json @@ -0,0 +1,65 @@ +{ + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "name": "aad", + "sourceRoot": "projects/aad/src", + "implicitDependencies": ["qer"], + "projectType": "library", + "prefix": "imx", + "generators": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:ng-packagr", + "options": { + "tsConfig": "projects/aad/tsconfig.lib.json", + "project": "projects/aad/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "projects/aad/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "projects/aad/tsconfig.lib.json" + }, + "dynamic": { + "project": "projects/aad/ng-package.dynamic.json" + }, + "remote-dev": { + "tsConfig": "projects/aad/tsconfig.lib.json" + }, + "remote-cornydqs": { + "tsConfig": "projects/aad/tsconfig.lib.json" + } + }, + "outputs": ["{workspaceRoot}/dist/aad"] + }, + "test": { + "executor": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/aad/src/test.ts", + "tsConfig": "projects/aad/tsconfig.spec.json", + "karmaConfig": "projects/aad/karma.conf.js", + "codeCoverageExclude": ["projects/o3t/src/**/test/*"], + "stylePreprocessorOptions": { + "includePaths": [ + "./shared/assets", + "./shared/scss", + "./node_modules", + "./node_modules/@elemental-ui/cadence-icon", + "./node_modules/@elemental-ui/core" + ] + } + } + }, + "lint": { + "executor": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": ["projects/aad/tsconfig.lib.json", "projects/aad/tsconfig.spec.json"], + "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + } + } + } +} diff --git a/imxweb/projects/aad/src/lib/aad-config.module.ts b/imxweb/projects/aad/src/lib/aad-config.module.ts index c685d7c79..2886cd6ad 100644 --- a/imxweb/projects/aad/src/lib/aad-config.module.ts +++ b/imxweb/projects/aad/src/lib/aad-config.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -34,13 +34,8 @@ import { ApiService } from './api.service'; import { InitService } from './init.service'; @NgModule({ - declarations: [ - ], - imports: [ - CommonModule, - TranslateModule, - EuiCoreModule - ] + declarations: [], + imports: [CommonModule, TranslateModule, EuiCoreModule], }) export class AadConfigModule { constructor( @@ -48,15 +43,18 @@ export class AadConfigModule { private readonly initService: InitService, private readonly aadApiService: ApiService, private readonly dynamicMethodService: DynamicMethodService, - private readonly entlTypeService: RequestableEntitlementTypeService) { + private readonly entlTypeService: RequestableEntitlementTypeService, + ) { this.logger.info(this, '🔥 AAD loaded'); this.initService.onInit(); this.entlTypeService.Register(async () => [ - new RequestableEntitlementType("AADDeniedServicePlan", + new RequestableEntitlementType( + 'AADDeniedServicePlan', this.aadApiService.apiClient, - "UID_AADDeniedServicePlan", - this.dynamicMethodService) + 'UID_AADDeniedServicePlan', + this.dynamicMethodService, + ), ]); this.logger.info(this, '▶️ AAD initialized'); } diff --git a/imxweb/projects/aad/src/lib/aad-extension/aad-extension.service.ts b/imxweb/projects/aad/src/lib/aad-extension/aad-extension.service.ts index 528ad97c2..15f505090 100644 --- a/imxweb/projects/aad/src/lib/aad-extension/aad-extension.service.ts +++ b/imxweb/projects/aad/src/lib/aad-extension/aad-extension.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,14 +30,13 @@ import { AppConfigService } from 'qbm'; import { AadPermissionsService } from '../admin/aad-permissions.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class AadExtensionService { - constructor( private readonly permissions: AadPermissionsService, - private appConfig: AppConfigService - ) { } + private appConfig: AppConfigService, + ) {} public async canReadInAzure(): Promise { return this.permissions.canReadInAzure(); @@ -46,5 +45,4 @@ export class AadExtensionService { public getReportForSubSku(uidSubSku: string): string { return `${this.appConfig.BaseUrl}/portal/targetsystem/aadsubsku/${uidSubSku}/licenceoverview`; } - } diff --git a/imxweb/projects/aad/src/lib/aad-extension/licence-overview-button/licence-overview-button.component.html b/imxweb/projects/aad/src/lib/aad-extension/licence-overview-button/licence-overview-button.component.html index 5bfb1924a..7c2eb4683 100644 --- a/imxweb/projects/aad/src/lib/aad-extension/licence-overview-button/licence-overview-button.component.html +++ b/imxweb/projects/aad/src/lib/aad-extension/licence-overview-button/licence-overview-button.component.html @@ -1,4 +1,10 @@ - \ No newline at end of file + diff --git a/imxweb/projects/aad/src/lib/aad-extension/licence-overview-button/licence-overview-button.component.scss b/imxweb/projects/aad/src/lib/aad-extension/licence-overview-button/licence-overview-button.component.scss deleted file mode 100644 index cbfd5c1fb..000000000 --- a/imxweb/projects/aad/src/lib/aad-extension/licence-overview-button/licence-overview-button.component.scss +++ /dev/null @@ -1,7 +0,0 @@ -// add styles if necessary - - -.imx-licence-button { - margin-left: 10px; - margin-right: 10px; -} \ No newline at end of file diff --git a/imxweb/projects/aad/src/lib/aad-extension/licence-overview-button/licence-overview-button.component.ts b/imxweb/projects/aad/src/lib/aad-extension/licence-overview-button/licence-overview-button.component.ts index 31e31143e..9c75e009d 100644 --- a/imxweb/projects/aad/src/lib/aad-extension/licence-overview-button/licence-overview-button.component.ts +++ b/imxweb/projects/aad/src/lib/aad-extension/licence-overview-button/licence-overview-button.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -32,27 +32,27 @@ import { AadExtensionService } from '../aad-extension.service'; @Component({ templateUrl: './licence-overview-button.component.html', - styleUrls: ['./licence-overview-button.component.scss'] }) export class LicenceOverviewButtonComponent implements OnInit { - public licenceOverview: EuiDownloadOptions; - @Input() public referrer: { type: string, uidGroup: string, defaultDownloadOptions: EuiDownloadOptions }; + @Input() public referrer: { type: string; uidGroup: string; defaultDownloadOptions: EuiDownloadOptions }; constructor( public aadService: AadExtensionService, - private permissions: AadPermissionsService - ) { } + private permissions: AadPermissionsService, + ) {} public async ngOnInit(): Promise { - const url = this.referrer.type === 'AADSubSku' - && await this.permissions.canReadInAzure() ? - this.aadService.getReportForSubSku(this.referrer.uidGroup) : ''; - - this.licenceOverview = url != null && url !== '' ? { - ... this.referrer.defaultDownloadOptions, - url - } : undefined; + const url = + this.referrer.type === 'AADSubSku' && (await this.permissions.canReadInAzure()) + ? this.aadService.getReportForSubSku(this.referrer.uidGroup) + : ''; + + if (url != null && url !== '') { + this.licenceOverview = { + ...this.referrer.defaultDownloadOptions, + url, + }; + } } - } diff --git a/imxweb/projects/aad/src/lib/admin/aad-permissions.service.ts b/imxweb/projects/aad/src/lib/admin/aad-permissions.service.ts index c00548729..25ec03781 100644 --- a/imxweb/projects/aad/src/lib/admin/aad-permissions.service.ts +++ b/imxweb/projects/aad/src/lib/admin/aad-permissions.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,12 +30,12 @@ import { UserModelService } from 'qer'; import { canReadInAzure } from './permissions-helper'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class AadPermissionsService { - constructor(private readonly userService: UserModelService) { } + constructor(private readonly userService: UserModelService) {} public async canReadInAzure(): Promise { - return canReadInAzure((await this.userService.getGroups()).map(group => group.Name)); + return canReadInAzure((await this.userService.getGroups()).map((group) => group?.Name)); } } diff --git a/imxweb/projects/aad/src/lib/admin/permissions-helper.ts b/imxweb/projects/aad/src/lib/admin/permissions-helper.ts index c9956e525..a3b9d7321 100644 --- a/imxweb/projects/aad/src/lib/admin/permissions-helper.ts +++ b/imxweb/projects/aad/src/lib/admin/permissions-helper.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,6 +24,6 @@ * */ -export function canReadInAzure(groups: string[]): boolean { - return groups.find(item => item === 'AAD_4_NAMESPACEADMIN_AZUREAD') != null; +export function canReadInAzure(groups: (string | undefined)[]): boolean { + return groups.find((item) => item === 'AAD_4_NAMESPACEADMIN_AZUREAD') != null; } diff --git a/imxweb/projects/aad/src/lib/api.service.spec.ts b/imxweb/projects/aad/src/lib/api.service.spec.ts index 1b61024ea..0672e835f 100644 --- a/imxweb/projects/aad/src/lib/api.service.spec.ts +++ b/imxweb/projects/aad/src/lib/api.service.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,9 +25,7 @@ */ describe('ApiService', () => { - it('is a dummy test', async () => { - expect({}).toBeDefined(); }); }); diff --git a/imxweb/projects/aad/src/lib/api.service.ts b/imxweb/projects/aad/src/lib/api.service.ts index 4667d3278..82e89a957 100644 --- a/imxweb/projects/aad/src/lib/api.service.ts +++ b/imxweb/projects/aad/src/lib/api.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,12 +26,12 @@ import { Injectable } from '@angular/core'; -import { V2Client, TypedClient } from 'imx-api-aad'; -import { ApiClient } from 'imx-qbm-dbts'; +import { V2Client, TypedClient } from '@imx-modules/imx-api-aad'; +import { ApiClient } from '@imx-modules/imx-qbm-dbts'; import { AppConfigService, ClassloggerService, ImxTranslationProviderService } from 'qbm'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ApiService { private tc: TypedClient; @@ -51,7 +51,8 @@ export class ApiService { constructor( private readonly config: AppConfigService, private readonly logger: ClassloggerService, - private readonly translationProvider: ImxTranslationProviderService) { + private readonly translationProvider: ImxTranslationProviderService, + ) { try { this.logger.debug(this, 'Initializing AAD API service'); diff --git a/imxweb/projects/aad/src/lib/azure-ad/aad-group/aad-group-denied-plans.component.html b/imxweb/projects/aad/src/lib/azure-ad/aad-group/aad-group-denied-plans.component.html index d2818e156..ab58e76f3 100644 --- a/imxweb/projects/aad/src/lib/azure-ad/aad-group/aad-group-denied-plans.component.html +++ b/imxweb/projects/aad/src/lib/azure-ad/aad-group/aad-group-denied-plans.component.html @@ -1,26 +1,30 @@

diff --git a/imxweb/projects/aad/src/lib/azure-ad/aad-group/aad-group-denied-plans.component.ts b/imxweb/projects/aad/src/lib/azure-ad/aad-group/aad-group-denied-plans.component.ts index 5eadf67ac..2a03d624b 100644 --- a/imxweb/projects/aad/src/lib/azure-ad/aad-group/aad-group-denied-plans.component.ts +++ b/imxweb/projects/aad/src/lib/azure-ad/aad-group/aad-group-denied-plans.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,25 +26,24 @@ import { Component, OnInit, ViewChild } from '@angular/core'; -import { PortalTargetsystemAadgroupDeniedserviceplans } from 'imx-api-aad'; -import { CollectionLoadParameters, DisplayColumns, EntitySchema, IClientProperty } from 'imx-qbm-dbts'; +import { PortalTargetsystemAadgroupDeniedserviceplans } from '@imx-modules/imx-api-aad'; +import { CollectionLoadParameters, DisplayColumns, EntitySchema, IClientProperty } from '@imx-modules/imx-qbm-dbts'; import { ClassloggerService, DataSourceToolbarFilter, DataSourceToolbarSettings, DataTableComponent, DynamicTabDataProviderDirective, - SettingsService + SettingsService, } from 'qbm'; import { AzureAdService } from '../azure-ad.service'; @Component({ selector: 'imx-aad-group-denied-plans', templateUrl: './aad-group-denied-plans.component.html', - styleUrls: ['../azure-ad-common.scss'] + styleUrls: ['../azure-ad-common.scss'], }) export class AadGroupDeniedPlansComponent implements OnInit { - @ViewChild('dataTable', { static: false }) public dataTable: DataTableComponent; public dstSettings: DataSourceToolbarSettings; public navigationState: CollectionLoadParameters; @@ -53,13 +52,13 @@ export class AadGroupDeniedPlansComponent implements OnInit { public readonly DisplayColumns = DisplayColumns; private displayedColumns: IClientProperty[] = []; - private referrer: { objecttable: string; objectuid: string; }; + private referrer: { objecttable: string; objectuid: string }; constructor( private readonly logger: ClassloggerService, private readonly aadService: AzureAdService, settings: SettingsService, - dataProvider: DynamicTabDataProviderDirective + dataProvider: DynamicTabDataProviderDirective, ) { this.navigationState = { PageSize: settings.DefaultPageSize, StartIndex: 0 }; this.entitySchemaAadGroupDeniedPlan = this.aadService.aadGroupDeniedPlansSchema; @@ -68,8 +67,8 @@ export class AadGroupDeniedPlansComponent implements OnInit { public async ngOnInit(): Promise { this.displayedColumns = [ - this.entitySchemaAadGroupDeniedPlan.Columns.UID_AADDeniedServicePlan, - this.entitySchemaAadGroupDeniedPlan.Columns.XOrigin + this.entitySchemaAadGroupDeniedPlan.Columns?.UID_AADDeniedServicePlan, + this.entitySchemaAadGroupDeniedPlan.Columns?.XOrigin, ]; await this.navigate(); } @@ -89,7 +88,6 @@ export class AadGroupDeniedPlansComponent implements OnInit { } private async navigate(): Promise { - this.aadService.handleOpenLoader(); try { const data = await this.aadService.getAadGroupDeniedPlans(this.referrer.objectuid, this.navigationState); @@ -100,10 +98,9 @@ export class AadGroupDeniedPlansComponent implements OnInit { navigationState: this.navigationState, filters: this.filterOptions, }; - this.logger.debug(this, `Head at ${data.Data.length + this.navigationState.StartIndex} of ${data.totalCount} item(s)`); + this.logger.debug(this, `Head at ${data.Data.length + (this.navigationState.StartIndex ?? 0)} of ${data.totalCount} item(s)`); } finally { this.aadService.handleCloseLoader(); } } - } diff --git a/imxweb/projects/aad/src/lib/azure-ad/aad-group/aad-group-subscriptions.component.html b/imxweb/projects/aad/src/lib/azure-ad/aad-group/aad-group-subscriptions.component.html index 20dd930be..b62e48724 100644 --- a/imxweb/projects/aad/src/lib/azure-ad/aad-group/aad-group-subscriptions.component.html +++ b/imxweb/projects/aad/src/lib/azure-ad/aad-group/aad-group-subscriptions.component.html @@ -1,23 +1,30 @@
-

#LDS#Heading Azure Active Directory Subscriptions

- - #LDS#Heading Azure Active Directory Subscriptions + + + (search)="onSearch($event)" + > - - - - - + + + -
diff --git a/imxweb/projects/aad/src/lib/azure-ad/aad-group/aad-group-subscriptions.component.ts b/imxweb/projects/aad/src/lib/azure-ad/aad-group/aad-group-subscriptions.component.ts index 01b5fe5f9..c742496c1 100644 --- a/imxweb/projects/aad/src/lib/azure-ad/aad-group/aad-group-subscriptions.component.ts +++ b/imxweb/projects/aad/src/lib/azure-ad/aad-group/aad-group-subscriptions.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,24 +26,24 @@ import { Component, OnInit, ViewChild } from '@angular/core'; -import { PortalTargetsystemAadgroupSubsku } from 'imx-api-aad'; -import { CollectionLoadParameters, DisplayColumns, EntitySchema, IClientProperty } from 'imx-qbm-dbts'; +import { PortalTargetsystemAadgroupSubsku } from '@imx-modules/imx-api-aad'; +import { CollectionLoadParameters, DisplayColumns, EntitySchema, IClientProperty } from '@imx-modules/imx-qbm-dbts'; import { ClassloggerService, DataSourceToolbarFilter, - DataSourceToolbarSettings, DataTableComponent, + DataSourceToolbarSettings, + DataTableComponent, DynamicTabDataProviderDirective, - SettingsService + SettingsService, } from 'qbm'; import { AzureAdService } from '../azure-ad.service'; @Component({ selector: 'imx-aad-group-subscriptions', templateUrl: './aad-group-subscriptions.component.html', - styleUrls: ['../azure-ad-common.scss'] + styleUrls: ['../azure-ad-common.scss'], }) export class AadGroupSubscriptionsComponent implements OnInit { - @ViewChild('dataTable', { static: false }) public dataTable: DataTableComponent; public dstSettings: DataSourceToolbarSettings; public navigationState: CollectionLoadParameters; @@ -52,13 +52,13 @@ export class AadGroupSubscriptionsComponent implements OnInit { public readonly DisplayColumns = DisplayColumns; private displayedColumns: IClientProperty[] = []; - private referrer: { objecttable: string; objectuid: string; }; + private referrer: { objecttable: string; objectuid: string }; constructor( private readonly logger: ClassloggerService, private readonly aadService: AzureAdService, settings: SettingsService, - dataProvider: DynamicTabDataProviderDirective + dataProvider: DynamicTabDataProviderDirective, ) { this.navigationState = { PageSize: settings.DefaultPageSize, StartIndex: 0 }; this.entitySchemaAadGroupSub = this.aadService.aadGroupSubSchema; @@ -66,10 +66,7 @@ export class AadGroupSubscriptionsComponent implements OnInit { } public async ngOnInit(): Promise { - this.displayedColumns = [ - this.entitySchemaAadGroupSub.Columns.UID_AADSubSku, - this.entitySchemaAadGroupSub.Columns.XOrigin - ]; + this.displayedColumns = [this.entitySchemaAadGroupSub.Columns.UID_AADSubSku, this.entitySchemaAadGroupSub.Columns?.XOrigin]; await this.navigate(); } @@ -87,7 +84,6 @@ export class AadGroupSubscriptionsComponent implements OnInit { await this.navigate(); } - private async navigate(): Promise { this.aadService.handleOpenLoader(); try { @@ -99,7 +95,7 @@ export class AadGroupSubscriptionsComponent implements OnInit { navigationState: this.navigationState, filters: this.filterOptions, }; - this.logger.debug(this, `Head at ${data.Data.length + this.navigationState.StartIndex} of ${data.totalCount} item(s)`); + this.logger.debug(this, `Head at ${data.Data.length + (this.navigationState.StartIndex ?? 0)} of ${data.totalCount} item(s)`); } finally { this.aadService.handleCloseLoader(); } diff --git a/imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-create-dialog.component.html b/imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-create-dialog.component.html index 1d5cfd097..d15fc739c 100644 --- a/imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-create-dialog.component.html +++ b/imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-create-dialog.component.html @@ -1,17 +1,22 @@
-
-

{{ data.title | translate }}

-
- -
+
+

{{ data.title | translate }}

+
+ +
-
+
-
diff --git a/imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-create-dialog.component.scss b/imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-create-dialog.component.scss deleted file mode 100644 index 3e9e3148a..000000000 --- a/imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-create-dialog.component.scss +++ /dev/null @@ -1,8 +0,0 @@ -@import '@elemental-ui/core/src/styles/_palette.scss'; - -.aad-user-create-dialog { - - h1 { - margin-bottom: 10px; - } -} diff --git a/imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-create-dialog.component.ts b/imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-create-dialog.component.ts index 9e38d6723..e245f11fc 100644 --- a/imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-create-dialog.component.ts +++ b/imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-create-dialog.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,9 +26,9 @@ import { Component, Inject, OnInit } from '@angular/core'; import { UntypedFormArray, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { IWriteValue } from 'imx-qbm-dbts'; +import { IWriteValue } from '@imx-modules/imx-qbm-dbts'; import { BaseCdr, ColumnDependentReference } from 'qbm'; export interface AadUserCreateDialogData { @@ -39,17 +39,15 @@ export interface AadUserCreateDialogData { @Component({ selector: 'imx-aad-user-create-dialog', templateUrl: './aad-user-create-dialog.component.html', - styleUrls: ['./aad-user-create-dialog.component.scss'] }) export class AadUserCreateDialogComponent implements OnInit { - public readonly detailsFormGroup: UntypedFormGroup; public cdrList: ColumnDependentReference[] = []; constructor( formBuilder: UntypedFormBuilder, public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: AadUserCreateDialogData + @Inject(MAT_DIALOG_DATA) public data: AadUserCreateDialogData, ) { this.detailsFormGroup = new UntypedFormGroup({ formArray: formBuilder.array([]) }); } @@ -67,9 +65,6 @@ export class AadUserCreateDialogComponent implements OnInit { } private setup(): void { - this.cdrList = [ - new BaseCdr(this.data.property.Column) - ]; + this.cdrList = [new BaseCdr(this.data.property.Column)]; } - } diff --git a/imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-denied-plans.component.html b/imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-denied-plans.component.html index 10966a22b..9e1f40f3c 100644 --- a/imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-denied-plans.component.html +++ b/imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-denied-plans.component.html @@ -1,37 +1,49 @@
-

#LDS#Heading Disabled Azure Active Directory Service Plans

- - #LDS#Heading Disabled Azure Active Directory Service Plans + + + [searchBoxText]="'#LDS#Search' | translate" + (navigationStateChanged)="onNavigationStateChanged($event)" + data-imx-identifier="aad-user-disabled-plans-toolbar" + (search)="onSearch($event)" + > - - - - - + + + - - +
- - -
diff --git a/imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-denied-plans.component.ts b/imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-denied-plans.component.ts index 24527b981..2c06a180d 100644 --- a/imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-denied-plans.component.ts +++ b/imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-denied-plans.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,15 +27,15 @@ import { Component, Input, OnInit, ViewChild } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; -import { PortalTargetsystemAaduserDeniedserviceplans } from 'imx-api-aad'; -import { CollectionLoadParameters, DbObjectKey, DisplayColumns, EntitySchema, IClientProperty } from 'imx-qbm-dbts'; +import { PortalTargetsystemAaduserDeniedserviceplans } from '@imx-modules/imx-api-aad'; +import { CollectionLoadParameters, DisplayColumns, EntitySchema, IClientProperty, TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { ClassloggerService, DataSourceToolbarFilter, DataSourceToolbarSettings, DataTableComponent, DynamicTabDataProviderDirective, - SettingsService + SettingsService, } from 'qbm'; import { AzureAdService } from '../azure-ad.service'; import { AadUserCreateDialogComponent } from './aad-user-create-dialog.component'; @@ -43,11 +43,10 @@ import { AadUserCreateDialogComponent } from './aad-user-create-dialog.component @Component({ selector: 'imx-aad-user-denied-plans', templateUrl: './aad-user-denied-plans.component.html', - styleUrls: ['../azure-ad-common.scss'] + styleUrls: ['../azure-ad-common.scss'], }) export class AadUserDeniedPlansComponent implements OnInit { - - @Input() public referrer: { objecttable: string; objectuid: string; }; + @Input() public referrer: { objecttable: string; objectuid: string }; @ViewChild('dataTable', { static: false }) public dataTable: DataTableComponent; public dstSettings: DataSourceToolbarSettings; @@ -64,7 +63,7 @@ export class AadUserDeniedPlansComponent implements OnInit { private readonly logger: ClassloggerService, private readonly aadService: AzureAdService, settings: SettingsService, - dataProvider: DynamicTabDataProviderDirective + dataProvider: DynamicTabDataProviderDirective, ) { this.navigationState = { PageSize: settings.DefaultPageSize, StartIndex: 0 }; this.entitySchemaAadUserDeniedPlan = this.aadService.aadUserDeniedPlansSchema; @@ -73,8 +72,8 @@ export class AadUserDeniedPlansComponent implements OnInit { public async ngOnInit(): Promise { this.displayedColumns = [ - this.entitySchemaAadUserDeniedPlan.Columns.UID_AADDeniedServicePlan, - this.entitySchemaAadUserDeniedPlan.Columns.XOrigin, + this.entitySchemaAadUserDeniedPlan.Columns?.UID_AADDeniedServicePlan, + this.entitySchemaAadUserDeniedPlan.Columns?.XOrigin, ]; await this.navigate(); } @@ -93,21 +92,20 @@ export class AadUserDeniedPlansComponent implements OnInit { await this.navigate(); } - public onDeniedPlanSelected(selected: PortalTargetsystemAaduserDeniedserviceplans[]): void { + public onDeniedPlanSelected(selected: TypedEntity[]): void { this.logger.debug(this, `Selected aad user disabled plans changed`); this.logger.trace(`New aad user disabled plan selections`, selected); - this.selectedUserDeniedPlans = selected; + this.selectedUserDeniedPlans = selected as PortalTargetsystemAaduserDeniedserviceplans[]; } public async showCreateModal(): Promise { - const aadDeniedPlan: PortalTargetsystemAaduserDeniedserviceplans = - this.aadService.generateAadUserDeniedPlanEntity(this.getUserKey()); + const aadDeniedPlan: PortalTargetsystemAaduserDeniedserviceplans = this.aadService.generateAadUserDeniedPlanEntity(this.getUserKey()); const dialogRef = this.dialog.open(AadUserCreateDialogComponent, { width: '600px', data: { property: aadDeniedPlan.UID_AADDeniedServicePlan, - title: '#LDS#Heading Assign Disabled Service Plan' - } + title: '#LDS#Heading Assign Disabled Service Plan', + }, }); dialogRef.afterClosed().subscribe(async (result) => { @@ -135,7 +133,6 @@ export class AadUserDeniedPlansComponent implements OnInit { } private async navigate(): Promise { - this.aadService.handleOpenLoader(); try { const data = await this.aadService.getAadUserDeniedPlans(this.getUserKey(), this.navigationState); @@ -146,7 +143,7 @@ export class AadUserDeniedPlansComponent implements OnInit { navigationState: this.navigationState, filters: this.filterOptions, }; - this.logger.debug(this, `Head at ${data.Data.length + this.navigationState.StartIndex} of ${data.totalCount} item(s)`); + this.logger.debug(this, `Head at ${data.Data.length + (this.navigationState.StartIndex ?? 0)} of ${data.totalCount} item(s)`); } finally { this.aadService.handleCloseLoader(); } @@ -155,5 +152,4 @@ export class AadUserDeniedPlansComponent implements OnInit { private getUserKey(): string { return this.referrer.objectuid; } - } diff --git a/imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-subscriptions.component.html b/imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-subscriptions.component.html index 97ea40946..e82d68353 100644 --- a/imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-subscriptions.component.html +++ b/imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-subscriptions.component.html @@ -1,35 +1,48 @@
-

#LDS#Heading Azure Active Directory Subscriptions

- - #LDS#Heading Azure Active Directory Subscriptions + + + [searchBoxText]="'#LDS#Search' | translate" + (navigationStateChanged)="onNavigationStateChanged($event)" + data-imx-identifier="aad-user-subscriptions-toolbar" + [itemStatus]="itemStatus" + (search)="onSearch($event)" + > - - - - - + + + - - +
-
- - -
diff --git a/imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-subscriptions.component.ts b/imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-subscriptions.component.ts index f459468a0..9b015af99 100644 --- a/imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-subscriptions.component.ts +++ b/imxweb/projects/aad/src/lib/azure-ad/aad-user/aad-user-subscriptions.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,18 +24,18 @@ * */ -import { Component, Input, OnInit, ViewChild } from '@angular/core'; +import { Component, OnInit, ViewChild } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; -import { PortalTargetsystemAaduserSubsku } from 'imx-api-aad'; -import { CollectionLoadParameters, DbObjectKey, DisplayColumns, EntitySchema, IClientProperty, XOrigin } from 'imx-qbm-dbts'; +import { PortalTargetsystemAaduserSubsku } from '@imx-modules/imx-api-aad'; +import { CollectionLoadParameters, DisplayColumns, EntitySchema, IClientProperty, TypedEntity, XOrigin } from '@imx-modules/imx-qbm-dbts'; import { ClassloggerService, DataSourceToolbarFilter, DataSourceToolbarSettings, DataTableComponent, DynamicTabDataProviderDirective, - SettingsService + SettingsService, } from 'qbm'; import { AzureAdService } from '../azure-ad.service'; import { AadUserCreateDialogComponent } from './aad-user-create-dialog.component'; @@ -43,10 +43,9 @@ import { AadUserCreateDialogComponent } from './aad-user-create-dialog.component @Component({ selector: 'imx-aad-user-subscriptions', templateUrl: './aad-user-subscriptions.component.html', - styleUrls: ['../azure-ad-common.scss'] + styleUrls: ['../azure-ad-common.scss'], }) export class AadUserSubscriptionsComponent implements OnInit { - @ViewChild('dataTable', { static: false }) public dataTable: DataTableComponent; public dstSettings: DataSourceToolbarSettings; @@ -58,11 +57,11 @@ export class AadUserSubscriptionsComponent implements OnInit { public readonly itemStatus = { enabled: (item: PortalTargetsystemAaduserSubsku): boolean => { return item.XOrigin.value === XOrigin.Direct; - } + }, }; private displayedColumns: IClientProperty[] = []; - private referrer: { objecttable: string; objectuid: string; }; + private referrer: { objecttable: string; objectuid: string }; constructor( private dialog: MatDialog, @@ -77,10 +76,7 @@ export class AadUserSubscriptionsComponent implements OnInit { } public async ngOnInit(): Promise { - this.displayedColumns = [ - this.entitySchemaAadUser.Columns.ObjectKeyAADSubSku, - this.entitySchemaAadUser.Columns.XOrigin - ]; + this.displayedColumns = [this.entitySchemaAadUser.Columns.ObjectKeyAADSubSku, this.entitySchemaAadUser.Columns?.XOrigin]; await this.navigate(); } @@ -98,10 +94,10 @@ export class AadUserSubscriptionsComponent implements OnInit { await this.navigate(); } - public onUserSubSelected(selected: PortalTargetsystemAaduserSubsku[]): void { + public onUserSubSelected(selected: TypedEntity[]): void { this.logger.debug(this, `Selected aad user subscriptions changed`); this.logger.trace(`New aad user subscription selections`, selected); - this.selectedUserSubs = selected; + this.selectedUserSubs = selected as PortalTargetsystemAaduserSubsku[]; } public async removeUserSubscriptions(): Promise { @@ -116,14 +112,13 @@ export class AadUserSubscriptionsComponent implements OnInit { } public async showCreateModal(): Promise { - const aadSub: PortalTargetsystemAaduserSubsku = - this.aadService.generateAadUserSubscriptionEntity(this.getUserKey()); + const aadSub: PortalTargetsystemAaduserSubsku = this.aadService.generateAadUserSubscriptionEntity(this.getUserKey()); const dialogRef = this.dialog.open(AadUserCreateDialogComponent, { width: '600px', data: { property: aadSub.ObjectKeyAADSubSku, - title: '#LDS#Heading Add Subscription' - } + title: '#LDS#Heading Add Subscription', + }, }); dialogRef.afterClosed().subscribe(async (result) => { @@ -140,7 +135,6 @@ export class AadUserSubscriptionsComponent implements OnInit { } private async navigate(): Promise { - this.aadService.handleOpenLoader(); try { const data = await this.aadService.getAadUserSubscriptions(this.getUserKey(), this.navigationState); @@ -151,7 +145,7 @@ export class AadUserSubscriptionsComponent implements OnInit { navigationState: this.navigationState, filters: this.filterOptions, }; - this.logger.debug(this, `Head at ${data.Data.length + this.navigationState.StartIndex} of ${data.totalCount} item(s)`); + this.logger.debug(this, `Head at ${data.Data.length + (this.navigationState.StartIndex ?? 0)} of ${data.totalCount} item(s)`); } finally { this.aadService.handleCloseLoader(); } @@ -160,5 +154,4 @@ export class AadUserSubscriptionsComponent implements OnInit { private getUserKey(): string { return this.referrer.objectuid; } - } diff --git a/imxweb/projects/aad/src/lib/azure-ad/azure-ad-common.scss b/imxweb/projects/aad/src/lib/azure-ad/azure-ad-common.scss index 936953d6d..1a59b654f 100644 --- a/imxweb/projects/aad/src/lib/azure-ad/azure-ad-common.scss +++ b/imxweb/projects/aad/src/lib/azure-ad/azure-ad-common.scss @@ -1,21 +1,11 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import 'base/mixins'; :host { - display: flex; - flex-direction: column; - height: 100%; -} - -.imx-content-card { - display: flex; - flex-direction: column; - overflow: hidden; + @include flex-column-container($height: 100%); } .imx-content { - height: 100%; - display: flex; - flex-direction: column; + @include flex-column-container($height: 100%); flex: 1 1 auto; overflow-y: auto; margin-bottom: -16px; @@ -24,19 +14,9 @@ .imx-card { margin: 0; margin-bottom: -16px; - - .governance-sidesheet-action-buttons { - .justify-start { - eui-icon { - font-size: 14px; - margin-right: 4px; - } - } - } } @media screen and (max-width: 480px) { - .imx-button-bar { flex-direction: column; display: block; diff --git a/imxweb/projects/aad/src/lib/azure-ad/azure-ad.module.ts b/imxweb/projects/aad/src/lib/azure-ad/azure-ad.module.ts index a35ef1970..1fa9eb267 100644 --- a/imxweb/projects/aad/src/lib/azure-ad/azure-ad.module.ts +++ b/imxweb/projects/aad/src/lib/azure-ad/azure-ad.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -47,7 +47,7 @@ import { LicenceOverviewButtonComponent } from '../aad-extension/licence-overvie AadUserDeniedPlansComponent, AadGroupSubscriptionsComponent, AadGroupDeniedPlansComponent, - LicenceOverviewButtonComponent + LicenceOverviewButtonComponent, ], imports: [ CommonModule, @@ -61,16 +61,13 @@ import { LicenceOverviewButtonComponent } from '../aad-extension/licence-overvie DataSourceToolbarModule, DataTableModule, ], - providers: [ - AzureAdService, - AadPermissionsService - ], + providers: [AzureAdService, AadPermissionsService], exports: [ AadUserSubscriptionsComponent, AadUserDeniedPlansComponent, AadGroupSubscriptionsComponent, AadGroupDeniedPlansComponent, - LicenceOverviewButtonComponent + LicenceOverviewButtonComponent, ], }) export class AzureAdModule {} diff --git a/imxweb/projects/aad/src/lib/azure-ad/azure-ad.service.ts b/imxweb/projects/aad/src/lib/azure-ad/azure-ad.service.ts index 587706171..e8f0f9353 100644 --- a/imxweb/projects/aad/src/lib/azure-ad/azure-ad.service.ts +++ b/imxweb/projects/aad/src/lib/azure-ad/azure-ad.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,6 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; import { Injectable } from '@angular/core'; import { EuiLoadingService } from '@elemental-ui/core'; import { @@ -32,17 +31,18 @@ import { PortalTargetsystemAadgroupSubsku, PortalTargetsystemAaduserDeniedserviceplans, PortalTargetsystemAaduserSubsku, -} from 'imx-api-aad'; -import { CollectionLoadParameters, EntitySchema, TypedEntityCollectionData } from 'imx-qbm-dbts'; +} from '@imx-modules/imx-api-aad'; +import { CollectionLoadParameters, EntitySchema, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { ApiService } from '../api.service'; @Injectable({ providedIn: 'root', }) export class AzureAdService { - private busyIndicator: OverlayRef; - - constructor(private readonly aadApiClient: ApiService, private readonly busyService: EuiLoadingService) { } + constructor( + private readonly aadApiClient: ApiService, + private readonly busyService: EuiLoadingService, + ) {} public get aadUserSchema(): EntitySchema { return this.aadApiClient.typedClient.PortalTargetsystemAaduserSubsku.GetSchema(); @@ -62,28 +62,28 @@ export class AzureAdService { public async getAadUserSubscriptions( uidAadUser: string, - navigationState: CollectionLoadParameters + navigationState: CollectionLoadParameters, ): Promise> { return this.aadApiClient.typedClient.PortalTargetsystemAaduserSubsku.Get(uidAadUser, navigationState); } public async getAadUserDeniedPlans( uidAadUser: string, - navigationState: CollectionLoadParameters + navigationState: CollectionLoadParameters, ): Promise> { return this.aadApiClient.typedClient.PortalTargetsystemAaduserDeniedserviceplans.Get(uidAadUser, navigationState); } public async getAadGroupDeniedPlans( uidAadGroup: string, - navigationState: CollectionLoadParameters + navigationState: CollectionLoadParameters, ): Promise> { return this.aadApiClient.typedClient.PortalTargetsystemAadgroupDeniedserviceplans.Get(uidAadGroup, navigationState); } public async getAadGroupSubscriptions( uidAadGroup: string, - navigationState: CollectionLoadParameters + navigationState: CollectionLoadParameters, ): Promise> { return this.aadApiClient.typedClient.PortalTargetsystemAadgroupSubsku.Get(uidAadGroup, navigationState); } @@ -92,37 +92,37 @@ export class AzureAdService { return this.aadApiClient.typedClient.PortalTargetsystemAaduserSubsku.createEntity({ Columns: { UID_AADUser: { - Value: uidAaduser - } - } + Value: uidAaduser, + }, + }, }); } public generateAadUserDeniedPlanEntity(uidAaduser: string): PortalTargetsystemAaduserDeniedserviceplans { return this.aadApiClient.typedClient.PortalTargetsystemAaduserDeniedserviceplans.createEntity({ Columns: { UID_AADUser: { - Value: uidAaduser - } - } + Value: uidAaduser, + }, + }, }); } public createAadUserSubscription( uidAadUser: string, - aadUserSubscription: PortalTargetsystemAaduserSubsku + aadUserSubscription: PortalTargetsystemAaduserSubsku, ): Promise> { return this.aadApiClient.typedClient.PortalTargetsystemAaduserSubsku.Post(uidAadUser, aadUserSubscription); } public createAadUserDeniedPlan( uidAadUser: string, - aadUserDeniedPlan: PortalTargetsystemAaduserDeniedserviceplans + aadUserDeniedPlan: PortalTargetsystemAaduserDeniedserviceplans, ): Promise> { return this.aadApiClient.typedClient.PortalTargetsystemAaduserDeniedserviceplans.Post(uidAadUser, aadUserDeniedPlan); } public async removeAadUserSubscriptions(uidAadUser: string, userSubscriptions: PortalTargetsystemAaduserSubsku[]): Promise { - const promises = []; + const promises: Promise[] = []; userSubscriptions.forEach((userSub) => { const key = userSub.GetEntity().GetKeys().join(); promises.push(this.aadApiClient.client.portal_targetsystem_aaduser_subsku_delete(uidAadUser, key)); @@ -131,7 +131,7 @@ export class AzureAdService { } public async removeAadUserDeniedPlans(uidAadUser: string, userDeniedPlans: PortalTargetsystemAaduserDeniedserviceplans[]): Promise { - const promises = []; + const promises: Promise[] = []; userDeniedPlans.forEach((deniedPlan) => { const deniedPlanValue = deniedPlan.UID_AADDeniedServicePlan.value; promises.push(this.aadApiClient.client.portal_targetsystem_aaduser_deniedserviceplans_delete(uidAadUser, deniedPlanValue)); @@ -140,17 +140,10 @@ export class AzureAdService { } public handleOpenLoader(): void { - if (!this.busyIndicator) { - this.busyIndicator = this.busyService.show(); - } + this.busyService.show(); } public handleCloseLoader(): void { - if (this.busyIndicator) { - setTimeout(() => { - this.busyService.hide(this.busyIndicator); - this.busyIndicator = undefined; - }); - } + this.busyService.hide(); } } diff --git a/imxweb/projects/aad/src/lib/init.service.ts b/imxweb/projects/aad/src/lib/init.service.ts index 1d9da174d..74ad0957b 100644 --- a/imxweb/projects/aad/src/lib/init.service.ts +++ b/imxweb/projects/aad/src/lib/init.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,47 +28,49 @@ import { Injectable } from '@angular/core'; import { ExtService, TabItem } from 'qbm'; import { LicenceOverviewButtonComponent } from './aad-extension/licence-overview-button/licence-overview-button.component'; -import { AadGroupSubscriptionsComponent } from './azure-ad/aad-group/aad-group-subscriptions.component'; +import { AadPermissionsService } from './admin/aad-permissions.service'; import { AadGroupDeniedPlansComponent } from './azure-ad/aad-group/aad-group-denied-plans.component'; -import { AadUserSubscriptionsComponent } from './azure-ad/aad-user/aad-user-subscriptions.component'; +import { AadGroupSubscriptionsComponent } from './azure-ad/aad-group/aad-group-subscriptions.component'; import { AadUserDeniedPlansComponent } from './azure-ad/aad-user/aad-user-denied-plans.component'; -import { AadPermissionsService } from './admin/aad-permissions.service'; +import { AadUserSubscriptionsComponent } from './azure-ad/aad-user/aad-user-subscriptions.component'; @Injectable({ providedIn: 'root' }) export class InitService { constructor( private readonly extService: ExtService, - private permission: AadPermissionsService - ) { - } + private permission: AadPermissionsService, + ) {} public onInit(): void { this.extService.register('buttonBarExtensionComponent', { instance: LicenceOverviewButtonComponent }); this.extService.register('groupSidesheet', { - instance: AadGroupSubscriptionsComponent, inputData: { + instance: AadGroupSubscriptionsComponent, + inputData: { id: 'subscriptions', label: '#LDS#Heading Azure Active Directory Subscriptions', - checkVisibility: async ref => this.isAadAccount(ref) && (this.permission.canReadInAzure()) + checkVisibility: async (ref) => this.isAadAccount(ref) && this.permission.canReadInAzure(), }, - sortOrder: 10 + sortOrder: 10, } as TabItem); this.extService.register('groupSidesheet', { - instance: AadGroupDeniedPlansComponent, inputData: { + instance: AadGroupDeniedPlansComponent, + inputData: { id: 'deniedPlans', label: '#LDS#Heading Disabled Azure Active Directory Service Plans', - checkVisibility: async ref => this.isAadAccount(ref) && (this.permission.canReadInAzure()) + checkVisibility: async (ref) => this.isAadAccount(ref) && this.permission.canReadInAzure(), }, - sortOrder: 20 + sortOrder: 20, } as TabItem); this.extService.register('accountSidesheet', { - instance: AadUserSubscriptionsComponent, inputData: { + instance: AadUserSubscriptionsComponent, + inputData: { id: 'subscriptions', label: '#LDS#Heading Azure Active Directory Subscriptions', - checkVisibility: async ref => this.isAadAccount(ref) && (this.permission.canReadInAzure()) + checkVisibility: async (ref) => this.isAadUser(ref) && this.permission.canReadInAzure(), }, - sortOrder: 10 + sortOrder: 10, } as TabItem); this.extService.register('accountSidesheet', { @@ -76,17 +78,20 @@ export class InitService { inputData: { id: 'deniedPlans', label: '#LDS#Heading Disabled Azure Active Directory Service Plans', - checkVisibility: async ref => this.isAadAccount(ref) && (this.permission.canReadInAzure()) + checkVisibility: async (ref) => this.isAadUser(ref) && this.permission.canReadInAzure(), }, - sortOrder: 20 + sortOrder: 20, } as TabItem); } - private isAadAccount(referrer: any): boolean { let isAad = false; isAad = referrer ? referrer.objecttable === 'AADGroup' : false; return isAad; } - + private isAadUser(referrer: any): boolean { + let isAad = false; + isAad = referrer ? referrer.objecttable === 'AADUser' : false; + return isAad; + } } diff --git a/imxweb/projects/aad/src/public_api.ts b/imxweb/projects/aad/src/public_api.ts index 61174781e..51abf2a8e 100644 --- a/imxweb/projects/aad/src/public_api.ts +++ b/imxweb/projects/aad/src/public_api.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -36,4 +36,4 @@ export { AadUserDeniedPlansComponent } from './lib/azure-ad/aad-user/aad-user-de export { AadGroupSubscriptionsComponent } from './lib/azure-ad/aad-group/aad-group-subscriptions.component'; export { AadGroupDeniedPlansComponent } from './lib/azure-ad/aad-group/aad-group-denied-plans.component'; export { AzureAdModule } from './lib/azure-ad/azure-ad.module'; -export { LicenceOverviewButtonComponent} from './lib/aad-extension/licence-overview-button/licence-overview-button.component' +export { LicenceOverviewButtonComponent } from './lib/aad-extension/licence-overview-button/licence-overview-button.component'; diff --git a/imxweb/projects/aad/src/test.ts b/imxweb/projects/aad/src/test.ts index f7ac7d548..159c87d7d 100644 --- a/imxweb/projects/aad/src/test.ts +++ b/imxweb/projects/aad/src/test.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,24 +26,14 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'core-js/es7/reflect'; -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +// Zone must be imported first. Be careful with prettier import organizing +import 'zone.js'; +import 'zone.js/testing'; + import { getTestBed } from '@angular/core/testing'; -import { - BrowserDynamicTestingModule, - platformBrowserDynamicTesting -} from '@angular/platform-browser-dynamic/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; +import 'core-js/es/reflect'; import { TestHelperModule } from 'qbm'; -declare const require: any; - // First, initialize the Angular testing environment. -getTestBed().initTestEnvironment( - [BrowserDynamicTestingModule, TestHelperModule], - platformBrowserDynamicTesting() -); -// Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); -// And load the modules. -context.keys().map(context); +getTestBed().initTestEnvironment([BrowserDynamicTestingModule, TestHelperModule], platformBrowserDynamicTesting()); diff --git a/imxweb/projects/aad/src/test/aad-common-test-mocks.ts b/imxweb/projects/aad/src/test/aad-common-test-mocks.ts index 80c586410..ebdd63a81 100644 --- a/imxweb/projects/aad/src/test/aad-common-test-mocks.ts +++ b/imxweb/projects/aad/src/test/aad-common-test-mocks.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,106 +24,104 @@ * */ -import { IEntityColumn, IEntity } from 'imx-qbm-dbts'; +import { IEntityColumn, IEntity } from '@imx-modules/imx-qbm-dbts'; import { ISessionState } from 'qbm'; import { Subject } from 'rxjs'; export class AadCommonTestData { - public static mockConfigService: any = { getConfig: () => { return Promise.resolve({ PersonConfig: { VI_MyData_WhitePages_ResultAttributes: { - Columns: ['col1', 'col2'] + Columns: ['col1', 'col2'], }, VI_PersonalData_Fields: { - Columns: ['col1', 'col2'] - } - } + Columns: ['col1', 'col2'], + }, + }, }); - } + }, }; public static mockAppConfigService: any = { Config: { Title: '', routeConfig: { - start: 'dashboard' - } + start: 'dashboard', + }, }, client: { imx_multilanguage_getcaptions_get: () => Promise.resolve({}), - imx_multilanguage_translations_get: () => Promise.resolve({}) - } + imx_multilanguage_translations_get: () => Promise.resolve({}), + }, }; public static mockAuthenticationServiceStub = { onSessionResponse: new Subject(), - update: jasmine.createSpy('update') + update: jasmine.createSpy('update'), }; public static mockSessionService: any = { TypedClient: { PortalTargetsystemUnsGroup: { - Get: () => Promise.resolve({}) + Get: () => Promise.resolve({}), }, PortalTargetsystemUnsAccount: { - Get: () => Promise.resolve({}) + Get: () => Promise.resolve({}), }, PortalPersonAll: { - Get: () => Promise.resolve({}) + Get: () => Promise.resolve({}), }, PortalAdminPerson: { - Get: () => Promise.resolve({}) + Get: () => Promise.resolve({}), }, PortalPerson: { - Get: () => Promise.resolve({Data: ['test1', 'test2']}) + Get: () => Promise.resolve({ Data: ['test1', 'test2'] }), }, - } + }, }; public static aadUserSchema: any = { Columns: { UID_AADUser: {}, XOrigin: {}, - ObjectKeyAADSubSku: {} - } + ObjectKeyAADSubSku: {}, + }, }; public static aadUserDeniedPlansSchema: any = { Columns: { UID_AADUser: {}, XOrigin: {}, - UID_AADDeniedServicePlan: {} - } + UID_AADDeniedServicePlan: {}, + }, }; public static aadGroupSubSchema: any = { Columns: { UID_AADGroup: {}, XOrigin: {}, - UID_AADSubSku: {} - } + UID_AADSubSku: {}, + }, }; public static aadGroupDeniedPlansSchema: any = { Columns: { UID_AADGroup: {}, XOrigin: {}, - UID_AADDeniedServicePlan: {} - } + UID_AADDeniedServicePlan: {}, + }, }; - public static mockAzureAdService = { aadUserSchema: AadCommonTestData.aadUserSchema, aadUserDeniedPlansSchema: AadCommonTestData.aadUserDeniedPlansSchema, aadGroupSubSchema: AadCommonTestData.aadGroupSubSchema, aadGroupDeniedPlansSchema: AadCommonTestData.aadGroupDeniedPlansSchema, - getAadUserSubscriptions: jasmine.createSpy('getAadUserSubscriptions').and.returnValue(Promise.resolve({ Data: []})), - getAadUserDeniedPlans: jasmine.createSpy('getAadUserDeniedPlans').and.returnValue(Promise.resolve({ Data: []})), - getAadGroupSubscriptions: jasmine.createSpy('getAadGroupSubscriptions').and.returnValue(Promise.resolve({ Data: []})), + getAadUserSubscriptions: jasmine.createSpy('getAadUserSubscriptions').and.returnValue(Promise.resolve({ Data: [] })), + getAadUserDeniedPlans: jasmine.createSpy('getAadUserDeniedPlans').and.returnValue(Promise.resolve({ Data: [] })), + getAadGroupSubscriptions: jasmine.createSpy('getAadGroupSubscriptions').and.returnValue(Promise.resolve({ Data: [] })), handleOpenLoader: jasmine.createSpy('handleOpenLoader'), handleCloseLoader: jasmine.createSpy('handleCloseLoader'), }; @@ -132,33 +130,30 @@ export class AadCommonTestData { ColumnName: '', GetMetadata: () => { return { - CanEdit: () => false, + CanEdit: () => false, GetDisplay: () => '', - GetMinLength: () => 0 + GetMinLength: () => 0, }; }, - GetValue: () => '' - + GetValue: () => '', } as IEntityColumn; public static mockEntityColumnWithValue = { ColumnName: '', GetMetadata: () => { return { - CanEdit: () => false, + CanEdit: () => false, GetDisplay: () => '', - GetMinLength: () => 0 + GetMinLength: () => 0, }; }, - GetValue: () => 'Test value 1' - + GetValue: () => 'Test value 1', } as IEntityColumn; public static mockEntity = { GetDisplay: () => 'Display value', GetKeys: () => ['1'], GetColumn: (name) => AadCommonTestData.mockEntityColumn, - Commit: () => Promise.resolve() + Commit: () => Promise.resolve(), } as IEntity; - } diff --git a/imxweb/projects/aad/tsconfig.lib.json b/imxweb/projects/aad/tsconfig.lib.json index b77b13c01..293b2f101 100644 --- a/imxweb/projects/aad/tsconfig.lib.json +++ b/imxweb/projects/aad/tsconfig.lib.json @@ -1,15 +1,16 @@ /* To learn more about this file see: https://angular.io/config/tsconfig. */ { "extends": "../../tsconfig.json", + "angularCompilerOptions": { + "strictTemplates": true + }, "compilerOptions": { "outDir": "../../out-tsc/lib", + "strictNullChecks": true, "declaration": true, "declarationMap": true, "inlineSources": true, "types": [] }, - "exclude": [ - "src/test.ts", - "**/*.spec.ts" - ] + "exclude": ["src/test.ts", "**/*.spec.ts"] } diff --git a/imxweb/projects/aad/tsconfig.lib.prod.json b/imxweb/projects/aad/tsconfig.lib.prod.json index 61c7592e7..fb40dbbb0 100644 --- a/imxweb/projects/aad/tsconfig.lib.prod.json +++ b/imxweb/projects/aad/tsconfig.lib.prod.json @@ -3,8 +3,5 @@ "extends": "./tsconfig.lib.json", "compilerOptions": { "declarationMap": false - }, - "angularCompilerOptions": { - "enableIvy": true } } diff --git a/imxweb/projects/aad/tsconfig.spec.json b/imxweb/projects/aad/tsconfig.spec.json index 16da33db0..6bd74e1f7 100644 --- a/imxweb/projects/aad/tsconfig.spec.json +++ b/imxweb/projects/aad/tsconfig.spec.json @@ -1,17 +1,10 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "strictNullChecks": false, "outDir": "../../out-tsc/spec", - "types": [ - "jasmine", - "node" - ] + "types": ["jasmine", "node"] }, - "files": [ - "src/test.ts" - ], - "include": [ - "**/*.spec.ts", - "**/*.d.ts" - ] + "files": ["src/test.ts"], + "include": ["**/*.spec.ts", "**/*.d.ts"] } diff --git a/imxweb/projects/aob/.compodocrc.json b/imxweb/projects/aob/.compodocrc.json index 6bac08861..7a85827e2 100644 --- a/imxweb/projects/aob/.compodocrc.json +++ b/imxweb/projects/aob/.compodocrc.json @@ -1,6 +1,6 @@ { "name": "IMX Web - AOB Library", - "output": "../../documentation/v92/aob", + "output": "../../documentation/v93/aob", "theme": "material", "assetsFolder": "../../compodoc/assets", "customLogo": "../../compodoc/assets/images/oneidentity-logo.png", diff --git a/imxweb/projects/aob/.eslintrc.json b/imxweb/projects/aob/.eslintrc.json new file mode 100644 index 000000000..5b8133bf5 --- /dev/null +++ b/imxweb/projects/aob/.eslintrc.json @@ -0,0 +1,35 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*", "**/*.spec.ts"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": ["imxweb/projects/aob/tsconfig.lib.json", "imxweb/projects/aob/tsconfig.spec.json"], + "createDefaultProgram": true + }, + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "imx", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "imx", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "rules": {} + } + ] +} diff --git a/imxweb/projects/aob/imx-plugin-config.json b/imxweb/projects/aob/imx-plugin-config.json index e60c4738a..d327d6ee2 100644 --- a/imxweb/projects/aob/imx-plugin-config.json +++ b/imxweb/projects/aob/imx-plugin-config.json @@ -1,8 +1,8 @@ { "qer-app-portal": [ - { - "Container": "aob", - "Name": "AobConfigModule" - } + { + "Container": "aob", + "Name": "AobConfigModule" + } ] } diff --git a/imxweb/projects/aob/karma.conf.js b/imxweb/projects/aob/karma.conf.js index 9d7592d3b..382501758 100644 --- a/imxweb/projects/aob/karma.conf.js +++ b/imxweb/projects/aob/karma.conf.js @@ -12,10 +12,10 @@ module.exports = function (config) { require('karma-jasmine-html-reporter'), require('karma-coverage-istanbul-reporter'), require('@angular-devkit/build-angular/plugins/karma'), - require('karma-junit-reporter') + require('karma-junit-reporter'), ], client: { - clearContext: false // leave Jasmine Spec Runner output visible in browser + clearContext: false, // leave Jasmine Spec Runner output visible in browser }, coverageIstanbulReporter: { dir: require('path').join(__dirname, 'results'), @@ -26,7 +26,7 @@ module.exports = function (config) { branches: 0, functions: 2, lines: 0, - } + }, }, junitReporter: { outputDir: require('path').join(__dirname, 'results'), @@ -36,18 +36,19 @@ module.exports = function (config) { colors: true, captureTimeout: 210000, browserDisconnectTolerance: 3, - browserDisconnectTimeout : 210000, - browserNoActivityTimeout : 210000, + browserDisconnectTimeout: 210000, + browserNoActivityTimeout: 210000, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], customLaunchers: { ChromeDebug: { base: 'Chrome', - flags: [ '--remote-debugging-port=9333' ] - } + flags: ['--remote-debugging-port=9333'], + }, }, singleRun: false, - restartOnFileChange: true + failOnEmptyTestSuite: false, + restartOnFileChange: true, }); }; diff --git a/imxweb/projects/aob/ng-package.dynamic.json b/imxweb/projects/aob/ng-package.dynamic.json index c13920961..b79a60632 100644 --- a/imxweb/projects/aob/ng-package.dynamic.json +++ b/imxweb/projects/aob/ng-package.dynamic.json @@ -16,12 +16,11 @@ "@elemental-ui/core", "@ngx-translate/core", "@ngx-translate/http-loader", - "applicationinsights-js", "billboard.js" ], - "dest": "../../html/aob", + "dest": "../../html/qer-app-portal/aob", "lib": { "entryFile": "src/public-api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] } } diff --git a/imxweb/projects/aob/ng-package.json b/imxweb/projects/aob/ng-package.json index b01d50055..e86e49790 100644 --- a/imxweb/projects/aob/ng-package.json +++ b/imxweb/projects/aob/ng-package.json @@ -3,6 +3,6 @@ "dest": "../../dist/aob", "lib": { "entryFile": "src/public-api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] } } diff --git a/imxweb/projects/aob/package.json b/imxweb/projects/aob/package.json index 5c8ce28bf..fec28616d 100644 --- a/imxweb/projects/aob/package.json +++ b/imxweb/projects/aob/package.json @@ -1,9 +1,8 @@ { "name": "aob", - "version": "9.2.1", + "version": "9.3.0", "private": true, "bundledDependencies": [ "imx-api-aob" ] } - diff --git a/imxweb/projects/aob/project.json b/imxweb/projects/aob/project.json new file mode 100644 index 000000000..0649ede03 --- /dev/null +++ b/imxweb/projects/aob/project.json @@ -0,0 +1,64 @@ +{ + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "name": "aob", + "sourceRoot": "projects/aob/src", + "implicitDependencies": ["qer"], + "projectType": "library", + "prefix": "imx", + "generators": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:ng-packagr", + "options": { + "tsConfig": "projects/aob/tsconfig.lib.json", + "project": "projects/aob/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "projects/aob/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "projects/aob/tsconfig.lib.json" + }, + "dynamic": { + "project": "projects/aob/ng-package.dynamic.json" + }, + "remote-dev": { + "tsConfig": "projects/aob/tsconfig.lib.json" + }, + "remote-qs": { + "tsConfig": "projects/aob/tsconfig.lib.json" + } + }, + "outputs": ["{workspaceRoot}/dist/aob"] + }, + "test": { + "executor": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/aob/src/test.ts", + "tsConfig": "projects/aob/tsconfig.spec.json", + "karmaConfig": "projects/aob/karma.conf.js", + "stylePreprocessorOptions": { + "includePaths": [ + "./shared/assets", + "./shared/scss", + "./node_modules", + "./node_modules/@elemental-ui/cadence-icon", + "./node_modules/@elemental-ui/core" + ] + } + } + }, + "lint": { + "executor": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": ["projects/aob/tsconfig.lib.json", "projects/aob/tsconfig.spec.json"], + "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + } + } + } +} diff --git a/imxweb/projects/aob/src/lib/accounts/accounts.module.ts b/imxweb/projects/aob/src/lib/accounts/accounts.module.ts index 5ba5e7b0f..9f8e51ed9 100644 --- a/imxweb/projects/aob/src/lib/accounts/accounts.module.ts +++ b/imxweb/projects/aob/src/lib/accounts/accounts.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,11 +31,7 @@ import { AccountsService } from './accounts.service'; @NgModule({ declarations: [], - imports: [ - CommonModule - ], - providers: [ - AccountsService - ] + imports: [CommonModule], + providers: [AccountsService], }) -export class AccountsModule { } +export class AccountsModule {} diff --git a/imxweb/projects/aob/src/lib/accounts/accounts.service.ts b/imxweb/projects/aob/src/lib/accounts/accounts.service.ts index 8574c5c9e..18b254a79 100644 --- a/imxweb/projects/aob/src/lib/accounts/accounts.service.ts +++ b/imxweb/projects/aob/src/lib/accounts/accounts.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,28 +26,28 @@ import { Injectable } from '@angular/core'; -import { ClassloggerService, ApiClientService } from 'qbm'; +import { PortalApplication, PortalApplicationusesaccount } from '@imx-modules/imx-api-aob'; import { CollectionLoadParameters, - FilterType, CompareOperator, - IForeignKeyInfo, - TypedEntityBuilder, DbObjectKey, - TypedEntity, EntityCollectionData, - ExtendedTypedEntityCollection -} from 'imx-qbm-dbts'; -import { PortalApplication, PortalApplicationusesaccount } from 'imx-api-aob'; -import { AobAccountContainer } from './aob-account-container'; + ExtendedTypedEntityCollection, + FilterType, + IForeignKeyInfo, + TypedEntity, + TypedEntityBuilder, +} from '@imx-modules/imx-qbm-dbts'; +import { ApiClientService, ClassloggerService } from 'qbm'; import { AobApiService } from '../aob-api-client.service'; +import { AobAccountContainer } from './aob-account-container'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class AccountsService { public get display(): string { - return this.aobClient.typedClient.PortalApplicationusesaccount.GetSchema().Display; + return this.aobClient.typedClient.PortalApplicationusesaccount.GetSchema().Display || ''; } private readonly builder = new TypedEntityBuilder(AobAccountContainer); @@ -57,25 +57,26 @@ export class AccountsService { constructor( private readonly aobClient: AobApiService, private readonly logger: ClassloggerService, - private readonly apiProvider: ApiClientService - ) { } + private readonly apiProvider: ApiClientService, + ) {} public async updateApplicationUsesAccounts( application: PortalApplication, - changeSet: { add: TypedEntity[], remove: TypedEntity[] } + changeSet: { add: TypedEntity[]; remove: TypedEntity[] }, ): Promise { const assignResult = await this.assign(application, changeSet.add); return assignResult && this.unassign(application, changeSet.remove); } public getCandidateTables(): ReadonlyArray { - return this.aobClient.typedClient.PortalApplicationusesaccountNew.createEntity() - .ObjectKeyAccount.GetMetadata().GetFkRelations(); + return this.aobClient.typedClient.PortalApplicationusesaccountNew.createEntity().ObjectKeyAccount.GetMetadata().GetFkRelations(); } public async getSelectedAccountLength(uidApplication: string): Promise { - return (await this.apiProvider.request(() => - this.aobClient.typedClient.PortalApplicationusesaccount.Get(uidApplication, { PageSize: -1 }))).totalCount; + return ( + (await this.apiProvider.request(() => this.aobClient.typedClient.PortalApplicationusesaccount.Get(uidApplication, { PageSize: -1 }))) + ?.totalCount || 0 + ); } public async getAssigned(application: string, parameters: CollectionLoadParameters = {}): Promise { @@ -84,56 +85,60 @@ export class AccountsService { this.logger.debug(this, 'getting assigned accounts...'); const appUsesAccountCollection = await this.apiProvider.request(() => - this.aobClient.typedClient.PortalApplicationusesaccount.Get(application, parameters)); + this.aobClient.typedClient.PortalApplicationusesaccount.Get(application, parameters), + ); return this.accountsWithTableInfo(appUsesAccountCollection, parameters); } - public async getFirstAndCount(uidApplication: string): Promise<{ first: TypedEntity, count: number }> { + public async getFirstAndCount(uidApplication: string): Promise<{ first: TypedEntity | undefined; count: number }> { const elements = await this.apiProvider.request(() => - this.aobClient.typedClient.PortalApplicationusesaccount.Get(uidApplication, { PageSize: 1 })); + this.aobClient.typedClient.PortalApplicationusesaccount.Get(uidApplication, { PageSize: 1 }), + ); - const accounts = await this.accountsWithTableInfo(elements, { PageSize: 1 }); + let accounts: AobAccountContainer[] = []; + if (!!elements) { + accounts = await this.accountsWithTableInfo(elements, { PageSize: 1 }); + } return { first: accounts.length === 0 ? undefined : accounts[0], - count: elements.totalCount + count: elements?.totalCount || 0, }; } - private async accountsWithTableInfo(appUsesAccountCollection: ExtendedTypedEntityCollection, - parameters: CollectionLoadParameters): Promise { - - const accountsAssigned = []; + private async accountsWithTableInfo( + appUsesAccountCollection: ExtendedTypedEntityCollection | undefined, + parameters: CollectionLoadParameters, + ): Promise { + const accountsAssigned: AobAccountContainer[] = []; const tables = this.getCandidateTables(); - if (appUsesAccountCollection && appUsesAccountCollection.Data && tables) { + if (!!appUsesAccountCollection && appUsesAccountCollection.Data && tables) { for (const appUsesAccount of appUsesAccountCollection.Data) { const tableName = DbObjectKey.FromXml(appUsesAccount.ObjectKeyAccount.value).TableName; - const table = tables.find(fkr => fkr.TableName === tableName); + const table = tables.find((fkr) => fkr.TableName === tableName); if (table) { - const accountCollection = await table.Get( - { - ...parameters, - ...{ - filter: [ - { - ColumnName: table.ColumnName, - Type: FilterType.Compare, - CompareOp: CompareOperator.Like, - Value1: `%${appUsesAccount.ObjectKeyAccount.value}%` - } - ], - search: undefined - } - } - ); - - if (accountCollection?.Entities?.length > 0) { + const accountCollection = await table.Get({ + ...parameters, + ...{ + filter: [ + { + ColumnName: table.ColumnName, + Type: FilterType.Compare, + CompareOp: CompareOperator.Like, + Value1: `%${appUsesAccount.ObjectKeyAccount.value}%`, + }, + ], + search: undefined, + }, + }); + + if (!!accountCollection?.Entities?.length) { const entity = this.builder.buildReadWriteEntity({ entitySchema: AobAccountContainer.GetEntitySchema(), - entityData: accountCollection.Entities[0] + entityData: accountCollection.Entities?.[0], }); this.appUsesAccounts[entity.GetEntity().GetKeys().join()] = appUsesAccount; accountsAssigned.push(entity); @@ -166,7 +171,7 @@ export class AccountsService { this.logger.debug(this, 'unassigning account from application...'); await this.aobClient.client.portal_applicationusesaccount_delete( application.UID_AOBApplication.value, - this.appUsesAccounts[account.GetEntity().GetKeys().join()].UID_AOBAppUsesAccount.value + this.appUsesAccounts[account.GetEntity().GetKeys().join()].UID_AOBAppUsesAccount.value, ); count++; } @@ -177,25 +182,23 @@ export class AccountsService { private async searchCandidates( table: IForeignKeyInfo, keyword: string, - parameters: CollectionLoadParameters = {} + parameters: CollectionLoadParameters = {}, ): Promise { this.logger.debug(this, 'searching accounts...'); - return table.Get( - { - ...parameters, - ...{ - filter: [ - { - ColumnName: table.ColumnName, - Type: FilterType.Compare, - CompareOp: CompareOperator.Like, - Value1: `%${keyword}%` - } - ], - search: undefined - } - } - ); + return table.Get({ + ...parameters, + ...{ + filter: [ + { + ColumnName: table.ColumnName, + Type: FilterType.Compare, + CompareOp: CompareOperator.Like, + Value1: `%${keyword}%`, + }, + ], + search: undefined, + }, + }); } } diff --git a/imxweb/projects/aob/src/lib/accounts/aob-account-container.ts b/imxweb/projects/aob/src/lib/accounts/aob-account-container.ts index d11617014..1e828dd12 100644 --- a/imxweb/projects/aob/src/lib/accounts/aob-account-container.ts +++ b/imxweb/projects/aob/src/lib/accounts/aob-account-container.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,19 +24,19 @@ * */ -import { TypedEntity, EntitySchema, ValType, DisplayColumns } from 'imx-qbm-dbts'; +import { TypedEntity, EntitySchema, ValType, DisplayColumns } from '@imx-modules/imx-qbm-dbts'; export class AobAccountContainer extends TypedEntity { - public static GetEntitySchema(): EntitySchema { - const columns = { - XObjectKey: { - Type: ValType.String, - ColumnName: 'XObjectKey' - } - }; + public static GetEntitySchema(): EntitySchema { + const columns = { + XObjectKey: { + Type: ValType.String, + ColumnName: 'XObjectKey', + }, + }; - columns[DisplayColumns.DISPLAY_PROPERTYNAME] = DisplayColumns.DISPLAY_PROPERTY; + columns[DisplayColumns.DISPLAY_PROPERTYNAME] = DisplayColumns.DISPLAY_PROPERTY; - return { Columns: columns }; - } + return { Columns: columns }; + } } diff --git a/imxweb/projects/aob/src/lib/aob-api-client.service.ts b/imxweb/projects/aob/src/lib/aob-api-client.service.ts index d20883d33..aae2888dc 100644 --- a/imxweb/projects/aob/src/lib/aob-api-client.service.ts +++ b/imxweb/projects/aob/src/lib/aob-api-client.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,12 +25,12 @@ */ import { Injectable } from '@angular/core'; -import { V2Client, TypedClient } from 'imx-api-aob'; -import { ApiClient } from 'imx-qbm-dbts'; +import { V2Client, TypedClient } from '@imx-modules/imx-api-aob'; +import { ApiClient } from '@imx-modules/imx-qbm-dbts'; import { AppConfigService, ClassloggerService, ImxTranslationProviderService } from 'qbm'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class AobApiService { private tc: TypedClient; @@ -50,7 +50,8 @@ export class AobApiService { constructor( private readonly config: AppConfigService, private readonly logger: ClassloggerService, - private readonly translationProvider: ImxTranslationProviderService) { + private readonly translationProvider: ImxTranslationProviderService, + ) { try { this.logger.debug(this, 'Initializing AOB API service'); diff --git a/imxweb/projects/aob/src/lib/aob-config.module.ts b/imxweb/projects/aob/src/lib/aob-config.module.ts index 5d56d0d0b..312fed21a 100644 --- a/imxweb/projects/aob/src/lib/aob-config.module.ts +++ b/imxweb/projects/aob/src/lib/aob-config.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,30 +26,30 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; -import { Routes, RouterModule } from '@angular/router'; +import { RouterModule, Routes } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; -import { ClassloggerService, DocChapterService, DocDocument, HELP_CONTEXTUAL, RouteGuardService } from 'qbm'; +import { ClassloggerService, HELP_CONTEXTUAL, RouteGuardService } from 'qbm'; import { TilesModule } from 'qer'; import { AobService } from './aob.service'; -import { ApplicationsComponent } from './applications/applications.component'; -import { ApplicationNavigationComponent } from './applications/application-navigation/application-navigation.component'; import { ApplicationDetailComponent } from './applications/application-detail.component'; +import { ApplicationNavigationComponent } from './applications/application-navigation/application-navigation.component'; +import { ApplicationsComponent } from './applications/applications.component'; import { ApplicationsModule } from './applications/applications.module'; import { EntitlementsModule } from './entitlements/entitlements.module'; -import { StartPageModule } from './start-page/start-page.module'; -import { AobApplicationsGuardService } from './guards/aob-applications-guard.service'; +import { LockInfoAlertComponent } from './extensions/service-items-edit/lock-info-alert/lock-info-alert.component'; import { GlobalKpiComponent } from './global-kpi/global-kpi.component'; +import { AobApplicationsGuardService } from './guards/aob-applications-guard.service'; import { AobKpiGuardService } from './guards/aob-kpi-guard.service'; -import { LockInfoAlertComponent } from './extensions/service-items-edit/lock-info-alert/lock-info-alert.component'; +import { StartPageModule } from './start-page/start-page.module'; const routes: Routes = [ { path: 'applications/kpi', component: GlobalKpiComponent, canActivate: [RouteGuardService, AobKpiGuardService], - resolve: [RouteGuardService] + resolve: [RouteGuardService], }, { path: 'applications', @@ -62,9 +62,9 @@ const routes: Routes = [ component: ApplicationNavigationComponent, canActivate: [RouteGuardService], resolve: [RouteGuardService], - data:{ - contextId: HELP_CONTEXTUAL.Applications - } + data: { + contextId: HELP_CONTEXTUAL.Applications, + }, }, { path: 'detail', @@ -75,7 +75,7 @@ const routes: Routes = [ }, { path: ':create:id', redirectTo: 'applications', pathMatch: 'full' }, ], - } + }, ]; @NgModule({ @@ -90,34 +90,15 @@ const routes: Routes = [ TranslateModule, RouterModule.forChild(routes), ], - declarations: [ - LockInfoAlertComponent - ], + declarations: [LockInfoAlertComponent], }) export class AobConfigModule { - constructor(private readonly initializer: AobService, - private readonly docSvc: DocChapterService, - private readonly logger: ClassloggerService) { + constructor( + private readonly initializer: AobService, + private readonly logger: ClassloggerService, + ) { this.logger.info(this, '🔥 AOB loaded'); this.initializer.onInit(routes); - this.configureDocPaths(); this.logger.info(this, '▶️ AOB initialized'); } - - private configureDocPaths() { - // Web Portal for Application Governance User Guide - var appgovDoc: DocDocument = { - paths: { - "en-US": "imx/doc/OneIM_AOB_UserGuide_en-us.html5/OneIM_AOB_UserGuide.html", - "de-DE": "imx/doc/OneIM_AOB_UserGuide_de-de.html5/OneIM_AOB_UserGuide.html", - "de-CH": "imx/doc/OneIM_AOB_UserGuide_de-de.html5/OneIM_AOB_UserGuide.html", - "de-AT": "imx/doc/OneIM_AOB_UserGuide_de-de.html5/OneIM_AOB_UserGuide.html" - } - }; - - this.docSvc.chapters["applications"] = { - chapterUid: "35FE656D-A608-4B16-A55F-B758D5B72F75", - document: appgovDoc - }; - } } diff --git a/imxweb/projects/aob/src/lib/aob.service.ts b/imxweb/projects/aob/src/lib/aob.service.ts index 7b7517653..3a42520a4 100644 --- a/imxweb/projects/aob/src/lib/aob.service.ts +++ b/imxweb/projects/aob/src/lib/aob.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,22 +25,22 @@ */ import { Injectable } from '@angular/core'; -import { Router, Route } from '@angular/router'; +import { Route, Router } from '@angular/router'; import { additionalColumnsForServiceItemsKey } from 'qer'; import { ExtService, MenuItem, MenuService } from 'qbm'; +import { LockInfoAlertExtension } from './extensions/service-items-edit/lock-info-alert/lock-info-alert-extension'; import { KpiTileComponent } from './global-kpi/kpi-tile/kpi-tile.component'; import { isAobApplicationAdmin, isAobApplicationOwner } from './permissions/aob-permissions-helper'; -import { LockInfoAlertExtension } from './extensions/service-items-edit/lock-info-alert/lock-info-alert-extension'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class AobService { constructor( private readonly router: Router, private readonly extService: ExtService, - private readonly menuService: MenuService + private readonly menuService: MenuService, ) { this.setupMenu(); } @@ -50,11 +50,11 @@ export class AobService { this.extService.register(additionalColumnsForServiceItemsKey, { inputData: { - columnName: 'UID_AOBApplication' - } + columnName: 'UID_AOBApplication', + }, }); this.extService.register('Dashboard-MediumTiles', { - instance: KpiTileComponent + instance: KpiTileComponent, }); this.extService.register(LockInfoAlertExtension.id, new LockInfoAlertExtension()); @@ -62,7 +62,7 @@ export class AobService { private addRoutes(routes: Route[]): void { const config = this.router.config; - routes.forEach(route => { + routes.forEach((route) => { config.unshift(route); }); this.router.resetConfig(config); @@ -72,24 +72,22 @@ export class AobService { this.menuService.addMenuFactories((preProps: string[], features: string[]) => { const items: MenuItem[] = []; if (isAobApplicationAdmin(features) || isAobApplicationOwner(features)) { - items.push( - { - id: 'AOB_Data_Applications', - title: '#LDS#Applications', - navigationCommands: { commands: ['/applications', { outlets: { primary: ['navigation'], content: ['detail'] } }] }, - sorting: '40-20', - } - ); + items.push({ + id: 'AOB_Data_Applications', + title: '#LDS#Applications', + navigationCommands: { commands: ['/applications', { outlets: { primary: ['navigation'], content: ['detail'] } }] }, + sorting: '40-20', + }); } if (items.length === 0) { - return null; + return undefined; } return { id: 'ROOT_Data', title: '#LDS#Data administration', sorting: '40', - items + items, }; }); } diff --git a/imxweb/projects/aob/src/lib/application-property/application-property.component.html b/imxweb/projects/aob/src/lib/application-property/application-property.component.html index 1f80df5e8..6659c3743 100644 --- a/imxweb/projects/aob/src/lib/application-property/application-property.component.html +++ b/imxweb/projects/aob/src/lib/application-property/application-property.component.html @@ -1,5 +1,5 @@ - +
{{ display }} -
\ No newline at end of file +
diff --git a/imxweb/projects/aob/src/lib/application-property/application-property.component.scss b/imxweb/projects/aob/src/lib/application-property/application-property.component.scss index 76c11896c..449272d08 100644 --- a/imxweb/projects/aob/src/lib/application-property/application-property.component.scss +++ b/imxweb/projects/aob/src/lib/application-property/application-property.component.scss @@ -8,18 +8,6 @@ min-height: 40px; } -.imx-user-icon { - color: $black-3; - background-color: $black-c; - opacity: 0.6; - border-radius: 50%; - text-align: center; - line-height: 35px; - min-width: 35px; - min-height: 35px; - margin-right: 12px; -} - .imx-application-property { display: flex; flex-direction: column; @@ -34,21 +22,3 @@ text-overflow: ellipsis; } } - -.eui-dark-theme { - :host { - .imx-user-icon { - color: $color-gray-20; - background-color: $color-gray-80; - } - } -} - -.eui-contrast-theme { - :host { - .imx-user-icon { - color: $color-gray-10; - background-color: $color-gray-100; - } - } -} diff --git a/imxweb/projects/aob/src/lib/application-property/application-property.component.ts b/imxweb/projects/aob/src/lib/application-property/application-property.component.ts index c9dd08a25..22181708e 100644 --- a/imxweb/projects/aob/src/lib/application-property/application-property.component.ts +++ b/imxweb/projects/aob/src/lib/application-property/application-property.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,7 +29,7 @@ import { Component, Input } from '@angular/core'; @Component({ selector: 'imx-application-property', templateUrl: './application-property.component.html', - styleUrls: ['./application-property.component.scss'] + styleUrls: ['./application-property.component.scss'], }) export class ApplicationPropertyComponent { @Input() public display: string; diff --git a/imxweb/projects/aob/src/lib/application-property/application-property.module.ts b/imxweb/projects/aob/src/lib/application-property/application-property.module.ts index e86c8cebc..b212a6906 100644 --- a/imxweb/projects/aob/src/lib/application-property/application-property.module.ts +++ b/imxweb/projects/aob/src/lib/application-property/application-property.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,15 +31,8 @@ import { EuiCoreModule } from '@elemental-ui/core'; import { ApplicationPropertyComponent } from './application-property.component'; @NgModule({ - declarations: [ - ApplicationPropertyComponent - ], - exports: [ - ApplicationPropertyComponent, - ], - imports: [ - CommonModule, - EuiCoreModule - ] + declarations: [ApplicationPropertyComponent], + exports: [ApplicationPropertyComponent], + imports: [CommonModule, EuiCoreModule], }) -export class ApplicationPropertyModule { } +export class ApplicationPropertyModule {} diff --git a/imxweb/projects/aob/src/lib/applications/application-content.interface.ts b/imxweb/projects/aob/src/lib/applications/application-content.interface.ts index 13e9765cb..320a0b4cd 100644 --- a/imxweb/projects/aob/src/lib/applications/application-content.interface.ts +++ b/imxweb/projects/aob/src/lib/applications/application-content.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,12 +24,12 @@ * */ -import { PortalApplication, PortalApplicationNew } from 'imx-api-aob'; +import { PortalApplication, PortalApplicationNew } from '@imx-modules/imx-api-aob'; import { Subject } from 'rxjs'; export interface ApplicationContent { - application: PortalApplication | PortalApplicationNew; - totalCount?: number; - keywords?: string; - loadingSubject?: Subject + application: PortalApplication | PortalApplicationNew | undefined; + totalCount?: number; + keywords?: string; + loadingSubject?: Subject; } diff --git a/imxweb/projects/aob/src/lib/applications/application-create/application-create.component.html b/imxweb/projects/aob/src/lib/applications/application-create/application-create.component.html index 653af41a9..29efc534d 100644 --- a/imxweb/projects/aob/src/lib/applications/application-create/application-create.component.html +++ b/imxweb/projects/aob/src/lib/applications/application-create/application-create.component.html @@ -2,31 +2,51 @@

#LDS#Add an icon, name and description to the application and assign a manager to it.

- + (controlCreated)="form.addControl(imageColumn.ColumnName, $event)" + > - + - + - + - + - + - - - +
diff --git a/imxweb/projects/aob/src/lib/applications/application-create/application-create.component.scss b/imxweb/projects/aob/src/lib/applications/application-create/application-create.component.scss index 93f46628c..744836116 100644 --- a/imxweb/projects/aob/src/lib/applications/application-create/application-create.component.scss +++ b/imxweb/projects/aob/src/lib/applications/application-create/application-create.component.scss @@ -1,54 +1,27 @@ -@import '@elemental-ui/core/src/styles/_palette.scss'; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/mixins'; :host { overflow-y: auto; - display: flex; - flex-direction: column; - height: 100%; - background-color: $asher-gray; + @include flex-column-container($height: 100%); + background-color: $color-gray-2; } .imx-content { - display: flex; - flex-direction: column; + @include flex-column-container-fill(); margin: 30px; overflow-y: auto; - flex: 1 1 auto; max-height: calc(100% - 60px); } -.imx-button-bar { - display: flex; - justify-content: flex-end; - margin-bottom: 0; - padding: 16px 32px; - border-top: 1px solid rgba(0, 0, 0, 0.12); - background-color: $white; - - button { - margin-left: 10px; - } - - .justify-start { - margin-right: auto; - } -} - .eui-dark-theme { :host { - background-color: $color-gray-80; - .imx-button-bar { - background-color: $color-gray-70; - } + background-color: $color-gray-80; } } .eui-contrast-theme { :host { - background-color: $color-gray-100; - .imx-button-bar { - background-color: $color-gray-90; - } + background-color: $color-gray-100; } } diff --git a/imxweb/projects/aob/src/lib/applications/application-create/application-create.component.ts b/imxweb/projects/aob/src/lib/applications/application-create/application-create.component.ts index dcfce96b1..a19b671d1 100644 --- a/imxweb/projects/aob/src/lib/applications/application-create/application-create.component.ts +++ b/imxweb/projects/aob/src/lib/applications/application-create/application-create.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,18 +26,18 @@ import { Component, Inject, OnDestroy } from '@angular/core'; import { UntypedFormGroup } from '@angular/forms'; -import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; import { Subscription } from 'rxjs'; -import { PortalApplicationNew } from 'imx-api-aob'; -import { IEntityColumn, IWriteValue, ValueStruct } from 'imx-qbm-dbts'; -import { BaseCdr, ColumnDependentReference, ConfirmationService } from 'qbm'; +import { PortalApplicationNew } from '@imx-modules/imx-api-aob'; +import { IEntityColumn, IWriteValue, ValueStruct } from '@imx-modules/imx-qbm-dbts'; import { TranslateService } from '@ngx-translate/core'; +import { BaseCdr, ColumnDependentReference, ConfirmationService } from 'qbm'; @Component({ selector: 'imx-application-create', templateUrl: './application-create.component.html', - styleUrls: ['./application-create.component.scss'] + styleUrls: ['./application-create.component.scss'], }) export class ApplicationCreateComponent implements OnDestroy { public readonly form = new UntypedFormGroup({}); @@ -53,10 +53,10 @@ export class ApplicationCreateComponent implements OnDestroy { private readonly subscriptions: Subscription[] = []; constructor( - @Inject(EUI_SIDESHEET_DATA) data: { application: PortalApplicationNew; }, + @Inject(EUI_SIDESHEET_DATA) data: { application: PortalApplicationNew }, private readonly sidesheetRef: EuiSidesheetRef, private readonly translateService: TranslateService, - confirmation: ConfirmationService + confirmation: ConfirmationService, ) { this.imageColumn = data.application.JPegPhoto.Column; @@ -65,35 +65,44 @@ export class ApplicationCreateComponent implements OnDestroy { this.description = new BaseCdr(data.application.Description.Column); - this.serviceCategory = new class { + this.serviceCategory = new (class { public get hint(): string { - return this.property.value?.length > 0 ? '' : - this.translateService.instant('#LDS#If you do not select a service category, a service category with the same name as the application is created and assigned.'); + return this.property.value?.length > 0 + ? '' + : this.translateService.instant( + '#LDS#If you do not select a service category, a service category with the same name as the application is created and assigned.', + ); } - public readonly column = this.property.Column; + public readonly column: IEntityColumn; constructor( private readonly property: IWriteValue, private readonly translateService: TranslateService, - ) {} + ) { + this.column = this.property.Column; + } public readonly isReadOnly = () => !this.property.GetMetadata().CanEdit(); - }(data.application.UID_AccProductGroup, this.translateService); + })(data.application.UID_AccProductGroup, this.translateService); this.manager = new BaseCdr(data.application.UID_PersonHead.Column); this.owner = new BaseCdr(data.application.UID_AERoleOwner.Column); - this.subscriptions.push(sidesheetRef.closeClicked().subscribe(async () => { - if ((data.application.GetEntity().GetDiffData()?.Data?.length > 0 || !this.form.pristine) && - !(await confirmation.confirmLeaveWithUnsavedChanges())) { - return; - } - - sidesheetRef.close(false); - })); + this.subscriptions.push( + sidesheetRef.closeClicked().subscribe(async () => { + if ( + (!!data.application.GetEntity().GetDiffData()?.Data?.length || !this.form.pristine) && + !(await confirmation.confirmLeaveWithUnsavedChanges()) + ) { + return; + } + + sidesheetRef.close(false); + }), + ); } public ngOnDestroy(): void { - this.subscriptions.forEach(s => s.unsubscribe()); + this.subscriptions.forEach((s) => s.unsubscribe()); } public async updateName(value: ValueStruct): Promise { diff --git a/imxweb/projects/aob/src/lib/applications/application-detail.component.html b/imxweb/projects/aob/src/lib/applications/application-detail.component.html index 1e33c4b47..b5391bde5 100644 --- a/imxweb/projects/aob/src/lib/applications/application-detail.component.html +++ b/imxweb/projects/aob/src/lib/applications/application-detail.component.html @@ -1,19 +1,26 @@
- + - +
-
{{ application.GetEntity().GetDisplay() }}
- +

{{ application.GetEntity().GetDisplay() }}

+
{{ application.Description.Column.GetDisplayValue() }}
-
- + @@ -21,7 +28,7 @@ - + @@ -43,14 +50,14 @@
-

- #LDS#Here you can get an overview of identities that have access to at least one application entitlement of this application. Additionally, you can view which application entitlements the respective identities have access to. +

+ {{ ldsIdentitiesWithAccessInfoText | translate }}

-
- +
+ {{ '#LDS#No Application Selected' | translate }} {{ '#LDS#Select an application to view its details.' | translate }} @@ -59,7 +66,14 @@ {{ (keywords ? '#LDS#No application found' : '#LDS#No applications available') | translate }} - diff --git a/imxweb/projects/aob/src/lib/applications/application-detail.component.scss b/imxweb/projects/aob/src/lib/applications/application-detail.component.scss index 244ed9bff..72b867aee 100644 --- a/imxweb/projects/aob/src/lib/applications/application-detail.component.scss +++ b/imxweb/projects/aob/src/lib/applications/application-detail.component.scss @@ -1,8 +1,7 @@ @use '@angular/material' as mat; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; -@import '../../../../../shared/assets/variables.scss'; - -$imx-component-height: calc(100vh - 180px); +@import 'base/mixins'; +@import 'base/variables'; :host { display: flex; @@ -11,14 +10,6 @@ $imx-component-height: calc(100vh - 180px); height: 100%; } -:host ::ng-deep .mat-tab-body-wrapper { - .mat-tab-body-content { - overflow: hidden; - height: 100%; - display: flex; - } -} - .imx-application-details { display: flex; overflow-x: hidden; @@ -28,7 +19,7 @@ $imx-component-height: calc(100vh - 180px); display: flex; flex-direction: column; flex: 1 1 auto; - height: $imx-component-height; + height: $IMX_Application_Details_Component_Height; overflow: hidden; } @@ -67,21 +58,9 @@ $imx-component-height: calc(100vh - 180px); flex-grow: 1; } -.imx-application-details-icon { - grid-column-start: 1; - grid-column-end: 2; - grid-row-start: 1; - grid-row-end: 3; +.imx-application-details-image { width: 42px; height: 42px; - align-self: center; - object-fit: cover; -} - -@mixin textOverflowEllipsis { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; } .imx-application-details-title { @@ -93,7 +72,7 @@ $imx-component-height: calc(100vh - 180px); font-size: 24px; min-height: 40px; margin: 0 15px 0 10px; - @include textOverflowEllipsis; + @include text-overflow-ellipsis; display: flex; flex-direction: row; align-items: center; @@ -106,7 +85,7 @@ $imx-component-height: calc(100vh - 180px); grid-row-end: 2; font-size: 14px; margin: 0 15px 0 10px; - @include textOverflowEllipsis; + @include text-overflow-ellipsis; } .imx-info { @@ -115,41 +94,19 @@ $imx-component-height: calc(100vh - 180px); margin-right: 50px; } -.mat-tab-group { - overflow: hidden; - height: 100%; -} - imx-application-hyperview { overflow: auto; flex-grow: 1; padding: 1em; } -// TODO: .mat-tab-body-wrapper does not pass information of the height to its child nodes, -// because it does not stretch to the height of its parent container (which is a mat-tab-group). -// Right now the only fix is to use the ng-deep selector. -::ng-deep .mat-tab-body-wrapper { - height: inherit; - - .mat-tab-body-content { - overflow: visible; - } -} :host { - ::ng-deep .mat-tab-body-content { - background-color: rgba($color-gray-10, 0.1); - } + .imx-application-content { background-color: $color-gray-0; } - .imx-application-content-no-app { - > .eui-icon { - color: $color-gray-30; - } - } .imx-application-details-subtitle { color: $color-gray-70; } @@ -157,28 +114,18 @@ imx-application-hyperview { .eui-dark-theme { :host { - ::ng-deep .mat-tab-labels { - - background-color:$color-gray-70; - } - .imx-application-content { - background-color:$color-gray-70; - } - ::ng-deep .mat-tab-body-content { - background-color:rgba($color-gray-70, 0.55); + .imx-application-content { + background-color: $color-gray-70; } - .imx-application-content-no-app { .imx-application-content-no-app-text, .imx-application-content-no-app-description { color: $color-gray-20; } - > .eui-icon { - color: $color-gray-60; - } } + .imx-application-details-subtitle { color: $color-gray-50; } @@ -187,21 +134,10 @@ imx-application-hyperview { .eui-contrast-theme { :host { - ::ng-deep .mat-tab-labels { - background-color: $color-gray-100; - } - - ::ng-deep .mat-tab-body-content { - background-color: $color-gray-100; - } .imx-application-content { background-color: $color-gray-100; } - .imx-application-content-no-app { - > .eui-icon { - color: $color-gray-90; - } - } + .imx-application-details-subtitle { color: $color-gray-50; } diff --git a/imxweb/projects/aob/src/lib/applications/application-detail.component.ts b/imxweb/projects/aob/src/lib/applications/application-detail.component.ts index c634cdc66..593277162 100644 --- a/imxweb/projects/aob/src/lib/applications/application-detail.component.ts +++ b/imxweb/projects/aob/src/lib/applications/application-detail.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,21 +30,21 @@ import { SafeUrl } from '@angular/platform-browser'; import { ActivatedRoute } from '@angular/router'; import { Subject, Subscription } from 'rxjs'; +import { PortalApplication } from '@imx-modules/imx-api-aob'; import { Base64ImageService, ClassloggerService } from 'qbm'; import { UserModelService } from 'qer'; -import { PortalApplication } from 'imx-api-aob'; -import { ApplicationsService } from './applications.service'; -import { ApplicationContent } from './application-content.interface'; import { AobPermissionsService } from '../permissions/aob-permissions.service'; +import { ApplicationContent } from './application-content.interface'; +import { ApplicationsService } from './applications.service'; @Component({ templateUrl: './application-detail.component.html', styleUrls: ['./application-detail.component.scss'], }) export class ApplicationDetailComponent implements ApplicationContent, OnInit, OnDestroy { - @Input() public application: PortalApplication; - - @Input() public loadingSubject: Subject; + @Input() public application: PortalApplication | undefined; + + @Input() public loadingSubject: Subject; public totalCount: number; public keywords: string; @@ -52,7 +52,10 @@ export class ApplicationDetailComponent implements ApplicationContent, OnInit, O public selectedTabIndex = 0; public showHelper = true; public isLoading = false; - + public hyperviewTableName = 'AOBApplication'; + public ldsIdentitiesWithAccessInfoText = + '#LDS#Here you can get an overview of identities that have access to at least one application entitlement of this application. Additionally, you can view which application entitlements the respective identities have access to.'; + private subscription: Subscription; constructor( @@ -60,21 +63,20 @@ export class ApplicationDetailComponent implements ApplicationContent, OnInit, O private readonly logger: ClassloggerService, private readonly applicationsProvider: ApplicationsService, public readonly userService: UserModelService, - public route:ActivatedRoute, - private readonly aobPermissionsService: AobPermissionsService + public route: ActivatedRoute, + private readonly aobPermissionsService: AobPermissionsService, ) { this.route.queryParams.subscribe(async (params) => { - if (Object.keys(params).length === 0) this.application = null; + if (Object.keys(params).length === 0) this.application = undefined; }); } - + public ngOnDestroy(): void { this.subscription?.unsubscribe(); } public async ngOnInit(): Promise { - - this.subscription = this.loadingSubject.subscribe(elem=> this.isLoading = elem); + this.subscription = this.loadingSubject.subscribe((elem) => (this.isLoading = elem)); this.isAdmin = await this.aobPermissionsService.isAobApplicationAdmin(); } @@ -89,7 +91,7 @@ export class ApplicationDetailComponent implements ApplicationContent, OnInit, O } public async reloadApplication(): Promise { - this.application = await this.applicationsProvider.reload(this.application.UID_AOBApplication.value); + this.application = await this.applicationsProvider.reload(this.application?.UID_AOBApplication.value || ''); } public createApplication(): void { diff --git a/imxweb/projects/aob/src/lib/applications/application-details/account-details.interface.ts b/imxweb/projects/aob/src/lib/applications/application-details/account-details.interface.ts index 164e2f064..1281a5921 100644 --- a/imxweb/projects/aob/src/lib/applications/application-details/account-details.interface.ts +++ b/imxweb/projects/aob/src/lib/applications/application-details/account-details.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,10 +24,10 @@ * */ -import { TypedEntity } from 'imx-qbm-dbts'; +import { TypedEntity } from '@imx-modules/imx-qbm-dbts'; export interface AccountDetails { count: number; - first: TypedEntity; + first: TypedEntity | undefined; uidApplication: string; } diff --git a/imxweb/projects/aob/src/lib/applications/application-details/application-details.component.html b/imxweb/projects/aob/src/lib/applications/application-details/application-details.component.html index b05ef7815..6c09e57ae 100644 --- a/imxweb/projects/aob/src/lib/applications/application-details/application-details.component.html +++ b/imxweb/projects/aob/src/lib/applications/application-details/application-details.component.html @@ -7,8 +7,12 @@ - - + +
{{ '-' + ('#LDS#None assigned' | translate) + '-' }} @@ -20,7 +24,7 @@ data-imx-identifier="imx-application-details-button-show-more-shops" *ngIf="assignedShops.length > 1" > - {{ '#LDS#and {0} more...' | translate | ldsReplace : assignedShops.length - 1 }} + {{ '#LDS#and {0} more...' | translate | ldsReplace: assignedShops.length - 1 }}
@@ -39,11 +43,11 @@ {{ '#LDS#Will be published' | translate }} - check_circle + check_circle {{ '#LDS#Published' | translate }}
- {{ application.ActivationDate.value | shortDate }} + {{ application?.ActivationDate?.value?.toString() || '' | shortDate }}
@@ -53,29 +57,64 @@ -
- check_circle - {{ (application ? '#LDS#ScreenReader_Checked' : '#LDS#ScreenReader_Unchecked') | translate }} +
+ + check_circle + + + {{ (application ? '#LDS#ScreenReader_Checked' : '#LDS#ScreenReader_Unchecked') | translate }} + {{ '#LDS#Application created' | translate }}
-
- check_circle - {{ (hasOwner ? '#LDS#ScreenReader_Checked' : '#LDS#ScreenReader_Unchecked') | translate }} +
+ + check_circle + + + {{ (hasOwner ? '#LDS#ScreenReader_Checked' : '#LDS#ScreenReader_Unchecked') | translate }} + {{ '#LDS#Manager assigned' | translate }}
-
- check_circle - {{ (assignedShops?.length > 0 ? '#LDS#ScreenReader_Checked' : '#LDS#ScreenReader_Unchecked') | translate }} +
+ + check_circle + + + {{ (!!assignedShops?.length ? '#LDS#ScreenReader_Checked' : '#LDS#ScreenReader_Unchecked') | translate }} + {{ '#LDS#Shop assigned' | translate }}
-
- check_circle - {{ (hasAssignedEntitlements ? '#LDS#ScreenReader_Checked' : '#LDS#ScreenReader_Unchecked') | translate }} +
+ + check_circle + + + {{ (hasAssignedEntitlements ? '#LDS#ScreenReader_Checked' : '#LDS#ScreenReader_Unchecked') | translate }} + {{ '#LDS#Application entitlements assigned' | translate }}
-
- check_circle - {{ (hasPublishedEntitlements ? '#LDS#ScreenReader_Checked' : '#LDS#ScreenReader_Unchecked') | translate }} +
+ + check_circle + + + {{ (hasPublishedEntitlements ? '#LDS#ScreenReader_Checked' : '#LDS#ScreenReader_Unchecked') | translate }} + {{ '#LDS#Application entitlements published' | translate }}
@@ -87,12 +126,12 @@ - - + +
{{ '-' + ('#LDS#None selected' | translate) + '-' }} - {{ accountsInformation.first.GetEntity().GetDisplay() }} + {{ accountsInformation.first?.GetEntity()?.GetDisplay() }}
- - - - - - - - - - - + + + + + + + + + + +
- - - - + + diff --git a/imxweb/projects/aob/src/lib/applications/application-details/application-details.component.scss b/imxweb/projects/aob/src/lib/applications/application-details/application-details.component.scss index 6d41c0cc3..2d342bd7a 100644 --- a/imxweb/projects/aob/src/lib/applications/application-details/application-details.component.scss +++ b/imxweb/projects/aob/src/lib/applications/application-details/application-details.component.scss @@ -1,11 +1,9 @@ @use '@angular/material' as mat; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/mixins'; :host { - display: flex; - flex-direction: column; - overflow: hidden; - flex: 1 1 auto; + @include flex-column-container-fill(); min-height: 100%; .imx-application-details-content .imx-application-details-grid.hidden { @@ -14,46 +12,27 @@ } .imx-application-details-content { - display: flex; + @include flex-row-container-fill(); overflow: hidden; - flex: 1 1 auto; imx-busy-indicator { flex: 0 0 0; } .imx-application-details-grid { - display: flex; + @include flex-row-container($max-width: 1500px, $max-height: 800px, $overflow: auto); flex-wrap: wrap; padding: 16px 24px; - overflow: auto; - max-width: 1500px; - max-height: 800px; - > .mat-card { + > .mat-mdc-card { background-color: transparent; } - .mat-card-title { + .mat-mdc-card-title { font-size: 18px; line-height: 36px; } - .mat-button { - vertical-align: middle; - padding: 0 2px; - min-width: 0; - margin-left: 5px; - - ::ng-deep .mat-button-wrapper { - vertical-align: text-bottom; - } - } - - .mat-button[hidden] { - display: none; - } - .mat-divider-horizontal { width: 50px; margin-bottom: 12px; @@ -66,9 +45,8 @@ min-width: 200px; box-shadow: none; - .mat-card-content { - display: flex; - flex-direction: column; + .mat-mdc-card-content { + @include flex-column-container(); margin-bottom: -15px; > * { @@ -77,37 +55,8 @@ } } -.imx-button-area { - background-color: transparent; - display: flex; - flex-direction: row; - justify-content: flex-end; - - .justify-start { - margin-right: auto; - justify-self: start; - } - - .imx-additional-actions { - margin-right: 5px; - margin-left: 5px; - } - - .mat-stroked-button, - .mat-raised-button { - eui-icon { - font-size: 14px; - margin-right: 4px; - } - } - > button:not(:last-of-type) { - margin-right: 16px; - } -} - .imx-application-property-multi { - display: flex; - flex-direction: column; + @include flex-column-container(); > span { font-weight: 600; @@ -115,9 +64,7 @@ text-overflow: ellipsis; } - > .mat-button { - width: auto; - padding: 0px; + > .mat-mdc-button { line-height: 1.3rem; align-self: flex-start; } @@ -127,10 +74,8 @@ li { list-style-type: none; margin: 10px; - .mat-card { - display: flex; - flex-direction: row; - height: 3.2rem; + .mat-mdc-card { + @include flex-row-container(); > .imx-card-title { font-size: 16px; @@ -143,22 +88,16 @@ li { } } -.mat-dialog-content { - flex: 1; - display: flex; - flex-direction: column; - height: inherit; -} - .imx-application-state { - display: flex; + @include flex-row-container(); + flex-wrap: wrap; flex: 0 0 64%; align-content: flex-start; box-shadow: none; padding: 0; - > .mat-card { + > .mat-mdc-card { background-color: transparent; } } @@ -170,22 +109,19 @@ li { padding-left: 5px; padding-right: 0; - .mat-card-content { - display: flex; - align-items: center; - } + .mat-mdc-card-content { + @include flex-row-container(); - .mat-chip { - border-radius: 3px; - width: max-content; - user-select: none; + align-items: center; } - @mixin publish-state-style { + .imx-application-unpublished-state, + .imx-application-willbepublished-state, + .imx-application-published-state { + @include flex-row-container(); + align-items: center; font-weight: 600; font-size: 12px; - display: flex; - align-items: center; .eui-icon, .mat-icon { @@ -193,21 +129,6 @@ li { } } - .imx-application-unpublished-state, - .imx-application-willbepublished-state { - @include publish-state-style; - } - - .imx-application-published-state { - @include publish-state-style; - - .mat-icon { - font-size: 14px; - width: 14px; - height: 14px; - } - } - .imx-application-publish-date { margin-left: 10px; font-size: 14px; @@ -220,7 +141,7 @@ li { padding-left: 0; padding-right: 0; - .mat-card-title { + .mat-mdc-card-title { line-height: 45px; } @@ -228,24 +149,13 @@ li { margin-bottom: 8px; } - .mat-icon { - margin-right: 5px; - font-size: 16px; - width: 16px; - height: 16px; - visibility: hidden; - } - - .checked .mat-icon { - visibility: visible; - } - span { font-size: 14px; } - .mat-card-content > div { - display: flex; + .mat-mdc-card-content > div { + @include flex-row-container(); + align-items: center; } } @@ -255,12 +165,13 @@ li { box-shadow: none; overflow: hidden; - .mat-card-title { + .mat-mdc-card-title { line-height: 45px; } - .mat-card-content { - display: flex; + .mat-mdc-card-content { + @include flex-row-container(); + flex-wrap: wrap; imx-column-info, @@ -276,18 +187,6 @@ li { margin: 10px 0; } -.mat-card-avatar { - padding: 5px; - height: 25px; - width: 25px; - user-select: none; -} - -:host ::ng-deep body .mat-dialog-container { - display: flex; - flex-direction: column; -} - //Themeing :host { .imx-application-property-multi { @@ -304,17 +203,6 @@ li { .imx-application-published-state { color: $color-green-60; } - - .imx-application-progress { - .mat-icon { - color: $color-green-60; - } - } - - .mat-card-avatar { - background-color: $color-gray-30; - color: $color-gray-0; - } } .eui-dark-theme { @@ -325,11 +213,6 @@ li { } } - .mat-card-avatar { - background-color: $color-gray-60; - color: $color-gray-80; - } - .imx-application-publish { .imx-application-unpublished-state, .imx-application-willbepublished-state { @@ -340,12 +223,6 @@ li { color: $color-green-40; } } - - .imx-application-progress { - .mat-icon { - color: $color-green-40; - } - } } } @@ -361,12 +238,7 @@ li { background-color: $color-gray-90; } - .mat-card-avatar { - background-color: $color-gray-80; - color: $color-gray-70; - } - - .mat-card { + .mat-mdc-card { border-color: $color-gray-90; } @@ -380,11 +252,5 @@ li { color: $color-green-20; } } - - .imx-application-progress { - .mat-icon { - color: $color-green-20; - } - } } } diff --git a/imxweb/projects/aob/src/lib/applications/application-details/application-details.component.ts b/imxweb/projects/aob/src/lib/applications/application-details/application-details.component.ts index 242752d74..a0feabdde 100644 --- a/imxweb/projects/aob/src/lib/applications/application-details/application-details.component.ts +++ b/imxweb/projects/aob/src/lib/applications/application-details/application-details.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,29 +24,28 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; -import { Component, Input, OnChanges, ViewChild, TemplateRef, OnInit } from '@angular/core'; +import { Platform } from '@angular/cdk/platform'; +import { Component, Input, OnChanges, OnInit, TemplateRef, ViewChild } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { MatMenuTrigger } from '@angular/material/menu'; import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; -import { Platform } from '@angular/cdk/platform'; import { TranslateService } from '@ngx-translate/core'; -import { PortalApplication } from 'imx-api-aob'; -import { BusyService, ClassloggerService, ConfirmationService, SnackBarService, TextContainer } from 'qbm'; -import { ShopsService } from '../../shops/shops.service'; -import { EntitlementsService } from '../../entitlements/entitlements.service'; -import { EntitlementFilter } from '../../entitlements/entitlement-filter'; -import { TypedEntity } from 'imx-qbm-dbts'; +import { PortalApplication } from '@imx-modules/imx-api-aob'; +import { TypedEntity } from '@imx-modules/imx-qbm-dbts'; +import { BusyService, calculateSidesheetWidth, ClassloggerService, ConfirmationService, SnackBarService, TextContainer } from 'qbm'; import { AccountsService } from '../../accounts/accounts.service'; -import { LifecycleAction } from '../../lifecycle-actions/lifecycle-action.enum'; +import { AobApiService } from '../../aob-api-client.service'; +import { EntitlementFilter } from '../../entitlements/entitlement-filter'; +import { EntitlementsService } from '../../entitlements/entitlements.service'; import { LifecycleActionComponent } from '../../lifecycle-actions/lifecycle-action.component'; +import { LifecycleAction } from '../../lifecycle-actions/lifecycle-action.enum'; +import { AobPermissionsService } from '../../permissions/aob-permissions.service'; +import { ShopsService } from '../../shops/shops.service'; import { ApplicationsService } from '../applications.service'; -import { AobApiService } from '../../aob-api-client.service'; import { EditApplicationComponent } from '../edit-application/edit-application.component'; -import { AccountDetails } from './account-details.interface'; -import { AobPermissionsService } from '../../permissions/aob-permissions.service'; import { ServiceCategoryComponent } from '../edit-application/service-category/service-category.component'; +import { AccountDetails } from './account-details.interface'; @Component({ selector: 'imx-application-details', @@ -57,7 +56,7 @@ export class ApplicationDetailsComponent implements OnChanges, OnInit { /** * The {@link PortalApplication | AobApplication} */ - @Input() public application: PortalApplication; + @Input() public application: PortalApplication | undefined; @ViewChild('multiValueControl', { static: true }) public chartDialog: TemplateRef; @@ -91,18 +90,18 @@ export class ApplicationDetailsComponent implements OnChanges, OnInit { private readonly translateService: TranslateService, private readonly confirmation: ConfirmationService, private readonly sidesheetService: EuiSidesheetService, - private readonly permissions: AobPermissionsService + private readonly permissions: AobPermissionsService, ) { - this.shopsDisplay = this.aobApiService.typedClient.PortalShops.GetSchema().Display; - this.accountsDisplay = this.aobApiService.typedClient.PortalApplicationusesaccount.GetSchema().Display; + this.shopsDisplay = this.aobApiService.typedClient.PortalShops.GetSchema().Display || ''; + this.accountsDisplay = this.aobApiService.typedClient.PortalApplicationusesaccount.GetSchema().Display || ''; this.busyService.busyStateChanged.subscribe((elem) => (this.isLoading = elem)); } public ngOnInit(): void { this.accountsInformation = { count: 0, - first: null, - uidApplication: this.application.UID_AOBApplication.value, + first: undefined, + uidApplication: this.application?.UID_AOBApplication.value || '', }; } @@ -120,12 +119,15 @@ export class ApplicationDetailsComponent implements OnChanges, OnInit { Message: '#LDS#Are you sure you want to delete the application?', }) ) { - let overlayRef: OverlayRef; - setTimeout(() => ((overlayRef = this.euiBusyService.show()))); + if (this.euiBusyService.overlayRefs.length === 0) { + this.euiBusyService.show(); + } try { - await this.applicationprovider.deleteApplication(this.application.UID_AOBApplication.value); + if (!!this.application) { + await this.applicationprovider.deleteApplication(this.application.UID_AOBApplication.value); + } } finally { - setTimeout(() => this.euiBusyService.hide(overlayRef)); + this.euiBusyService.hide(); } } } @@ -137,8 +139,8 @@ export class ApplicationDetailsComponent implements OnChanges, OnInit { await this.sidesheetService .open(EditApplicationComponent, { title: await this.translateService.get('#LDS#Heading Edit Application').toPromise(), - subTitle: this.application.GetEntity().GetDisplay(), - width: '768px', + subTitle: this.application?.GetEntity().GetDisplay(), + width: calculateSidesheetWidth(800), padding: '0', data: this.application, testId: 'edit-application', @@ -147,7 +149,9 @@ export class ApplicationDetailsComponent implements OnChanges, OnInit { .afterClosed() .toPromise(); this.applicationprovider.applicationRefresh.next(true); - this.application = await this.applicationprovider.reload(this.application.UID_AOBApplication.value); + if (this.application) { + this.application = await this.applicationprovider.reload(this.application?.UID_AOBApplication.value); + } this.reloadData(); } @@ -171,10 +175,10 @@ export class ApplicationDetailsComponent implements OnChanges, OnInit { if (publishData) { this.logger.debug( this, - `Publishing application) ${publishData.date && publishData.publishFuture ? `on ${publishData.date}` : 'now'}` + `Publishing application) ${publishData.date && publishData.publishFuture ? `on ${publishData.date}` : 'now'}`, ); - if (await this.applicationprovider.publish(this.application, publishData)) { + if (!!this.application && (await this.applicationprovider.publish(this.application, publishData))) { let publishMessage: TextContainer = { key: '#LDS#The application was successfully published. It takes a moment for the changes to take effect.', }; @@ -211,10 +215,10 @@ export class ApplicationDetailsComponent implements OnChanges, OnInit { if (result) { this.logger.debug(this, 'Unpublish application...'); - if (await this.applicationprovider.unpublish(this.application)) { + if (this.application && (await this.applicationprovider.unpublish(this.application))) { this.snackbar.open( { key: '#LDS#The application was successfully unpublished. It takes a moment for the changes to take effect.' }, - '#LDS#Close' + '#LDS#Close', ); } else { this.logger.error(this, 'Attempt to unpublish the application failed'); @@ -229,28 +233,28 @@ export class ApplicationDetailsComponent implements OnChanges, OnInit { * Checks if the specified {@link PortalApplication|application} can be publish. */ public get isPublishable(): boolean { - return this.application != null && this.application.ActivationDate.GetMetadata().CanEdit() && !this.published(); + return this.application != null && this.application?.ActivationDate.GetMetadata().CanEdit() && !this.published(); } /** * Checks if the specified {@link PortalApplication|application} can be unnpublish. */ public get isUnpublishable(): boolean { - return this.application != null && this.application.IsInActive.GetMetadata().CanEdit() && !this.notPublished(); + return this.application != null && this.application?.IsInActive.GetMetadata().CanEdit() && !this.notPublished(); } // TODO 222863: Use the same filter as for entitlements: public notPublished(): boolean { - return this.application.IsInActive && this.application.IsInActive.value && !this.application.ActivationDate.value; + return !!this.application?.IsInActive && !!this.application?.IsInActive.value && !this.application?.ActivationDate.value; } public willPublish(): boolean { - return this.application.IsInActive && this.application.IsInActive.value && this.application.ActivationDate.value != null; + return !!this.application?.IsInActive && !!this.application?.IsInActive.value && this.application?.ActivationDate.value != null; } public published(): boolean { - return !this.application.IsInActive.value; + return !this.application?.IsInActive.value; } /** @@ -272,34 +276,37 @@ export class ApplicationDetailsComponent implements OnChanges, OnInit { public showMore(elements: TypedEntity[], title: string, iconText?: string): void { const content = { parts: elements.map((part) => part.GetEntity().GetDisplay()), caption: title, icon: iconText }; - this.dialog.open(this.chartDialog, { data: content, width: '600px', height: '400px' }); + this.dialog.open(this.chartDialog, { data: content, width: '600px' }); } /** * Shows a dialog, that displays additionals accounts (because in the beginning there are only 3 elements displayed) */ public async showMoreAccounts(): Promise { - let overlayRef: OverlayRef; - setTimeout(() => ((overlayRef = this.euiBusyService.show()))); + if (this.euiBusyService.overlayRefs.length === 0) { + this.euiBusyService.show(); + } try { - const elements = await this.accountsProvider.getAssigned(this.application.UID_AOBApplication.value, { - PageSize: this.accountsInformation.count, - }); - this.showMore(elements, this.accountsDisplay); + if (!!this.application) { + const elements = await this.accountsProvider.getAssigned(this.application.UID_AOBApplication.value, { + PageSize: this.accountsInformation.count, + }); + this.showMore(elements, this.accountsDisplay); + } } finally { - setTimeout(() => this.euiBusyService.hide(overlayRef)); + this.euiBusyService.hide(); } } public async editServiceCategories(): Promise { await this.sidesheetService .open(ServiceCategoryComponent, { - title: await this.translateService.get('#LDS#Heading Edit Service Categories').toPromise(), - subTitle: this.application.GetEntity().GetDisplay(), - width: 'max(700px,70%)', + title: await this.translateService.get('#LDS#Heading Manage Service Categories').toPromise(), + subTitle: this.application?.GetEntity().GetDisplay(), + width: calculateSidesheetWidth(), padding: '0', data: this.application, - testId: 'edit-application-servicecategory' + testId: 'edit-application-servicecategory', }) .afterClosed() .toPromise(); @@ -308,27 +315,32 @@ export class ApplicationDetailsComponent implements OnChanges, OnInit { private async reloadData(): Promise { const isBusy = this.busyService.beginBusy(); try { - if (this.application.AuthenticationRoot.value) { - this.application.AuthenticationRootHelper.value = this.application.AuthenticationRoot.value; + if (this.application?.AuthenticationRoot.value) { + this.application.AuthenticationRootHelper.value = this.application?.AuthenticationRoot.value; } - this.hasOwner = this.application.UID_PersonHead.value != null && this.application.UID_PersonHead.value !== ''; + this.hasOwner = this.application?.UID_PersonHead.value != null && this.application?.UID_PersonHead.value !== ''; await this.updateShops(); - const accountInfo = await this.accountsProvider.getFirstAndCount(this.application.UID_AOBApplication.value); + const accountInfo = !!this.application + ? await this.accountsProvider.getFirstAndCount(this.application.UID_AOBApplication.value) + : { count: 0, first: undefined }; + this.accountsInformation = { ...this.accountsInformation, ...{ count: accountInfo.count, first: accountInfo.first }, ...{ count: accountInfo.count, first: accountInfo.first }, }; - const applicationEntitlements = await this.entitlementsProvider.getEntitlementsForApplication(this.application); - if (applicationEntitlements) { + const applicationEntitlements = !!this.application + ? await this.entitlementsProvider.getEntitlementsForApplication(this.application) + : undefined; + if (!!applicationEntitlements) { const entitlementFilter = new EntitlementFilter(); this.hasPublishedEntitlements = applicationEntitlements.Data.filter(entitlementFilter.published).length > 0; this.hasAssignedEntitlements = applicationEntitlements.Data.length > 0; const publishedAndWillBePublished = applicationEntitlements.Data.filter( - (elem) => entitlementFilter.published(elem) || entitlementFilter.willPublish(elem) + (elem) => entitlementFilter.published(elem) || entitlementFilter.willPublish(elem), ); this.canBeDeleted = (await this.permissions.isAobApplicationAdmin()) && publishedAndWillBePublished.length === 0; } else { @@ -340,12 +352,15 @@ export class ApplicationDetailsComponent implements OnChanges, OnInit { } private async updateShops(): Promise { - const appInShop = await this.shopsProvider.getApplicationInShop(this.application.UID_AOBApplication.value); - if (appInShop == null) { - this.logger.error(this, 'TypedEntityCollectionData is undefined'); - return false; + const appInShop = !!this.application + ? await this.shopsProvider.getApplicationInShop(this.application.UID_AOBApplication.value) + : undefined; + if (!!appInShop) { + this.assignedShops = appInShop.Data; + return true; } - this.assignedShops = appInShop.Data; - return true; + this.logger.error(this, 'TypedEntityCollectionData is undefined'); + this.assignedShops = []; + return false; } } diff --git a/imxweb/projects/aob/src/lib/applications/application-hyperview/application-hyperview.component.html b/imxweb/projects/aob/src/lib/applications/application-hyperview/application-hyperview.component.html deleted file mode 100644 index 8ce8b016d..000000000 --- a/imxweb/projects/aob/src/lib/applications/application-hyperview/application-hyperview.component.html +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/imxweb/projects/aob/src/lib/applications/application-hyperview/application-hyperview.component.scss b/imxweb/projects/aob/src/lib/applications/application-hyperview/application-hyperview.component.scss deleted file mode 100644 index f19569117..000000000 --- a/imxweb/projects/aob/src/lib/applications/application-hyperview/application-hyperview.component.scss +++ /dev/null @@ -1,23 +0,0 @@ -@import '@elemental-ui/core/src/styles/_eui_palette.scss'; -:host { - background-color: transparent; - display: flex; - flex-direction: column; - - imx-busy-service { - justify-self: center; - align-self: center; - } -} - -.eui-dark-theme { - :host { - background-color: transparent; - } -} - -.eui-contrast-theme { - :host { - background-color: $color-gray-100; - } -} diff --git a/imxweb/projects/aob/src/lib/applications/application-hyperview/application-hyperview.component.ts b/imxweb/projects/aob/src/lib/applications/application-hyperview/application-hyperview.component.ts deleted file mode 100644 index bc41dcd87..000000000 --- a/imxweb/projects/aob/src/lib/applications/application-hyperview/application-hyperview.component.ts +++ /dev/null @@ -1,65 +0,0 @@ -/* - * ONE IDENTITY LLC. PROPRIETARY INFORMATION - * - * This software is confidential. One Identity, LLC. or one of its affiliates or - * subsidiaries, has supplied this software to you under terms of a - * license agreement, nondisclosure agreement or both. - * - * You may not copy, disclose, or use this software except in accordance with - * those terms. - * - * - * Copyright 2023 One Identity LLC. - * ALL RIGHTS RESERVED. - * - * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR - * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, OR - * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE - * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE - * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING - * THIS SOFTWARE OR ITS DERIVATIVES. - * - */ - -import { Component, Input, OnChanges } from '@angular/core'; - - -import { BusyService, ClassloggerService } from 'qbm'; -import { ShapeData } from 'imx-api-qer'; -import { ApplicationHyperviewService } from './application-hyperview.service'; - -@Component({ - selector: 'imx-application-hyperview', - templateUrl: './application-hyperview.component.html', - styleUrls: ['./application-hyperview.component.scss'], -}) -export class ApplicationHyperviewComponent implements OnChanges { - public shapes: ShapeData[]; - - @Input() public uidApplication: string; - - public busyService = new BusyService(); - public isLoading = false; - - constructor(private classlogger: ClassloggerService, private hyperviewprovider: ApplicationHyperviewService) { - this.busyService.busyStateChanged.subscribe((elem) => (this.isLoading = elem)); - } - - public async ngOnChanges(): Promise { - const isBusy = this.busyService.beginBusy(); - try { - this.shapes = await this.hyperviewprovider.get(this.uidApplication); - if (this.shapes) { - this.classlogger.debug(this, 'hyperview loaded'); - this.classlogger.trace(this, this.shapes); - } else { - this.classlogger.error(this, 'ShapeData[] is undefined'); - } - } finally { - isBusy.endBusy(); - } - } -} diff --git a/imxweb/projects/aob/src/lib/applications/application-hyperview/application-hyperview.module.ts b/imxweb/projects/aob/src/lib/applications/application-hyperview/application-hyperview.module.ts deleted file mode 100644 index 6f6502844..000000000 --- a/imxweb/projects/aob/src/lib/applications/application-hyperview/application-hyperview.module.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* - * ONE IDENTITY LLC. PROPRIETARY INFORMATION - * - * This software is confidential. One Identity, LLC. or one of its affiliates or - * subsidiaries, has supplied this software to you under terms of a - * license agreement, nondisclosure agreement or both. - * - * You may not copy, disclose, or use this software except in accordance with - * those terms. - * - * - * Copyright 2023 One Identity LLC. - * ALL RIGHTS RESERVED. - * - * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR - * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, OR - * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE - * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE - * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING - * THIS SOFTWARE OR ITS DERIVATIVES. - * - */ - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { TranslateModule } from '@ngx-translate/core'; -import { MatButtonModule } from '@angular/material/button'; -import { MatDialogModule } from '@angular/material/dialog'; - -import { ApplicationHyperviewComponent } from './application-hyperview.component'; -import { HyperViewModule, BusyIndicatorModule } from 'qbm'; -import { ApplicationHyperviewService} from './application-hyperview.service'; -import { EuiCoreModule } from '@elemental-ui/core'; - -@NgModule({ - declarations: [ApplicationHyperviewComponent], - imports: [CommonModule, HyperViewModule, TranslateModule, MatDialogModule, MatButtonModule, EuiCoreModule, BusyIndicatorModule], - providers: [ApplicationHyperviewService], - exports: [ApplicationHyperviewComponent], -}) -export class ApplicationHyperviewModule {} diff --git a/imxweb/projects/aob/src/lib/applications/application-image-select/application-image-select.component.html b/imxweb/projects/aob/src/lib/applications/application-image-select/application-image-select.component.html index 9dbabd3d1..1d10ae9f4 100644 --- a/imxweb/projects/aob/src/lib/applications/application-image-select/application-image-select.component.html +++ b/imxweb/projects/aob/src/lib/applications/application-image-select/application-image-select.component.html @@ -1,6 +1,2 @@ - + diff --git a/imxweb/projects/aob/src/lib/applications/application-image-select/application-image-select.component.scss b/imxweb/projects/aob/src/lib/applications/application-image-select/application-image-select.component.scss index 3364f1f4f..c9e89c84d 100644 --- a/imxweb/projects/aob/src/lib/applications/application-image-select/application-image-select.component.scss +++ b/imxweb/projects/aob/src/lib/applications/application-image-select/application-image-select.component.scss @@ -1 +1 @@ -// styles \ No newline at end of file +// styles diff --git a/imxweb/projects/aob/src/lib/applications/application-image-select/application-image-select.component.ts b/imxweb/projects/aob/src/lib/applications/application-image-select/application-image-select.component.ts index fee0c8d96..94d09ef54 100644 --- a/imxweb/projects/aob/src/lib/applications/application-image-select/application-image-select.component.ts +++ b/imxweb/projects/aob/src/lib/applications/application-image-select/application-image-select.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,14 +28,14 @@ import { AfterViewInit, Component, EventEmitter, Input, OnChanges, Output, Simpl import { UntypedFormControl } from '@angular/forms'; import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; -import { IEntityColumn } from 'imx-qbm-dbts'; +import { IEntityColumn } from '@imx-modules/imx-qbm-dbts'; import { Base64ImageService, EntityColumnContainer } from 'qbm'; import { ImageSelectorDialogComponent } from './image-selector-dialog/image-selector-dialog.component'; @Component({ selector: 'imx-application-image-select', templateUrl: './application-image-select.component.html', - styleUrls: ['./application-image-select.component.scss'] + styleUrls: ['./application-image-select.component.scss'], }) export class ApplicationImageSelectComponent implements OnChanges, AfterViewInit { public readonly control = new UntypedFormControl(); @@ -47,32 +47,37 @@ export class ApplicationImageSelectComponent implements OnChanges, AfterViewInit @Output() public controlCreated = new EventEmitter(); private readonly icons = { - // tslint:disable-next-line:max-line-length - application: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAlCAYAAAAA7LqSAAAAAXNSR0IArs4c6QAAAeJJREFUWAntWbFKA0EQde/CYSGk0SqIiEhEVNBW/YLkD4SQIsc1/oCVtZWlJLlK0C/w6ivE1kIiIiJcKgtttNKId74Jd7JcYrMwa052YZm5eTs782Z2mz0xhRGGYSmKohnSizaazeabECIWnU5nD8kfJ0kyVzQSlC9I9DFdCwSOdJNA4HfkcD+ucMC+YO+Nw1IbYXGGI/cFzEMLhkpm1CSfEHje87wVy7L25ZhEEFgV2DrsdRkj3bbtHcLgt5kSHi6BT4WIaB1I4ALJvFBQJHQK8VNd6JfAHgmDDLD2mfR0PLRarSvSXde9gbhO7UOhnQiqV2+327MUPY7jBoScwy6wJcIga1gr39tl3/e3Cet2uxsQW6RnQ8AhyT50yfQI9RGvmo9JRwYE7mBfG4MBSm5hX8WUCxCV8ot1fCOZacQZIUGxgdkQIyRSTPyG5YmcYOEBORVgnCHHWpZnnsgHLtlrBk6yxJX4lPOTz5lsL5xuiExay0xHTEeYKmCOFlNhlbc1HVEuHZOj6QhTYZW3NR1RLh2To+kIU2GVtzUdUS4dk6PpCFNhlbc1HVEuHZPjv+nInzyZMjQloo7Ir+EMMbRsGRORQEsoxiB4+A5KjuM0BoMBPe8vMsZi2xr/WHrlcvn8Gxm0iGtOIek+AAAAAElFTkSuQmCC', - // tslint:disable-next-line:max-line-length + // eslint-disable-next-line max-len + application: + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAlCAYAAAAA7LqSAAAAAXNSR0IArs4c6QAAAeJJREFUWAntWbFKA0EQde/CYSGk0SqIiEhEVNBW/YLkD4SQIsc1/oCVtZWlJLlK0C/w6ivE1kIiIiJcKgtttNKId74Jd7JcYrMwa052YZm5eTs782Z2mz0xhRGGYSmKohnSizaazeabECIWnU5nD8kfJ0kyVzQSlC9I9DFdCwSOdJNA4HfkcD+ucMC+YO+Nw1IbYXGGI/cFzEMLhkpm1CSfEHje87wVy7L25ZhEEFgV2DrsdRkj3bbtHcLgt5kSHi6BT4WIaB1I4ALJvFBQJHQK8VNd6JfAHgmDDLD2mfR0PLRarSvSXde9gbhO7UOhnQiqV2+327MUPY7jBoScwy6wJcIga1gr39tl3/e3Cet2uxsQW6RnQ8AhyT50yfQI9RGvmo9JRwYE7mBfG4MBSm5hX8WUCxCV8ot1fCOZacQZIUGxgdkQIyRSTPyG5YmcYOEBORVgnCHHWpZnnsgHLtlrBk6yxJX4lPOTz5lsL5xuiExay0xHTEeYKmCOFlNhlbc1HVEuHZOj6QhTYZW3NR1RLh2To+kIU2GVtzUdUS4dk6PpCFNhlbc1HVEuHZPjv+nInzyZMjQloo7Ir+EMMbRsGRORQEsoxiB4+A5KjuM0BoMBPe8vMsZi2xr/WHrlcvn8Gxm0iGtOIek+AAAAAElFTkSuQmCC', + // eslint-disable-next-line max-len box: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAABqklEQVRoQ+1YPUvDQBh+riSiBJRubo7FWXD1B7h3L4gHyWQGF62DH4tDnBo4Ebp39we4Cs7i6OZWFIJiQk4OtFiqJL3cBYnvrXnf573ng9wlDA1ZrCE8QET+mpPkCDliSQGKliVhtWHJEW3pLDU20xEhxApjbCvP83VLwhmBbbVa91LKG8758xfgxJHRaLQwHo9PAOwbmWYf5Lzdbve73e67GjUhEkXRkud5FwC4/T0YmSCSJNkLw/CViBjRszoIOVJdQ7MI5IhZPauj/ezIcDhcTNP0QEq5WX2GfQTG2K3rume9Xu9t6vVrf7TdCc28a9nVzC76r44IIfoAju2Onxv9iHOu7oMzi4jMraWZhloceQSwm6bpXZk9u667AeASwFqZ+s+aWohMHVBFm9P8bKiFyHWWZTtBEDwVkVDPB4PBquM4VwC2y9TX6Yia9QBARazMUpHqlCn8VlOLI3PuSauciNCBqBWc4iaKFkWrOCVaFRQtipZWcIqbKFoUreKUaFX842jFcdxxHOdUSrmspZ3hJsbYS5Zlh77vq0+FmUX/tQwLXhmuMY58ANUk7zNkl1kaAAAAAElFTkSuQmCC', - // tslint:disable-next-line:max-line-length - openfolder: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAC80lEQVRoQ+2Zz2sTQRTHv28zadUiklp602tP4lH/AoUePElErYdQs1uSWmi9qNBa6UUKpoptdJtDD9KDVUEPRdSbJ68qiKIX9VoTtBRt2cyTDSQQJbNhmgnTkr0F9v34zHf2fWc3hF1y0S7hQAfENiU7inQUMbQCna1laGG109YpsrCwcEgIcZqZu3QzEtFWEASPs9nsd90cOnE1kJWVla5SqXQTwLhOon9i5hKJxJVkMrnVglxNpaiB5HK5vT09PXMAvKYi1Tf5Gxsb4xMTE79bkKupFKZAngK4J6U0oogQoiylfOd53s8qpSmQplZxmzfNJhKJyer23ckgddtXBfKVmR8BWNvmypkK/9bb2/skUhEiGnRd97mpLlqdt5Ei7wEkPc/72OqCpvI1AnlbLpfPZDKZT2Hh6elpp7+//4CU0jHViG7eeDweuK77KxKEmalQKIwx823dYobj1gFcigTxfX8fgDsALhpuSDs9M+cjQebn5w8KIZaJ6KR2JfOBU5Eg+Xx+IBaLPQRw1Hw/ehWIaCgSpFAoHJdSvgSwX6+M+ShmPhEJsri4eIqZn5lvR7vCuuM40SC+74fH+px2GfOBFatQKtLX1/elWCzOENFV8/3oVWDmF0EQnFeCxGKx8C3P6tEL4IEQIqsEkVKu2T56Qw/p7u6+rAQJxbZ99AKY8jxvRgkSj8cTto/e0ENc111WggghBiwfvQg9ZGRk5FXUwz5o+eiteEg6nX7TEISZzwEYsnn0Aqi9bjQEkVKmHMfJWH7qrXjI6OjoDxVIlogmLT/1VjxkeHh4XbW1rhPRDZtPvVUPSaVSf1Qgd4ko/PJo7am36iGh3zUEAbAK4JreCag9UVUPUYIQ0QdmPtuelvSqVD1EBfIZQBHAMb0SbYmqeYgKpC2dbLNI3Ser2jOytLS0Z3Nz8xYRhd6xE67Vcrl8IZPJlOoUCX/4vn8EwBiAw5aTFInofjqdfk1E/B+I5c0r2+v8q2ubeh1FOooYWoG/nVypz2zv/LMAAAAASUVORK5CYII=', - // tslint:disable-next-line:max-line-length - server: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAACM0lEQVRoQ+2ZMWvbUBDH717jOMWQYA8eswfSvdDQD1ACHQJa4sWLJAcSqAvdWkS7FeJCCrakxYuzCDIEQj5AaSB7Ct0zerBJwLSu23elpTG1ooCe7GfLL8+bhd7d/f7/QzpOCIr8UBEO0CBpc1I7kmpHPM97BAB7ALCatkJD9VwCwIFlWRc314et1Ww2l/r9/j4i7qQc4m95RFTPZrMvy+Xy9z//hyC1Wu1hLpf7AADWPIAAgNfr9V5Uq9VvIiA7nPOvYUDG2BoA1GcELg7COd+oVCpn4YIbjcYTxthnDTKeAuKOAIAarTWecNJOJ3JEWjVjBI4GCYJgsdPpbM3By/CG/bJQKBwZhvFj5PE7hjKpOKrm0Og4DisWiyucc5YKme8ogjHG2+32leM4/NasFQTBg263WwWA92mG+K+2V/l8vmYYxi+RESWNbOKPXyI6R8TrMA0RLSPi4xlRioPoWUuuVffYEbnCJo4u7kjiVHIPKg5CROj7/vJgMFiQK+RkomcymZ+maV4jIumhcTKaTjbKyPSrF3STFTdWNL2g0wu6WI2S6CbxF6Iy069e0CXqmNiHxFsrdujp3hhvQccYe0pEzyJqaxHRF5k1I+I6AJTCORDxlHP+6d/1eAs6z/NeA8DbiGAl0zQPZYL4vr9NRK2IHG8sy3oXlfvOBZ0yIK7r7iLiZsTmpG7b9rFMR1zXfR71LZOITmzb/ijkiMxCZcRWc/crQ6lpxdSOTEvpuHmUceQ3am+oQn9k2zIAAAAASUVORK5CYII=', - // tslint:disable-next-line:max-line-length - hdd: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAETklEQVRoQ+2ZTWhjVRTH/+e91kZSnSaLSgdk3MksdDcKulDQcasDQ1BnHJAmvYkfwWZRdKD6tDhKF4lUSt5LUgaGGUfCDKPoShwYEcWPhagL6U4XnWAXDWObmkjfO3JLMoSQvps2uamG3uW795x7fvece+659xEGpNGAcOAA5L/myQOP/C89UigUwq7rvgbg4T4DfG+a5gfRaHRNNW9HoeU4zjSAtEqZpv6UECKj0t0pyCyAd1TKNPW/KYSYU+nWBfIVgBsAfvQ8ryKNMAwjCOAYgMcBPKYyrKm/7yBrzJxh5vPxePwmEXE7Y5mZbNs+TEQvEpEM2bACqn8gRHTZdd2ziUTi912sNLLZ7H2maZ5j5ud85PoGYlUqlflUKvX3biAaY9Pp9J3BYHAGgLWDvH4QIjpbrVbTyWSytheIhszCwsJIIBBIMfO5Nnr0ghDRhWq1+moymfyrG4gmmLsDgcCHzHymRZ9WkN8Mw3g2Fov90guIho58Pv+g53kfAzjar6w1FwqF3o5EIm4vQYrFolkul98CIM+tRtPmkZLnec8kEokfegnR0JXNZh8yDOMTABP1b3pAiOgKM0eFELdUIJZlGePj44fkuNXV1VuWZXkqGcdxDhFRgZlPagVh5vfC4fCsKqwKhcKRra2tDBGdkAYx87WhoaHpaDT6hx+MDK+1tbU5InpDKwiAuBDC8TNGptORkZF5AMmWcQu1Wm1Gla4dxxEAbK0gRHR6amrqkh+I9Ibruh8BeKRl3LemaT6v8koulzvFzBf3HWRpaemw67qXmFkWiLcbEd0wTfPU5OTkTb+F6AsIAOX9oJ5GUwBkeDW3mVAolFbtr5b7j56sBSATCoVej0Qi//itqqyhRkdHp5hZVrggoszGxkZOVZMVi8U7yuXy+wC25QBoA/kSwBkhREmVSvfS7ziOPD8uAHhSN8g6M5+Ix+PX92KoSsa27SeI6BqAu3SDSP1OpVKZVoWJyujW/npJL+/nMv02mrbQkhOsAzgphPhit8b6jXcc5ykAV5q8oXWPbNtCRN+4rnt6p1uhZVlDExMTwVKptN5JaZLP5496nne+zZOTVo80YD4lIhGLxf5sXul6iMjU+wqAZXm4maZ5dWVlZbkdVD6fv4eZHWZ+uo3H9IPISdvVUIuLi/cODw9fZuZHWwz7FcBFwzA+a0C11mT7BlKfeFm+e9VqtauyjvI5EJvtlFDy2eg4gPt99k5/PNJsADN/bRhGplqtXh8bG3M3NzfnieilLhOCVhDpAcfzvJ8MwzgC4OX649ttm5n5OyKSj3MPABjvAkYbyM8AXhBCyNDYborN2gXDtqgeEGaeFUK82/qSaNv2cSLq6blSXwE9IDvdR3K53DFm/rzLMGrnPT0gA+MRAIOxR5rODd+s1e0Ob5LXE1o9NLBTVT0FGYxfbwPzM7TTGNjPcR39Q9xPAzud+wCk05Xq17gDj/RrpTud51+Swm1RKPvqDgAAAABJRU5ErkJggg==' + // eslint-disable-next-line max-len + openfolder: + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAC80lEQVRoQ+2Zz2sTQRTHv28zadUiklp602tP4lH/AoUePElErYdQs1uSWmi9qNBa6UUKpoptdJtDD9KDVUEPRdSbJ68qiKIX9VoTtBRt2cyTDSQQJbNhmgnTkr0F9v34zHf2fWc3hF1y0S7hQAfENiU7inQUMbQCna1laGG109YpsrCwcEgIcZqZu3QzEtFWEASPs9nsd90cOnE1kJWVla5SqXQTwLhOon9i5hKJxJVkMrnVglxNpaiB5HK5vT09PXMAvKYi1Tf5Gxsb4xMTE79bkKupFKZAngK4J6U0oogQoiylfOd53s8qpSmQplZxmzfNJhKJyer23ckgddtXBfKVmR8BWNvmypkK/9bb2/skUhEiGnRd97mpLlqdt5Ei7wEkPc/72OqCpvI1AnlbLpfPZDKZT2Hh6elpp7+//4CU0jHViG7eeDweuK77KxKEmalQKIwx823dYobj1gFcigTxfX8fgDsALhpuSDs9M+cjQebn5w8KIZaJ6KR2JfOBU5Eg+Xx+IBaLPQRw1Hw/ehWIaCgSpFAoHJdSvgSwX6+M+ShmPhEJsri4eIqZn5lvR7vCuuM40SC+74fH+px2GfOBFatQKtLX1/elWCzOENFV8/3oVWDmF0EQnFeCxGKx8C3P6tEL4IEQIqsEkVKu2T56Qw/p7u6+rAQJxbZ99AKY8jxvRgkSj8cTto/e0ENc111WggghBiwfvQg9ZGRk5FXUwz5o+eiteEg6nX7TEISZzwEYsnn0Aqi9bjQEkVKmHMfJWH7qrXjI6OjoDxVIlogmLT/1VjxkeHh4XbW1rhPRDZtPvVUPSaVSf1Qgd4ko/PJo7am36iGh3zUEAbAK4JreCag9UVUPUYIQ0QdmPtuelvSqVD1EBfIZQBHAMb0SbYmqeYgKpC2dbLNI3Ser2jOytLS0Z3Nz8xYRhd6xE67Vcrl8IZPJlOoUCX/4vn8EwBiAw5aTFInofjqdfk1E/B+I5c0r2+v8q2ubeh1FOooYWoG/nVypz2zv/LMAAAAASUVORK5CYII=', + // eslint-disable-next-line max-len + server: + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAACM0lEQVRoQ+2ZMWvbUBDH717jOMWQYA8eswfSvdDQD1ACHQJa4sWLJAcSqAvdWkS7FeJCCrakxYuzCDIEQj5AaSB7Ct0zerBJwLSu23elpTG1ooCe7GfLL8+bhd7d/f7/QzpOCIr8UBEO0CBpc1I7kmpHPM97BAB7ALCatkJD9VwCwIFlWRc314et1Ww2l/r9/j4i7qQc4m95RFTPZrMvy+Xy9z//hyC1Wu1hLpf7AADWPIAAgNfr9V5Uq9VvIiA7nPOvYUDG2BoA1GcELg7COd+oVCpn4YIbjcYTxthnDTKeAuKOAIAarTWecNJOJ3JEWjVjBI4GCYJgsdPpbM3By/CG/bJQKBwZhvFj5PE7hjKpOKrm0Og4DisWiyucc5YKme8ogjHG2+32leM4/NasFQTBg263WwWA92mG+K+2V/l8vmYYxi+RESWNbOKPXyI6R8TrMA0RLSPi4xlRioPoWUuuVffYEbnCJo4u7kjiVHIPKg5CROj7/vJgMFiQK+RkomcymZ+maV4jIumhcTKaTjbKyPSrF3STFTdWNL2g0wu6WI2S6CbxF6Iy069e0CXqmNiHxFsrdujp3hhvQccYe0pEzyJqaxHRF5k1I+I6AJTCORDxlHP+6d/1eAs6z/NeA8DbiGAl0zQPZYL4vr9NRK2IHG8sy3oXlfvOBZ0yIK7r7iLiZsTmpG7b9rFMR1zXfR71LZOITmzb/ijkiMxCZcRWc/crQ6lpxdSOTEvpuHmUceQ3am+oQn9k2zIAAAAASUVORK5CYII=', + // eslint-disable-next-line max-len + hdd: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAETklEQVRoQ+2ZTWhjVRTH/+e91kZSnSaLSgdk3MksdDcKulDQcasDQ1BnHJAmvYkfwWZRdKD6tDhKF4lUSt5LUgaGGUfCDKPoShwYEcWPhagL6U4XnWAXDWObmkjfO3JLMoSQvps2uamG3uW795x7fvece+659xEGpNGAcOAA5L/myQOP/C89UigUwq7rvgbg4T4DfG+a5gfRaHRNNW9HoeU4zjSAtEqZpv6UECKj0t0pyCyAd1TKNPW/KYSYU+nWBfIVgBsAfvQ8ryKNMAwjCOAYgMcBPKYyrKm/7yBrzJxh5vPxePwmEXE7Y5mZbNs+TEQvEpEM2bACqn8gRHTZdd2ziUTi912sNLLZ7H2maZ5j5ud85PoGYlUqlflUKvX3biAaY9Pp9J3BYHAGgLWDvH4QIjpbrVbTyWSytheIhszCwsJIIBBIMfO5Nnr0ghDRhWq1+moymfyrG4gmmLsDgcCHzHymRZ9WkN8Mw3g2Fov90guIho58Pv+g53kfAzjar6w1FwqF3o5EIm4vQYrFolkul98CIM+tRtPmkZLnec8kEokfegnR0JXNZh8yDOMTABP1b3pAiOgKM0eFELdUIJZlGePj44fkuNXV1VuWZXkqGcdxDhFRgZlPagVh5vfC4fCsKqwKhcKRra2tDBGdkAYx87WhoaHpaDT6hx+MDK+1tbU5InpDKwiAuBDC8TNGptORkZF5AMmWcQu1Wm1Gla4dxxEAbK0gRHR6amrqkh+I9Ibruh8BeKRl3LemaT6v8koulzvFzBf3HWRpaemw67qXmFkWiLcbEd0wTfPU5OTkTb+F6AsIAOX9oJ5GUwBkeDW3mVAolFbtr5b7j56sBSATCoVej0Qi//itqqyhRkdHp5hZVrggoszGxkZOVZMVi8U7yuXy+wC25QBoA/kSwBkhREmVSvfS7ziOPD8uAHhSN8g6M5+Ix+PX92KoSsa27SeI6BqAu3SDSP1OpVKZVoWJyujW/npJL+/nMv02mrbQkhOsAzgphPhit8b6jXcc5ykAV5q8oXWPbNtCRN+4rnt6p1uhZVlDExMTwVKptN5JaZLP5496nne+zZOTVo80YD4lIhGLxf5sXul6iMjU+wqAZXm4maZ5dWVlZbkdVD6fv4eZHWZ+uo3H9IPISdvVUIuLi/cODw9fZuZHWwz7FcBFwzA+a0C11mT7BlKfeFm+e9VqtauyjvI5EJvtlFDy2eg4gPt99k5/PNJsADN/bRhGplqtXh8bG3M3NzfnieilLhOCVhDpAcfzvJ8MwzgC4OX649ttm5n5OyKSj3MPABjvAkYbyM8AXhBCyNDYborN2gXDtqgeEGaeFUK82/qSaNv2cSLq6blSXwE9IDvdR3K53DFm/rzLMGrnPT0gA+MRAIOxR5rODd+s1e0Ob5LXE1o9NLBTVT0FGYxfbwPzM7TTGNjPcR39Q9xPAzud+wCk05Xq17gDj/RrpTud51+Swm1RKPvqDgAAAABJRU5ErkJggg==', }; private readonly defaultIcon = this.icons.application; - constructor(private readonly dialog: MatDialog, private readonly imageProvider: Base64ImageService) { } + constructor( + private readonly dialog: MatDialog, + private readonly imageProvider: Base64ImageService, + ) {} public ngOnChanges(changes: SimpleChanges): void { if (changes.column && this.column) { - const cdr = new class { + const cdr = new (class { public get hint(): string { - return !this.column.GetValue()?.length ? - '#LDS#If you do not select an icon, the default icon will be used.' : ''; + return !this.column.GetValue()?.length ? '#LDS#If you do not select an icon, the default icon will be used.' : ''; } constructor(public readonly column: IEntityColumn) {} public readonly isReadOnly = () => !this.column.GetMetadata().CanEdit(); - }(this.column); + })(this.column); this.columnContainer.init(cdr); @@ -91,8 +96,8 @@ export class ApplicationImageSelectComponent implements OnChanges, AfterViewInit title: '#LDS#Edit Application Icon', icons: this.icons, imageUrl: this.control.value, - defaultIcon: 'application' - } + defaultIcon: 'application', + }, }; const result = await this.dialog.open(ImageSelectorDialogComponent, imageSelectorDialogParams).afterClosed().toPromise(); diff --git a/imxweb/projects/aob/src/lib/applications/application-image-select/image-selector-dialog/image-selector-dialog-parameter.interface.ts b/imxweb/projects/aob/src/lib/applications/application-image-select/image-selector-dialog/image-selector-dialog-parameter.interface.ts index 1129f4419..62dd5d540 100644 --- a/imxweb/projects/aob/src/lib/applications/application-image-select/image-selector-dialog/image-selector-dialog-parameter.interface.ts +++ b/imxweb/projects/aob/src/lib/applications/application-image-select/image-selector-dialog/image-selector-dialog-parameter.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -36,7 +36,7 @@ export interface ImageSelectorDialogParameter { /** * the predefined list of icons */ - icons: { [name: string]: string; }; + icons: { [name: string]: string }; /** * the default icon to select diff --git a/imxweb/projects/aob/src/lib/applications/application-image-select/image-selector-dialog/image-selector-dialog.component.html b/imxweb/projects/aob/src/lib/applications/application-image-select/image-selector-dialog/image-selector-dialog.component.html index 8c2b44ca8..8b899c6f2 100644 --- a/imxweb/projects/aob/src/lib/applications/application-image-select/image-selector-dialog/image-selector-dialog.component.html +++ b/imxweb/projects/aob/src/lib/applications/application-image-select/image-selector-dialog/image-selector-dialog.component.html @@ -1,35 +1,52 @@
-

{{ title | translate }}

+

{{ title | translate }}

- {{'#LDS#Select an Icon' | translate}} -
- + {{ '#LDS#Select an Icon' | translate }} +
+
- {{'#LDS#WC_Or' | translate}} - + {{ '#LDS#WC_Or' | translate }} + - - + + - - -
{{'#LDS#Please select an image in PNG format.' | translate}}
+
+ +
+ +
{{ '#LDS#Please select an image in PNG format.' | translate }}
-
- -
diff --git a/imxweb/projects/aob/src/lib/applications/application-image-select/image-selector-dialog/image-selector-dialog.component.scss b/imxweb/projects/aob/src/lib/applications/application-image-select/image-selector-dialog/image-selector-dialog.component.scss index c77f97766..7a0fc3b52 100644 --- a/imxweb/projects/aob/src/lib/applications/application-image-select/image-selector-dialog/image-selector-dialog.component.scss +++ b/imxweb/projects/aob/src/lib/applications/application-image-select/image-selector-dialog/image-selector-dialog.component.scss @@ -1,60 +1,15 @@ @import '@elemental-ui/core/src/styles/_palette.scss'; +@import 'base/mixins'; -.mat-dialog-title { - font-weight: 600; - font-size: 24px; -} - -.mat-dialog-content { - display: flex; - flex-direction: column; +.mat-mdc-dialog-content { + @include flex-column-container(); align-items: center; - >span { + > span { font-size: 20px; } - - >.mat-raised-button { - background-color: $corbin-orange; - margin-bottom: 30px; - margin-top: 20px - } -} - -.mat-dialog-actions { - display: flex; - justify-content: flex-end; - - button { - margin-bottom: 10px; - margin-left: 10px; - } } .imx-hidden-element { display: none; } - -.imx-image-selector-list { - flex-direction: row; - display: flex; -} - -.imx-default-preview-image { - box-shadow: none; -} - -.eui-icon { - color: $black-9; - user-select: none; -} - -.mat-card { - box-shadow: none; - border: 3px solid transparent; - cursor: pointer; -} - -.mat-card.selected { - border: 3px solid $iris-blue; -} diff --git a/imxweb/projects/aob/src/lib/applications/application-image-select/image-selector-dialog/image-selector-dialog.component.ts b/imxweb/projects/aob/src/lib/applications/application-image-select/image-selector-dialog/image-selector-dialog.component.ts index e88f3b6a0..0ed9a7d53 100644 --- a/imxweb/projects/aob/src/lib/applications/application-image-select/image-selector-dialog/image-selector-dialog.component.ts +++ b/imxweb/projects/aob/src/lib/applications/application-image-select/image-selector-dialog/image-selector-dialog.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,12 +25,12 @@ */ import { Component, Inject, OnDestroy } from '@angular/core'; -import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { SafeUrl } from '@angular/platform-browser'; import { Subscription } from 'rxjs'; -import { ImageSelectorDialogParameter } from './image-selector-dialog-parameter.interface'; import { Base64ImageService, ClassloggerService, FileSelectorService } from 'qbm'; +import { ImageSelectorDialogParameter } from './image-selector-dialog-parameter.interface'; /** * A dialog to select an icon from a predefined list or upload an own one. @@ -38,10 +38,12 @@ import { Base64ImageService, ClassloggerService, FileSelectorService } from 'qbm @Component({ selector: 'imx-image-selector-dialog', templateUrl: './image-selector-dialog.component.html', - styleUrls: ['./image-selector-dialog.component.scss'] + styleUrls: ['./image-selector-dialog.component.scss'], }) export class ImageSelectorDialogComponent implements OnDestroy { - public get imageIsSelected(): boolean { return this.selectedIconName == null; } + public get imageIsSelected(): boolean { + return this.selectedIconName == null; + } public imageUrl: string; public fileFormatError = false; @@ -49,9 +51,9 @@ export class ImageSelectorDialogComponent implements OnDestroy { public readonly iconNames: string[] = []; public readonly title: string; - private selectedIconName: string; + private selectedIconName: string | undefined; - private readonly icons: { [name: string]: string; }; + private readonly icons: { [name: string]: string }; private readonly subscriptions: Subscription[] = []; constructor( @@ -59,19 +61,17 @@ export class ImageSelectorDialogComponent implements OnDestroy { public readonly imageHandler: Base64ImageService, private logger: ClassloggerService, @Inject(MAT_DIALOG_DATA) data: ImageSelectorDialogParameter, - private readonly fileSelector: FileSelectorService + private readonly fileSelector: FileSelectorService, ) { this.subscriptions.push( - this.fileSelector.fileFormatError.subscribe(() => this.fileFormatError = true), - this.fileSelector.fileSelected.subscribe(imageUrl => this.selectImage(imageUrl)) + this.fileSelector.fileFormatError.subscribe(() => (this.fileFormatError = true)), + this.fileSelector.fileSelected.subscribe((imageUrl) => this.selectImage(imageUrl)), ); this.title = data.title; this.icons = data.icons; - Object.keys(data.icons).forEach(iconName => - this.iconNames.push(iconName) - ); + Object.keys(data.icons).forEach((iconName) => this.iconNames.push(iconName)); if (data.imageUrl) { this.logger.debug(this, 'show and select actual assigned icon'); @@ -83,7 +83,7 @@ export class ImageSelectorDialogComponent implements OnDestroy { } public ngOnDestroy(): void { - this.subscriptions.forEach(s => s.unsubscribe()); + this.subscriptions.forEach((s) => s.unsubscribe()); } public onSave(): void { @@ -106,8 +106,11 @@ export class ImageSelectorDialogComponent implements OnDestroy { this.selectedIconName = name; } - public emitFiles(files: FileList): void { - this.fileSelector.emitFiles(files, 'image/png'); + public emitFiles(event: EventTarget | null): void { + const files: FileList | null = (event as HTMLInputElement)?.files; + if (files) { + this.fileSelector.emitFiles(files, 'image/png'); + } } public resetFileFormatErrorState(): void { diff --git a/imxweb/projects/aob/src/lib/applications/application-navigation/application-navigation.component.html b/imxweb/projects/aob/src/lib/applications/application-navigation/application-navigation.component.html index c93f3de85..13bf4970f 100644 --- a/imxweb/projects/aob/src/lib/applications/application-navigation/application-navigation.component.html +++ b/imxweb/projects/aob/src/lib/applications/application-navigation/application-navigation.component.html @@ -1,15 +1,26 @@ -
+
-

{{'#LDS#Applications' | translate}}

+

{{ '#LDS#Applications' | translate }}

-
- + {{ '#LDS#Only show applications with KPI issues' | translate }} - {{'#LDS#Applications' | translate}} [itemStatus]="status" [busyService]="busyService" (search)="onSearch($event)" - [alwaysVisible]="true"> + [alwaysVisible]="true" + > - + (selected)="onTileSelected($event)" + > - +
- diff --git a/imxweb/projects/aob/src/lib/applications/application-navigation/application-navigation.component.scss b/imxweb/projects/aob/src/lib/applications/application-navigation/application-navigation.component.scss index 23d277629..297eb4d82 100644 --- a/imxweb/projects/aob/src/lib/applications/application-navigation/application-navigation.component.scss +++ b/imxweb/projects/aob/src/lib/applications/application-navigation/application-navigation.component.scss @@ -8,12 +8,11 @@ $imx-component-width: 400px; height: inherit; max-width: $imx-component-width; - h1 { - font-size: 24px; + h2 { font-weight: 600; } - .mat-checkbox { + .mat-mdc-checkbox { margin-top: 10px; font-size: 12px; } @@ -22,11 +21,6 @@ $imx-component-width: 400px; flex-grow: 1; } - imx-data-source-paginator { - margin-top: 10px; - min-width: $imx-component-width; - } - .imx-main-container { display: flex; height: inherit; @@ -49,7 +43,6 @@ $imx-component-width: 400px; } .imx-icon-button { - line-height: 27px; background-color: $color-gray-0; margin-left: 15px; } @@ -66,10 +59,6 @@ $imx-component-width: 400px; } } - ::ng-deep .mat-badge-warn .mat-badge-content { - background-color: $color-gray-60; - } - ::ng-deep .badgeContainer { width: 355px; } @@ -78,10 +67,10 @@ $imx-component-width: 400px; margin-left: 0px; } - ::ng-deep .mat-card.imx-data-tile-container { + ::ng-deep .mat-mdc-card.imx-data-tile-container { padding: 35px 50px 0 25px; - .imx-data-tile-subtitle.mat-subheading-1 { + .imx-data-tile-subtitle.mat-body-2 { align-self: baseline; top: 10px; white-space: normal; @@ -92,7 +81,7 @@ $imx-component-width: 400px; line-height: 18px; } - .imx-data-tile-subtitle.mat-subheading-1:after { + .imx-data-tile-subtitle.mat-body-2:after { content: ''; text-align: right; position: absolute; @@ -105,14 +94,14 @@ $imx-component-width: 400px; // If line-clamp is supported, use it instead (Firefox 68+, Chrome 14+, Edge 17+) @supports (-webkit-line-clamp: 3) { - .imx-data-tile-subtitle.mat-subheading-1 { + .imx-data-tile-subtitle.mat-body-2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; height: auto; top: 0px; } - .imx-data-tile-subtitle.mat-subheading-1:after { + .imx-data-tile-subtitle.mat-body-2:after { display: none; } } @@ -125,12 +114,8 @@ $imx-component-width: 400px; background-color: $color-gray-90; } - ::ng-deep .mat-badge-warn .mat-badge-content { - background-color: $color-gray-40; - } - .imx-icon-button { - background-color: $color-gray-80; + background-color: $color-gray-70; } ::ng-deep .imx-selected-icon-container { @@ -145,10 +130,6 @@ $imx-component-width: 400px; background-color: $color-gray-90; } - ::ng-deep .mat-badge-warn .mat-badge-content { - background-color: $color-gray-40; - } - .imx-icon-button { background-color: $color-gray-80; } @@ -158,11 +139,3 @@ $imx-component-width: 400px; } } } - -.eui-dark-theme { - :host { - .mat-icon-button{ - background-color: $color-gray-70; - } - } -} diff --git a/imxweb/projects/aob/src/lib/applications/application-navigation/application-navigation.component.ts b/imxweb/projects/aob/src/lib/applications/application-navigation/application-navigation.component.ts index 0cbfa7985..fa321b593 100644 --- a/imxweb/projects/aob/src/lib/applications/application-navigation/application-navigation.component.ts +++ b/imxweb/projects/aob/src/lib/applications/application-navigation/application-navigation.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,18 +24,25 @@ * */ -import { Component, Output, EventEmitter, OnInit, ViewChild } from '@angular/core'; import { Overlay } from '@angular/cdk/overlay'; +import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Subject } from 'rxjs'; -import { PortalApplication, PortalApplicationNew } from 'imx-api-aob'; -import { CollectionLoadParameters, TypedEntityCollectionData, TypedEntity } from 'imx-qbm-dbts'; -import { BusyService, ClassloggerService, DataSourceToolbarSettings, DataTileBadge, DataTilesComponent, SettingsService } from 'qbm'; -import { ApplicationsService } from '../applications.service'; +import { PortalApplication, PortalApplicationNew } from '@imx-modules/imx-api-aob'; +import { CollectionLoadParameters, TypedEntity, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; +import { + BusyService, + ClassloggerService, + DataSourceItemStatus, + DataSourceToolbarSettings, + DataTileBadge, + DataTilesComponent, + SettingsService, +} from 'qbm'; import { UserModelService } from 'qer'; import { AobPermissionsService } from '../../permissions/aob-permissions.service'; - +import { ApplicationsService } from '../applications.service'; /** * This component shows a list of {@link PortalApplication[]|applications} each in an @@ -47,7 +54,7 @@ import { AobPermissionsService } from '../../permissions/aob-permissions.service styleUrls: ['./application-navigation.component.scss'], }) export class ApplicationNavigationComponent implements OnInit { - public dstSettings: DataSourceToolbarSettings; + public dstSettings: DataSourceToolbarSettings | undefined; public readonly entitySchema = PortalApplication.GetEntitySchema(); public selectable = true; public multiselect = false; @@ -64,7 +71,7 @@ export class ApplicationNavigationComponent implements OnInit { */ @Output() public readonly dataSourceChanged = new EventEmitter<{ keywords?: string; - dataSource: TypedEntityCollectionData; + dataSource?: TypedEntityCollectionData; }>(); /** @@ -75,9 +82,10 @@ export class ApplicationNavigationComponent implements OnInit { /** * a status that defines, how badges are calculated */ - public readonly status = { + public readonly status: DataSourceItemStatus = { getBadges: (application: PortalApplication | PortalApplicationNew): DataTileBadge[] => this.appService.getApplicationBadges(application), + enabled: (item: TypedEntity) => true, }; private navigationState: CollectionLoadParameters & { @@ -94,13 +102,11 @@ export class ApplicationNavigationComponent implements OnInit { private readonly route: ActivatedRoute, private readonly applicationsProvider: ApplicationsService, private readonly aobPermissionsService: AobPermissionsService, - public overlay: Overlay + public overlay: Overlay, ) {} - public async ngOnInit(): Promise { - - this.isAdmin = await this.aobPermissionsService.isAobApplicationAdmin(); + this.isAdmin = await this.aobPermissionsService.isAobApplicationAdmin(); this.applicationsProvider.applicationRefresh.subscribe((res) => { if (res) { @@ -114,10 +120,10 @@ export class ApplicationNavigationComponent implements OnInit { * Emits the applicationSelected event, if the selected tile changes * @param selection the {@link PortalApplication | application} that is selected */ - public async onSelectionChanged(selection: PortalApplication[]): Promise { + public async onSelectionChanged(selection: TypedEntity[]): Promise { const app = selection[0]; if (app) { - this.applicationSelected.emit(app.UID_AOBApplication.value); + this.applicationSelected.emit(app.GetEntity().GetColumn('UID_AOBApplication').GetValue()); } } /** @@ -156,9 +162,9 @@ export class ApplicationNavigationComponent implements OnInit { this.dataSourceChanged.emit({ keywords, dataSource }); this.route.queryParams.subscribe(async (params) => { - if (params.id) { - let app = dataSource.Data.find((x) => x.UID_AOBApplication.value == params.id); - this.selectedEntity = app; + const selectedEntity = dataSource?.Data.find((x) => x.UID_AOBApplication.value == params.id); + if (params.id && selectedEntity) { + this.selectedEntity = selectedEntity; } }); } finally { @@ -181,7 +187,7 @@ export class ApplicationNavigationComponent implements OnInit { this.navigationState.StartIndex = 0; if (keywords == null || keywords.length === 0) { - this.navigationState.search = null; + this.navigationState.search = ''; } else { this.navigationState.search = keywords; } diff --git a/imxweb/projects/aob/src/lib/applications/applications-routing.module.ts b/imxweb/projects/aob/src/lib/applications/applications-routing.module.ts index d2dfc6e76..fcceed600 100644 --- a/imxweb/projects/aob/src/lib/applications/applications-routing.module.ts +++ b/imxweb/projects/aob/src/lib/applications/applications-routing.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -42,27 +42,27 @@ const routes: Routes = [ path: 'navigation', component: ApplicationNavigationComponent, canActivate: [RouteGuardService], - resolve: [RouteGuardService] + resolve: [RouteGuardService], }, { path: 'detail', component: ApplicationDetailComponent, outlet: 'content', canActivate: [RouteGuardService], - resolve: [RouteGuardService] + resolve: [RouteGuardService], }, { path: '', redirectTo: 'navigation', - pathMatch: 'full' - } - ] + pathMatch: 'full', + }, + ], }, - { path: ':create:id', redirectTo: 'applications', pathMatch: 'full' } + { path: ':create:id', redirectTo: 'applications', pathMatch: 'full' }, ]; @NgModule({ imports: [RouterModule.forChild(routes)], - exports: [RouterModule] + exports: [RouterModule], }) -export class ApplicationsRoutingModule { } +export class ApplicationsRoutingModule {} diff --git a/imxweb/projects/aob/src/lib/applications/applications.component.html b/imxweb/projects/aob/src/lib/applications/applications.component.html index e5f971373..0b77541ab 100644 --- a/imxweb/projects/aob/src/lib/applications/applications.component.html +++ b/imxweb/projects/aob/src/lib/applications/applications.component.html @@ -2,5 +2,5 @@
- +
diff --git a/imxweb/projects/aob/src/lib/applications/applications.component.scss b/imxweb/projects/aob/src/lib/applications/applications.component.scss index a556393a6..574fcaba3 100644 --- a/imxweb/projects/aob/src/lib/applications/applications.component.scss +++ b/imxweb/projects/aob/src/lib/applications/applications.component.scss @@ -1,7 +1,6 @@ @import '@elemental-ui/core/src/styles/_palette.scss'; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; - :host { display: flex; margin: 0; @@ -20,19 +19,11 @@ overflow: hidden; } -.mat-raised-button { - justify-self: center; - margin-top: 30px; -} - .eui-dark-theme { :host { .imx-application-content { background-color: $color-gray-80; } - ::ng-deep .imx-application-content .mat-tab-body{ - background-color: $color-gray-80; - } } } @@ -41,8 +32,5 @@ .imx-application-content { background-color: $color-gray-90; } - ::ng-deep .imx-application-content .mat-tab-body{ - background-color: $color-gray-90; - } } } diff --git a/imxweb/projects/aob/src/lib/applications/applications.component.ts b/imxweb/projects/aob/src/lib/applications/applications.component.ts index 23874dd17..b4800df23 100644 --- a/imxweb/projects/aob/src/lib/applications/applications.component.ts +++ b/imxweb/projects/aob/src/lib/applications/applications.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,18 +25,18 @@ */ import { Component, OnDestroy, OnInit } from '@angular/core'; -import { Router, ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { Subject, Subscription } from 'rxjs'; import { EuiLoadingService } from '@elemental-ui/core'; -import { PortalApplication } from 'imx-api-aob'; +import { PortalApplication } from '@imx-modules/imx-api-aob'; +import { TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { ClassloggerService, SnackBarService } from 'qbm'; -import { TypedEntityCollectionData } from 'imx-qbm-dbts'; -import { ApplicationsService } from './applications.service'; -import { ApplicationNavigationComponent } from './application-navigation/application-navigation.component'; -import { ApplicationDetailComponent } from './application-detail.component'; import { ApplicationContent } from './application-content.interface'; +import { ApplicationDetailComponent } from './application-detail.component'; +import { ApplicationNavigationComponent } from './application-navigation/application-navigation.component'; +import { ApplicationsService } from './applications.service'; @Component({ selector: 'imx-applications', @@ -44,7 +44,7 @@ import { ApplicationContent } from './application-content.interface'; styleUrls: ['./applications.component.scss'], }) export class ApplicationsComponent implements OnDestroy, OnInit { - private selectedApplication: PortalApplication; + private selectedApplication: PortalApplication | undefined; private dataSource: TypedEntityCollectionData; private applicationNavigation: ApplicationNavigationComponent; private applicationContent: ApplicationContent; @@ -57,7 +57,7 @@ export class ApplicationsComponent implements OnDestroy, OnInit { private readonly busyService: EuiLoadingService, private readonly router: Router, private readonly route: ActivatedRoute, - private readonly snackbar: SnackBarService + private readonly snackbar: SnackBarService, ) { this.subscriptions.push( this.applicationsProvider.onApplicationCreated.subscribe(async (uidApplication: string) => { @@ -75,19 +75,23 @@ export class ApplicationsComponent implements OnDestroy, OnInit { this.applicationContent.application = this.selectedApplication; return this.redirectToAppDetails(this.selectedApplication.UID_AOBApplication.value); - }) + }), ); this.subscriptions.push( this.applicationsProvider.onApplicationDeleted.subscribe(async (uidApplication: string) => { - const index = this.dataSource.Data.findIndex(elem => elem.UID_AOBApplication.value === uidApplication); + const index = this.dataSource.Data.findIndex((elem) => elem.UID_AOBApplication.value === uidApplication); if (index < 0) { - return this.redirectToAppDetails(null); + return this.redirectToAppDetails(''); } const removed = this.dataSource.Data.splice(index, 1); - this.applicationContent.application = null; - this.snackbar.open({ key: '#LDS#The application "{0}" has been successfully deleted.', parameters: [removed[0].GetEntity().GetDisplay()] }); - return this.redirectToAppDetails(null); - })); + this.applicationContent.application = undefined; + this.snackbar.open({ + key: '#LDS#The application "{0}" has been successfully deleted.', + parameters: [removed[0].GetEntity().GetDisplay()], + }); + return this.redirectToAppDetails(''); + }), + ); } public ngOnInit(): void { @@ -102,8 +106,9 @@ export class ApplicationsComponent implements OnDestroy, OnInit { if (params.id) { this.selectedApplication = await this.applicationsProvider.reload(params.id); } - - this.applicationContent.application = this.selectedApplication; + if (this.selectedApplication) { + this.applicationContent.application = this.selectedApplication; + } if (!this.selectedApplication) { this.applicationNavigation.clearSelection(); @@ -126,18 +131,17 @@ export class ApplicationsComponent implements OnDestroy, OnInit { this.applicationNavigation.loadingSubject = this.loadingSubject; this.subscriptions.push( - this.applicationNavigation.applicationSelected.subscribe((uidApplication: string) => - this.redirectToAppDetails(uidApplication) - ) + this.applicationNavigation.applicationSelected.subscribe((uidApplication: string) => this.redirectToAppDetails(uidApplication)), ); this.subscriptions.push( - this.applicationNavigation.dataSourceChanged.subscribe((changeSet: - { keywords: string; dataSource: TypedEntityCollectionData }) => { - this.dataSource = changeSet?.dataSource; - this.applicationContent.totalCount = this.dataSource?.totalCount ?? 0; - this.applicationContent.keywords = changeSet?.keywords; - }) + this.applicationNavigation.dataSourceChanged.subscribe( + (changeSet: { keywords: string; dataSource: TypedEntityCollectionData }) => { + this.dataSource = changeSet?.dataSource; + this.applicationContent.totalCount = this.dataSource?.totalCount ?? 0; + this.applicationContent.keywords = changeSet?.keywords; + }, + ), ); } @@ -151,7 +155,7 @@ export class ApplicationsComponent implements OnDestroy, OnInit { } // reload app: - this.selectedApplication = await this.applicationsProvider.reload(this.selectedApplication.UID_AOBApplication.value) + this.selectedApplication = await this.applicationsProvider.reload(this.selectedApplication.UID_AOBApplication.value); if (applicationContent instanceof ApplicationDetailComponent) { this.applicationContent.application = this.selectedApplication; @@ -162,16 +166,17 @@ export class ApplicationsComponent implements OnDestroy, OnInit { return ( route.children && route.children.length > 1 && - route.children[1].routeConfig.outlet.toLowerCase() === 'content' && - route.children[1].routeConfig.path.toLowerCase() === 'edit' + route.children[1].routeConfig?.outlet?.toLowerCase() === 'content' && + route.children[1].routeConfig?.path?.toLowerCase() === 'edit' ); } private async redirectToAppDetails(uidApplication: string): Promise { this.logger.trace(this, 'Redirecting to details view.'); - return this.router.navigate( - ['applications', { outlets: { content: ['detail'] } }], - uidApplication ? { queryParams: { id: uidApplication } } : undefined) + return this.router.navigate( + ['applications', { outlets: { content: ['detail'] } }], + uidApplication ? { queryParams: { id: uidApplication } } : undefined, + ); } } diff --git a/imxweb/projects/aob/src/lib/applications/applications.module.ts b/imxweb/projects/aob/src/lib/applications/applications.module.ts index 3fa03370a..2e9733c35 100644 --- a/imxweb/projects/aob/src/lib/applications/applications.module.ts +++ b/imxweb/projects/aob/src/lib/applications/applications.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,9 +24,11 @@ * */ -import { NgModule } from '@angular/core'; +import { OverlayModule } from '@angular/cdk/overlay'; +import { PortalModule } from '@angular/cdk/portal'; import { CommonModule } from '@angular/common'; -import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; +import { NgModule } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatBadgeModule } from '@angular/material/badge'; import { MatButtonModule } from '@angular/material/button'; @@ -35,53 +37,50 @@ import { MatDialogModule } from '@angular/material/dialog'; import { MatDividerModule } from '@angular/material/divider'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; -import { MatTabsModule } from '@angular/material/tabs'; -import { TranslateModule } from '@ngx-translate/core'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { OverlayModule } from '@angular/cdk/overlay'; import { RouterModule } from '@angular/router'; -import { PortalModule } from '@angular/cdk/portal'; +import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; +import { TranslateModule } from '@ngx-translate/core'; -import { ApplicationDetailComponent } from './application-detail.component'; -import { ApplicationDetailsComponent } from './application-details/application-details.component'; -import { ApplicationHyperviewModule } from './application-hyperview/application-hyperview.module'; -import { ApplicationNavigationComponent } from './application-navigation/application-navigation.component'; -import { ApplicationsComponent } from './applications.component'; -import { ApplicationsService } from './applications.service'; -import { ColumnInfoModule } from '../column-info/column-info.module'; -import { EditApplicationComponent } from './edit-application/edit-application.component'; -import { EntitlementsModule } from '../entitlements/entitlements.module'; -import { KpiModule } from '../kpi/kpi.module'; +import { MatTooltipModule } from '@angular/material/tooltip'; import { + BusyIndicatorModule, + CdrModule, ClassloggerModule, DataSourceToolbarModule, + DataTableModule, DataTilesModule, - QbmModule, - SelectModule, - LdsReplaceModule, - FkAdvancedPickerModule, + DataTreeModule, + DataTreeWrapperModule, + DataViewModule, + DateModule, EntityModule, + FkAdvancedPickerModule, + HelpContextualModule, ImageModule, - CdrModule, - DataTableModule, - DateModule, InfoModalDialogModule, - HelpContextualModule, - BusyIndicatorModule, - DataTreeModule, - DataTreeWrapperModule + LdsReplaceModule, + QbmModule, } from 'qbm'; -import { AobUserModule } from '../user/user.module'; +import { ObjectHyperviewModule } from 'qer'; import { ApplicationPropertyModule } from '../application-property/application-property.module'; +import { ColumnInfoModule } from '../column-info/column-info.module'; +import { EntitlementsModule } from '../entitlements/entitlements.module'; +import { KpiModule } from '../kpi/kpi.module'; +import { AobUserModule } from '../user/user.module'; import { ApplicationCreateComponent } from './application-create/application-create.component'; +import { ApplicationDetailComponent } from './application-detail.component'; +import { ApplicationDetailsComponent } from './application-details/application-details.component'; import { ApplicationImageSelectComponent } from './application-image-select/application-image-select.component'; import { ImageSelectorDialogComponent } from './application-image-select/image-selector-dialog/image-selector-dialog.component'; +import { ApplicationNavigationComponent } from './application-navigation/application-navigation.component'; +import { ApplicationsComponent } from './applications.component'; +import { ApplicationsService } from './applications.service'; import { AuthenticationRootComponent } from './edit-application/authentication-root/authentication-root.component'; +import { EditApplicationComponent } from './edit-application/edit-application.component'; +import { EditServiceCategoryInformationComponent } from './edit-application/service-category/edit-service-category-information/edit-service-category-information.component'; +import { ServiceCategoryComponent } from './edit-application/service-category/service-category.component'; import { IdentitiesComponent } from './identities/identities.component'; import { IdentityDetailComponent } from './identities/identity-detail/identity-detail.component'; -import { ServiceCategoryComponent } from './edit-application/service-category/service-category.component'; -import { MatTooltipModule } from '@angular/material/tooltip'; -import { EditServiceCategoryInformationComponent } from './edit-application/service-category/edit-service-category-information/edit-service-category-information.component'; @NgModule({ declarations: [ @@ -97,11 +96,10 @@ import { EditServiceCategoryInformationComponent } from './edit-application/serv IdentitiesComponent, IdentityDetailComponent, ServiceCategoryComponent, - EditServiceCategoryInformationComponent + EditServiceCategoryInformationComponent, ], imports: [ CommonModule, - ApplicationHyperviewModule, ApplicationPropertyModule, ClassloggerModule, ColumnInfoModule, @@ -118,8 +116,6 @@ import { EditServiceCategoryInformationComponent } from './edit-application/serv MatDividerModule, MatIconModule, MatInputModule, - MatTabsModule, - SelectModule, MatDialogModule, MatTooltipModule, QbmModule, @@ -142,12 +138,10 @@ import { EditServiceCategoryInformationComponent } from './edit-application/serv InfoModalDialogModule, HelpContextualModule, BusyIndicatorModule, + ObjectHyperviewModule, + DataViewModule, ], - providers: [ - ApplicationsService - ], - exports: [ - ApplicationsComponent, - ], + providers: [ApplicationsService], + exports: [ApplicationsComponent], }) -export class ApplicationsModule { } +export class ApplicationsModule {} diff --git a/imxweb/projects/aob/src/lib/applications/applications.service.ts b/imxweb/projects/aob/src/lib/applications/applications.service.ts index 4199479a6..01f4a9bfd 100644 --- a/imxweb/projects/aob/src/lib/applications/applications.service.ts +++ b/imxweb/projects/aob/src/lib/applications/applications.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,15 +24,14 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; -import { Injectable, ErrorHandler } from '@angular/core'; +import { ErrorHandler, Injectable } from '@angular/core'; import { EuiLoadingService, EuiSidesheetService, EuiTheme } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; import { BehaviorSubject, Subject } from 'rxjs'; -import { PortalApplication, PortalApplicationInteractive, PortalApplicationNew } from 'imx-api-aob'; -import { TypedEntityCollectionData, CollectionLoadParameters, EntitySchema } from 'imx-qbm-dbts'; -import { ApiClientService, ClassloggerService, DataTileBadge, SnackBarService } from 'qbm'; +import { PortalApplication, PortalApplicationNew } from '@imx-modules/imx-api-aob'; +import { CollectionLoadParameters, EntitySchema, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; +import { ApiClientService, calculateSidesheetWidth, ClassloggerService, DataTileBadge, SnackBarService } from 'qbm'; import { AobApiService } from '../aob-api-client.service'; import { ApplicationCreateComponent } from './application-create/application-create.component'; @@ -65,10 +64,10 @@ export class ApplicationsService { return bodyClasses.contains(EuiTheme.LIGHT) ? EuiTheme.LIGHT : bodyClasses.contains(EuiTheme.DARK) - ? EuiTheme.DARK - : bodyClasses.contains(EuiTheme.CONTRAST) - ? EuiTheme.CONTRAST - : ''; + ? EuiTheme.DARK + : bodyClasses.contains(EuiTheme.CONTRAST) + ? EuiTheme.CONTRAST + : ''; } constructor( @@ -79,7 +78,7 @@ export class ApplicationsService { private readonly translateService: TranslateService, private readonly sidesheet: EuiSidesheetService, private readonly snackbar: SnackBarService, - private readonly busyService: EuiLoadingService + private readonly busyService: EuiLoadingService, ) { this.translateService.get('#LDS#Published').subscribe((trans: string) => (this.publishedText = trans)); this.translateService.get('#LDS#KPI issues').subscribe((trans: string) => (this.kpiErrorsText = trans)); @@ -89,16 +88,16 @@ export class ApplicationsService { /** * Encapsules the aob/applications GET endpoint and delivers a list of {@link PortalApplication[]|applications}. */ - public async get(parameters: CollectionLoadParameters = {}): Promise> { + public async get(parameters: CollectionLoadParameters = {}): Promise | undefined> { if (this.aobClient.typedClient == null) { - return new Promise>((resolve) => resolve(null)); + return new Promise((resolve) => resolve(undefined)); } return this.apiProvider.request(() => this.aobClient.typedClient.PortalApplication.Get(parameters)); } - public async reload(uidApplication: string): Promise { + public async reload(uidApplication: string): Promise { return await this.apiProvider.request( - async () => (await this.aobClient.typedClient.PortalApplicationInteractive.Get_byid(uidApplication)).Data[0] + async () => (await this.aobClient.typedClient.PortalApplicationInteractive.Get_byid(uidApplication)).Data[0], ); } @@ -187,7 +186,7 @@ export class ApplicationsService { .open(ApplicationCreateComponent, { title: await this.translateService.get('#LDS#Heading Create Application').toPromise(), padding: '0px', - width: 'max(600px, 60%)', + width: calculateSidesheetWidth(), disableClose: true, testId: 'create-aob-application', data: { @@ -198,8 +197,9 @@ export class ApplicationsService { .toPromise(); if (result) { - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.busyService.show())); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } try { await application.GetEntity().Commit(true); @@ -208,7 +208,7 @@ export class ApplicationsService { } catch (error) { this.errorHandler.handleError(error); } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } } else { this.snackbar.open({ key: '#LDS#The creation of the application has been canceled.' }); @@ -245,7 +245,7 @@ export class ApplicationsService { /** * Updates the badges, that are used on the application tiles, according to the used theme - * + * * @param theme Currently used EuiTheme */ private updateBadges(theme: string): void { diff --git a/imxweb/projects/aob/src/lib/applications/edit-application/authentication-root/authentication-root.component.html b/imxweb/projects/aob/src/lib/applications/edit-application/authentication-root/authentication-root.component.html index 9b3e68fb0..a5f803630 100644 --- a/imxweb/projects/aob/src/lib/applications/edit-application/authentication-root/authentication-root.component.html +++ b/imxweb/projects/aob/src/lib/applications/edit-application/authentication-root/authentication-root.component.html @@ -1,11 +1,16 @@ - + - {{ '#LDS#Please specify a value for {0}.' | translate | ldsReplace:application.AuthenticationRoot.GetMetadata().GetDisplay() }} + {{ '#LDS#Please specify a value for {0}.' | translate | ldsReplace: application.AuthenticationRoot.GetMetadata().GetDisplay() }} - + (valueChange)="form.updateValueAndValidity()" +> diff --git a/imxweb/projects/aob/src/lib/applications/edit-application/authentication-root/authentication-root.component.scss b/imxweb/projects/aob/src/lib/applications/edit-application/authentication-root/authentication-root.component.scss index 3364f1f4f..c9e89c84d 100644 --- a/imxweb/projects/aob/src/lib/applications/edit-application/authentication-root/authentication-root.component.scss +++ b/imxweb/projects/aob/src/lib/applications/edit-application/authentication-root/authentication-root.component.scss @@ -1 +1 @@ -// styles \ No newline at end of file +// styles diff --git a/imxweb/projects/aob/src/lib/applications/edit-application/authentication-root/authentication-root.component.ts b/imxweb/projects/aob/src/lib/applications/edit-application/authentication-root/authentication-root.component.ts index 6c1654663..fa6507ccf 100644 --- a/imxweb/projects/aob/src/lib/applications/edit-application/authentication-root/authentication-root.component.ts +++ b/imxweb/projects/aob/src/lib/applications/edit-application/authentication-root/authentication-root.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,16 +27,16 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { AbstractControl, UntypedFormGroup } from '@angular/forms'; -import { PortalApplication } from 'imx-api-aob'; +import { PortalApplication } from '@imx-modules/imx-api-aob'; import { BaseCdr } from 'qbm'; @Component({ selector: 'imx-authentication-root', templateUrl: './authentication-root.component.html', - styleUrls: ['./authentication-root.component.scss'] + styleUrls: ['./authentication-root.component.scss'], }) export class AuthenticationRootComponent implements OnInit { - public readonly form = new UntypedFormGroup({}, __ => { + public readonly form = new UntypedFormGroup({}, (__) => { if (this.application == null) { return null; } @@ -46,7 +46,7 @@ export class AuthenticationRootComponent implements OnInit { return !authenticationRootValue?.length && isAuthenticationIntegrated ? { relationInvalid: true } : null; }); - public authenticationRootWrapper: BaseCdr; + public authenticationRootWrapper: BaseCdr; @Input() public application: PortalApplication; @@ -58,7 +58,7 @@ export class AuthenticationRootComponent implements OnInit { if (!authenticationRootHelper.value?.length) { await authenticationRootHelper.Column.PutValueStruct({ DataValue: this.application.AuthenticationRoot.value, - DisplayValue: this.application.AuthenticationRoot.Column.GetDisplayValue() + DisplayValue: this.application.AuthenticationRoot.Column.GetDisplayValue(), }); } diff --git a/imxweb/projects/aob/src/lib/applications/edit-application/edit-application.component.html b/imxweb/projects/aob/src/lib/applications/edit-application/edit-application.component.html index 9320c4a3c..95fa82ee8 100644 --- a/imxweb/projects/aob/src/lib/applications/edit-application/edit-application.component.html +++ b/imxweb/projects/aob/src/lib/applications/edit-application/edit-application.component.html @@ -1,19 +1,27 @@
- -
-
- + + +
+ - + + + - - + - + - + - + - + + + + + + + - + - - + - + - - + - + - - + - +
@@ -54,13 +65,13 @@
- -
diff --git a/imxweb/projects/aob/src/lib/applications/edit-application/edit-application.component.scss b/imxweb/projects/aob/src/lib/applications/edit-application/edit-application.component.scss deleted file mode 100644 index b890d9141..000000000 --- a/imxweb/projects/aob/src/lib/applications/edit-application/edit-application.component.scss +++ /dev/null @@ -1,26 +0,0 @@ -.imx-form { - display: flex; - justify-content: space-between; - margin-right: 10px; -} - -.imx-input-fields { - display: flex; - flex-direction: column; - flex-grow: 1; - max-width: 600px; -} - -.imx-checkbox-error { - margin-left: 24px; -} - -.eui-sidesheet-actions { - .justify-start { - margin-right: auto; - } - - button:not(:last-of-type):not(.justify-start) { - margin-right: 10px; - } -} diff --git a/imxweb/projects/aob/src/lib/applications/edit-application/edit-application.component.ts b/imxweb/projects/aob/src/lib/applications/edit-application/edit-application.component.ts index 41fd09e75..6fb93d1f2 100644 --- a/imxweb/projects/aob/src/lib/applications/edit-application/edit-application.component.ts +++ b/imxweb/projects/aob/src/lib/applications/edit-application/edit-application.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,33 +24,31 @@ * */ -import { Component, Output, EventEmitter, ErrorHandler, Inject } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; -import { EuiLoadingService, EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { Component, ErrorHandler, EventEmitter, Inject, Output } from '@angular/core'; +import { AbstractControl, UntypedFormGroup } from '@angular/forms'; +import { EUI_SIDESHEET_DATA, EuiLoadingService, EuiSidesheetRef } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { PortalApplication, PortalShops } from 'imx-api-aob'; -import { DbObjectKey, TypedEntity } from 'imx-qbm-dbts'; -import { ShopsService } from '../../shops/shops.service'; -import { AccountsService } from '../../accounts/accounts.service'; +import { MatDialog } from '@angular/material/dialog'; +import { PortalApplication, PortalShops } from '@imx-modules/imx-api-aob'; +import { DbObjectKey, TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { ClassloggerService, - SnackBarService, - TypedEntitySelectionData, ConfirmationService, LdsReplacePipe, - TranslationEditorComponent + SnackBarService, + TranslationEditorComponent, + TypedEntitySelectionData, } from 'qbm'; -import { SelectionContainer } from './selection-container'; +import { AccountsService } from '../../accounts/accounts.service'; +import { ShopsService } from '../../shops/shops.service'; import { ApplicationContent } from '../application-content.interface'; -import { MatDialog } from '@angular/material/dialog'; +import { SelectionContainer } from './selection-container'; @Component({ selector: 'imx-edit-application', templateUrl: './edit-application.component.html', - styleUrls: ['./edit-application.component.scss'] }) - export class EditApplicationComponent implements ApplicationContent { public readonly applicationForm = new UntypedFormGroup({}); @@ -74,7 +72,7 @@ export class EditApplicationComponent implements ApplicationContent { private readonly translate: TranslateService, private readonly ldsReplace: LdsReplacePipe, @Inject(EUI_SIDESHEET_DATA) public application: PortalApplication, - private readonly dialog: MatDialog + private readonly dialog: MatDialog, ) { this.sidesheetRef.closeClicked().subscribe(async () => { if (!this.hasUnsavedChanges()) { @@ -91,8 +89,8 @@ export class EditApplicationComponent implements ApplicationContent { this.shopsData = this.getShopsData(); } - public shopSelectionChanged(selection: PortalShops[]): void { - this.shopsSelection.selected = selection; + public shopSelectionChanged(selection: TypedEntity[]): void { + this.shopsSelection.selected = selection as PortalShops[]; } public accountSelectionChanged(selection: TypedEntity[]): void { @@ -108,7 +106,9 @@ export class EditApplicationComponent implements ApplicationContent { } public async submitData(): Promise { - const overlayRef = this.busyService.show(); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } try { await this.saveShops(); @@ -123,7 +123,7 @@ export class EditApplicationComponent implements ApplicationContent { } catch (error) { this.errorHandler.handleError(error); } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); this.sidesheetRef.close(); } } @@ -138,41 +138,48 @@ export class EditApplicationComponent implements ApplicationContent { this.sidesheetRef.close(); } - public editTranslation(){ - const dialogConfig = { - data: this.application, - width: '600px' - } - this.dialog.open(TranslationEditorComponent,dialogConfig); + public editTranslation() { + const dialogConfig = { + data: this.application, + width: '600px', + }; + this.dialog.open(TranslationEditorComponent, dialogConfig); } - private getShopsData(): TypedEntitySelectionData { + public addAndValidate(event: { name: string; control: AbstractControl }) { + this.applicationForm.addControl(event.name, event.control); + event.control.updateValueAndValidity(); + } + + private getShopsData(): TypedEntitySelectionData { return { title: '#LDS#Heading Edit IT Shop Structures', valueWrapper: { display: this.shopsProvider.display, name: 'shops', - canEdit: true + canEdit: true, }, getInitialDisplay: async () => { const info = await this.shopsProvider.getFirstAndCount(this.application.UID_AOBApplication.value); - return this.buildInitalValue(info.first, info.count); + if (info.first) { + return this.buildInitalValue(info.first, info.count); + } + return ''; }, getSelected: async () => { - this.shopsSelection.init((await this.shopsProvider.getApplicationInShop( - this.application.UID_AOBApplication.value, { PageSize: 100000 } - ))?.Data); + this.shopsSelection.init( + (await this.shopsProvider.getApplicationInShop(this.application.UID_AOBApplication.value, { PageSize: 100000 }))?.Data || [], + ); return this.shopsSelection.selected; }, - getTyped: parameters => this.shopsProvider.get(parameters), + getTyped: (parameters) => this.shopsProvider.get(parameters), }; } private async saveShops(): Promise { if (this.shopsData) { this.logger.debug(this, 'submitData - update shops...'); - const result = - await this.shopsProvider.updateApplicationInShops(this.application, this.shopsSelection.getChangeSet()); + const result = await this.shopsProvider.updateApplicationInShops(this.application, this.shopsSelection.getChangeSet()); if (!result) { this.logger.error(this, 'Attempt to update the shops failed'); } @@ -185,27 +192,29 @@ export class EditApplicationComponent implements ApplicationContent { valueWrapper: { display: this.accountsProvider.display, name: 'accounts', - canEdit: true + canEdit: true, }, getInitialDisplay: async () => { const info = await this.accountsProvider.getFirstAndCount(this.application.UID_AOBApplication.value); - return this.buildInitalValue(info.first, info.count); + if (info.first) { + return this.buildInitalValue(info.first, info.count); + } + return ''; }, getSelected: async () => { - this.accountsSelection.init(await this.accountsProvider.getAssigned( - this.application.UID_AOBApplication.value, - { PageSize: 100000 } - )); + this.accountsSelection.init( + await this.accountsProvider.getAssigned(this.application.UID_AOBApplication.value, { PageSize: 100000 }), + ); return this.accountsSelection.selected; }, dynamicFkRelation: { tables: this.accountsProvider.getCandidateTables(), - getSelectedTableName: selected => { + getSelectedTableName: (selected) => { if (selected?.length > 0) { return DbObjectKey.FromXml(selected[0].GetEntity().GetColumn('XObjectKey').GetValue()).TableName; } - return undefined; - } + return ''; + }, }, }; } @@ -213,8 +222,7 @@ export class EditApplicationComponent implements ApplicationContent { private async saveAccounts(): Promise { if (this.accountsData) { this.logger.debug(this, 'submitData - update accounts...'); - const result = - await this.accountsProvider.updateApplicationUsesAccounts(this.application, this.accountsSelection.getChangeSet()); + const result = await this.accountsProvider.updateApplicationUsesAccounts(this.application, this.accountsSelection.getChangeSet()); if (!result) { this.logger.error(this, 'Attempt to update the accounts failed'); } @@ -222,8 +230,10 @@ export class EditApplicationComponent implements ApplicationContent { } private async buildInitalValue(entity: TypedEntity, count: number): Promise { - return count < 1 ? '' : - count === 1 ? entity.GetEntity().GetDisplay() : - this.ldsReplace.transform(await this.translate.get('#LDS#{0} items selected').toPromise(), count); + return count < 1 + ? '' + : count === 1 + ? entity.GetEntity().GetDisplay() + : this.ldsReplace.transform(await this.translate.get('#LDS#{0} items selected').toPromise(), count); } } diff --git a/imxweb/projects/aob/src/lib/applications/edit-application/selection-container.spec.ts b/imxweb/projects/aob/src/lib/applications/edit-application/selection-container.spec.ts index 01c178e9c..359399c82 100644 --- a/imxweb/projects/aob/src/lib/applications/edit-application/selection-container.spec.ts +++ b/imxweb/projects/aob/src/lib/applications/edit-application/selection-container.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,45 +31,46 @@ describe('SelectionContainer', () => { { assigned: [], selected: [], - expected: { adds: 0, removes: 0 } + expected: { adds: 0, removes: 0 }, }, { assigned: [0], selected: [], - expected: { adds: 0, removes: 1 } + expected: { adds: 0, removes: 1 }, }, { assigned: [0], selected: [0], - expected: { adds: 0, removes: 0 } + expected: { adds: 0, removes: 0 }, }, { assigned: [], selected: [0], - expected: { adds: 1, removes: 0 } + expected: { adds: 1, removes: 0 }, }, { assigned: [1], selected: [0], - expected: { adds: 1, removes: 1 } + expected: { adds: 1, removes: 1 }, }, { - assigned: [0,1], - selected: [0,1], - expected: { adds: 0, removes: 0 } + assigned: [0, 1], + selected: [0, 1], + expected: { adds: 0, removes: 0 }, }, { - assigned: [0,1], + assigned: [0, 1], selected: [], - expected: { adds: 0, removes: 2 } - } - ].forEach(testcase => - it('creates the correct changeSet, assigned ' + testcase.assigned + ', selected ' + testcase.selected, () => { - const selected = new SelectionContainer<{ id: string }>(item => item.id); - selected.init(testcase.assigned.map(id => ({ id: id.toString() }))); - selected.selected = testcase.selected.map(id => ({ id: id.toString() })); - const change = selected.getChangeSet(); - expect(change.add.length).toEqual(testcase.expected.adds); - expect(change.remove.length).toEqual(testcase.expected.removes); - })); -}); \ No newline at end of file + expected: { adds: 0, removes: 2 }, + }, + ].forEach((testcase) => + it('creates the correct changeSet, assigned ' + testcase.assigned + ', selected ' + testcase.selected, () => { + const selected = new SelectionContainer<{ id: string }>((item) => item.id); + selected.init(testcase.assigned.map((id) => ({ id: id.toString() }))); + selected.selected = testcase.selected.map((id) => ({ id: id.toString() })); + const change = selected.getChangeSet(); + expect(change.add.length).toEqual(testcase.expected.adds); + expect(change.remove.length).toEqual(testcase.expected.removes); + }), + ); +}); diff --git a/imxweb/projects/aob/src/lib/applications/edit-application/selection-container.ts b/imxweb/projects/aob/src/lib/applications/edit-application/selection-container.ts index 9ca5eabbf..b33253c41 100644 --- a/imxweb/projects/aob/src/lib/applications/edit-application/selection-container.ts +++ b/imxweb/projects/aob/src/lib/applications/edit-application/selection-container.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,27 +25,28 @@ */ export class SelectionContainer { - public selected: T[] = []; + public selected: T[] = []; - private assigned: T[] = []; + private assigned: T[] = []; - constructor(private getKey: (item: T) => string) { } + constructor(private getKey: (item: T) => string) {} - public init(assigned: T[]): void { - this.assigned = assigned; - this.selected = assigned.slice(); - } + public init(assigned: T[]): void { + this.assigned = assigned; + this.selected = assigned.slice(); + } - public getChangeSet(): { add: T[], remove: T[] } { - return { - add: this.selected.filter(item => this.assigned.find(itemAssigned => this.equals(item, itemAssigned)) == null), - remove: this.selected == null || this.selected.length === 0 ? - this.assigned : - this.assigned.filter(itemAssigned => this.selected.find(item => this.equals(item, itemAssigned)) == null) - }; - } + public getChangeSet(): { add: T[]; remove: T[] } { + return { + add: this.selected.filter((item) => this.assigned.find((itemAssigned) => this.equals(item, itemAssigned)) == null), + remove: + this.selected == null || this.selected.length === 0 + ? this.assigned + : this.assigned.filter((itemAssigned) => this.selected.find((item) => this.equals(item, itemAssigned)) == null), + }; + } - private equals(item1: T, item2: T): boolean { - return this.getKey(item1) === this.getKey(item2); - } + private equals(item1: T, item2: T): boolean { + return this.getKey(item1) === this.getKey(item2); + } } diff --git a/imxweb/projects/aob/src/lib/applications/edit-application/service-category/edit-service-category-information/edit-service-category-information.component.html b/imxweb/projects/aob/src/lib/applications/edit-application/service-category/edit-service-category-information/edit-service-category-information.component.html index e21239303..6b0183ab1 100644 --- a/imxweb/projects/aob/src/lib/applications/edit-application/service-category/edit-service-category-information/edit-service-category-information.component.html +++ b/imxweb/projects/aob/src/lib/applications/edit-application/service-category/edit-service-category-information/edit-service-category-information.component.html @@ -4,7 +4,7 @@ @@ -12,7 +12,13 @@
-
diff --git a/imxweb/projects/aob/src/lib/applications/edit-application/service-category/edit-service-category-information/edit-service-category-information.component.ts b/imxweb/projects/aob/src/lib/applications/edit-application/service-category/edit-service-category-information/edit-service-category-information.component.ts index ecb7cd33c..44559480a 100644 --- a/imxweb/projects/aob/src/lib/applications/edit-application/service-category/edit-service-category-information/edit-service-category-information.component.ts +++ b/imxweb/projects/aob/src/lib/applications/edit-application/service-category/edit-service-category-information/edit-service-category-information.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,11 +25,11 @@ */ import { Component, Inject, OnInit } from '@angular/core'; +import { FormArray, FormGroup } from '@angular/forms'; import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; -import { IEntity } from 'imx-qbm-dbts'; +import { IEntity } from '@imx-modules/imx-qbm-dbts'; import { BaseCdr, CdrFactoryService, ColumnDependentReference, ConfirmationService } from 'qbm'; import { ServiceCategoryService } from '../service-category.service'; -import { FormArray, FormGroup } from '@angular/forms'; interface ServiceItemForm { cdrArray: FormArray; @@ -40,7 +40,7 @@ interface ServiceItemForm { styleUrls: ['./edit-service-category-information.component.scss'], }) export class EditServiceCategoryInformationComponent implements OnInit { - public cdrList: ColumnDependentReference[]; + public cdrList: (ColumnDependentReference | undefined)[]; public readonly formGroup: FormGroup; @@ -53,10 +53,10 @@ export class EditServiceCategoryInformationComponent implements OnInit { private readonly confirm: ConfirmationService, private readonly ref: EuiSidesheetRef, private readonly cdrFactoryService: CdrFactoryService, - @Inject(EUI_SIDESHEET_DATA) public data: { serviceCategory: IEntity; isNew: boolean } + @Inject(EUI_SIDESHEET_DATA) public data: { serviceCategory: IEntity; isNew: boolean }, ) { - this.formGroup = new FormGroup({ - cdrArray: new FormArray([]), + this.formGroup = new FormGroup({ + cdrArray: new FormArray([]), }); this.ref.closeClicked().subscribe(async () => { @@ -75,11 +75,11 @@ export class EditServiceCategoryInformationComponent implements OnInit { schema.Columns.UID_PWODecisionMethod, schema.Columns.UID_OrgAttestator, schema.Columns.JPegPhoto, - ].map(elem=> elem.ColumnName); + ].map((elem) => elem.ColumnName || ''); this.cdrList = this.cdrFactoryService.buildCdrFromColumnList(this.data.serviceCategory, columnNames); - if (!this.data.isNew) { + if (!this.data.isNew && schema.Columns.UID_AccProductGroupParent.ColumnName) { this.cdrList.push(new BaseCdr(this.data.serviceCategory.GetColumn(schema.Columns.UID_AccProductGroupParent.ColumnName))); } } diff --git a/imxweb/projects/aob/src/lib/applications/edit-application/service-category/service-category-tree-database.ts b/imxweb/projects/aob/src/lib/applications/edit-application/service-category/service-category-tree-database.ts index 5975c22c1..86373023c 100644 --- a/imxweb/projects/aob/src/lib/applications/edit-application/service-category/service-category-tree-database.ts +++ b/imxweb/projects/aob/src/lib/applications/edit-application/service-category/service-category-tree-database.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,10 +27,10 @@ import { OverlayRef } from '@angular/cdk/overlay'; import { EuiLoadingService } from '@elemental-ui/core'; -import { CollectionLoadParameters, IEntity, TypedEntityCollectionData } from 'imx-qbm-dbts'; -import { PortalServicecategories } from 'imx-api-qer'; +import { PortalServicecategories } from '@imx-modules/imx-api-qer'; +import { CollectionLoadParameters, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; -import { TreeDatabase, TreeNodeResultParameter, SettingsService } from 'qbm'; +import { SettingsService, TreeDatabase, TreeNodeResultParameter } from 'qbm'; import { ServiceCategoryService } from './service-category.service'; /** Provider of servicecategory-data for the imx-data-tree */ @@ -39,7 +39,7 @@ export class ServiceCategoryTreeDatabase extends TreeDatabase { private readonly loadingServiceElemental: EuiLoadingService, private readonly settings: SettingsService, private readonly serviceCategoriesProvider: ServiceCategoryService, - private readonly uidRootElement: string + private readonly uidRootElement: string, ) { super(); this.identifierColumnName = 'FullPath'; @@ -56,10 +56,9 @@ export class ServiceCategoryTreeDatabase extends TreeDatabase { private async loadRoot(showLoading: boolean) { let entities: TreeNodeResultParameter; - let overlayRef: OverlayRef; - if (showLoading) { - setTimeout(() => (overlayRef = this.loadingServiceElemental.show())); + if (showLoading && this.loadingServiceElemental.overlayRefs.length === 0) { + this.loadingServiceElemental.show(); } try { const opts: CollectionLoadParameters = { @@ -81,7 +80,7 @@ export class ServiceCategoryTreeDatabase extends TreeDatabase { }; } finally { if (showLoading) { - setTimeout(() => this.loadingServiceElemental.hide(overlayRef)); + this.loadingServiceElemental.hide(); } } @@ -92,8 +91,8 @@ export class ServiceCategoryTreeDatabase extends TreeDatabase { let entities: TreeNodeResultParameter; let overlayRef: OverlayRef; - if (showLoading) { - setTimeout(() => (overlayRef = this.loadingServiceElemental.show())); + if (showLoading && this.loadingServiceElemental.overlayRefs.length === 0) { + this.loadingServiceElemental.show(); } try { const opts = { @@ -110,7 +109,7 @@ export class ServiceCategoryTreeDatabase extends TreeDatabase { }; } finally { if (showLoading) { - setTimeout(() => this.loadingServiceElemental.hide(overlayRef)); + this.loadingServiceElemental.hide(); } } diff --git a/imxweb/projects/aob/src/lib/applications/edit-application/service-category/service-category.component.html b/imxweb/projects/aob/src/lib/applications/edit-application/service-category/service-category.component.html index a561cecad..e2b3577ae 100644 --- a/imxweb/projects/aob/src/lib/applications/edit-application/service-category/service-category.component.html +++ b/imxweb/projects/aob/src/lib/applications/edit-application/service-category/service-category.component.html @@ -1,5 +1,5 @@
- +
- - diff --git a/imxweb/projects/aob/src/lib/applications/edit-application/service-category/service-category.component.scss b/imxweb/projects/aob/src/lib/applications/edit-application/service-category/service-category.component.scss deleted file mode 100644 index cd288b7c8..000000000 --- a/imxweb/projects/aob/src/lib/applications/edit-application/service-category/service-category.component.scss +++ /dev/null @@ -1,11 +0,0 @@ -:host { - .eui-sidesheet-content{ - display: flex; - flex-direction: column; - .mat-card { - display: flex; - flex-direction: column; - flex: 1 1 auto; - } - } -} \ No newline at end of file diff --git a/imxweb/projects/aob/src/lib/applications/edit-application/service-category/service-category.component.ts b/imxweb/projects/aob/src/lib/applications/edit-application/service-category/service-category.component.ts index 1faf1943b..d3e1a98cf 100644 --- a/imxweb/projects/aob/src/lib/applications/edit-application/service-category/service-category.component.ts +++ b/imxweb/projects/aob/src/lib/applications/edit-application/service-category/service-category.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,20 +25,26 @@ */ import { Component, Inject, ViewChild } from '@angular/core'; -import { ServiceCategoryService } from './service-category.service'; import { EUI_SIDESHEET_DATA, EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; -import { PortalApplication } from 'imx-api-aob'; +import { PortalApplication } from '@imx-modules/imx-api-aob'; import { TranslateService } from '@ngx-translate/core'; +import { ServiceCategoryService } from './service-category.service'; -import { IEntity } from 'imx-qbm-dbts'; -import { SettingsService, DataTreeWrapperComponent, SnackBarService, TreeDatabase, ConfirmationService } from 'qbm'; -import { ServiceCategoryTreeDatabase } from './service-category-tree-database'; +import { IEntity } from '@imx-modules/imx-qbm-dbts'; +import { + calculateSidesheetWidth, + ConfirmationService, + DataTreeWrapperComponent, + SettingsService, + SnackBarService, + TreeDatabase, +} from 'qbm'; import { EditServiceCategoryInformationComponent } from './edit-service-category-information/edit-service-category-information.component'; +import { ServiceCategoryTreeDatabase } from './service-category-tree-database'; @Component({ selector: 'imx-service-category', templateUrl: './service-category.component.html', - styleUrls: ['./service-category.component.scss'], }) export class ServiceCategoryComponent { public treeDatabase: ServiceCategoryTreeDatabase; @@ -53,7 +59,7 @@ export class ServiceCategoryComponent { private readonly sidesheet: EuiSidesheetService, private readonly translate: TranslateService, private readonly confirm: ConfirmationService, - @Inject(EUI_SIDESHEET_DATA) public application: PortalApplication + @Inject(EUI_SIDESHEET_DATA) public application: PortalApplication, ) { this.initializeTree(); } @@ -65,17 +71,19 @@ export class ServiceCategoryComponent { const overlay = this.busyService.show(); try { await entity.Commit(true); - } catch(exception){ + } catch (exception) { await entity.DiscardChanges(); throw exception; - } - finally { + } finally { this.busyService.hide(overlay); } this.treeDatabase.updateNodeItem(entity); if (oldParent !== entity.GetColumn('UID_AccProductGroupParent').GetValue()) { this.tryToMoveElement(entity, entity.GetColumn('UID_AccProductGroupParent').GetValue()); - this.snackbar.open({ key: '#LDS#The changes have been successfully saved.' }); + this.snackbar.open({ + key: '#LDS#The parent of the service category "{0}" has been successfully changed.', + parameters: [entity.GetDisplay()], + }); } } else { await entity.DiscardChanges(); @@ -85,7 +93,12 @@ export class ServiceCategoryComponent { public async onDelete(evt: Event, entity: IEntity): Promise { evt.stopPropagation(); - if(!(await this.confirm.confirmDelete('#LDS#Heading Delete Service Category', '#LDS#Are you sure you want to delete the service category?'))){ + if ( + !(await this.confirm.confirmDelete( + '#LDS#Heading Delete Service Category', + '#LDS#Are you sure you want to delete the service category?', + )) + ) { return; } @@ -97,7 +110,7 @@ export class ServiceCategoryComponent { } this.dataTreeWrapper.deleteNode(entity, false); - this.snackbar.open({ key: '#LDS#The changes have been successfully saved.' }); + this.snackbar.open({ key: '#LDS#The service category "{0}" has been successfully deleted.', parameters: [entity.GetDisplay()] }); } public async addChildCategory(evt: Event, entity: IEntity): Promise { @@ -115,7 +128,7 @@ export class ServiceCategoryComponent { } finally { this.busyService.hide(overlay); } - this.snackbar.open({ key: '#LDS#The changes have been successfully saved.' }); + this.snackbar.open({ key: '#LDS#The service category "{0}" has been successfully created.', parameters: [newEntity.GetDisplay()] }); this.add(entity, newEntity); } else { await entity.DiscardChanges(); @@ -162,7 +175,7 @@ export class ServiceCategoryComponent { this.busyService, this.settingsService, this.categoryService, - this.application.UID_AccProductGroup.value + this.application.UID_AccProductGroup.value, ); this.treeDatabase.initialized.subscribe(async () => { @@ -176,12 +189,12 @@ export class ServiceCategoryComponent { private async editServiceCategory(serviceCategory: IEntity, mode: 'edit' | 'create'): Promise { return await this.sidesheet .open(EditServiceCategoryInformationComponent, { - title: await this.translate - .get(mode === 'edit' ? '#LDS#Heading Edit Service Category' : '#LDS#Heading Create Service Category') - .toPromise(), + title: await this.translate.instant( + mode === 'edit' ? '#LDS#Heading Edit Service Category' : '#LDS#Heading Create Service Category', + ), subTitle: mode === 'edit' ? serviceCategory.GetDisplay() : '', padding: '0px', - width: 'max(650px, 65%)', + width: calculateSidesheetWidth(1000), disableClose: true, testId: mode === 'edit' ? 'edit-service-category' : 'create-service-category', data: { serviceCategory, isNew: mode === 'create' }, diff --git a/imxweb/projects/aob/src/lib/applications/edit-application/service-category/service-category.service.ts b/imxweb/projects/aob/src/lib/applications/edit-application/service-category/service-category.service.ts index 780e02074..9ba94186c 100644 --- a/imxweb/projects/aob/src/lib/applications/edit-application/service-category/service-category.service.ts +++ b/imxweb/projects/aob/src/lib/applications/edit-application/service-category/service-category.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,8 +25,14 @@ */ import { Injectable } from '@angular/core'; -import { PortalServicecategories } from 'imx-api-qer'; -import { CollectionLoadParameters, EntityCollectionData, EntitySchema, IEntity, TypedEntityCollectionData } from 'imx-qbm-dbts'; +import { PortalServicecategories } from '@imx-modules/imx-api-qer'; +import { + CollectionLoadParameters, + EntityCollectionData, + EntitySchema, + IEntity, + TypedEntityCollectionData, +} from '@imx-modules/imx-qbm-dbts'; import { QerApiService } from 'qer'; @Injectable({ diff --git a/imxweb/projects/aob/src/lib/applications/identities/identities.component.html b/imxweb/projects/aob/src/lib/applications/identities/identities.component.html index e35148562..75c23dd5a 100644 --- a/imxweb/projects/aob/src/lib/applications/identities/identities.component.html +++ b/imxweb/projects/aob/src/lib/applications/identities/identities.component.html @@ -1,42 +1,49 @@
- - - - - - - - - -
\ No newline at end of file + + + + + + + + +
diff --git a/imxweb/projects/aob/src/lib/applications/identities/identities.component.scss b/imxweb/projects/aob/src/lib/applications/identities/identities.component.scss index bfd53f420..df80448df 100644 --- a/imxweb/projects/aob/src/lib/applications/identities/identities.component.scss +++ b/imxweb/projects/aob/src/lib/applications/identities/identities.component.scss @@ -14,25 +14,4 @@ flex: 1 1 auto; overflow: hidden; } - - ::ng-deep .mat-paginator { - background-color: transparent; - } -} - -.eui-dark-theme { - :host { - ::ng-deep .mat-paginator { - background-color: transparent; - } - } -} - -.eui-contrast-theme { - :host { - background-color: $color-gray-100; - } - ::ng-deep .mat-paginator { - background-color: transparent; - } } diff --git a/imxweb/projects/aob/src/lib/applications/identities/identities.component.ts b/imxweb/projects/aob/src/lib/applications/identities/identities.component.ts index bbbb8700d..a593949a7 100644 --- a/imxweb/projects/aob/src/lib/applications/identities/identities.component.ts +++ b/imxweb/projects/aob/src/lib/applications/identities/identities.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,8 +26,7 @@ import { Component, Input, OnChanges, SimpleChanges, ViewChild } from '@angular/core'; import { EuiSidesheetService } from '@elemental-ui/core'; -import { TranslateService } from '@ngx-translate/core'; -import { PortalApplication, PortalApplicationIdentities } from 'imx-api-aob'; +import { PortalApplication, PortalApplicationIdentities } from '@imx-modules/imx-api-aob'; import { CollectionLoadParameters, DisplayColumns, @@ -35,8 +34,9 @@ import { ExtendedTypedEntityCollection, IClientProperty, TypedEntity, -} from 'imx-qbm-dbts'; -import { BusyService, DataSourceToolbarSettings, DataTableComponent, DataTileMenuItem } from 'qbm'; +} from '@imx-modules/imx-qbm-dbts'; +import { TranslateService } from '@ngx-translate/core'; +import { BusyService, calculateSidesheetWidth, DataSourceToolbarSettings, DataTableComponent, DataTileMenuItem } from 'qbm'; import { IdentityDetailData } from './identity-detail-data'; import { IdentityDetailComponent } from './identity-detail/identity-detail.component'; import { IdentityService } from './identity.service'; @@ -65,7 +65,7 @@ export class IdentitiesComponent implements OnChanges { constructor( private readonly identityService: IdentityService, private readonly sidesheetService: EuiSidesheetService, - private readonly translateService: TranslateService + private readonly translateService: TranslateService, ) { this.entitySchema = this.identityService.getSchema(); this.initNavigationState(); @@ -130,7 +130,7 @@ export class IdentitiesComponent implements OnChanges { * Opens a side sheet that displays identity details * @param item the identity, that details should be opened */ - public async onOpenDetails(item: TypedEntity): Promise { + public async onOpenDetails(item?: TypedEntity): Promise { const data: IdentityDetailData = { application: this.application, selectedItem: item, @@ -138,8 +138,8 @@ export class IdentitiesComponent implements OnChanges { this.sidesheetService.open(IdentityDetailComponent, { title: await this.translateService.get('#LDS#Heading View Application Entitlements').toPromise(), - subTitle: data.selectedItem.GetEntity().GetDisplay(), - width: 'max(768px, 70%)', + subTitle: data.selectedItem?.GetEntity().GetDisplay(), + width: calculateSidesheetWidth(1100, 0.7), testId: 'identity-view-application-entitlements', data: data, }); @@ -171,7 +171,7 @@ export class IdentitiesComponent implements OnChanges { this.navigationState = { PageSize: 25, StartIndex: 0, - search: null, + search: '', }; } } diff --git a/imxweb/projects/aob/src/lib/applications/identities/identity-detail-data.ts b/imxweb/projects/aob/src/lib/applications/identities/identity-detail-data.ts index 4d4c67e0c..86cea3e74 100644 --- a/imxweb/projects/aob/src/lib/applications/identities/identity-detail-data.ts +++ b/imxweb/projects/aob/src/lib/applications/identities/identity-detail-data.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,10 +24,10 @@ * */ -import { PortalApplication } from "imx-api-aob"; -import { TypedEntity } from "imx-qbm-dbts"; +import { PortalApplication } from '@imx-modules/imx-api-aob'; +import { TypedEntity } from '@imx-modules/imx-qbm-dbts'; export interface IdentityDetailData { - application: PortalApplication, - selectedItem: TypedEntity; + application: PortalApplication; + selectedItem: TypedEntity | undefined; } diff --git a/imxweb/projects/aob/src/lib/applications/identities/identity-detail/identity-detail.component.html b/imxweb/projects/aob/src/lib/applications/identities/identity-detail/identity-detail.component.html index ac9ac5ac0..4012e34e3 100644 --- a/imxweb/projects/aob/src/lib/applications/identities/identity-detail/identity-detail.component.html +++ b/imxweb/projects/aob/src/lib/applications/identities/identity-detail/identity-detail.component.html @@ -1,21 +1,28 @@ - - - - - - - + + + + + {{ entitySchema?.Columns?.[DisplayColumns.DISPLAY_PROPERTYNAME]?.Display }} + + + {{ item.GetEntity().GetDisplay() }} + + + + {{ entitySchema.Columns.OrderState.Display }} + +
{{ item.OrderState?.Column.GetDisplayValue() }}
+ +
+ + {{ entitySchema.Columns.ValidUntil.Display }} +
{{ translateProvider.GetColumnDisplay('ValidUntil', entitySchema) + ': ' + (item.ValidUntil.value | shortDate) }}
-
-
-
- + + + + diff --git a/imxweb/projects/aob/src/lib/applications/identities/identity-detail/identity-detail.component.ts b/imxweb/projects/aob/src/lib/applications/identities/identity-detail/identity-detail.component.ts index 27ffd3885..36686f013 100644 --- a/imxweb/projects/aob/src/lib/applications/identities/identity-detail/identity-detail.component.ts +++ b/imxweb/projects/aob/src/lib/applications/identities/identity-detail/identity-detail.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,16 +25,16 @@ */ import { Component, Inject, OnInit } from '@angular/core'; -import { EuiLoadingService, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; -import { PortalApplicationIdentitiesbyidentity } from 'imx-api-aob'; +import { EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { PortalApplicationIdentitiesbyidentity } from '@imx-modules/imx-api-aob'; import { CollectionLoadParameters, DisplayColumns, EntitySchema, ExtendedTypedEntityCollection, IClientProperty, -} from 'imx-qbm-dbts'; -import { DataSourceToolbarSettings, ImxTranslationProviderService } from 'qbm'; +} from '@imx-modules/imx-qbm-dbts'; +import { DataSourceToolbarSettings, DataViewSource, ImxTranslationProviderService } from 'qbm'; import { IdentityDetailData } from '../identity-detail-data'; import { IdentityService } from '../identity.service'; @@ -42,6 +42,7 @@ import { IdentityService } from '../identity.service'; selector: 'imx-identity-detail', templateUrl: './identity-detail.component.html', styleUrls: ['./identity-detail.component.scss'], + providers: [DataViewSource], }) export class IdentityDetailComponent implements OnInit { public dstSettings: DataSourceToolbarSettings; @@ -53,15 +54,10 @@ export class IdentityDetailComponent implements OnInit { constructor( @Inject(EUI_SIDESHEET_DATA) public data: IdentityDetailData, private readonly identityService: IdentityService, - private readonly busyService: EuiLoadingService, public readonly translateProvider: ImxTranslationProviderService, + public dataSource: DataViewSource, ) { this.entitySchema = this.identityService.getByIdentitySchema(); - this.navigationState = { - PageSize: 25, - StartIndex: 0, - search: null, - }; this.displayedColumns = [ this.entitySchema.Columns[DisplayColumns.DISPLAY_PROPERTYNAME], this.entitySchema.Columns['OrderState'], @@ -70,35 +66,15 @@ export class IdentityDetailComponent implements OnInit { } public async ngOnInit(): Promise { - await this.setDst(); - } - - public async onNavigationStateChanged(navigationState: CollectionLoadParameters): Promise { - this.navigationState = navigationState; - this.setDst(); - } - - public async onSearch(keywords: string): Promise { - this.navigationState.StartIndex = 0; - this.navigationState.search = keywords; - this.setDst(); - } - - private async setDst(): Promise { - this.dstSettings = { - dataSource: await this.getData(), - entitySchema: this.entitySchema, - navigationState: this.navigationState, - displayedColumns: this.displayedColumns, - }; - } - - private async getData(): Promise> { - this.busyService.show(); - try { - return await this.identityService.getByIdentity(this.data.application.GetEntity().GetKeys()[0], this.data.selectedItem.GetEntity().GetKeys()[0], this.navigationState); - } finally { - this.busyService.hide(); - } + this.dataSource.init({ + execute: (params: CollectionLoadParameters): Promise> => + this.identityService.getByIdentity( + this.data.application.GetEntity().GetKeys()[0], + this.data.selectedItem?.GetEntity().GetKeys()[0] || '', + params, + ), + schema: this.entitySchema, + columnsToDisplay: this.displayedColumns, + }); } } diff --git a/imxweb/projects/aob/src/lib/applications/identities/identity.service.ts b/imxweb/projects/aob/src/lib/applications/identities/identity.service.ts index 922ac573f..4bec7b212 100644 --- a/imxweb/projects/aob/src/lib/applications/identities/identity.service.ts +++ b/imxweb/projects/aob/src/lib/applications/identities/identity.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,18 +25,20 @@ */ import { Injectable } from '@angular/core'; -import { PortalApplicationIdentities, PortalApplicationIdentitiesbyidentity } from 'imx-api-aob'; -import { CollectionLoadParameters, EntitySchema, ExtendedTypedEntityCollection } from 'imx-qbm-dbts'; +import { PortalApplicationIdentities, PortalApplicationIdentitiesbyidentity } from '@imx-modules/imx-api-aob'; +import { CollectionLoadParameters, EntitySchema, ExtendedTypedEntityCollection } from '@imx-modules/imx-qbm-dbts'; import { AobApiService } from '../../aob-api-client.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class IdentityService { + constructor(private readonly api: AobApiService) {} - constructor(private readonly api: AobApiService) { } - - public async get(applicationId: string, params?: CollectionLoadParameters): Promise> { + public async get( + applicationId: string, + params?: CollectionLoadParameters, + ): Promise> { return await this.api.typedClient.PortalApplicationIdentities.Get(applicationId, params); } @@ -44,12 +46,15 @@ export class IdentityService { return this.api.typedClient.PortalApplicationIdentities.GetSchema(); } - public async getByIdentity(applicationId: string, identityId: string, params?: CollectionLoadParameters): Promise> { + public async getByIdentity( + applicationId: string, + identityId: string, + params?: CollectionLoadParameters, + ): Promise> { return await this.api.typedClient.PortalApplicationIdentitiesbyidentity.Getbyidentity(applicationId, identityId, params); } public getByIdentitySchema(): EntitySchema { return this.api.typedClient.PortalApplicationIdentitiesbyidentity.GetSchema(); } - } diff --git a/imxweb/projects/aob/src/lib/column-info/column-info.component.html b/imxweb/projects/aob/src/lib/column-info/column-info.component.html index 1f7f32672..4488436a5 100644 --- a/imxweb/projects/aob/src/lib/column-info/column-info.component.html +++ b/imxweb/projects/aob/src/lib/column-info/column-info.component.html @@ -1,5 +1,5 @@ - +
- {{ property?.Column.GetDisplayValue() || ('-' + (placeholder || ('#LDS#Not set' | translate)) + '-') }} + {{ property?.Column?.GetDisplayValue() || '-' + (placeholder || ('#LDS#Not set' | translate)) + '-' }}
-
\ No newline at end of file +
diff --git a/imxweb/projects/aob/src/lib/column-info/column-info.component.scss b/imxweb/projects/aob/src/lib/column-info/column-info.component.scss index 19f8c1fcc..1ced8b697 100644 --- a/imxweb/projects/aob/src/lib/column-info/column-info.component.scss +++ b/imxweb/projects/aob/src/lib/column-info/column-info.component.scss @@ -5,7 +5,7 @@ overflow: hidden; text-overflow: ellipsis; - >span { + > span { font-weight: 600; color: $black-6; } @@ -14,7 +14,7 @@ .eui-dark-theme { :host { .imx-user-properties { - >span { + > span { color: $color-gray-10; } } @@ -24,7 +24,7 @@ .eui-contrast-theme { :host { .imx-user-properties { - >span { + > span { color: $color-gray-0; } } diff --git a/imxweb/projects/aob/src/lib/column-info/column-info.component.ts b/imxweb/projects/aob/src/lib/column-info/column-info.component.ts index 76e163352..5944444c4 100644 --- a/imxweb/projects/aob/src/lib/column-info/column-info.component.ts +++ b/imxweb/projects/aob/src/lib/column-info/column-info.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,12 +26,12 @@ import { Component, Input } from '@angular/core'; -import { EntityValue } from 'imx-qbm-dbts'; +import { EntityValue } from '@imx-modules/imx-qbm-dbts'; @Component({ selector: 'imx-column-info', templateUrl: './column-info.component.html', - styleUrls: ['./column-info.component.scss'] + styleUrls: ['./column-info.component.scss'], }) export class ColumnInfoComponent { @Input() public property: EntityValue; diff --git a/imxweb/projects/aob/src/lib/column-info/column-info.module.ts b/imxweb/projects/aob/src/lib/column-info/column-info.module.ts index 284305d82..739aa8ada 100644 --- a/imxweb/projects/aob/src/lib/column-info/column-info.module.ts +++ b/imxweb/projects/aob/src/lib/column-info/column-info.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -49,8 +49,8 @@ import { ApplicationPropertyModule } from '../application-property/application-p ReactiveFormsModule, MatButtonModule, MatFormFieldModule, - MatInputModule + MatInputModule, ], - exports: [ColumnInfoComponent] + exports: [ColumnInfoComponent], }) -export class ColumnInfoModule { } +export class ColumnInfoModule {} diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlement-add/entitlements-add.component.html b/imxweb/projects/aob/src/lib/entitlements/entitlement-add/entitlements-add.component.html index 2587ada98..8124ed285 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlement-add/entitlements-add.component.html +++ b/imxweb/projects/aob/src/lib/entitlements/entitlement-add/entitlements-add.component.html @@ -1,75 +1,89 @@
- - {{LdsInfoAlert | translate}} + + {{ LdsInfoAlert | translate }} - -
- - {{'#LDS#Application entitlement type' | translate }} - - - {{entitlementSourceType.display}} - - - -
- - - - - - - + + +
+ + {{ '#LDS#Application entitlement type' | translate }} + + + {{ entitlementSourceType.display }} + + + + +
+ + + {{ entitySchema?.Columns?.[DisplayColumns.DISPLAY_PROPERTYNAME]?.Display }} + + +
{{ item?.GetEntity().GetDisplay() }}
+
+ +
{{ item?.GetEntity().GetDisplay() }}
+
{{ item?.Description?.Column.GetDisplayValue() }}
+
+
- - - -
{{ data?.GetEntity().GetDisplay() }}
-
{{ data?.Description.Column.GetDisplayValue()}}
-
-
- - - - - - + + {{ entitySchema?.Columns?.CanonicalName?.Display }} + +
{{ item.CanonicalName?.Column.GetDisplayValue() }}
+
-
- + + {{ entitySchema?.Columns?.UID_UNSRoot?.Display }} + +
{{ item.UID_UNSRoot?.Column.GetDisplayValue() }}
+ +
+ +
+ + +
-
-

- - {{selections?.length || 0}} - {{'#LDS#Selected application entitlements' | translate }} -

- - -
diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlement-add/entitlements-add.component.scss b/imxweb/projects/aob/src/lib/entitlements/entitlement-add/entitlements-add.component.scss index bd50127f4..89541e189 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlement-add/entitlements-add.component.scss +++ b/imxweb/projects/aob/src/lib/entitlements/entitlement-add/entitlements-add.component.scss @@ -1,42 +1,11 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; +@import 'base/mixins.scss'; -.imx-helper-alert { - margin: 10px 0 20px auto; - width: 100%; +.eui-sidesheet-content{ + @include flex-column-container() } - -.imx-selected-entitlements { - margin: 0; - line-height: 36px; - flex: auto; - - ::ng-deep .imx-badge.eui-badge .eui-badge-content { - font-size: 14px; - line-height: 14px; - } -} - -.imx-badge { - background-color: inherit; -} - -.eui-sidesheet-content { - display: flex; - flex-direction: column; - .imx-sidesheet-content { - flex: 1 1 auto; - height: inherit; - overflow: hidden; - display: flex; - flex-direction: column; - } -} - .eui-sidesheet-actions { > * { align-self: center; } - > *:not(:last-child) { - margin-right: 10px; - } } diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlement-add/entitlements-add.component.ts b/imxweb/projects/aob/src/lib/entitlements/entitlement-add/entitlements-add.component.ts index d683dde62..95b657f5b 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlement-add/entitlements-add.component.ts +++ b/imxweb/projects/aob/src/lib/entitlements/entitlement-add/entitlements-add.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,25 +24,34 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; -import { Component, OnInit, Inject } from '@angular/core'; -import { EuiLoadingService, EuiSidesheetRef, EuiSidesheetService, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; -import { TranslateService } from '@ngx-translate/core'; +import { Component, Inject, OnInit } from '@angular/core'; import { MatSelectChange } from '@angular/material/select'; +import { EUI_SIDESHEET_DATA, EuiLoadingService, EuiSidesheetRef, EuiSidesheetService } from '@elemental-ui/core'; +import { TranslateService } from '@ngx-translate/core'; +import { EntitlementSystemRoleInput } from '@imx-modules/imx-api-aob'; import { - DataSourceToolbarSettings, + CollectionLoadParameters, + DataModel, + DisplayColumns, + EntitySchema, + IClientProperty, + TypedEntity, + TypedEntityCollectionData, +} from '@imx-modules/imx-qbm-dbts'; +import { + calculateSidesheetWidth, + CdrFactoryService, ClassloggerService, + DataViewInitParameters, + DataViewSource, HELPER_ALERT_KEY_PREFIX, - StorageService, MetadataService, - DataSourceToolbarFilter, - CdrFactoryService, + SettingsService, + StorageService, } from 'qbm'; -import { CollectionLoadParameters, IClientProperty, EntitySchema, DisplayColumns, TypedEntity, FilterData } from 'imx-qbm-dbts'; -import { EntitlementsService } from '../entitlements.service'; import { AddEntitlementParameter, EntitlementSourceType, EntitlementsType } from '../entitlements.model'; -import { EntitlementSystemRoleInput } from 'imx-api-aob'; +import { EntitlementsService } from '../entitlements.service'; import { SystemRoleConfigComponent } from './system-role-config/system-role-config.component'; const helperAlertKey = `${HELPER_ALERT_KEY_PREFIX}_addNewEntitlements`; @@ -54,15 +63,13 @@ const helperAlertKey = `${HELPER_ALERT_KEY_PREFIX}_addNewEntitlements`; selector: 'imx-entitlements-add', templateUrl: './entitlements-add.component.html', styleUrls: ['./entitlements-add.component.scss'], + providers: [DataViewSource], }) export class EntitlementsAddComponent implements OnInit { public readonly EntitlementsType = EntitlementsType; // Enables use of this Enum in Angular Templates. public readonly DisplayColumns = DisplayColumns; // Enables use of this static class in Angular Templates. - public settings: DataSourceToolbarSettings; public entitySchema: EntitySchema; - public navigationState: CollectionLoadParameters; public displayedColumns: IClientProperty[] = []; - public highlightedEntity: TypedEntity; public selectedSourceType: EntitlementsType = EntitlementsType.UnsGroup; public isSystemRolesEnabled: boolean; public entitlementsLabel: string; @@ -74,8 +81,6 @@ export class EntitlementsAddComponent implements OnInit { return !this.storageService.isHelperAlertDismissed(helperAlertKey); } - private filters: DataSourceToolbarFilter[]; - constructor( public readonly sidesheetRef: EuiSidesheetRef, @Inject(EUI_SIDESHEET_DATA) private data: AddEntitlementParameter, @@ -86,6 +91,8 @@ export class EntitlementsAddComponent implements OnInit { private readonly metaData: MetadataService, private readonly sidesheet: EuiSidesheetService, private readonly translateService: TranslateService, + public dataSource: DataViewSource, + public readonly settingsService: SettingsService, ) { this.selectedSourceType = data.defaultType; this.isSystemRolesEnabled = data.isSystemRolesEnabled; @@ -97,14 +104,14 @@ export class EntitlementsAddComponent implements OnInit { await this.metaData.updateNonExisting(['ESet', 'UNSGroup', 'QERResource', 'RPSReport', 'TSBAccountDef']); this.entitlementSourceTypes = [ - { entitlementsType: EntitlementsType.UnsGroup, display: this.metaData.tables.UNSGroup.Display }, - { entitlementsType: EntitlementsType.Qerresource, display: this.metaData.tables.QERResource.Display }, - { entitlementsType: EntitlementsType.Rpsreport, display: this.metaData.tables.RPSReport.Display }, - { entitlementsType: EntitlementsType.Tsbaccountdef, display: this.metaData.tables.TSBAccountDef.Display }, + { entitlementsType: EntitlementsType.UnsGroup, display: this.metaData.tables.UNSGroup?.Display || '' }, + { entitlementsType: EntitlementsType.Qerresource, display: this.metaData.tables.QERResource?.Display || '' }, + { entitlementsType: EntitlementsType.Rpsreport, display: this.metaData.tables.RPSReport?.Display || '' }, + { entitlementsType: EntitlementsType.Tsbaccountdef, display: this.metaData.tables.TSBAccountDef?.Display || '' }, ]; if (this.isSystemRolesEnabled) { - this.entitlementSourceTypes.unshift({ entitlementsType: EntitlementsType.Eset, display: this.metaData.tables.ESet.Display }); + this.entitlementSourceTypes.unshift({ entitlementsType: EntitlementsType.Eset, display: this.metaData.tables.ESet?.Display || '' }); } await this.useSource(this.selectedSourceType); @@ -132,10 +139,9 @@ export class EntitlementsAddComponent implements OnInit { public async onCreateRole(): Promise { const entitlementSystemRoleInput: EntitlementSystemRoleInput = await this.sidesheet .open(SystemRoleConfigComponent, { - // TODO: make LDS Heading - title: await this.translateService.get('#LDS#Create new system role').toPromise(), + title: await this.translateService.get('#LDS#Heading Create Empty System Role').toPromise(), padding: '0px', - width: 'max(500px, 50%)', + width: calculateSidesheetWidth(800, 0.5), testId: 'create-role-sidesheet', data: { uid: this.data.uidApplication, createOnly: true }, }) @@ -158,7 +164,7 @@ export class EntitlementsAddComponent implements OnInit { .open(SystemRoleConfigComponent, { title: await this.translateService.get('#LDS#Heading Merge Application Entitlements into System Role').toPromise(), padding: '0px', - width: 'max(500px, 50%)', + width: calculateSidesheetWidth(800, 0.5), testId: 'add-to-existing-role-sidesheet', data: { uid: this.data.uidApplication, createOnly: false }, }) @@ -188,77 +194,41 @@ export class EntitlementsAddComponent implements OnInit { this.selections = selection; } - public onHighlightedEntityChanged(entity: TypedEntity): void { - this.highlightedEntity = entity; - } - - public onNavigationStateChanged(newState: CollectionLoadParameters): void { - this.navigationState = newState; - this.updateCandidates(); - } - - public onSearch(keywords: string): void { - this.logger.debug(this, `Searching for: ${keywords}`); - this.navigationState.StartIndex = 0; - this.navigationState.search = keywords; - this.updateCandidates(); - } - - public async onFilterByTree(filters: FilterData[]): Promise { - this.navigationState.filter = filters; - this.updateCandidates(); - } - private async useSource(type: EntitlementsType): Promise { this.selectedSourceType = type; this.entitySchema = this.entitlementsProvider.candidateSchema(type); - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.busyService.show())); - try { - this.filters = (await this.entitlementsProvider.getDataModel(type))?.Filters; - } finally { - setTimeout(() => this.busyService.hide(overlayRef)); - } - - this.displayedColumns = this.getDisplayedColumnsForEntitlement(this.entitySchema, type); - this.navigationState = { StartIndex: 0, PageSize: 25 }; - this.highlightedEntity = null; - - this.updateCandidates(); - } - private async updateCandidates(): Promise { - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.busyService.show())); + let dataModel: DataModel | undefined = undefined; + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } try { - this.logger.debug(this, 'Get candidates from the server...'); - const data = await this.entitlementsProvider.getCandidates(this.selectedSourceType, this.navigationState); - - this.settings = { - dataSource: data, - displayedColumns: this.displayedColumns, - entitySchema: this.entitySchema, - navigationState: this.navigationState, - filters: this.filters, - filterTree: - this.selectedSourceType === EntitlementsType.UnsGroup - ? { - filterMethode: async (parentkey) => { - return this.entitlementsProvider.getEntitlementsFilterTree({ - parentkey, - }); - }, - multiSelect: false, - } - : undefined, - }; - - if (data == null) { - this.logger.error(this, 'TypedEntityCollectionData is undefined'); - } + dataModel = await this.entitlementsProvider.getDataModel(type); } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } + this.displayedColumns = this.getDisplayedColumnsForEntitlement(this.entitySchema, type); + this.dataSource.state.set({ PageSize: this.settingsService?.DefaultPageSize, StartIndex: 0 }); + const dataViewInitParameters: DataViewInitParameters = { + execute: (params: CollectionLoadParameters): Promise | undefined> => + this.entitlementsProvider.getCandidates(this.selectedSourceType, params), + schema: this.entitySchema, + columnsToDisplay: this.displayedColumns, + dataModel, + selectionChange: (selection: Array) => this.onSelectionChanged(selection), + filterTree: + this.selectedSourceType === EntitlementsType.UnsGroup + ? { + filterMethode: async (parentkey) => { + return this.entitlementsProvider.getEntitlementsFilterTree({ + parentkey, + }); + }, + multiSelect: false, + } + : undefined, + }; + await this.dataSource.init(dataViewInitParameters); } private getDisplayedColumnsForEntitlement(entitySchema: EntitySchema, type: EntitlementsType): IClientProperty[] { diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlement-add/system-role-config/system-role-config.component.html b/imxweb/projects/aob/src/lib/entitlements/entitlement-add/system-role-config/system-role-config.component.html index d8e762dd4..0bae06b32 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlement-add/system-role-config/system-role-config.component.html +++ b/imxweb/projects/aob/src/lib/entitlements/entitlement-add/system-role-config/system-role-config.component.html @@ -1,31 +1,38 @@ -
- - {{ LdsAddOrCreate | translate }} +
+ + {{ (data.createOnly ? LdsCreate : LdsAddOrCreate) | translate }} - - + - {{'#LDS#Create new system role' | translate}} + {{ '#LDS#Create new system role' | translate }} - {{'#LDS#Add to existing system role' | translate}} + {{ '#LDS#Add to existing system role' | translate }}
#LDS#Name - +
- - + + - + [attr.data-imx-identifier]="'multi-select-formcontrol-list' + candidate.GetEntity().GetKeys()[0]" + >
{{ candidate.GetEntity().GetDisplay() }}
@@ -37,17 +44,28 @@

#LDS#There are currently no system roles.

-
-
- -
diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlement-add/system-role-config/system-role-config.component.scss b/imxweb/projects/aob/src/lib/entitlements/entitlement-add/system-role-config/system-role-config.component.scss index 250a9b378..9067d07bf 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlement-add/system-role-config/system-role-config.component.scss +++ b/imxweb/projects/aob/src/lib/entitlements/entitlement-add/system-role-config/system-role-config.component.scss @@ -1,4 +1,5 @@ -@import '@elemental-ui/core/src/styles/_palette.scss'; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/mixins'; .imx-viewport { height: 300px; @@ -6,27 +7,12 @@ overflow-x: hidden; } -.imx-helper-alert { - margin: 10px 0 20px auto; - width: 100%; -} - -.mat-radio-button { +.mat-mdc-radio-button { margin-right: 20px; } -.mat-radio-group { - padding: 10px 10px 30px 10px; - display: flex; - flex-direction: row; -} - .eui-sidesheet-content { - height: inherit; - overflow: hidden; - display: flex; - flex-direction: column; - + @include flex-column-container($overflow: hidden, $height: inherit); .imx-content-card { flex: 1 1 auto; @@ -37,14 +23,9 @@ text-align: center; margin: 20px 0; - .eui-icon { - font-size: 100px; - color: rgba($black-c, 0.55); - } - p { margin: 0; font-size: 18px; - color: $black-9; + color: $color-gray-40; } } diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlement-add/system-role-config/system-role-config.component.ts b/imxweb/projects/aob/src/lib/entitlements/entitlement-add/system-role-config/system-role-config.component.ts index 58b5dcf8c..a4e765841 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlement-add/system-role-config/system-role-config.component.ts +++ b/imxweb/projects/aob/src/lib/entitlements/entitlement-add/system-role-config/system-role-config.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,28 +25,26 @@ */ import { ListRange } from '@angular/cdk/collections'; -import { OverlayRef } from '@angular/cdk/overlay'; import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; -import { Subscription } from 'rxjs'; import { AfterViewInit, ChangeDetectorRef, Component, Inject, OnDestroy, ViewChild } from '@angular/core'; import { UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; import { MatSelectionListChange } from '@angular/material/list'; import { MatRadioChange } from '@angular/material/radio'; -import { EuiLoadingService, EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { EUI_SIDESHEET_DATA, EuiLoadingService, EuiSidesheetRef } from '@elemental-ui/core'; +import { Subscription } from 'rxjs'; -import { CollectionLoadParameters, TypedEntity } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { SettingsService } from 'qbm'; import { SystemRoleConfigService } from './system-role-config.service'; @Component({ templateUrl: './system-role-config.component.html', - styleUrls: ['./system-role-config.component.scss'] + styleUrls: ['./system-role-config.component.scss'], }) export class SystemRoleConfigComponent implements AfterViewInit, OnDestroy { - public readonly form: UntypedFormGroup; public type: 'new' | 'existing' = 'new'; - public selectedRole: string; + public selectedRole: string | undefined; public showHelperAlert = true; @@ -56,9 +54,8 @@ export class SystemRoleConfigComponent implements AfterViewInit, OnDestroy { private parameters: CollectionLoadParameters; private readonly subscriptions: Subscription[] = []; - constructor( - @Inject(EUI_SIDESHEET_DATA) public readonly data: { uid: string, createOnly: boolean }, + @Inject(EUI_SIDESHEET_DATA) public readonly data: { uid: string; createOnly: boolean }, private readonly sidesheetRef: EuiSidesheetRef, private readonly busyService: EuiLoadingService, private readonly addToExistingProvider: SystemRoleConfigService, @@ -72,40 +69,44 @@ export class SystemRoleConfigComponent implements AfterViewInit, OnDestroy { this.parameters = { PageSize: this.settingsService.DefaultPageSize, StartIndex: 0 }; if (this.viewport) { - this.subscriptions.push(this.viewport.renderedRangeStream.subscribe(async (range: ListRange) => { - if (range.end === (this.settingsService.DefaultPageSize + this.parameters.StartIndex)) { - this.parameters.StartIndex += this.settingsService.DefaultPageSize; + this.subscriptions.push( + this.viewport.renderedRangeStream.subscribe(async (range: ListRange) => { + if (range.end === this.settingsService.DefaultPageSize + (this.parameters.StartIndex || 0)) { + this.parameters.StartIndex = (this.parameters.StartIndex || 0) + this.settingsService.DefaultPageSize; - const tmpCandidates = Object.assign([], this.candidates); - await this.loadData(this.parameters); + const tmpCandidates = Object.assign([], this.candidates); + await this.loadData(this.parameters); - this.candidates.unshift(...tmpCandidates); - this.changeDetectorRef.detectChanges(); - } - })); + this.candidates.unshift(...tmpCandidates); + this.changeDetectorRef.detectChanges(); + } + }), + ); } } public ngOnDestroy(): void { - this.subscriptions.forEach(s => s.unsubscribe()); + this.subscriptions.forEach((s) => s.unsubscribe()); } public async typeChanged(evt: MatRadioChange): Promise { if (evt.value === 'existing') { if (this.viewport) { - this.subscriptions.push(this.viewport.renderedRangeStream.subscribe(async (range: ListRange) => { - if (range.end === (this.settingsService.DefaultPageSize + this.parameters.StartIndex)) { - this.parameters.StartIndex += this.settingsService.DefaultPageSize; - - const tmpCandidates = Object.assign([], this.candidates); - await this.loadData(this.parameters); - - this.candidates.unshift(...tmpCandidates); - this.changeDetectorRef.detectChanges(); - } - })); + this.subscriptions.push( + this.viewport.renderedRangeStream.subscribe(async (range: ListRange) => { + if (range.end === this.settingsService.DefaultPageSize + (this.parameters.StartIndex || 0)) { + this.parameters.StartIndex = (this.parameters.StartIndex || 0) + this.settingsService.DefaultPageSize; + + const tmpCandidates = Object.assign([], this.candidates); + await this.loadData(this.parameters); + + this.candidates.unshift(...tmpCandidates); + this.changeDetectorRef.detectChanges(); + } + }), + ); } - this.selectedRole = null; + this.selectedRole = undefined; await this.loadData({ ...this.parameters, ...{ StartIndex: 0 } }); } } @@ -120,23 +121,26 @@ export class SystemRoleConfigComponent implements AfterViewInit, OnDestroy { public close(createNew: boolean): void { if (createNew) { - this.sidesheetRef.close({ NameNewEset: this.form.get('name').value }); + this.sidesheetRef.close({ NameNewEset: this.form.get('name')?.value }); } else { this.sidesheetRef.close({ UidEset: this.selectedRole }); } } private async loadData(newState?: CollectionLoadParameters): Promise { - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } try { - this.candidates = (await this.addToExistingProvider.getExistingRoles(this.data.uid, newState)) - .Data; + this.candidates = (await this.addToExistingProvider.getExistingRoles(this.data.uid, newState || {})).Data; } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } } - public LdsAddOrCreate = '#LDS#Here you can merge the application entitlements into a new system role or add the application entitlements to an existing system role. The system role will then be assigned to the application as an application entitlement.'; + public LdsAddOrCreate = + '#LDS#Here you can merge the application entitlements into a new system role or add the application entitlements to an existing system role. The system role will then be assigned to the application as an application entitlement.'; + public LdsCreate = + '#LDS#Here you can create an empty system role. The system role will then be assigned to the application as an application entitlement.'; } diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlement-add/system-role-config/system-role-config.service.ts b/imxweb/projects/aob/src/lib/entitlements/entitlement-add/system-role-config/system-role-config.service.ts index 5f1760132..48d06ef2c 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlement-add/system-role-config/system-role-config.service.ts +++ b/imxweb/projects/aob/src/lib/entitlements/entitlement-add/system-role-config/system-role-config.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,27 +25,25 @@ */ import { Injectable } from '@angular/core'; -import { PortalEntitlementSystemrole } from 'imx-api-aob'; +import { PortalEntitlementSystemrole } from '@imx-modules/imx-api-aob'; -import { CollectionLoadParameters, EntitySchema, TypedEntityCollectionData } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, EntitySchema, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { AobApiService } from '../../../aob-api-client.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class SystemRoleConfigService { - - constructor( - private readonly aobClient: AobApiService, - ) { - } + constructor(private readonly aobClient: AobApiService) {} public get roleSchema(): EntitySchema { return this.aobClient.typedClient.PortalEntitlementcandidatesEset.GetSchema(); } - public async getExistingRoles(application: string, parameters: CollectionLoadParameters): - Promise> { + public async getExistingRoles( + application: string, + parameters: CollectionLoadParameters, + ): Promise> { return this.aobClient.typedClient.PortalEntitlementSystemrole.Get(application, parameters); } } diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlement-detail/entitlement-detail.component.html b/imxweb/projects/aob/src/lib/entitlements/entitlement-detail/entitlement-detail.component.html index d76de4206..17d2d95d5 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlement-detail/entitlement-detail.component.html +++ b/imxweb/projects/aob/src/lib/entitlements/entitlement-detail/entitlement-detail.component.html @@ -2,5 +2,6 @@ [entitlement]="data?.entitlement" [serviceItem]="data?.serviceItem" (controlCreated)="form.addControl('entitlementedit', $event)" - (saved)="afterSave($event)"> + (saved)="afterSave($event)" +> diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlement-detail/entitlement-detail.component.scss b/imxweb/projects/aob/src/lib/entitlements/entitlement-detail/entitlement-detail.component.scss index 3364f1f4f..c9e89c84d 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlement-detail/entitlement-detail.component.scss +++ b/imxweb/projects/aob/src/lib/entitlements/entitlement-detail/entitlement-detail.component.scss @@ -1 +1 @@ -// styles \ No newline at end of file +// styles diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlement-detail/entitlement-detail.component.ts b/imxweb/projects/aob/src/lib/entitlements/entitlement-detail/entitlement-detail.component.ts index c598d71e3..0bff3fd8e 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlement-detail/entitlement-detail.component.ts +++ b/imxweb/projects/aob/src/lib/entitlements/entitlement-detail/entitlement-detail.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,17 +26,17 @@ import { Component, Inject, OnDestroy } from '@angular/core'; import { UntypedFormGroup } from '@angular/forms'; -import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; import { Subscription } from 'rxjs'; +import { TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { ConfirmationService, SnackBarService } from 'qbm'; -import { TypedEntity } from 'imx-qbm-dbts'; import { EntitlementWrapper } from '../entitlement-wrapper.interface'; @Component({ selector: 'imx-entitlement-detail', templateUrl: './entitlement-detail.component.html', - styleUrls: ['./entitlement-detail.component.scss'] + styleUrls: ['./entitlement-detail.component.scss'], }) export class EntitlementDetailComponent implements OnDestroy { public readonly form = new UntypedFormGroup({}); @@ -47,35 +47,37 @@ export class EntitlementDetailComponent implements OnDestroy { @Inject(EUI_SIDESHEET_DATA) public readonly data: EntitlementWrapper, private readonly sidesheetRef: EuiSidesheetRef, private readonly snackbar: SnackBarService, - confirmation: ConfirmationService + confirmation: ConfirmationService, ) { - this.subscriptions.push(this.sidesheetRef.closeClicked().subscribe(async () => { - const entitlementHasChanges = this.entityHasChanges(this.data.entitlement); - const serviceItemHasChanges = this.entityHasChanges(this.data.serviceItem); - const hasChanges = entitlementHasChanges || serviceItemHasChanges || !this.form.pristine; + this.subscriptions.push( + this.sidesheetRef.closeClicked().subscribe(async () => { + const entitlementHasChanges = this.entityHasChanges(this.data.entitlement); + const serviceItemHasChanges = this.entityHasChanges(this.data?.serviceItem); + const hasChanges = entitlementHasChanges || serviceItemHasChanges || !this.form.pristine; - if (hasChanges && !(await confirmation.confirmLeaveWithUnsavedChanges())) { - return; - } + if (hasChanges && !(await confirmation.confirmLeaveWithUnsavedChanges())) { + return; + } - if (entitlementHasChanges) { - await this.data.entitlement.GetEntity().DiscardChanges(); - } + if (entitlementHasChanges) { + await this.data.entitlement?.GetEntity().DiscardChanges(); + } - if (serviceItemHasChanges) { - await this.data.serviceItem.GetEntity().DiscardChanges(); - } + if (serviceItemHasChanges) { + await this.data?.serviceItem?.GetEntity().DiscardChanges(); + } - if (hasChanges) { - this.snackbar.open({ key: '#LDS#The changes were discarded.' }); - } + if (hasChanges) { + this.snackbar.open({ key: '#LDS#The changes were discarded.' }); + } - this.sidesheetRef.close(false); - })); + this.sidesheetRef.close(false); + }), + ); } public ngOnDestroy(): void { - this.subscriptions.forEach(s => s.unsubscribe()); + this.subscriptions.forEach((s) => s.unsubscribe()); } public afterSave(success: boolean): void { @@ -86,7 +88,7 @@ export class EntitlementDetailComponent implements OnDestroy { this.sidesheetRef.close(true); } - private entityHasChanges(entity: TypedEntity): boolean { - return entity != null && entity.GetEntity().GetDiffData()?.Data?.length > 0; + private entityHasChanges(entity: TypedEntity | undefined): boolean { + return entity != null && !!entity?.GetEntity().GetDiffData()?.Data?.length; } } diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/entitlement-edit-auto-add.component.html b/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/entitlement-edit-auto-add.component.html index 317f5c16a..0bfcc1428 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/entitlement-edit-auto-add.component.html +++ b/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/entitlement-edit-auto-add.component.html @@ -1,13 +1,32 @@ -
- - - - +
+ + + +
-
- + -
diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/entitlement-edit-auto-add.component.scss b/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/entitlement-edit-auto-add.component.scss deleted file mode 100644 index bbfe24996..000000000 --- a/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/entitlement-edit-auto-add.component.scss +++ /dev/null @@ -1,24 +0,0 @@ -.eui-sidesheet-content { - height: inherit; - overflow: hidden; - display: flex; - flex-direction: column; - - .imx-content-card { - flex: 1 1 auto; - overflow: auto; - } -} - -.eui-sidesheet-actions { - .justify-start { - margin-right: auto; - display: flex; - flex-direction: column; - justify-content: center; - } - - button:not(:last-of-type):not(.justify-start) { - margin-right: 10px; - } -} \ No newline at end of file diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/entitlement-edit-auto-add.component.ts b/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/entitlement-edit-auto-add.component.ts index 9054ec639..9a6d97453 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/entitlement-edit-auto-add.component.ts +++ b/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/entitlement-edit-auto-add.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,23 +24,21 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; import { Component, Inject, OnDestroy } from '@angular/core'; -import { EuiLoadingService, EuiSidesheetRef, EuiSidesheetService, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { EUI_SIDESHEET_DATA, EuiLoadingService, EuiSidesheetRef, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { Subscription } from 'rxjs'; import _ from 'lodash'; +import { Subscription } from 'rxjs'; -import { EntitlementToAddData, PortalApplication } from 'imx-api-aob'; -import { isExpressionInvalid, SqlExpression, SqlWizardExpression } from 'imx-qbm-dbts'; -import { ConfirmationService, SnackBarService } from 'qbm'; +import { EntitlementToAddData, PortalApplication, SqlWizardWrite } from '@imx-modules/imx-api-aob'; +import { isExpressionInvalid, SqlExpression, SqlWizardExpression } from '@imx-modules/imx-qbm-dbts'; +import { calculateSidesheetWidth, ConfirmationService, SnackBarService } from 'qbm'; import { EntitlementEditAutoAddService } from './entitlement-edit-auto-add.service'; import { EntitlementToAddDataWrapperService } from './entitlement-to-add-data-wrapper.service'; import { MappedEntitlementsPreviewComponent } from './mapped-entitlements-preview/mapped-entitlements-preview.component'; @Component({ templateUrl: './entitlement-edit-auto-add.component.html', - styleUrls: ['./entitlement-edit-auto-add.component.scss'], }) export class EntitlementEditAutoAddComponent implements OnDestroy { private subscriptions: Subscription[] = []; @@ -66,12 +64,12 @@ export class EntitlementEditAutoAddComponent implements OnDestroy { private readonly snackbar: SnackBarService, private readonly sidesheet: EuiSidesheetService, private readonly translateService: TranslateService, - confirm: ConfirmationService + confirm: ConfirmationService, ) { this.subscriptions.push( sidesheetRef.closeClicked().subscribe(async () => { if (this.exprHasntChanged || (await confirm.confirmLeaveWithUnsavedChanges())) sidesheetRef.close(this.reload); - }) + }), ); this.sqlExpression = _.cloneDeep(data.sqlExpression); } @@ -81,29 +79,25 @@ export class EntitlementEditAutoAddComponent implements OnDestroy { } public async showResults(withSave: boolean): Promise { - let overlay: OverlayRef; - setTimeout(() => { - overlay = this.busyService.show(); - }); + this.showBusyIndicator(); let elements: EntitlementToAddData; try { await this.setExtendedData(this.sqlExpression.Expression); elements = await this.svc.showEntitlementsToMap(this.data.application.InteractiveEntityStateData); } finally { - setTimeout(() => { - this.busyService.hide(overlay); - }); + this.busyService.hide(); } if (!elements) { return; } const entitlementToAdd = this.entitlementToAddWrapperService.buildTypedEntities(elements); - const saveChanges: { save: boolean; map: boolean } = await this.sidesheet.open(MappedEntitlementsPreviewComponent, { + const saveChanges: { save: boolean; map: boolean } = await this.sidesheet + .open(MappedEntitlementsPreviewComponent, { title: await this.translateService.get('#LDS#Heading View Matching Application Entitlements').toPromise(), subTitle: this.data.application.GetEntity().GetDisplay(), padding: '0px', - width: 'max(550px, 55%)', + width: calculateSidesheetWidth(1000), testId: 'mapped-entitlements-preview-sidesheet', data: { entitlementToAdd, @@ -116,18 +110,14 @@ export class EntitlementEditAutoAddComponent implements OnDestroy { this.reload = true; if (withSave && saveChanges?.save) { - setTimeout(() => { - overlay = this.busyService.show(); - }); + this.showBusyIndicator(); try { await this.data.application.GetEntity().Commit(false); if (saveChanges.map) { this.svc.mapEntitlementsToApplication(this.data.application.UID_AOBApplication.value); } } finally { - setTimeout(() => { - this.busyService.hide(overlay); - }); + this.busyService.hide(); } this.sidesheetRef.close(true); this.snackbar.open({ @@ -137,15 +127,11 @@ export class EntitlementEditAutoAddComponent implements OnDestroy { }); } else { // reset the extended data if you do not save the data - setTimeout(() => { - overlay = this.busyService.show(); - }); + this.showBusyIndicator(); try { await this.setExtendedData(this.data.sqlExpression.Expression); } finally { - setTimeout(() => { - this.busyService.hide(overlay); - }); + this.busyService.hide(); } } } @@ -154,28 +140,38 @@ export class EntitlementEditAutoAddComponent implements OnDestroy { // check if the sqlWizard has a valid expression return ( this.exprHasntChanged || - this.sqlExpression?.Expression?.Expressions.length === 0 || + this.sqlExpression?.Expression?.Expressions?.length === 0 || isExpressionInvalid(this.sqlExpression) || - !this.hasValuesSet(this.sqlExpression.Expression) + !this.hasValuesSet(this.sqlExpression?.Expression) ); } - private hasValuesSet(sqlExpression: SqlExpression, checkCurrent: boolean = false): boolean { - const current = !checkCurrent || sqlExpression.Value != null; + private hasValuesSet(sqlExpression: SqlExpression | undefined, checkCurrent: boolean = false): boolean { + const current = !checkCurrent || sqlExpression?.Value != null; - if (sqlExpression.Expressions?.length > 0) { - return current && sqlExpression.Expressions.every((elem) => this.hasValuesSet(elem, true)); + if (!!sqlExpression?.Expressions?.length) { + return current && !!sqlExpression?.Expressions?.every((elem) => this.hasValuesSet(elem, true)); } return current; } - private async setExtendedData(sqlExpression: SqlExpression): Promise { + private async setExtendedData(sqlExpression: SqlExpression | undefined): Promise { + const SqlExpression: SqlWizardWrite = { Filters: [] }; + if (sqlExpression) { + SqlExpression.Filters?.push(sqlExpression); + } return this.data.application.setExtendedData({ ...this.data.application.extendedData, ...{ - SqlExpression: { Filters: [sqlExpression] }, + SqlExpression, }, }); } + + private showBusyIndicator(): void { + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } + } } diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/entitlement-edit-auto-add.service.ts b/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/entitlement-edit-auto-add.service.ts index c30249983..0bfde95d2 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/entitlement-edit-auto-add.service.ts +++ b/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/entitlement-edit-auto-add.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,17 +26,17 @@ import { Injectable } from '@angular/core'; -import { EntitlementToAddData } from 'imx-api-aob'; -import { CollectionLoadParameters, EntityCollectionData, FilterProperty, InteractiveEntityStateData } from 'imx-qbm-dbts'; +import { EntitlementToAddData } from '@imx-modules/imx-api-aob'; +import { CollectionLoadParameters, EntityCollectionData, FilterProperty, InteractiveEntityStateData } from '@imx-modules/imx-qbm-dbts'; import { SqlWizardApiService } from 'qbm'; import { AobApiService } from '../../aob-api-client.service'; @Injectable({ providedIn: 'root' }) export class EntitlementEditAutoAddService implements SqlWizardApiService { - constructor(private readonly api: AobApiService) { } + constructor(private readonly api: AobApiService) {} public async getFilterProperties(table: string): Promise { - return (await this.api.client.portal_application_sqlwizard_tables_columns_get(table)).Properties; + return (await this.api.client.portal_application_sqlwizard_tables_columns_get(table)).Properties || []; } public async getCandidates(parentTable: string, options?: CollectionLoadParameters): Promise { @@ -44,11 +44,13 @@ export class EntitlementEditAutoAddService implements SqlWizardApiService { } public async showEntitlementsToMap(state: InteractiveEntityStateData): Promise { - return this.api.client.portal_application_interactive_entitlementstoadd_get( - state.EntityId, { keys: state.Keys, state: state.State }); + return this.api.client.portal_application_interactive_entitlementstoadd_get(state?.EntityId || '', { + keys: state.Keys, + state: state.State, + }); } public async mapEntitlementsToApplication(uidApplication: string): Promise { - return this.api.client.portal_application_map_post(uidApplication); + return this.api.client.portal_application_map_post(uidApplication); } } diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/entitlement-to-add-data-wrapper.service.ts b/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/entitlement-to-add-data-wrapper.service.ts index 26078436f..ea6426c20 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/entitlement-to-add-data-wrapper.service.ts +++ b/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/entitlement-to-add-data-wrapper.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,31 +26,26 @@ import { Injectable } from '@angular/core'; -import { EntitlementData, EntitlementToAddData } from 'imx-api-aob'; -import { - EntityColumnData, - EntityData, - TypedEntityBuilder, - TypedEntityCollectionData -} from 'imx-qbm-dbts'; +import { EntitlementData, EntitlementToAddData } from '@imx-modules/imx-api-aob'; +import { EntityColumnData, EntityData, TypedEntityBuilder, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { EntitlementToAddTyped } from './entitlement-to-add-typed'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class EntitlementToAddDataWrapperService { - public readonly entitySchema = EntitlementToAddTyped.GetEntitySchema(); private readonly builder = new TypedEntityBuilder(EntitlementToAddTyped); public buildTypedEntities(data: EntitlementToAddData): TypedEntityCollectionData { - const entities = data.Data.map((element, index) => this.buildEntityData(element, `${index}`)); + const entities = data.Data?.map((element, index) => this.buildEntityData(element, `${index}`)); return this.builder.buildReadWriteEntities( { - TotalCount: entities.length, - Entities: entities.sort((a, b) => this.compareElement(a, b)) + TotalCount: entities?.length || 0, + Entities: entities?.sort((a, b) => this.compareElement(a, b)), }, - this.entitySchema); + this.entitySchema, + ); } private buildEntityData(data: EntitlementData, key: string): EntityData { @@ -60,18 +55,17 @@ export class EntitlementToAddDataWrapperService { ret.IsAssignedToOther = { Value: data.IsAssignedToOther, IsReadOnly: true }; ret.DisplayName = { Value: data.DisplayName, IsReadOnly: true }; ret.CanonicalName = { Value: data.CanonicalName, IsReadOnly: true }; - ret.UID_AOBApplicationConflicted = - { + ret.UID_AOBApplicationConflicted = { Value: data.UID_AOBApplicationConflicted, IsReadOnly: true, - DisplayValue: data.DisplayApplicationConflicted + DisplayValue: data.DisplayApplicationConflicted, }; return { Columns: ret, Keys: [key] }; } private compareElement(a: EntityData, b: EntityData): number { - const val1 = `${(a.Columns.IsAssignedToMe.Value ? 1 : a.Columns.IsAssignedToOther.Value ? 0 : 2)} ${a.Columns.DisplayName.Value}`; - const val2 = `${(b.Columns.IsAssignedToMe.Value ? 1 : b.Columns.IsAssignedToOther.Value ? 0 : 2)} ${b.Columns.DisplayName.Value}`; + const val1 = `${a.Columns?.IsAssignedToMe.Value ? 1 : a.Columns?.IsAssignedToOther.Value ? 0 : 2} ${a.Columns?.DisplayName.Value}`; + const val2 = `${b.Columns?.IsAssignedToMe.Value ? 1 : b.Columns?.IsAssignedToOther.Value ? 0 : 2} ${b.Columns?.DisplayName.Value}`; return val1.localeCompare(val2); } } diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/entitlement-to-add-typed.ts b/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/entitlement-to-add-typed.ts index ab76aa0b7..f521328d9 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/entitlement-to-add-typed.ts +++ b/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/entitlement-to-add-typed.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,10 +24,9 @@ * */ -import { DisplayPattern, EntitySchema, IClientProperty, IReadValue, TypedEntity, ValType } from 'imx-qbm-dbts'; +import { DisplayPattern, EntitySchema, IClientProperty, IReadValue, TypedEntity, ValType } from '@imx-modules/imx-qbm-dbts'; export class EntitlementToAddTyped extends TypedEntity { - public readonly IsAssignedToMe: IReadValue = this.GetEntityValue('IsAssignedToMe'); public readonly IsAssignedToOther: IReadValue = this.GetEntityValue('IsAssignedToOther'); public readonly DisplayName: IReadValue = this.GetEntityValue('DisplayName'); @@ -39,29 +38,29 @@ export class EntitlementToAddTyped extends TypedEntity { ret.IsAssignedToMe = { Type: ValType.Bool, - ColumnName: 'IsAssignedToMe' + ColumnName: 'IsAssignedToMe', }; ret.IsAssignedToOther = { Type: ValType.Bool, - ColumnName: 'IsAssignedToOther' + ColumnName: 'IsAssignedToOther', }; ret.DisplayName = { Type: ValType.String, - ColumnName: 'DisplayName' + ColumnName: 'DisplayName', }; ret.CanonicalName = { Type: ValType.String, - ColumnName: 'CanonicalName' + ColumnName: 'CanonicalName', }; ret.UID_AOBApplicationConflicted = { Type: ValType.String, - ColumnName: 'UID_AOBApplicationConflicted' + ColumnName: 'UID_AOBApplicationConflicted', }; return { TypeName: 'EntitlementToAddTyped', DisplayPattern: new DisplayPattern('%DisplayName%'), - Columns: ret + Columns: ret, }; } } diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/mapped-entitlements-preview/mapped-entitlements-preview.component.html b/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/mapped-entitlements-preview/mapped-entitlements-preview.component.html index dc599c38e..e170daa82 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/mapped-entitlements-preview/mapped-entitlements-preview.component.html +++ b/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/mapped-entitlements-preview/mapped-entitlements-preview.component.html @@ -1,66 +1,73 @@
- +
- {{alertText}} + {{ alertText }}
- {{speedupText}} + {{ speedupText }}
- +
#LDS#Matching application entitlements - {{getCount('')}} + {{ getCount('') }} #LDS#Application entitlements to add - {{getCount('add')}} + {{ getCount('add') }} #LDS#Application entitlements already assigned to this application - {{getCount('assigned')}} + {{ getCount('assigned') }} #LDS#Application entitlements assigned to another application - {{getCount('conflicted')}} + {{ getCount('conflicted') }}
- -
- - - -
{{ entity.DisplayName.Column.GetDisplayValue() }}
-
{{ entity.CanonicalName.Column.GetDisplayValue()}}
-
-
- - - - {{(entity.IsAssignedToMe.value ? '#LDS#Already assigned to this application' :'#LDS#Assigned to other application') | - translate}} + + + {{ '#LDS#Application entitlement' | translate }} + +
{{ item.DisplayName.Column.GetDisplayValue() }}
+
{{ item.CanonicalName.Column.GetDisplayValue() }}
+ +
+ + + + + {{ + (item.IsAssignedToMe.value ? '#LDS#Already assigned to this application' : '#LDS#Assigned to other application') | translate + }} -
-
- - - -
- - + + + + {{ '#LDS#Already assigned to following application' | translate }} + +
{{ item.UID_AOBApplicationConflicted?.Column.GetDisplayValue() }}
+ +
+
-
- - {{'#LDS#Assign application entitlements after saving' |translate}} +
+ + {{ '#LDS#Assign application entitlements after saving' | translate }} -
diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/mapped-entitlements-preview/mapped-entitlements-preview.component.scss b/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/mapped-entitlements-preview/mapped-entitlements-preview.component.scss index b86f6a61e..12a1910bd 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/mapped-entitlements-preview/mapped-entitlements-preview.component.scss +++ b/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/mapped-entitlements-preview/mapped-entitlements-preview.component.scss @@ -1,35 +1,7 @@ -.eui-sidesheet-content { - height: inherit; - overflow: hidden; - display: flex; - flex-direction: column; - - .imx-content-card { - flex: 1 1 auto; - overflow: hidden; - - .imx-table-container { - display: flex; - flex-direction: column; - overflow: hidden; - height: calc(100% - 80px); - flex: 1 1 auto; - } - } -} - -.eui-sidesheet-actions { - .justify-start { - margin-right: auto; - display: flex; - flex-direction: column; - justify-content: center; - height: auto; - } +@import 'base/mixins'; - button:not(:last-of-type):not(.justify-start) { - margin-right: 10px; - } +.eui-sidesheet-content { + @include flex-column-container($overflow: hidden, $height: inherit); } .imx-info-text { diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/mapped-entitlements-preview/mapped-entitlements-preview.component.ts b/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/mapped-entitlements-preview/mapped-entitlements-preview.component.ts index 14c060d29..aaebe4ab4 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/mapped-entitlements-preview/mapped-entitlements-preview.component.ts +++ b/imxweb/projects/aob/src/lib/entitlements/entitlement-edit-auto-add/mapped-entitlements-preview/mapped-entitlements-preview.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,21 +25,20 @@ */ import { Component, Inject, OnInit } from '@angular/core'; -import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; -import { CollectionLoadParameters, DisplayColumns, EntitySchema, TypedEntityCollectionData } from 'imx-qbm-dbts'; -import { DataSourceToolbarSettings } from 'qbm'; +import { DisplayColumns, EntitySchema, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; +import { DataViewSource } from 'qbm'; import { EntitlementToAddTyped } from '../entitlement-to-add-typed'; @Component({ templateUrl: './mapped-entitlements-preview.component.html', styleUrls: ['./mapped-entitlements-preview.component.scss'], + providers: [DataViewSource], }) export class MappedEntitlementsPreviewComponent implements OnInit { - public settings: DataSourceToolbarSettings; public readonly DisplayColumns = DisplayColumns; public entitySchema: EntitySchema; - public navigationState: CollectionLoadParameters = {StartIndex:0, PageSize:20}; public alertText = '#LDS#Here you can get an overview of application entitlements that will be added to this application by the conditions. Application entitlements that are already assigned to an application will be skipped.'; @@ -52,7 +51,8 @@ export class MappedEntitlementsPreviewComponent implements OnInit { withSave: boolean; entitlementToAdd: TypedEntityCollectionData; }, - private readonly sidesheetRef: EuiSidesheetRef + private readonly sidesheetRef: EuiSidesheetRef, + public dataSource: DataViewSource, ) {} public apply(map: boolean): void { @@ -72,29 +72,19 @@ export class MappedEntitlementsPreviewComponent implements OnInit { return this.data.entitlementToAdd.totalCount; } - public navigate(source: CollectionLoadParameters): void { - this.entitySchema = EntitlementToAddTyped.GetEntitySchema(); - this.navigationState = { ...this.navigationState, ...source }; - - const data = this.data.entitlementToAdd.Data.slice(this.navigationState.StartIndex, this.navigationState.StartIndex + this.navigationState.PageSize); - const displayedColumns = [ - this.entitySchema.Columns.DisplayName, - this.entitySchema.Columns.IsAssignedToMe, - this.entitySchema.Columns.UID_AOBApplicationConflicted, - ]; - this.settings = { - displayedColumns: displayedColumns, - dataSource: { - Data: data, - totalCount: this.data.entitlementToAdd.totalCount, - }, - entitySchema: this.entitySchema, - navigationState: this.navigationState, - }; - } - public async ngOnInit(): Promise { + this.entitySchema = EntitlementToAddTyped.GetEntitySchema(); - this.navigate({}); + const displayedColumns = [ + this.entitySchema.Columns.DisplayName, + this.entitySchema.Columns.IsAssignedToMe, + this.entitySchema.Columns.UID_AOBApplicationConflicted, + ]; + this.dataSource.init({ + execute: (): Promise> => Promise.resolve(this.data.entitlementToAdd), + schema: this.entitySchema, + columnsToDisplay: displayedColumns, + localSource: true, + }); } } diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlement-edit/entitlement-edit.component.html b/imxweb/projects/aob/src/lib/entitlements/entitlement-edit/entitlement-edit.component.html index 64acc8ffe..b2f0d7960 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlement-edit/entitlement-edit.component.html +++ b/imxweb/projects/aob/src/lib/entitlements/entitlement-edit/entitlement-edit.component.html @@ -1,23 +1,45 @@
- +
- + - + (controlCreated)="form.addControl('tags', $event)" + > - +
- - + +
diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlement-edit/entitlement-edit.component.scss b/imxweb/projects/aob/src/lib/entitlements/entitlement-edit/entitlement-edit.component.scss index 76f0a2a43..7536ad2ef 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlement-edit/entitlement-edit.component.scss +++ b/imxweb/projects/aob/src/lib/entitlements/entitlement-edit/entitlement-edit.component.scss @@ -1,11 +1,5 @@ -:host { - display: flex; - flex-direction: column; - height: 100%; +@import 'base/mixins'; - .eui-sidesheet-content { - display: flex; - flex-direction: column; - overflow: auto; - } +:host { + @include flex-column-container($height: 100%); } diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlement-edit/entitlement-edit.component.ts b/imxweb/projects/aob/src/lib/entitlements/entitlement-edit/entitlement-edit.component.ts index 140d8ce4a..40108bcd7 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlement-edit/entitlement-edit.component.ts +++ b/imxweb/projects/aob/src/lib/entitlements/entitlement-edit/entitlement-edit.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,16 +24,16 @@ * */ -import { Component, Input, ErrorHandler, OnChanges, SimpleChanges, OnInit, Output, EventEmitter } from '@angular/core'; -import { UntypedFormGroup, AbstractControl } from '@angular/forms'; -import { OverlayRef } from '@angular/cdk/overlay'; +import { Component, ErrorHandler, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; +import { AbstractControl, UntypedFormGroup } from '@angular/forms'; import { EuiLoadingService } from '@elemental-ui/core'; -import { PortalEntitlement, PortalEntitlementServiceitem } from 'imx-api-aob'; +import { Router } from '@angular/router'; +import { PortalEntitlement, PortalEntitlementServiceitem } from '@imx-modules/imx-api-aob'; +import { DbObjectKey, TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { CdrFactoryService, ClassloggerService, ColumnDependentReference } from 'qbm'; -import { DbObjectKey, TypedEntity } from 'imx-qbm-dbts'; import { ServiceItemTagsService } from 'qer'; -import { Router } from '@angular/router'; +import { AobPermissionsService } from '../../permissions/aob-permissions.service'; /** * A component that provides a form for viewing and editing entitlements/roles. @@ -41,7 +41,7 @@ import { Router } from '@angular/router'; @Component({ selector: 'imx-entitlement-edit', templateUrl: './entitlement-edit.component.html', - styleUrls: ['./entitlement-edit.component.scss'] + styleUrls: ['./entitlement-edit.component.scss'], }) export class EntitlementEditComponent implements OnChanges, OnInit { public readonly form = new UntypedFormGroup({}); @@ -49,9 +49,10 @@ export class EntitlementEditComponent implements OnChanges, OnInit { public productTagsInitial: string[] = []; public productTagsSelected: string[]; public loadingTags: boolean; - - public cdrList: ColumnDependentReference[] = []; - public cdrListServiceItem: ColumnDependentReference[] = []; + + public cdrList: (ColumnDependentReference | undefined)[] = []; + public cdrListServiceItem: (ColumnDependentReference | undefined)[] = []; + public isEditableEntitlement: boolean; @Input() public entitlement: PortalEntitlement; @Input() public serviceItem: PortalEntitlementServiceitem; @@ -65,31 +66,30 @@ export class EntitlementEditComponent implements OnChanges, OnInit { private readonly router: Router, private readonly errorHandler: ErrorHandler, private readonly cdrFactoryService: CdrFactoryService, - private readonly busyService: EuiLoadingService - ) { } + private readonly permissions: AobPermissionsService, + private readonly busyService: EuiLoadingService, + ) {} - public ngOnInit(): void { + public async ngOnInit(): Promise { this.controlCreated.emit(this.form); - const entitlementColumns = [ - 'Ident_AOBEntitlement', - 'Description', - 'ObjectKeyAdditionalApprover' - ]; + const entitlementColumns = ['Ident_AOBEntitlement', 'Description', 'ObjectKeyAdditionalApprover']; this.cdrList = this.cdrFactoryService.buildCdrFromColumnList(this.entitlement.GetEntity(), entitlementColumns); - + if (this.serviceItem) { const serviceIemColumns = [ - 'UID_OrgRuler', - 'UID_PWODecisionMethod', - 'UID_QERTermsOfUse', - 'UID_AccProductParamCategory', + 'UID_OrgRuler', + 'UID_PWODecisionMethod', + 'UID_QERTermsOfUse', + 'UID_AccProductParamCategory', 'UID_AccProductGroup', - 'ProductURL', - 'IsApproveRequiresMfa' + 'ProductURL', + 'IsApproveRequiresMfa', ]; this.cdrListServiceItem = this.cdrFactoryService.buildCdrFromColumnList(this.serviceItem.GetEntity(), serviceIemColumns); } + + this.isEditableEntitlement = await this.editableEntitlement(); } public async ngOnChanges(changes: SimpleChanges): Promise { @@ -117,8 +117,9 @@ export class EntitlementEditComponent implements OnChanges, OnInit { private async initTags(entitlement: PortalEntitlement): Promise { this.loadingTags = true; - this.productTagsInitial = (await this.tagProvider.getTags(entitlement.UID_AccProduct.value)) - .Data.map(elem => elem.Ident_DialogTag.value); + this.productTagsInitial = (await this.tagProvider.getTags(entitlement.UID_AccProduct.value)).Data.map( + (elem) => elem.Ident_DialogTag.value, + ); this.productTagsSelected = this.productTagsInitial.slice(); this.loadingTags = false; @@ -127,8 +128,9 @@ export class EntitlementEditComponent implements OnChanges, OnInit { private async tryCommit(typedEntities: TypedEntity[]): Promise { let success = false; - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } try { for (const typedEntity of typedEntities) { @@ -148,7 +150,7 @@ export class EntitlementEditComponent implements OnChanges, OnInit { } catch (error) { this.errorHandler.handleError(error); } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } return success; @@ -157,26 +159,23 @@ export class EntitlementEditComponent implements OnChanges, OnInit { private async saveTags(): Promise { const changeSet = this.getTagsChangeSet(); - return this.tagProvider.updateTags( - this.entitlement.UID_AccProduct.value, - changeSet.add, - changeSet.remove - ); + return this.tagProvider.updateTags(this.entitlement.UID_AccProduct.value, changeSet.add, changeSet.remove); } - private getTagsChangeSet(): { add: string[], remove: string[] } { + private getTagsChangeSet(): { add: string[]; remove: string[] } { this.logger.trace(this, 'save tags initial', this.productTagsInitial); this.logger.trace(this, 'save tags selected', this.productTagsSelected); return { - add: this.productTagsSelected.filter(elem => this.productTagsInitial.indexOf(elem) < 0), - remove: this.productTagsInitial.filter(elem => this.productTagsSelected.indexOf(elem) < 0) + add: this.productTagsSelected.filter((elem) => this.productTagsInitial.indexOf(elem) < 0), + remove: this.productTagsInitial.filter((elem) => this.productTagsSelected.indexOf(elem) < 0), }; } - public get isEditableEntitlement() { - // TODO: only if IsOwnerOrAdmin is set - return DbObjectKey.FromXml(this.entitlement.ObjectKeyElement.value).TableName == "ESet"; + public async editableEntitlement(): Promise { + return (await this.permissions.isAobApplicationOwnerOrAdmin()) + ? DbObjectKey.FromXml(this.entitlement.ObjectKeyElement.value).TableName == 'ESet' + : false; } public manageEntitlement() { diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlement-edit/entitlement-edit.module.ts b/imxweb/projects/aob/src/lib/entitlements/entitlement-edit/entitlement-edit.module.ts index 57e7546e0..052b95537 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlement-edit/entitlement-edit.module.ts +++ b/imxweb/projects/aob/src/lib/entitlements/entitlement-edit/entitlement-edit.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -40,12 +40,8 @@ import { CdrModule } from 'qbm'; import { ServiceItemTagsModule } from 'qer'; @NgModule({ - declarations: [ - EntitlementEditComponent - ], - exports: [ - EntitlementEditComponent - ], + declarations: [EntitlementEditComponent], + exports: [EntitlementEditComponent], imports: [ CdrModule, CommonModule, @@ -58,7 +54,7 @@ import { ServiceItemTagsModule } from 'qer'; MatInputModule, ReactiveFormsModule, ServiceItemTagsModule, - TranslateModule - ] + TranslateModule, + ], }) -export class EntitlementEditModule { } +export class EntitlementEditModule {} diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlement-filter.spec.ts b/imxweb/projects/aob/src/lib/entitlements/entitlement-filter.spec.ts index 64ce883e8..2714d3dd7 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlement-filter.spec.ts +++ b/imxweb/projects/aob/src/lib/entitlements/entitlement-filter.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,13 +25,13 @@ */ import { EntitlementFilter } from './entitlement-filter'; -import { PortalEntitlement } from 'imx-api-aob'; -import { DbVal } from 'imx-qbm-dbts'; +import { PortalEntitlement } from '@imx-modules/imx-api-aob'; +import { DbVal } from '@imx-modules/imx-qbm-dbts'; describe('EntitlementFilter', () => { const entitlementNotPublished = { IsInActive: { value: true }, - ActivationDate: { value: null } + ActivationDate: { value: null }, }; const dateLaterThanMinDate = DbVal.MinDate; @@ -39,19 +39,15 @@ describe('EntitlementFilter', () => { const entitlementWillPublish = { IsInActive: { value: true }, - ActivationDate: { value: dateLaterThanMinDate } + ActivationDate: { value: dateLaterThanMinDate }, }; const entitlementPublished = { IsInActive: { value: false }, - ActivationDate: { value: null } + ActivationDate: { value: null }, }; - const mockEntitlementData = [ - entitlementNotPublished, - entitlementWillPublish, - entitlementPublished - ]; + const mockEntitlementData = [entitlementNotPublished, entitlementWillPublish, entitlementPublished]; it('notPublished', () => { const filter = new EntitlementFilter(); diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlement-filter.ts b/imxweb/projects/aob/src/lib/entitlements/entitlement-filter.ts index 71d710a7a..a1944a9d6 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlement-filter.ts +++ b/imxweb/projects/aob/src/lib/entitlements/entitlement-filter.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { PortalEntitlement } from 'imx-api-aob'; +import { PortalEntitlement } from '@imx-modules/imx-api-aob'; export class EntitlementFilter { public notPublished(entitlement: PortalEntitlement): boolean { diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlement-wrapper.interface.ts b/imxweb/projects/aob/src/lib/entitlements/entitlement-wrapper.interface.ts index 1035d0da2..226e9dad4 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlement-wrapper.interface.ts +++ b/imxweb/projects/aob/src/lib/entitlements/entitlement-wrapper.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,9 +24,9 @@ * */ -import { PortalEntitlement, PortalEntitlementServiceitem } from 'imx-api-aob'; +import { PortalEntitlement, PortalEntitlementServiceitem } from '@imx-modules/imx-api-aob'; export interface EntitlementWrapper { - entitlement: PortalEntitlement; - serviceItem: PortalEntitlementServiceitem; + entitlement?: PortalEntitlement; + serviceItem?: PortalEntitlementServiceitem; } diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlements.component.html b/imxweb/projects/aob/src/lib/entitlements/entitlements.component.html index c36bd8bb1..ddea33ea8 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlements.component.html +++ b/imxweb/projects/aob/src/lib/entitlements/entitlements.component.html @@ -1,112 +1,119 @@ -
- - - - - - - - - +
+
+
- - - - - - - - - - - - - - - - - - - -
- + + + + + + + +
+ +
+ + + {{ entitySchema?.Columns?.Ident_AOBEntitlement?.Display }} + +
{{ item.Ident_AOBEntitlement.Column.GetDisplayValue() }}
+
{{ item.Description.Column.GetDisplayValue() }}
+ +
+ + {{ '#LDS#Type' | translate }} + + + {{ getType(item) }} + + + + + + +
+
+ {{ '#LDS#Automatic' | translate }} +
+
+ +
+
+ +
+ + {{ entitySchema?.Columns?.UID_AERoleOwner?.Display }} + +
{{ item.UID_AERoleOwner.Column.GetDisplayValue() }}
+ +
+ + {{ '#LDS#Assigned' | translate }} + +
{{ item.XDateInserted.Column.GetDisplayValue() }}
+ +
+ + {{ '#LDS#Status' | translate }} + +
{{ '#LDS#Not published' | translate }}
+
+
{{ '#LDS#Will be published' | translate }}
+
{{ item.ActivationDate.Column.GetDisplayValue() }}
+
+
+
{{ '#LDS#Published' | translate }}
+
{{ '#LDS#(not yet requestable)' | translate }}
+
+ +
+
+
+ + +
+
+
+
+

{{ '#LDS#No application entitlements assigned' | translate }} @@ -116,23 +123,25 @@

- -
- - - - - - +
-

- -

+

diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlements.component.scss b/imxweb/projects/aob/src/lib/entitlements/entitlements.component.scss index 39d13c33e..13f763d3f 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlements.component.scss +++ b/imxweb/projects/aob/src/lib/entitlements/entitlements.component.scss @@ -1,6 +1,5 @@ -@use '@angular/material' as mat; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; -@import '@angular/material/theming'; +@import 'base/mixins'; :host { display: flex; @@ -11,31 +10,20 @@ } .imx-entitlements-site { + &.imx-hidden{ + display:none; + } padding: 16px 40px; height: 100%; display: flex; flex-direction: column; flex: 1 1 auto; -} - -.imx-badge-container { - > div { - display: table-cell; - - > .eui-badge { - margin: 0 5px; - - ::ng-deep .eui-badge-content { - white-space: nowrap; - } - } + .imx-entitlements-row-actions{ + display: flex; + gap: 8px; } } - -// Create a config with the default typography levels. -$config: mat.define-typography-config(); - imx-data-source-toolbar-custom button { margin-right: 5px; } @@ -56,10 +44,6 @@ imx-data-source-toolbar-custom button { margin-top: 30px; } - > .eui-icon { - color: $color-gray-30; - } - .imx-aob-entitlements-empty-description { margin-bottom: 10px; } @@ -68,7 +52,7 @@ imx-data-source-toolbar-custom button { display: flex; flex-direction: column; - > .mat-raised-button { + > .mat-mdc-unelevated-button { margin-bottom: 15px; } > :last-child { @@ -77,34 +61,6 @@ imx-data-source-toolbar-custom button { } } -.imx-aob-entitlements-header { - display: flex; - flex-wrap: wrap; - margin: 10px 0 8px 0; - - > h3 { - font-size: 18px; - margin-right: 10px; - font-weight: normal; - line-height: 36px; - } - - > button { - text-transform: uppercase; - } - - > span { - line-height: 36px; - font-size: mat.font-size($config, button); - } -} - -.imx-mat-card-title { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - .imx-aob-data-container { flex-grow: 1; align-content: flex-start; @@ -115,20 +71,8 @@ div[subtitle] { color: $color-gray-40; } -.eui-dark-theme { - :host { - .imx-aob-entitlements-empty > .eui-icon { - color: $color-gray-60; - } - } -} - .eui-contrast-theme { :host { - .imx-aob-entitlements-empty > .eui-icon { - color: $color-gray-40; - } - .imx-entitlements-site { background-color: $color-gray-100; } diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlements.component.ts b/imxweb/projects/aob/src/lib/entitlements/entitlements.component.ts index 3321f6199..4054daea5 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlements.component.ts +++ b/imxweb/projects/aob/src/lib/entitlements/entitlements.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,41 +24,48 @@ * */ -import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewChild } from '@angular/core'; +import { Platform } from '@angular/cdk/platform'; +import { Component, effect, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; -import { OverlayRef } from '@angular/cdk/overlay'; -import { Platform } from '@angular/cdk/platform'; import { TranslateService } from '@ngx-translate/core'; +import { EntitlementSystemRoleInput, PortalApplication, PortalEntitlement, PortalEntitlementServiceitem } from '@imx-modules/imx-api-aob'; import { - SnackBarService, - DataSourceToolbarSettings, + CollectionLoadParameters, + DbObjectKey, + DisplayColumns, + EntitySchema, + TypedEntity, + TypedEntityCollectionData, + ValType, +} from '@imx-modules/imx-qbm-dbts'; +import { + BusyService, + calculateSidesheetWidth, ClassloggerService, + DataTableComponent, DataTileBadge, - TextContainer, + DataViewInitParameters, + DataViewSource, + MetadataService, SettingsService, + SnackBarService, SystemInfoService, - MetadataService, - DataTableComponent, - DataTilesComponent, - ClientPropertyForTableColumns, - BusyService, + TextContainer, } from 'qbm'; -import { PortalEntitlement, PortalApplication, PortalEntitlementServiceitem, EntitlementSystemRoleInput } from 'imx-api-aob'; -import { CollectionLoadParameters, IClientProperty, ValType, DisplayColumns, EntitySchema, TypedEntity, DbObjectKey } from 'imx-qbm-dbts'; -import { EntitlementsService } from './entitlements.service'; -import { LifecycleAction } from '../lifecycle-actions/lifecycle-action.enum'; import { LifecycleActionComponent } from '../lifecycle-actions/lifecycle-action.component'; -import { EntitlementsAddComponent } from './entitlement-add/entitlements-add.component'; -import { EntitlementsType } from './entitlements.model'; -import { EntitlementFilter } from './entitlement-filter'; -import { ShopsService } from '../shops/shops.service'; +import { LifecycleAction } from '../lifecycle-actions/lifecycle-action.enum'; import { ServiceItemsService } from '../service-items/service-items.service'; +import { ShopsService } from '../shops/shops.service'; +import { EntitlementsAddComponent } from './entitlement-add/entitlements-add.component'; import { EntitlementDetailComponent } from './entitlement-detail/entitlement-detail.component'; -import { EntitlementWrapper } from './entitlement-wrapper.interface'; import { EntitlementEditAutoAddComponent } from './entitlement-edit-auto-add/entitlement-edit-auto-add.component'; import { EntitlementEditAutoAddService } from './entitlement-edit-auto-add/entitlement-edit-auto-add.service'; +import { EntitlementFilter } from './entitlement-filter'; +import { EntitlementWrapper } from './entitlement-wrapper.interface'; +import { EntitlementsType } from './entitlements.model'; +import { EntitlementsService } from './entitlements.service'; /** * A component for viewing, editing and acting all {@link PortalEntitlement|entitlements} for a given {@link PortalApplication|application}. @@ -73,37 +80,38 @@ import { EntitlementEditAutoAddService } from './entitlement-edit-auto-add/entit selector: 'imx-entitlements', templateUrl: './entitlements.component.html', styleUrls: ['./entitlements.component.scss'], + providers: [DataViewSource], }) export class EntitlementsComponent implements OnChanges { /** The {@link PortalApplication|application} */ @Input() public application: PortalApplication; @Output() public reloadRequested = new EventEmitter(); - @ViewChild('table') public table: DataTableComponent; - @ViewChild('tiles') public tiles: DataTilesComponent; + // @ViewChild('table') public table: DataTableComponent; public readonly DisplayColumns = DisplayColumns; // Enables use of this static class in Angular Templates. public readonly EntitlementsType = EntitlementsType; // Enables use of this static class in Angular Templates. - public dstSettings: DataSourceToolbarSettings; - public navigationState: CollectionLoadParameters; + // public dstSettings: DataSourceToolbarSettings; + // public navigationState: CollectionLoadParameters; public selections: PortalEntitlement[] = []; public publishDisabled = true; public unpublishDisabled = true; public unassignedDisabled = true; public isUsedInDialog = false; - public readonly entitySchema: EntitySchema; + public entitySchema: EntitySchema; public readonly filter = new EntitlementFilter(); public busyService = new BusyService(); public isSystemRoleEnabled: boolean; public isLoading = false; + public hideTable = false; /** * Checks, if there's a dynamic assignment rule attached to this application */ public get hasConditionForDynamicAssignment(): boolean { return ( - this.application.extendedDataRead?.SqlExpression?.Expressions?.length && - this.application.extendedDataRead?.SqlExpression?.Expressions[0].Expression.Expressions.length > 0 + !!this.application.extendedDataRead?.SqlExpression?.Expressions?.length && + !!this.application.extendedDataRead?.SqlExpression?.Expressions?.[0].Expression?.Expressions?.length ); } @@ -117,7 +125,6 @@ export class EntitlementsComponent implements OnChanges { }, }; - private readonly displayedColumns: ClientPropertyForTableColumns[]; private readonly updatedTableNames: string[] = []; constructor( @@ -136,34 +143,16 @@ export class EntitlementsComponent implements OnChanges { private readonly systemInfo: SystemInfoService, private readonly metadata: MetadataService, private readonly autoAddService: EntitlementEditAutoAddService, + public dataSource: DataViewSource, ) { this.entitySchema = entitlementsProvider.entitlementSchema; - this.displayedColumns = [ - this.entitySchema.Columns.Ident_AOBEntitlement, - { - Type: ValType.String, - ColumnName: 'badges', - untranslatedDisplay: '#LDS#Badges', - }, - { - Type: ValType.String, - ColumnName: 'type', - untranslatedDisplay: '#LDS#Type', - }, - this.entitySchema.Columns.UID_AERoleOwner, - { - Type: ValType.Date, - ColumnName: 'assigned', - untranslatedDisplay: '#LDS#Assigned', - }, - { - Type: ValType.String, - ColumnName: 'status', - untranslatedDisplay: '#LDS#Status', - }, - ]; this.busyService.busyStateChanged.subscribe((elem) => (this.isLoading = elem)); + effect(() => { + if (!!this.dataSource.collectionData()) { + this.updateTableNames(this.dataSource.collectionData().Data); + } + }); } public async ngOnChanges(changes: SimpleChanges): Promise { @@ -172,8 +161,8 @@ export class EntitlementsComponent implements OnChanges { } const isBusy = this.busyService.beginBusy(); try { - this.isSystemRoleEnabled = (await this.systemInfo.get()).PreProps.includes('ESET'); - this.getData(true); + this.isSystemRoleEnabled = !!(await this.systemInfo.get()).PreProps?.includes('ESET'); + this.getData(); } finally { isBusy.endBusy(); } @@ -203,8 +192,9 @@ export class EntitlementsComponent implements OnChanges { const candidates = await this.sidesheet .open(EntitlementsAddComponent, { title: await this.translateService.get('#LDS#Heading Assign Application Entitlements').toPromise(), + subTitle: this.application.GetEntity().GetDisplay(), padding: '0px', - width: 'max(600px, 60%)', + width: calculateSidesheetWidth(), testId: 'entitlements-sidesheet', data: { defaultType: this.isSystemRoleEnabled ? EntitlementsType.Eset : EntitlementsType.UnsGroup, @@ -231,12 +221,11 @@ export class EntitlementsComponent implements OnChanges { * Applys the dynamic assignment rule, that is defined for the application */ public async applyMappingForDynamicAssignments(): Promise { - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.euiBusyService.show())); + this.showBusyIndicator(); try { this.autoAddService.mapEntitlementsToApplication(this.application.UID_AOBApplication.value); } finally { - setTimeout(() => this.euiBusyService.hide(overlayRef)); + this.euiBusyService.hide(); } this.snackbar.open({ key: '#LDS#The application entitlements are added now. This may take some time.' }); } @@ -255,10 +244,10 @@ export class EntitlementsComponent implements OnChanges { subTitle: this.application.GetEntity().GetDisplay(), padding: '0px', disableClose: true, - width: 'max(600px, 60%)', + width: calculateSidesheetWidth(), testId: 'entitlements-edit-auto-add-sidesheet', data: { - sqlExpression: this.application.extendedDataRead.SqlExpression.Expressions[0], + sqlExpression: this.application.extendedDataRead.SqlExpression?.Expressions?.[0], application: this.application, }, }) @@ -269,12 +258,11 @@ export class EntitlementsComponent implements OnChanges { return; } - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.euiBusyService.show())); + this.showBusyIndicator(); try { this.reloadRequested.emit(); } finally { - setTimeout(() => this.euiBusyService.hide(overlayRef)); + this.euiBusyService.hide(); } } @@ -282,7 +270,7 @@ export class EntitlementsComponent implements OnChanges { * Call to publish the specified {@link PortalEntitlement|entitlement} or the list of selected {@link PortalEntitlement|entitlements}. * @param aobEntitlement the {@link PortalEntitlement|entitlement} to publish */ - public async publish(aobEntitlement: PortalEntitlement = null): Promise { + public async publish(aobEntitlement?: PortalEntitlement): Promise { const shops = await this.shopsProvider.getApplicationInShop(this.application.UID_AOBApplication.value); if (shops == null) { @@ -330,7 +318,7 @@ export class EntitlementsComponent implements OnChanges { }; } this.snackbar.open(publishMessage, '#LDS#Close'); - await this.getData(); + await this.dataSource.updateState(); } if (publishCount < selectedEntitlements.length) { @@ -349,7 +337,7 @@ export class EntitlementsComponent implements OnChanges { * Call to unpublish the specified {@link PortalEntitlement|entitlement} or the list of selected {@link PortalEntitlement|entitlements}. * @param aobEntitlement the {@link PortalEntitlement|entitlement} to unpublish */ - public async unpublish(aobEntitlement: PortalEntitlement = null): Promise { + public async unpublish(aobEntitlement?: PortalEntitlement): Promise { const selectedEntitlements = aobEntitlement != null ? [aobEntitlement] : this.selections; const dialogConfig = { data: { action: LifecycleAction.Unpublish, elements: selectedEntitlements, type: 'AobEntitlement' }, @@ -371,7 +359,7 @@ export class EntitlementsComponent implements OnChanges { this.logger.debug(this, 'Deselect unpublished entitlement(s)/role(s)'); this.clearSelections(); this.snackbar.open({ key: '#LDS#Application entitlements unpublished' }, '#LDS#Close'); - await this.getData(); + await this.dataSource.updateState(); } if (unpublishCount < selectedEntitlements.length) { @@ -391,7 +379,7 @@ export class EntitlementsComponent implements OnChanges { * from the associated {@link PortalApplication|application}. * @param aobEntitlement the {@link PortalEntitlement|entitlement} to unassign */ - public async unassign(aobEntitlement: PortalEntitlement = null): Promise { + public async unassign(aobEntitlement?: PortalEntitlement): Promise { const selectedEntitlements = aobEntitlement != null ? [aobEntitlement] : this.selections; const dialogConfig = { data: { action: LifecycleAction.Unassign, elements: selectedEntitlements, type: 'AobEntitlement' }, @@ -403,19 +391,18 @@ export class EntitlementsComponent implements OnChanges { if (result) { this.logger.debug(this, 'Remove entitlement(s) from application'); if (selectedEntitlements.length > 0) { - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.euiBusyService.show())); + this.showBusyIndicator(); try { const unassignResult = await this.entitlementsProvider.unassign(selectedEntitlements); if (unassignResult) { this.clearSelections(); - await this.getData(); + await this.dataSource.updateState(); this.snackbar.open({ key: '#LDS#Application entitlements unassigned' }, '#LDS#Close'); } else { this.logger.error(this, 'Attempt to unassign entitlement(s)/role(s) failed'); } } finally { - setTimeout(() => this.euiBusyService.hide(overlayRef)); + this.euiBusyService.hide(); } } this.matDialog.closeAll(); @@ -427,25 +414,53 @@ export class EntitlementsComponent implements OnChanges { /** * Call to get data from server depending on the {@link CollectionLoadParameters}. - * @param resetNavigationState Indicates wether the navigation state should be reset or not. - * @param newState Optional argument for loading new date for the given {@link CollectionLoadParameters}. */ - public async getData(resetNavigationState: boolean = false, newState?: CollectionLoadParameters): Promise { - if (resetNavigationState) { - this.navigationState = { PageSize: this.settingsService.DefaultPageSize, StartIndex: 0 }; - } else if (newState) { - this.navigationState = newState; + public async getData(): Promise { + this.hideTable = false; + this.dataSource.state.set({ PageSize: this.settingsService?.DefaultPageSize, StartIndex: 0 }); + this.dataSource.itemStatus = this.status; + const columnsToDisplay = [ + this.entitySchema.Columns.Ident_AOBEntitlement, + { + ColumnName: 'badges', + Type: ValType.String, + }, + { + ColumnName: 'type', + Type: ValType.String, + }, + this.entitySchema.Columns.UID_AERoleOwner, + { + ColumnName: 'assigned', + Type: ValType.Date, + }, + { + ColumnName: 'status', + Type: ValType.String, + }, + ]; + const dataViewInitParameters: DataViewInitParameters = { + execute: (params: CollectionLoadParameters): Promise | undefined> => + this.entitlementsProvider.getEntitlementsForApplication(this.application, params), + schema: this.entitySchema, + columnsToDisplay, + highlightEntity: (entitlement: PortalEntitlement) => { + this.editEntitlement(entitlement); + }, + selectionChange: (selection: Array) => this.onSelectionChanged(selection), + }; + await this.dataSource.init(dataViewInitParameters); + if (this.dataSource.collectionData().Data.length === 0) { + this.hideTable = true; } - - await this.navigate(); } /** * Opens a side sheet to edit entitlement information * @param entitlement the entitlement to edit */ - public async editEntitlement(entitlement: PortalEntitlement): Promise { - const entitlementWrapper = await this.createEntitlementWrapper(entitlement); + public async editEntitlement(entitlement: TypedEntity): Promise { + const entitlementWrapper = await this.createEntitlementWrapper(entitlement as PortalEntitlement); if (entitlementWrapper?.entitlement == null) { this.logger.error(this, 'AobEntitlement is undefined'); @@ -456,7 +471,7 @@ export class EntitlementsComponent implements OnChanges { title: await this.translateService.get('#LDS#Heading Edit Application Entitlement').toPromise(), subTitle: entitlementWrapper.entitlement.GetEntity().GetDisplay(), padding: '0', - width: 'max(600px, 60%)', + width: calculateSidesheetWidth(), disableClose: true, data: entitlementWrapper, testId: 'application-entitlement-edit-sidesheet', @@ -467,17 +482,16 @@ export class EntitlementsComponent implements OnChanges { * Triggered action from the {@link DataTableComponent} or {@link TilesComponent} after the selection have changed. */ public onSelectionChanged(selection: PortalEntitlement[]): void { - this.selections = selection; - this.unassignedDisabled = true; this.publishDisabled = true; this.unpublishDisabled = true; + this.selections = selection; if (this.selections == null || this.selections.length === 0) { return; } - this.unassignedDisabled = selection.some((elem) => elem.IsDynamic.value === true); + this.unassignedDisabled = this.selections.some((elem) => elem.IsDynamic.value === true); let foundPublished = false; let foundUnpublished = false; @@ -510,82 +524,51 @@ export class EntitlementsComponent implements OnChanges { } } - /** - * Triggered action from the {@link DataSourceToolbar|DataSourceToolbar} after the grouping have changed. - */ - public onGroupingChanged(col: IClientProperty): void { - this.logger.trace(this, 'grouping changed', col); - } - public getType(data: PortalEntitlement): string { if (data?.ObjectKeyElement?.value == null) { return ''; } const tableName = DbObjectKey.FromXml(data.ObjectKeyElement.value)?.TableName; - return tableName != null && this.metadata.tables[tableName] ? this.metadata.tables[tableName]?.DisplaySingular : ''; + return tableName != null && this.metadata.tables[tableName] ? this.metadata.tables[tableName]?.DisplaySingular || '' : ''; } private async addDirectEntitlements(candidates: TypedEntity[]): Promise { - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.euiBusyService.show())); + this.showBusyIndicator(); try { const assignedCount = await this.entitlementsProvider.assign(this.application, candidates); if (assignedCount > 0) { - await this.getData(); + await this.dataSource.updateState(); } if (assignedCount < candidates.length) { this.logger.error(this, 'Attempt to assign entitlement(s)/role(s) failed'); } } finally { - setTimeout(() => this.euiBusyService.hide(overlayRef)); + this.euiBusyService.hide(); } } private async buildRoleAndAddItToApplication(entitlementSystemRoleInput: EntitlementSystemRoleInput): Promise { - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.euiBusyService.show())); + this.showBusyIndicator(); let success = true; try { await this.entitlementsProvider.addElementsToRole(entitlementSystemRoleInput); } catch { success = false; } finally { - setTimeout(() => this.euiBusyService.hide(overlayRef)); + this.euiBusyService.hide(); } if (success) { this.snackbar.open( { key: '#LDS#The application entitlements have been successfully merged into the system role and added to application.' }, '#LDS#Close', ); - await this.getData(); + await this.dataSource.updateState(); } } private clearSelections(): void { - this.selections = []; - this.table?.clearSelection(); - this.tiles?.clearSelection(); - } - - private async navigate(): Promise { - const isBusy = this.busyService.beginBusy(); - try { - const data = await this.entitlementsProvider.getEntitlementsForApplication(this.application, this.navigationState); - await this.updateTableNames(data.Data); - if (data) { - this.dstSettings = { - displayedColumns: this.displayedColumns, - dataSource: data, - entitySchema: this.entitySchema, - navigationState: this.navigationState, - }; - } else { - this.logger.error(this, 'TypedEntityCollectionData is undefined'); - } - } finally { - isBusy.endBusy(); - } + this.dataSource.selection.clear(); } private async updateTableNames(objs: PortalEntitlement[]): Promise { @@ -602,11 +585,10 @@ export class EntitlementsComponent implements OnChanges { } private async createEntitlementWrapper(entitlement: PortalEntitlement): Promise { - let entitlementReloaded: PortalEntitlement; - let serviceItem: PortalEntitlementServiceitem; + let entitlementReloaded: PortalEntitlement | undefined; + let serviceItem: PortalEntitlementServiceitem | undefined = undefined; - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.euiBusyService.show())); + this.showBusyIndicator(); try { entitlementReloaded = await this.entitlementsProvider.reload(entitlement); @@ -614,7 +596,7 @@ export class EntitlementsComponent implements OnChanges { serviceItem = (await this.serviceItemsProvider.get(entitlement))?.Data?.[0]; } } finally { - setTimeout(() => this.euiBusyService.hide(overlayRef)); + this.euiBusyService.hide(); } return { @@ -622,4 +604,10 @@ export class EntitlementsComponent implements OnChanges { serviceItem, }; } + + private showBusyIndicator(): void { + if (this.euiBusyService.overlayRefs.length === 0) { + this.euiBusyService.show(); + } + } } diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlements.model.ts b/imxweb/projects/aob/src/lib/entitlements/entitlements.model.ts index df8882ba6..58da102cf 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlements.model.ts +++ b/imxweb/projects/aob/src/lib/entitlements/entitlements.model.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,14 +24,14 @@ * */ -import { IEntityColumn, TypedEntity } from "imx-qbm-dbts"; +import { IEntityColumn, TypedEntity } from '@imx-modules/imx-qbm-dbts'; export enum EntitlementsType { UnsGroup, Eset, Qerresource, Rpsreport, - Tsbaccountdef + Tsbaccountdef, } export interface AddEntitlementParameter { diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlements.module.ts b/imxweb/projects/aob/src/lib/entitlements/entitlements.module.ts index 17ef753a5..cd8fbeae6 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlements.module.ts +++ b/imxweb/projects/aob/src/lib/entitlements/entitlements.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,49 +24,51 @@ * */ -import { NgModule } from '@angular/core'; +import { ScrollingModule } from '@angular/cdk/scrolling'; import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatBadgeModule } from '@angular/material/badge'; import { MatButtonModule } from '@angular/material/button'; import { MatButtonToggleModule } from '@angular/material/button-toggle'; import { MatCardModule } from '@angular/material/card'; import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatDividerModule } from '@angular/material/divider'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; +import { MatMenuModule } from '@angular/material/menu'; import { MatRadioModule } from '@angular/material/radio'; -import { MatTableModule } from '@angular/material/table'; -import { MatTooltipModule } from '@angular/material/tooltip'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; -import { MatMenuModule } from '@angular/material/menu'; +import { MatTooltipModule } from '@angular/material/tooltip'; import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; import { TranslateModule } from '@ngx-translate/core'; -import { ScrollingModule } from '@angular/cdk/scrolling'; -import { MatDividerModule } from '@angular/material/divider'; +import { MatTableModule } from '@angular/material/table'; import { + CdrModule, ClassloggerModule, - DataTilesModule, DataSourceToolbarModule, DataTableModule, + DataTilesModule, + DataViewModule, DisableControlModule, - QbmModule, + InfoModalDialogModule, LdsReplaceModule, - CdrModule, + QbmModule, + SelectedElementsModule, SqlWizardModule, - InfoModalDialogModule } from 'qbm'; -import { EntitlementsAddComponent } from './entitlement-add/entitlements-add.component'; -import { EntitlementsComponent } from './entitlements.component'; -import { EntitlementsService } from './entitlements.service'; -import { AobUserModule } from '../user/user.module'; import { LifecycleActionsModule } from '../lifecycle-actions/lifecycle-actions.module'; -import { EntitlementEditModule } from './entitlement-edit/entitlement-edit.module'; -import { EntitlementDetailComponent } from './entitlement-detail/entitlement-detail.component'; +import { AobUserModule } from '../user/user.module'; +import { EntitlementsAddComponent } from './entitlement-add/entitlements-add.component'; import { SystemRoleConfigComponent } from './entitlement-add/system-role-config/system-role-config.component'; +import { EntitlementDetailComponent } from './entitlement-detail/entitlement-detail.component'; import { EntitlementEditAutoAddComponent } from './entitlement-edit-auto-add/entitlement-edit-auto-add.component'; import { MappedEntitlementsPreviewComponent } from './entitlement-edit-auto-add/mapped-entitlements-preview/mapped-entitlements-preview.component'; +import { EntitlementEditModule } from './entitlement-edit/entitlement-edit.module'; +import { EntitlementsComponent } from './entitlements.component'; +import { EntitlementsService } from './entitlements.service'; @NgModule({ declarations: [ @@ -75,7 +77,7 @@ import { MappedEntitlementsPreviewComponent } from './entitlement-edit-auto-add/ EntitlementDetailComponent, SystemRoleConfigComponent, EntitlementEditAutoAddComponent, - MappedEntitlementsPreviewComponent + MappedEntitlementsPreviewComponent, ], imports: [ ClassloggerModule, @@ -111,13 +113,11 @@ import { MappedEntitlementsPreviewComponent } from './entitlement-edit-auto-add/ CdrModule, ScrollingModule, SqlWizardModule, - InfoModalDialogModule - ], - providers: [ - EntitlementsService - ], - exports: [ - EntitlementsComponent + InfoModalDialogModule, + SelectedElementsModule, + DataViewModule, ], + providers: [EntitlementsService], + exports: [EntitlementsComponent], }) -export class EntitlementsModule { } +export class EntitlementsModule {} diff --git a/imxweb/projects/aob/src/lib/entitlements/entitlements.service.ts b/imxweb/projects/aob/src/lib/entitlements/entitlements.service.ts index 4c45939d2..af97941ae 100644 --- a/imxweb/projects/aob/src/lib/entitlements/entitlements.service.ts +++ b/imxweb/projects/aob/src/lib/entitlements/entitlements.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,23 +24,33 @@ * */ -import { Injectable, ErrorHandler } from '@angular/core'; +import { ErrorHandler, Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; -import { CollectionLoadParameters, TypedEntityCollectionData, IWriteValue, FilterType, CompareOperator, EntitySchema, TypedEntity, DataModel, ExtendedTypedEntityCollection, FilterData, FilterTreeData } from 'imx-qbm-dbts'; -import { DataTileBadge, ApiClientService, ClassloggerService } from 'qbm'; import { - PortalEntitlement, - PortalApplication, EntitlementSystemRoleInput, portal_entitlementcandidates_UNSGroup_filtertree_get_args, -} from 'imx-api-aob'; -import { EntitlementFilter } from './entitlement-filter'; + PortalApplication, + PortalEntitlement, +} from '@imx-modules/imx-api-aob'; +import { + CollectionLoadParameters, + CompareOperator, + DataModel, + EntitySchema, + FilterTreeData, + FilterType, + IWriteValue, + TypedEntity, + TypedEntityCollectionData, +} from '@imx-modules/imx-qbm-dbts'; +import { ApiClientService, ClassloggerService, DataTileBadge } from 'qbm'; import { AobApiService } from '../aob-api-client.service'; +import { EntitlementFilter } from './entitlement-filter'; import { EntitlementsType } from './entitlements.model'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class EntitlementsService { private readonly filter = new EntitlementFilter(); @@ -52,27 +62,34 @@ export class EntitlementsService { private readonly apiProvider: ApiClientService, private readonly logger: ClassloggerService, private readonly errorHandler: ErrorHandler, - translate: TranslateService) { - translate.get('#LDS#Published').subscribe((value: string) => this.badgePublished = { - content: value, - color: '#618f3e' - }); - - translate.get('#LDS#Will be published').subscribe((value: string) => this.badgeWillPublish = { - content: value, - color: '#f4770b' - }); + translate: TranslateService, + ) { + translate.get('#LDS#Published').subscribe( + (value: string) => + (this.badgePublished = { + content: value, + color: '#618f3e', + }), + ); + + translate.get('#LDS#Will be published').subscribe( + (value: string) => + (this.badgeWillPublish = { + content: value, + color: '#f4770b', + }), + ); } public get entitlementSchema(): EntitySchema { return this.aobClient.typedClient.PortalEntitlement.GetSchema(); } - public async get(parameters: CollectionLoadParameters = {}): Promise> { + public async get(parameters: CollectionLoadParameters = {}): Promise | undefined> { return this.apiProvider.request(() => this.aobClient.typedClient.PortalEntitlement.Get(parameters)); } - public async getInteractive(uid: string): Promise> { + public async getInteractive(uid: string): Promise | undefined> { return this.apiProvider.request(() => this.aobClient.typedClient.PortalEntitlementInteractive.Get_byid(uid)); } @@ -97,9 +114,10 @@ export class EntitlementsService { } } - - public async getCandidates(type: EntitlementsType, parameters: CollectionLoadParameters = {}): - Promise> { + public async getCandidates( + type: EntitlementsType, + parameters: CollectionLoadParameters = {}, + ): Promise | undefined> { switch (type) { case EntitlementsType.Eset: return this.apiProvider.request(() => this.aobClient.typedClient.PortalEntitlementcandidatesEset.Get(parameters)); @@ -116,42 +134,40 @@ export class EntitlementsService { } } - public async getDataModel(type: EntitlementsType): Promise { + public async getDataModel(type: EntitlementsType): Promise { if (type === EntitlementsType.UnsGroup) { return this.aobClient.client.portal_entitlementcandidates_UNSGroup_datamodel_get(undefined); } return undefined; } - - - public async getEntitlementsForApplication(application: PortalApplication, collectionLoadParameters: CollectionLoadParameters = {}): - Promise> { + public async getEntitlementsForApplication( + application: PortalApplication, + collectionLoadParameters: CollectionLoadParameters = {}, + ): Promise | undefined> { return this.get({ ...collectionLoadParameters, ...{ filter: [ + ...(collectionLoadParameters.filter || []), { ColumnName: 'UID_AOBApplication', Type: FilterType.Compare, CompareOp: CompareOperator.Equal, - Value1: application.UID_AOBApplication.value - } - ] - } + Value1: application.UID_AOBApplication.value, + }, + ], + }, }); } - public async reload(entitlement: PortalEntitlement): Promise { + public async reload(entitlement: PortalEntitlement): Promise { const collection = await this.getInteractive(entitlement.UID_AOBEntitlement.value); return collection && collection.Data && collection.Data.length > 0 ? collection.Data[0] : undefined; } - public async assign( - application: PortalApplication, - candidates: TypedEntity[] - ): Promise { + public async assign(application: PortalApplication, candidates: TypedEntity[]): Promise { let assignCount = 0; for (const candidate of candidates) { @@ -170,7 +186,7 @@ export class EntitlementsService { public async unassign(entitlements: PortalEntitlement[]): Promise { return this.apiProvider.request(async () => { - let result = null; + let result; for (const entitlement of entitlements) { result = await this.aobClient.client.portal_entitlement_delete(entitlement.UID_AOBEntitlement.value); } @@ -178,15 +194,16 @@ export class EntitlementsService { }); } - public async publish(entitlements: PortalEntitlement[], publishData: { publishFuture: boolean, date: Date }): Promise { + public async publish(entitlements: PortalEntitlement[], publishData: { publishFuture: boolean; date: Date }): Promise { let publishCount = 0; for (const entitlement of entitlements) { - const interactive = (await this.aobClient.typedClient.PortalEntitlementInteractive.Get_byid(entitlement.GetEntity().GetKeys()[0])).Data[0]; + const interactive = (await this.aobClient.typedClient.PortalEntitlementInteractive.Get_byid(entitlement.GetEntity().GetKeys()[0])) + .Data[0]; if (!publishData.publishFuture) { - await interactive.IsInActive.Column.PutValue(publishData.publishFuture); + await interactive.IsInActive.Column.PutValue(publishData.publishFuture); } else { - await interactive.ActivationDate.Column.PutValue(publishData.date); + await interactive.ActivationDate.Column.PutValue(publishData.date); } this.logger.debug(this, 'Commit change: publish entitlement...'); @@ -202,7 +219,8 @@ export class EntitlementsService { let unpublishCount = 0; for (const entitlement of entitlements) { - const interactive = (await this.aobClient.typedClient.PortalEntitlementInteractive.Get_byid(entitlement.GetEntity().GetKeys()[0])).Data[0]; + const interactive = (await this.aobClient.typedClient.PortalEntitlementInteractive.Get_byid(entitlement.GetEntity().GetKeys()[0])) + .Data[0]; await interactive.IsInActive.Column.PutValue(true); await interactive.ActivationDate.Column.PutValue(null); @@ -228,7 +246,6 @@ export class EntitlementsService { } public getEntitlementBadges(entitlement: PortalEntitlement): DataTileBadge[] { - if (this.filter.published(entitlement)) { return [this.badgePublished]; } @@ -240,17 +257,14 @@ export class EntitlementsService { return []; } - public async getEntitlementsFilterTree(options: portal_entitlementcandidates_UNSGroup_filtertree_get_args): Promise{ + public async getEntitlementsFilterTree(options: portal_entitlementcandidates_UNSGroup_filtertree_get_args): Promise { return this.aobClient.client.portal_entitlementcandidates_UNSGroup_filtertree_get(options); } - private async createNew( - element: TypedEntity, - uidAobApplication: IWriteValue - ): Promise { + private async createNew(element: TypedEntity, uidAobApplication: IWriteValue): Promise { const entitlement = (await this.aobClient.typedClient.PortalEntitlementInteractive.Get()).Data[0]; - entitlement.ObjectKeyElement.value = element.GetEntity().GetColumn('XObjectKey').GetValue(), - entitlement.UID_AOBApplication.value = uidAobApplication.value; + (entitlement.ObjectKeyElement.value = element.GetEntity().GetColumn('XObjectKey').GetValue()), + (entitlement.UID_AOBApplication.value = uidAobApplication.value); return entitlement; } } diff --git a/imxweb/projects/aob/src/lib/entitlements/publish-data.interface.ts b/imxweb/projects/aob/src/lib/entitlements/publish-data.interface.ts index d89dc8cde..6220520cc 100644 --- a/imxweb/projects/aob/src/lib/entitlements/publish-data.interface.ts +++ b/imxweb/projects/aob/src/lib/entitlements/publish-data.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,6 +25,6 @@ */ export interface PublishData { - publishFuture: boolean; - date?: Date; + publishFuture: boolean; + date?: Date; } diff --git a/imxweb/projects/aob/src/lib/extensions/service-items-edit/lock-info-alert/lock-info-alert-extension.ts b/imxweb/projects/aob/src/lib/extensions/service-items-edit/lock-info-alert/lock-info-alert-extension.ts index 60939276c..8cad4313b 100644 --- a/imxweb/projects/aob/src/lib/extensions/service-items-edit/lock-info-alert/lock-info-alert-extension.ts +++ b/imxweb/projects/aob/src/lib/extensions/service-items-edit/lock-info-alert/lock-info-alert-extension.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/aob/src/lib/extensions/service-items-edit/lock-info-alert/lock-info-alert.component.html b/imxweb/projects/aob/src/lib/extensions/service-items-edit/lock-info-alert/lock-info-alert.component.html index 62feb96cf..6b79d7831 100644 --- a/imxweb/projects/aob/src/lib/extensions/service-items-edit/lock-info-alert/lock-info-alert.component.html +++ b/imxweb/projects/aob/src/lib/extensions/service-items-edit/lock-info-alert/lock-info-alert.component.html @@ -1,5 +1,8 @@ - + - {{'#LDS#You cannot edit all properties, because the service item is assigned to an application and therefore some properties are locked.' | translate }} + {{ + '#LDS#You cannot edit all properties, because the service item is assigned to an application and therefore some properties are locked.' + | translate + }} - \ No newline at end of file + diff --git a/imxweb/projects/aob/src/lib/extensions/service-items-edit/lock-info-alert/lock-info-alert.component.scss b/imxweb/projects/aob/src/lib/extensions/service-items-edit/lock-info-alert/lock-info-alert.component.scss index f7902c16a..3722d7c34 100644 --- a/imxweb/projects/aob/src/lib/extensions/service-items-edit/lock-info-alert/lock-info-alert.component.scss +++ b/imxweb/projects/aob/src/lib/extensions/service-items-edit/lock-info-alert/lock-info-alert.component.scss @@ -1,9 +1,5 @@ +@import 'base/mixins'; + :host { - display: flex; - flex-direction: column; - - .imx-helper-alert { - margin-bottom: 20px; - width: 100%; - } + @include flex-column-container(); } diff --git a/imxweb/projects/aob/src/lib/extensions/service-items-edit/lock-info-alert/lock-info-alert.component.spec.ts b/imxweb/projects/aob/src/lib/extensions/service-items-edit/lock-info-alert/lock-info-alert.component.spec.ts index f561a4463..ec978ec68 100644 --- a/imxweb/projects/aob/src/lib/extensions/service-items-edit/lock-info-alert/lock-info-alert.component.spec.ts +++ b/imxweb/projects/aob/src/lib/extensions/service-items-edit/lock-info-alert/lock-info-alert.component.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -34,9 +34,8 @@ describe('LockInfoAlertComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ LockInfoAlertComponent ] - }) - .compileComponents(); + declarations: [LockInfoAlertComponent], + }).compileComponents(); }); beforeEach(() => { diff --git a/imxweb/projects/aob/src/lib/extensions/service-items-edit/lock-info-alert/lock-info-alert.component.ts b/imxweb/projects/aob/src/lib/extensions/service-items-edit/lock-info-alert/lock-info-alert.component.ts index 39a32b3f8..245b77506 100644 --- a/imxweb/projects/aob/src/lib/extensions/service-items-edit/lock-info-alert/lock-info-alert.component.ts +++ b/imxweb/projects/aob/src/lib/extensions/service-items-edit/lock-info-alert/lock-info-alert.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,15 +26,14 @@ import { Component, Input, OnInit } from '@angular/core'; -import { PortalServiceitems } from 'imx-api-qer'; +import { PortalServiceitems } from '@imx-modules/imx-api-qer'; @Component({ selector: 'imx-lock-info-alert', templateUrl: './lock-info-alert.component.html', - styleUrls: ['./lock-info-alert.component.scss'] + styleUrls: ['./lock-info-alert.component.scss'], }) -export class LockInfoAlertComponent implements OnInit { - +export class LockInfoAlertComponent implements OnInit { @Input() public serviceItem: PortalServiceitems; public isLocked = false; @@ -42,5 +41,4 @@ export class LockInfoAlertComponent implements OnInit { public ngOnInit(): void { this.isLocked = this.serviceItem?.GetEntity()?.GetColumn('LockInfo')?.GetValue(); } - } diff --git a/imxweb/projects/aob/src/lib/global-kpi/global-kpi.component.html b/imxweb/projects/aob/src/lib/global-kpi/global-kpi.component.html index bab33a2aa..155fea935 100644 --- a/imxweb/projects/aob/src/lib/global-kpi/global-kpi.component.html +++ b/imxweb/projects/aob/src/lib/global-kpi/global-kpi.component.html @@ -1,15 +1,31 @@ +

+ {{ '#LDS#Global KPIs' | translate }} +

- {{ '#LDS#Global KPIs' | translate }} - {{ '#LDS#These KPIs give you an overview of your applications, their contents and states.' | translate }} + {{ + '#LDS#These KPIs give you an overview of your applications, their contents and states.' | translate + }}
  • - {{!kpi.calculated ? '-' : !kpi.currentDataValue ? '' : kpi.currentDataValue.toLocaleString(this.browserCulture, - { minimumFractionDigits: 0, maximumFractionDigits: 3 })}} + {{ + !kpi.calculated + ? '-' + : !kpi.currentDataValue + ? '' + : kpi.currentDataValue.toLocaleString(this.browserCulture, { minimumFractionDigits: 0, maximumFractionDigits: 3 }) + }} {{ kpi.chartData.Display }} - +
@@ -17,26 +33,30 @@
-
-
{{data.chartData.Display }}
-
-

{{ data.chartData.Description}}

-
- +

{{ data.chartData.Description }}

+
+
-

{{'#LDS#History' | translate}}

-
+

{{ '#LDS#History' | translate }}

+
-
diff --git a/imxweb/projects/aob/src/lib/global-kpi/global-kpi.component.scss b/imxweb/projects/aob/src/lib/global-kpi/global-kpi.component.scss index 9075b2fda..d655716f5 100644 --- a/imxweb/projects/aob/src/lib/global-kpi/global-kpi.component.scss +++ b/imxweb/projects/aob/src/lib/global-kpi/global-kpi.component.scss @@ -1,9 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; - -:host { - margin: 10px; - display: flex; -} +@import '@elemental-ui/core/src/styles/_palette.scss'; li { list-style-type: none; @@ -13,7 +8,7 @@ li { > .imx-kpi-display { display: table-cell; - width: 100% + width: 100%; } :nth-child(3n) { @@ -30,15 +25,12 @@ li { } } -.mat-card { - width: 771px; - height: auto; - margin: 40px 10px 0px; +.mat-mdc-card { display: flex; flex-direction: column; } -.mat-card-content { +.mat-mdc-card-content { padding: 0 10px 0 35px; overflow: auto; @@ -47,16 +39,14 @@ li { } } -.mat-card-title { +.mat-mdc-card-title { padding: 20px 33px 0px; - font-size: 24px; font-weight: 600; margin-bottom: 0; } -.mat-card-subtitle { +.mat-mdc-card-subtitle { font-size: 16px; - color: $black-3; font-weight: 400; padding: 5px 33px 20px; margin-bottom: 0; @@ -69,7 +59,7 @@ p { margin-top: 0; } -h1 { +h2 { font-size: 16px; font-weight: 600; margin: 30px 0 15px 0; @@ -82,18 +72,11 @@ h1 { display: table; } -.imx-chart { - display: flex; - justify-content: center; - flex-direction: row; -} - -.mat-dialog-title { - display: flex; +.mat-mdc-dialog-title { margin: 0; } -.mat-dialog-content { +.mat-mdc-dialog-content { max-height: 100%; } @@ -108,11 +91,6 @@ h1 { margin: 0; } -.imx-close-dialog-button { - right: -15px; - top: -15px; -} - .imx-aob-kpi-empty { display: flex; flex-direction: column; @@ -123,10 +101,6 @@ h1 { span { margin-left: 24px; } - - > .eui-icon { - color: $black-b; - } } ::ng-deep .bb-chart-arc text { diff --git a/imxweb/projects/aob/src/lib/global-kpi/global-kpi.component.ts b/imxweb/projects/aob/src/lib/global-kpi/global-kpi.component.ts index 95ed9c5c2..05452228a 100644 --- a/imxweb/projects/aob/src/lib/global-kpi/global-kpi.component.ts +++ b/imxweb/projects/aob/src/lib/global-kpi/global-kpi.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,23 +24,22 @@ * */ -import { Component, OnInit, ViewChild, TemplateRef } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { EuiLoadingService } from '@elemental-ui/core'; -import { OverlayRef } from '@angular/cdk/overlay'; -import { MatDialog } from '@angular/material/dialog'; -import { ChartOptions, pie } from 'billboard.js'; import { KeyValue } from '@angular/common'; +import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { EuiLoadingService } from '@elemental-ui/core'; +import { TranslateService } from '@ngx-translate/core'; +import { ChartOptions, pie, PrimitiveArray } from 'billboard.js'; -import { ChartDto, ChartDataPoint } from 'imx-api-aob'; -import { LineChartOptions, XAxisInformation, YAxisInformation, SeriesInformation, ClassloggerService } from 'qbm'; +import { ChartDataPoint, ChartDto } from '@imx-modules/imx-api-aob'; +import { ClassloggerService, LineChartOptions, SeriesInformation, XAxisInformation, YAxisInformation } from 'qbm'; import { GlobalKpiService } from './global-kpi.service'; import { KpiData } from './kpi-data.interface'; @Component({ selector: 'imx-global-kpi', templateUrl: './global-kpi.component.html', - styleUrls: ['./global-kpi.component.scss'] + styleUrls: ['./global-kpi.component.scss'], }) /** * Builds a component, that displays global KPI data (key performance indicators) @@ -60,8 +59,7 @@ export class GlobalKpiComponent implements OnInit { private business: string; private central: string; private others: string; - private readonly colors: string[] = ['#B4E6F4', '#82D5ED', '#50C4E6', '#2BB7E0', - '#05AADB', '#04A3D7', '#0499D2', '#0390CD', '#017FC4']; + private readonly colors: string[] = ['#B4E6F4', '#82D5ED', '#50C4E6', '#2BB7E0', '#05AADB', '#04A3D7', '#0499D2', '#0390CD', '#017FC4']; constructor( private readonly logger: ClassloggerService, @@ -72,27 +70,23 @@ export class GlobalKpiComponent implements OnInit { ) { this.browserCulture = this.translateService.currentLang; - translateService.get('#LDS#No data available') - .subscribe((trans: string) => this.noDataLoaded = trans); - translateService.get('#LDS#Used in business roles') - .subscribe((trans: string) => this.business = trans); - translateService.get('#LDS#Using central directory') - .subscribe((trans: string) => this.central = trans); - translateService.get('#LDS#Other') - .subscribe((trans: string) => this.others = trans); + translateService.get('#LDS#No data available').subscribe((trans: string) => (this.noDataLoaded = trans)); + translateService.get('#LDS#Used in business roles').subscribe((trans: string) => (this.business = trans)); + translateService.get('#LDS#Using central directory').subscribe((trans: string) => (this.central = trans)); + translateService.get('#LDS#Other').subscribe((trans: string) => (this.others = trans)); } /** * Loads the chartDto objects and inits the component */ public async ngOnInit(): Promise { - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } try { - this.kpiData = (await this.kpiProvider.get()) - .map((elem, i) => this.buildKpiData(i, elem)); + this.kpiData = (await this.kpiProvider.get())?.map((elem, i) => this.buildKpiData(i, elem)) || []; } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } } @@ -101,10 +95,11 @@ export class GlobalKpiComponent implements OnInit { * @param chart the data of a single chart */ public showDetails(kpi: KpiData): void { - if (kpi.chartData.Data.length === 0 ) { return; } + if (kpi.chartData.Data?.length === 0) { + return; + } kpi.chartDetails = this.buildLineOptions(kpi.chartData, kpi.pieChart != null, kpi.names); - this.matDialog.open(this.chartDialog, - { data: kpi, minWidth: 600, autoFocus: false }); + this.matDialog.open(this.chartDialog, { data: kpi, minWidth: 600, autoFocus: false }); } /** @@ -114,26 +109,26 @@ export class GlobalKpiComponent implements OnInit { */ public isCalculated(chart: ChartDto): boolean { return chart.Data != null && chart.Data.length > 0 && chart.Data[0].Points != null; - } + } /** * @ignore determines whether an application has chart data or not * @returns true, if there are any KPIs else false */ public hasKpis(): boolean { - return (this.kpiData != null && this.kpiData.length > 0 && this.kpiData.some(elem => elem != null)); + return this.kpiData != null && this.kpiData.length > 0 && this.kpiData.some((elem) => elem != null); } private buildKpiData(chartIndex: number, chart: ChartDto): KpiData { const calc = this.isCalculated(chart); if (!calc) { - return { index: chartIndex, chartData: chart, calculated: false, names : [] }; + return { index: chartIndex, chartData: chart, calculated: false, names: [] }; } - let currentData: number = null; - let pieOptions: ChartOptions = null; - let captions: string[]; + let currentData: number | undefined; + let pieOptions: ChartOptions | undefined; + let captions: string[] = []; switch (chartIndex) { case 0: @@ -142,8 +137,8 @@ export class GlobalKpiComponent implements OnInit { case 3: case 7: case 8: - currentData = chart.Data[0].Points[0].Value; - captions = chart.Data.map(ch => ch.Name); + currentData = chart.Data?.[0].Points?.[0].Value; + captions = chart.Data?.map((ch) => ch.Name || '') || []; break; case 4: pieOptions = this.buildSingleValueChart(chart, this.central); @@ -155,14 +150,14 @@ export class GlobalKpiComponent implements OnInit { break; case 5: pieOptions = this.buildPieChart(chart); - captions = chart.Data.map(ch => ch.Name); + captions = chart.Data?.map((ch) => ch.Name || '') || []; break; } return { index: chartIndex, chartData: chart, calculated: calc, names: captions, currentDataValue: currentData, pieChart: pieOptions }; } - private buildSingleValueChart(chart: ChartDto, name: string): ChartOptions { + private buildSingleValueChart(chart: ChartDto, name: string): ChartOptions | undefined { if (!this.isCalculated(chart)) { this.logger.debug(this, 'The chart is not calculated yet, therefore no chart is build'); return undefined; @@ -171,28 +166,30 @@ export class GlobalKpiComponent implements OnInit { return { size: { width: 271, height: 271 }, data: { - columns: [['data1', chart.Data[0].Points[0].Value], - ['data2', 100 - chart.Data[0].Points[0].Value]], + columns: [ + ['data1', chart.Data?.[0].Points?.[0].Value || 0], + ['data2', 100 - (chart.Data?.[0].Points?.[0].Value || 0)], + ], names: { data1: name, data2: this.others }, colors: { data1: this.colors[0], data2: '#CCC' }, type: pie(), empty: { label: { - text: this.noDataLoaded - } - } - }, pie: { + text: this.noDataLoaded, + }, + }, + }, + pie: { padAngle: 0.01, label: { - format: d => `${d.toLocaleString(this.browserCulture, - { minimumFractionDigits: 2, maximumFractionDigits: 2 })}%`, - threshold: 0.06 + format: (d) => `${d.toLocaleString(this.browserCulture, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}%`, + threshold: 0.06, }, - } + }, }; } - private buildPieChart(chart: ChartDto): ChartOptions { + private buildPieChart(chart: ChartDto): ChartOptions | undefined { if (!this.isCalculated(chart)) { this.logger.debug(this, 'The chart is not calculated yet, therefore no chart is build'); return undefined; @@ -201,23 +198,27 @@ export class GlobalKpiComponent implements OnInit { return { size: { width: 271, height: 271 }, data: { - columns: chart.Data.map((ch, index) => ['data' + index, ch.Points[0].Value]) as any[], - names: this.toObject(chart.Data.map((ch, index) => ({ key: 'data' + index, value: ch.Name }))), - colors: this.toObject(chart.Data.map((ch, index) => ({ key: 'data' + index, value: this.colors[index] }))), + columns: chart?.Data?.map((ch, index): PrimitiveArray => ['data' + index, ch?.Points?.[0].Value || 0]), + names: this.toObject( + chart?.Data?.map((ch, index): KeyValue => ({ key: 'data' + index, value: ch.Name || '' })) || [], + ), + colors: this.toObject( + chart?.Data?.map((ch, index): KeyValue => ({ key: 'data' + index, value: this.colors[index] })) || [], + ), type: pie(), empty: { label: { - text: this.noDataLoaded - } - } - }, pie: { + text: this.noDataLoaded, + }, + }, + }, + pie: { padAngle: 0.01, label: { - format: d => `${d.toLocaleString(this.browserCulture, - { minimumFractionDigits: 2, maximumFractionDigits: 2 })}%`, - threshold: 0.06 + format: (d) => `${d.toLocaleString(this.browserCulture, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}%`, + threshold: 0.06, }, - } + }, }; } @@ -234,7 +235,6 @@ export class GlobalKpiComponent implements OnInit { ret[elem.key] = elem.value; } return ret; - } /** @@ -243,7 +243,7 @@ export class GlobalKpiComponent implements OnInit { * @returns true, if the chart has multiple points else it return false */ private hasMultipleDataPoints(chart: ChartDto): boolean { - return this.isCalculated(chart) && chart.Data[0].Points.length > 1; + return this.isCalculated(chart) && (chart?.Data?.[0]?.Points?.length || 0) > 1; } /** @@ -252,39 +252,43 @@ export class GlobalKpiComponent implements OnInit { * @returns a ChartOptions object, that can be used by billboard.js */ private buildLineOptions(chart: ChartDto, isPercentage: boolean, names: string[]): ChartOptions { - const yAxis = new YAxisInformation(chart.Data.map((serie, id) => - new SeriesInformation(names[id], - serie.Points.map((point: ChartDataPoint) => point.Value), - this.colors[id] - ))); + const yAxis = new YAxisInformation( + chart.Data?.map( + (serie, id) => new SeriesInformation(names[id], serie.Points?.map((point: ChartDataPoint) => point.Value) || [], this.colors[id]), + ) || [], + ); yAxis.tickConfiguration = { - format: (d) => isPercentage ? `${d.toLocaleString(this.browserCulture)}%` : `${d.toLocaleString(this.browserCulture)}`, - values: this.accumulateTicks(chart) + format: (d) => (isPercentage ? `${d.toLocaleString(this.browserCulture)}%` : `${d.toLocaleString(this.browserCulture)}`), + values: this.accumulateTicks(chart), }; const lineChartOptions = new LineChartOptions( - new XAxisInformation('date', - chart.Data[0].Points.map((point: ChartDataPoint) => new Date(point.Date)), { + new XAxisInformation('date', chart.Data?.[0]?.Points?.map((point: ChartDataPoint) => new Date(point.Date)) || [], { count: 6, - format: (d: Date) => d.toLocaleDateString(this.browserCulture) + format: (d: Date) => d.toLocaleDateString(this.browserCulture), }), - yAxis + yAxis, ); lineChartOptions.useCurvedLines = false; - lineChartOptions.hideLegend = chart.Data.length === 1; + lineChartOptions.hideLegend = chart.Data?.length === 1; lineChartOptions.showPoints = !this.hasMultipleDataPoints(chart); lineChartOptions.size = { width: 500, height: 325 }; return lineChartOptions.options; } private accumulateTicks(chart: ChartDto): number[] { - const ret = []; - const max = Math.max.apply(Math, chart.Data.map(o => this.getMax(o.Points))); + const ret: number[] = []; + const max = Math.max.apply( + Math, + chart.Data?.map((o) => this.getMax(o.Points || [])), + ); const steps = Math.max(1, Math.trunc(max / 10)) + 1; ret.push(0); - if (max <= 0) { return ret; } + if (max <= 0) { + return ret; + } for (let index = 1; index <= 10; index++) { ret.push(index * steps); @@ -293,6 +297,9 @@ export class GlobalKpiComponent implements OnInit { } private getMax(points: ChartDataPoint[]): number { - return Math.max.apply(Math, points.map(o => o.Value)); + return Math.max.apply( + Math, + points.map((o) => o.Value), + ); } } diff --git a/imxweb/projects/aob/src/lib/global-kpi/global-kpi.module.ts b/imxweb/projects/aob/src/lib/global-kpi/global-kpi.module.ts index e99992a8a..ba85ab3ad 100644 --- a/imxweb/projects/aob/src/lib/global-kpi/global-kpi.module.ts +++ b/imxweb/projects/aob/src/lib/global-kpi/global-kpi.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -39,16 +39,8 @@ import { TilesModule } from 'qer'; @NgModule({ declarations: [GlobalKpiComponent, KpiTileComponent], - imports: [ - CommonModule, - MatCardModule, - MatButtonModule, - MatDialogModule, - EuiCoreModule, - TranslateModule, - TilesModule - ], + imports: [CommonModule, MatCardModule, MatButtonModule, MatDialogModule, EuiCoreModule, TranslateModule, TilesModule], providers: [GlobalKpiService], - exports: [GlobalKpiComponent] + exports: [GlobalKpiComponent], }) -export class GlobalKpiModule { } +export class GlobalKpiModule {} diff --git a/imxweb/projects/aob/src/lib/global-kpi/global-kpi.service.ts b/imxweb/projects/aob/src/lib/global-kpi/global-kpi.service.ts index efcf78222..72009c409 100644 --- a/imxweb/projects/aob/src/lib/global-kpi/global-kpi.service.ts +++ b/imxweb/projects/aob/src/lib/global-kpi/global-kpi.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,26 +26,26 @@ import { Injectable } from '@angular/core'; +import { ChartDto } from '@imx-modules/imx-api-aob'; import { ApiClientService } from 'qbm'; -import { ChartDto } from 'imx-api-aob'; import { AobApiService } from '../aob-api-client.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) /** * A service that provides endpoints for KPI features */ export class GlobalKpiService { - constructor( private aobClient: AobApiService, - private readonly apiProvider: ApiClientService) { } + private readonly apiProvider: ApiClientService, + ) {} /** * Encapsules the aob/kpi GET endpoint */ - public async get(): Promise { + public async get(): Promise { return this.apiProvider.request(() => this.aobClient.client.portal_kpi_get()); } } diff --git a/imxweb/projects/aob/src/lib/global-kpi/kpi-data.interface.ts b/imxweb/projects/aob/src/lib/global-kpi/kpi-data.interface.ts index 9f2507c2c..ad85cc749 100644 --- a/imxweb/projects/aob/src/lib/global-kpi/kpi-data.interface.ts +++ b/imxweb/projects/aob/src/lib/global-kpi/kpi-data.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,14 +26,14 @@ import { ChartOptions } from 'billboard.js'; -import { ChartDto } from 'imx-api-aob'; +import { ChartDto } from '@imx-modules/imx-api-aob'; export interface KpiData { - index: number; - chartData: ChartDto; - chartDetails?: ChartOptions; - currentDataValue?: number; - pieChart?: ChartOptions; - calculated: boolean; - names: string[]; + index: number; + chartData: ChartDto; + chartDetails?: ChartOptions; + currentDataValue?: number; + pieChart?: ChartOptions; + calculated: boolean; + names: string[]; } diff --git a/imxweb/projects/aob/src/lib/global-kpi/kpi-tile/kpi-tile.component.html b/imxweb/projects/aob/src/lib/global-kpi/kpi-tile/kpi-tile.component.html index fb955bfb2..2024fb70f 100644 --- a/imxweb/projects/aob/src/lib/global-kpi/kpi-tile/kpi-tile.component.html +++ b/imxweb/projects/aob/src/lib/global-kpi/kpi-tile/kpi-tile.component.html @@ -1,13 +1,14 @@ - - - - - \ No newline at end of file + + + + + diff --git a/imxweb/projects/aob/src/lib/global-kpi/kpi-tile/kpi-tile.component.scss b/imxweb/projects/aob/src/lib/global-kpi/kpi-tile/kpi-tile.component.scss deleted file mode 100644 index f3903437e..000000000 --- a/imxweb/projects/aob/src/lib/global-kpi/kpi-tile/kpi-tile.component.scss +++ /dev/null @@ -1,9 +0,0 @@ -:host { - .mat-button{ - text-transform: uppercase; - } - - .mat-button ::ng-deep eui-icon{ - margin-left: 15px; - } -} \ No newline at end of file diff --git a/imxweb/projects/aob/src/lib/global-kpi/kpi-tile/kpi-tile.component.ts b/imxweb/projects/aob/src/lib/global-kpi/kpi-tile/kpi-tile.component.ts index 0a9a482cd..68f657902 100644 --- a/imxweb/projects/aob/src/lib/global-kpi/kpi-tile/kpi-tile.component.ts +++ b/imxweb/projects/aob/src/lib/global-kpi/kpi-tile/kpi-tile.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,13 +31,12 @@ import { AobPermissionsService } from '../../permissions/aob-permissions.service @Component({ selector: 'imx-kpi-tile', templateUrl: './kpi-tile.component.html', - styleUrls: ['./kpi-tile.component.scss'], }) export class KpiTileComponent { - - constructor(private readonly router: Router, + constructor( + private readonly router: Router, private readonly aobPermissionService: AobPermissionsService, - ) { } + ) {} public isAobOwner: boolean; @@ -48,5 +47,4 @@ export class KpiTileComponent { public goToGlobalKpi(): void { this.router.navigate(['applications/kpi']); } - } diff --git a/imxweb/projects/aob/src/lib/guards/aob-applications-guard.service.ts b/imxweb/projects/aob/src/lib/guards/aob-applications-guard.service.ts index d21fdd464..86cba3acb 100644 --- a/imxweb/projects/aob/src/lib/guards/aob-applications-guard.service.ts +++ b/imxweb/projects/aob/src/lib/guards/aob-applications-guard.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { Injectable, OnDestroy } from '@angular/core'; -import { CanActivate, Router } from '@angular/router'; +import { Router } from '@angular/router'; import { Observable, Subscription } from 'rxjs'; import { AppConfigService, AuthenticationService, ISessionState } from 'qbm'; @@ -34,15 +34,15 @@ import { AobPermissionsService } from '../permissions/aob-permissions.service'; @Injectable({ providedIn: 'root', }) -export class AobApplicationsGuardService implements CanActivate, OnDestroy { +export class AobApplicationsGuardService implements OnDestroy { private onSessionResponse: Subscription; constructor( private readonly aobPermissionService: AobPermissionsService, private readonly authentication: AuthenticationService, private readonly appConfig: AppConfigService, - private readonly router: Router - ) { } + private readonly router: Router, + ) {} public canActivate(): Observable { return new Observable((observer) => { @@ -51,7 +51,7 @@ export class AobApplicationsGuardService implements CanActivate, OnDestroy { const isApplicationOwner = await this.aobPermissionService.isAobApplicationOwner(); const isApplicationAdmin = await this.aobPermissionService.isAobApplicationAdmin(); if (!isApplicationOwner && !isApplicationAdmin) { - this.router.navigate([this.appConfig.Config.routeConfig.start], { queryParams: {} }); + this.router.navigate([this.appConfig.Config.routeConfig?.start], { queryParams: {} }); } observer.next(isApplicationOwner || isApplicationAdmin); observer.complete(); diff --git a/imxweb/projects/aob/src/lib/guards/aob-kpi-guard.service.ts b/imxweb/projects/aob/src/lib/guards/aob-kpi-guard.service.ts index 53569b5e9..c728dcabf 100644 --- a/imxweb/projects/aob/src/lib/guards/aob-kpi-guard.service.ts +++ b/imxweb/projects/aob/src/lib/guards/aob-kpi-guard.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { Injectable, OnDestroy } from '@angular/core'; -import { CanActivate, Router } from '@angular/router'; +import { Router } from '@angular/router'; import { Observable, Subscription } from 'rxjs'; import { AppConfigService, AuthenticationService, ISessionState } from 'qbm'; @@ -34,15 +34,15 @@ import { AobPermissionsService } from '../permissions/aob-permissions.service'; @Injectable({ providedIn: 'root', }) -export class AobKpiGuardService implements CanActivate, OnDestroy { +export class AobKpiGuardService implements OnDestroy { private onSessionResponse: Subscription; constructor( private readonly aobPermissionService: AobPermissionsService, private readonly authentication: AuthenticationService, private readonly appConfig: AppConfigService, - private readonly router: Router - ) { } + private readonly router: Router, + ) {} public canActivate(): Observable { return new Observable((observer) => { @@ -50,7 +50,7 @@ export class AobKpiGuardService implements CanActivate, OnDestroy { if (sessionState.IsLoggedIn) { const isApplicationAdmin = await this.aobPermissionService.isAobApplicationAdmin(); if (!isApplicationAdmin) { - this.router.navigate([this.appConfig.Config.routeConfig.start], { queryParams: {} }); + this.router.navigate([this.appConfig.Config.routeConfig?.start], { queryParams: {} }); } observer.next(isApplicationAdmin); observer.complete(); diff --git a/imxweb/projects/aob/src/lib/images/image.service.ts b/imxweb/projects/aob/src/lib/images/image.service.ts index c6b0d7a41..09ad7de55 100644 --- a/imxweb/projects/aob/src/lib/images/image.service.ts +++ b/imxweb/projects/aob/src/lib/images/image.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,18 +31,19 @@ import { ApiClientService } from 'qbm'; import { QerApiService } from 'qer'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ImageService { constructor( private readonly qerClient: QerApiService, private readonly sanitizer: DomSanitizer, - private readonly apiProvider: ApiClientService) { } + private readonly apiProvider: ApiClientService, + ) {} /** * Gets the person image URL converted to a SafeUrl */ - public async getPersonImageUrl(uid: string): Promise { + public async getPersonImageUrl(uid: string | undefined): Promise { if (uid && uid.length > 0) { const imageData = await this.apiProvider.request(() => this.qerClient.client.portal_person_image_get(uid)); diff --git a/imxweb/projects/aob/src/lib/kpi/kpi-overview/kpi-overview.component.html b/imxweb/projects/aob/src/lib/kpi/kpi-overview/kpi-overview.component.html index 996a1dd5d..511f9049d 100644 --- a/imxweb/projects/aob/src/lib/kpi/kpi-overview/kpi-overview.component.html +++ b/imxweb/projects/aob/src/lib/kpi/kpi-overview/kpi-overview.component.html @@ -1,14 +1,14 @@ -
-

- {{ '#LDS#KPIs That Affect This Application' | translate }} -

- -
-

{{ '#LDS#Failed' | translate }}

-
    -
  • - +
    +

    + {{ '#LDS#KPIs That Affect This Application' | translate }} +

    + +
    +

    {{ '#LDS#Failed' | translate }}

    +
      +
    • +
      @@ -16,47 +16,61 @@

      {{ '#LDS#Failed' | translate }}

      {{ chart.Display }}
      - - + + -
      -
    • -
    -

    {{ '#LDS#Passed' | translate }}

    -
      -
    • - + +
    • +
    +

    {{ '#LDS#Passed' | translate }}

    +
      +
    • +
      - -
      {{ chart.Display }}
      -
      -
      - - - -
      -
    • -
    + +
    {{ chart.Display }}
    +
    +
    + + + + +
  • +
+
-
-
-
{{ (data.failed ? '#LDS#Failed': '#LDS#Passed') | translate }}
-

{{ data.chart.display }}

{{ data.chart.description }}

-
+
@@ -64,8 +78,8 @@

{{ data.chart.display }}

-
- +
+

{{ '#LDS#There are no KPIs affecting this application.' | translate }}

diff --git a/imxweb/projects/aob/src/lib/kpi/kpi-overview/kpi-overview.component.scss b/imxweb/projects/aob/src/lib/kpi/kpi-overview/kpi-overview.component.scss index 71f3c2ee9..ca7887dab 100644 --- a/imxweb/projects/aob/src/lib/kpi/kpi-overview/kpi-overview.component.scss +++ b/imxweb/projects/aob/src/lib/kpi/kpi-overview/kpi-overview.component.scss @@ -5,7 +5,7 @@ flex: 1 1 auto; display: flex; flex-direction: column; - .mat-card > .mat-card-actions:last-child { + .mat-mdc-card > .mat-mdc-card-actions:last-child { margin-bottom: 0; } } @@ -25,11 +25,12 @@ li { margin: 0 10px 10px 0; } -.mat-card { +.mat-mdc-card { padding: 5px 10px; width: 340px; height: 68px; display: flex; + flex-direction: row; } h2 { @@ -42,10 +43,6 @@ p { margin-top: 0; } -eui-billboard { - width: 500px; -} - h3 { margin-top: 20px; font-size: 18px; @@ -58,10 +55,6 @@ h4 { margin: 30px 0 15px 0; } -.imx-card-clickable { - cursor: pointer; -} - .imx-kpi-text { margin: 0; vertical-align: middle; @@ -73,6 +66,7 @@ h4 { align-self: center; margin-bottom: 0; margin-right: 5px; + font-size: 14px; } .imx-spacer { @@ -105,29 +99,13 @@ h4 { padding: 20px 0 0 20px; } -.imx-chart { - display: flex; - justify-content: center; - flex-direction: column; -} - -.mat-dialog-title { - display: flex; - margin: 0px 0 10px; -} - .imx-title-text { text-align: center; margin: 0 24px 0 60px; flex: 1; } -.imx-close-dialog-button { - right: -15px; - top: -15px; -} - -.imx-aob-kpi-empty { +.imx-no-results { display: flex; flex-direction: column; justify-content: center; @@ -142,7 +120,7 @@ h4 { .eui-light-theme { :host { li { - .mat-card { + .mat-mdc-card { background-color: $color-gray-0; } } @@ -175,14 +153,9 @@ h4 { .eui-dark-theme { :host { - .imx-kpi-list > li > .mat-card { + .imx-kpi-list > li > .mat-mdc-card { background-color: $color-gray-60; } - .imx-aob-kpi-empty { - > .eui-icon { - color: $color-gray-5; - } - } .imx-pass { color: $color-green-40; @@ -210,16 +183,9 @@ h4 { .eui-contrast-theme { :host { - .imx-kpi-list > li > .mat-card { + .imx-kpi-list > li > .mat-mdc-card { background-color: $color-gray-80; } - .imx-aob-kpi-empty { - background-color: $color-gray-90; - - > .eui-icon { - color: $color-gray-0; - } - } .imx-pass { color: $color-green-20; diff --git a/imxweb/projects/aob/src/lib/kpi/kpi-overview/kpi-overview.component.ts b/imxweb/projects/aob/src/lib/kpi/kpi-overview/kpi-overview.component.ts index 1a0753638..078fe030e 100644 --- a/imxweb/projects/aob/src/lib/kpi/kpi-overview/kpi-overview.component.ts +++ b/imxweb/projects/aob/src/lib/kpi/kpi-overview/kpi-overview.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,13 +24,13 @@ * */ -import { Component, OnChanges, Input, TemplateRef, ViewChild, SimpleChanges } from '@angular/core'; +import { Component, Input, OnChanges, SimpleChanges, TemplateRef, ViewChild } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; -import { ChartOptions } from 'billboard.js'; import { TranslateService } from '@ngx-translate/core'; +import { ChartOptions } from 'billboard.js'; -import { XAxisInformation, LineChartOptions, YAxisInformation, SeriesInformation, ClassloggerService, BusyService } from 'qbm'; -import { ChartDto, PortalApplication, ChartDataPoint } from 'imx-api-aob'; +import { ChartDataPoint, ChartDto, PortalApplication } from '@imx-modules/imx-api-aob'; +import { BusyService, ClassloggerService, LineChartOptions, SeriesInformation, XAxisInformation, YAxisInformation } from 'qbm'; import { KpiOverviewService } from './kpi-overview.service'; /** @@ -79,7 +79,7 @@ export class KpiOverviewComponent implements OnChanges { readonly translateService: TranslateService, private kpiOverviewProvider: KpiOverviewService, private logger: ClassloggerService, - private matDialog: MatDialog + private matDialog: MatDialog, ) { translateService.get('#LDS#Error threshold').subscribe((trans: string) => (this.errorThreshhold = trans)); this.busyService.busyStateChanged.subscribe((elem) => (this.isLoading = elem)); @@ -89,17 +89,16 @@ export class KpiOverviewComponent implements OnChanges { * Displays a busyIndicator and initializes the data, when the OnChanges life cycle hook is called */ public async ngOnChanges(changes: SimpleChanges): Promise { - if (changes.application?.currentValue?.UID_AOBApplication.value === changes.application?.previousValue?.UID_AOBApplication.value) { return; - } - + } + const isBusy = this.busyService.beginBusy(); try { const data = await this.kpiOverviewProvider.get(this.application.UID_AOBApplication.value); if (data) { - this.chartDataPass = data.filter((kpi) => kpi.Data.length > 0 && kpi.Data[0].Points[0].Value < kpi.ErrorThreshold); - this.chartDataFail = data.filter((kpi) => kpi.Data.length > 0 && kpi.Data[0].Points[0].Value >= kpi.ErrorThreshold); + this.chartDataPass = data.filter((kpi) => !!kpi.Data?.length && (kpi.Data?.[0].Points?.[0].Value || 0) < kpi.ErrorThreshold); + this.chartDataFail = data.filter((kpi) => !!kpi.Data?.length && (kpi.Data?.[0].Points?.[0].Value || 0) >= kpi.ErrorThreshold); } else { this.logger.error(this, 'ChartDto[] is undefined'); } @@ -113,8 +112,15 @@ export class KpiOverviewComponent implements OnChanges { * @param chart the data of a single chart */ public showDetails(chart: ChartDto, isFail: boolean): void { - if (!this.hasDetails(chart)) { return; } - this.matDialog.open(this.chartDialog, { data: { chart: this.getChartData(chart), failed: isFail }, width: '600px', autoFocus: false, panelClass: 'kpi-overview-modal' }); + if (!this.hasDetails(chart)) { + return; + } + this.matDialog.open(this.chartDialog, { + data: { chart: this.getChartData(chart), failed: isFail }, + width: '600px', + autoFocus: false, + panelClass: 'kpi-overview-modal', + }); } /** @@ -148,7 +154,7 @@ export class KpiOverviewComponent implements OnChanges { * @returns true, if the chart has multiple points else it return false */ private hasMultipleDataPoints(chart: ChartDto): boolean { - return this.hasDetails(chart) && chart.Data[0].Points.length > 1; + return this.hasDetails(chart) && (chart.Data?.[0].Points?.length || 0) > 1; } /** @@ -167,13 +173,9 @@ export class KpiOverviewComponent implements OnChanges { */ private buildOptions(chart: ChartDto): ChartOptions { const yAxis = new YAxisInformation( - chart.Data.map( - (serie) => - new SeriesInformation( - chart.Display, - serie.Points.map((point: ChartDataPoint) => point.Value) - ) - ) + chart.Data?.map( + (serie) => new SeriesInformation(chart.Display || '', serie.Points?.map((point: ChartDataPoint) => point.Value) || []), + ) || [], ); const browserCulture = this.translateService.currentLang; @@ -181,15 +183,11 @@ export class KpiOverviewComponent implements OnChanges { yAxis.tickConfiguration = { format: (d) => d.toLocaleString(browserCulture), values: this.accumulateTicks(chart) }; const lineChartOptions = new LineChartOptions( - new XAxisInformation( - 'date', - chart.Data[0].Points.map((point: ChartDataPoint) => new Date(point.Date)), - { - count: 6, - format: (d: Date) => d.toLocaleDateString(browserCulture), - } - ), - yAxis + new XAxisInformation('date', chart.Data?.[0].Points?.map((point: ChartDataPoint) => new Date(point.Date)) || [], { + count: 6, + format: (d: Date) => d.toLocaleDateString(browserCulture), + }), + yAxis, ); lineChartOptions.useCurvedLines = false; lineChartOptions.hideLegend = true; @@ -202,17 +200,17 @@ export class KpiOverviewComponent implements OnChanges { ]; lineChartOptions.size = { height: 400, - width: 500 - } + width: 500, + }; this.logger.debug(this, 'Options', lineChartOptions.options); return lineChartOptions.options; } private accumulateTicks(chart: ChartDto): number[] { - const ret = []; + let ret: number[] = []; const max = Math.max.apply( Math, - chart.Data[0].Points.map((o) => o.Value) + chart.Data?.[0].Points?.map((o) => o.Value), ); const steps = Math.max(1, Math.trunc(max / 10)) + 1; diff --git a/imxweb/projects/aob/src/lib/kpi/kpi-overview/kpi-overview.service.ts b/imxweb/projects/aob/src/lib/kpi/kpi-overview/kpi-overview.service.ts index 93968c199..88e26c27c 100644 --- a/imxweb/projects/aob/src/lib/kpi/kpi-overview/kpi-overview.service.ts +++ b/imxweb/projects/aob/src/lib/kpi/kpi-overview/kpi-overview.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,20 +26,20 @@ import { Injectable } from '@angular/core'; +import { ChartDto } from '@imx-modules/imx-api-aob'; import { ApiClientService } from 'qbm'; -import { ChartDto } from 'imx-api-aob'; import { AobApiService } from '../../aob-api-client.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class KpiOverviewService { - constructor( private readonly aobClient: AobApiService, - private readonly apiProvider: ApiClientService) { } + private readonly apiProvider: ApiClientService, + ) {} - public async get(uidapplication: string): Promise { + public async get(uidapplication: string): Promise { return this.apiProvider.request(() => this.aobClient.client.portal_application_kpi_get(uidapplication)); } } diff --git a/imxweb/projects/aob/src/lib/kpi/kpi.module.ts b/imxweb/projects/aob/src/lib/kpi/kpi.module.ts index e867e9ec7..cf85dc90a 100644 --- a/imxweb/projects/aob/src/lib/kpi/kpi.module.ts +++ b/imxweb/projects/aob/src/lib/kpi/kpi.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-action-parameter.interface.ts b/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-action-parameter.interface.ts index bb1723b6f..f64fb4719 100644 --- a/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-action-parameter.interface.ts +++ b/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-action-parameter.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,14 +24,13 @@ * */ -import { PortalEntitlement, PortalApplicationinshop, PortalApplication } from 'imx-api-aob'; +import { PortalEntitlement, PortalApplicationinshop, PortalApplication } from '@imx-modules/imx-api-aob'; import { LifecycleAction } from './lifecycle-action.enum'; /** * Interface for the {@link MAT_DIALOG_DATA|MAT_DIALOG_DATA} of the {@link LifecycleActionComponent|LifecycleActionComponent} */ export interface LifecycleActionParameter { - /** The mode to show the dialog. */ action: LifecycleAction; diff --git a/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-action.component.html b/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-action.component.html index afcc34a60..5ff294111 100644 --- a/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-action.component.html +++ b/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-action.component.html @@ -1,75 +1,105 @@ -

{{ dialogTitle }}

-
+

{{ dialogTitle }}

+
- - - - - -
{{ data?.GetEntity().GetDisplay() }}
-
{{ data?.Description.Column.GetDisplayValue()}}
-
-
- - -
+ + + + {{ tableDisplay }} + + +
{{ item?.GetEntity().GetDisplay() }}
+
{{ item?.Description.Column.GetDisplayValue() }}
+ +
+ + {{ entitySchema.Columns.UID_AERoleOwner.Display }} + +
{{ item.UID_AERoleOwner?.Column.GetDisplayValue() }}
+ +
+
- {{'#LDS#*If you unassign an application entitlement from this application, it is removed from this application. The application entitlement can still be reassigned as required.' | translate }} + {{ + '#LDS#*If you unassign an application entitlement from this application, it is removed from this application. The application entitlement can still be reassigned as required.' + | translate + }}
{{ ('#LDS#Shops' | translate) + (data.type === 'AobEntitlement' ? '*' : '') }}
- {{ shop.GetEntity().GetDisplay() }} + {{ shop.GetEntity().GetDisplay() }} - {{ (data.type === 'AobEntitlement' ? '#LDS#Application entitlements cannot be published. No shop is assigned to the application. Please assign at least one shop to the application.': - '#LDS#No shop is assigned to the application. Without a shop, the assigned application entitlements cannot be requested. Please assign at least one shop to the application.' - ) | translate }} + {{ + (data.type === 'AobEntitlement' + ? '#LDS#Application entitlements cannot be published. No shop is assigned to the application. Please assign at least one shop to the application.' + : '#LDS#No shop is assigned to the application. Without a shop, the assigned application entitlements cannot be requested. Please assign at least one shop to the application.' + ) | translate + }} - *{{'#LDS#IT Shops are set at the application level.' | translate }} -
-
-
-
{{ '#LDS#Publication Date' | translate }}
- - {{ '#LDS#Now' | translate }} - {{ '#LDS#Later' | translate }} - - - {{(isApplication()? '#LDS#The application will be published immediately. The exact time may vary.': - '#LDS#The application entitlement will be published immediately. The exact time may vary.')| translate}} - - - - - - {{ '#LDS#Please specify a date that lies in the future.' | translate }} - - - {{ '#LDS#Please enter a valid date or select a date from the calendar.' | translate }} - - - - {{ (isApplication() - ? '#LDS#The application will be published on {0} at {1} (your local time).' - : '#LDS#The application entitlement will be published on {0} at {1} (your local time) if the application is published.') - | translate | ldsReplace: dateString : timeString }} -
+ *{{ '#LDS#IT Shops are set at the application level.' | translate }}
+
+
{{ '#LDS#Publication Date' | translate }}
+ + {{ '#LDS#Now' | translate }} + {{ '#LDS#Later' | translate }} + + + {{ + (isApplication() + ? '#LDS#The application will be published immediately. The exact time may vary.' + : '#LDS#The application entitlement will be published immediately. The exact time may vary.' + ) | translate + }} + + + + + + {{ '#LDS#Please specify a date that lies in the future.' | translate }} + + + {{ '#LDS#Please enter a valid date or select a date from the calendar.' | translate }} + + + + {{ + (isApplication() + ? '#LDS#The application will be published on {0} at {1} (your local time).' + : '#LDS#The application entitlement will be published on {0} at {1} (your local time) if the application is published.' + ) + | translate + | ldsReplace: dateString : timeString + }} +
-
+
- -
diff --git a/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-action.component.scss b/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-action.component.scss index 1fc29a6b2..573cdfea4 100644 --- a/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-action.component.scss +++ b/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-action.component.scss @@ -1,5 +1,6 @@ @import '@elemental-ui/core/src/styles/_palette.scss'; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/mixins'; :host { display: flex; @@ -11,15 +12,7 @@ imx-data-source-toolbar { display: none; } -.mat-table { - box-shadow: none; - - .mat-cell { - max-width: 20vw; - } -} - -.mat-card-avatar { +.mat-mdc-card-avatar { background-color: $black-b; color: $white; padding: 5px; @@ -28,14 +21,6 @@ imx-data-source-toolbar { user-select: none; } -.mat-dialog-content { - overflow: hidden; - flex: 1; - display: flex; - flex-direction: column; - height: inherit; -} - .mat-column-__Display { max-width: 500px; } @@ -69,19 +54,9 @@ div[imxActionsLabel] { overflow: auto; } -.mat-dialog-actions { - display: flex; - justify-content: flex-end; - - button { - margin-left: 10px; - margin-top: 10px; - } -} - -@mixin imx-entitlements-settings-block { - display: flex; - flex-direction: column; +.imx-entitlements-publish-itshops, +.imx-entitlements-publish-date { + @include flex-column-container(); padding-bottom: 20px; h5 { @@ -90,12 +65,11 @@ div[imxActionsLabel] { } .imx-entitlements-publish-itshops { - @include imx-entitlements-settings-block; overflow: auto; width: 380px; margin-right: 20px; - .mat-card { + .mat-mdc-card { display: flex; align-items: center; height: 50px; @@ -103,11 +77,9 @@ div[imxActionsLabel] { margin-bottom: 15px; } - .mat-card-title { + .mat-mdc-card-title { font-size: 16px; margin-left: 10px; - text-overflow: ellipsis; - overflow: hidden; white-space: nowrap; } @@ -116,25 +88,17 @@ div[imxActionsLabel] { color: $black-9; } - .mat-error { + .mat-mdc-form-field-error { font-size: 12px; margin-right: 15px; } } .imx-entitlements-publish-date { - @include imx-entitlements-settings-block; + @include flex-column-container; + gap: 25px; width: 400px; - .mat-radio-button { - margin-right: 60px; - } - - .mat-form-field { - width: 159px; - margin-top: 30px; - } - .mat-small { display: block; font-size: 14px; @@ -145,18 +109,9 @@ div[imxActionsLabel] { .eui-dark-theme { :host { - .mat-card-avatar { - background-color: $color-gray-60; - color: $color-gray-80; - } - } -} - -.eui-contrast-theme { - :host { - .mat-card-avatar { - background-color: $color-gray-80; - color: $color-gray-100; + .mat-mdc-card-avatar { + background-color: #616566; + color: #2f3233; } } } diff --git a/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-action.component.spec.ts b/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-action.component.spec.ts index 71377784c..b85688b65 100644 --- a/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-action.component.spec.ts +++ b/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-action.component.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,42 +24,55 @@ * */ -import { Pipe, PipeTransform, Component, Input } from '@angular/core'; +import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; +import { Component, Input, Pipe, PipeTransform } from '@angular/core'; import { ComponentFixture, TestBed, fakeAsync, tick, waitForAsync } from '@angular/core/testing'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatDatepickerModule } from '@angular/material/datepicker'; -import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatRadioModule } from '@angular/material/radio'; -import { HttpClientModule } from '@angular/common/http'; import { EuiCoreModule } from '@elemental-ui/core'; +import { TranslateFakeLoader, TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { LoggerTestingModule } from 'ngx-logger/testing'; -import { TranslateModule, TranslateLoader, TranslateFakeLoader } from '@ngx-translate/core'; +import { MatTableModule } from '@angular/material/table'; +import { PortalApplication, PortalEntitlement } from '@imx-modules/imx-api-aob'; +import { + ClassloggerService, + ConfirmationService, + DataViewModule, + DataViewSource, + FakeDataViewSource, + LdsReplacePipe, + MessageDialogService, + MetadataService, + SqlWizardApiService, + clearStylesFromDOM, +} from 'qbm'; +import { AobApiService } from '../aob-api-client.service'; import { LifecycleActionComponent } from './lifecycle-action.component'; -import { LdsReplacePipe, clearStylesFromDOM, MetadataService } from 'qbm'; import { LifecycleAction } from './lifecycle-action.enum'; -import { AobApiService } from '../aob-api-client.service'; -import { PortalApplication, PortalEntitlement } from 'imx-api-aob'; const mockMatDialogRef = { - close: jasmine.createSpy('close') + close: jasmine.createSpy('close'), }; @Pipe({ name: 'ldsReplace' }) class MockLdsReplacePipe implements PipeTransform { - transform() { } + transform() {} } const mockLdsReplacePipe = { - transform: jasmine.createSpy('transform').and.callFake((value: string) => value) + transform: jasmine.createSpy('transform').and.callFake((value: string) => value), }; @Component({ selector: 'imx-data-source-toolbar', - template: '

MockDataSourceToolbarComponent

' + template: '

MockDataSourceToolbarComponent

', }) class MockDataSourceToolbarComponent { @Input() public dst: any; @@ -69,7 +82,7 @@ class MockDataSourceToolbarComponent { @Component({ selector: 'imx-data-table', - template: '

MockDataTableComponent

' + template: '

MockDataTableComponent

', }) class MockDataTableComponent { @Input() public dst: any; @@ -77,7 +90,7 @@ class MockDataTableComponent { @Component({ selector: 'imx-data-table-column', - template: '

MockDataTableColumnComponent

' + template: '

MockDataTableColumnComponent

', }) class MockDataTableColumnComponent { @Input() public entityColumn: any; @@ -97,8 +110,8 @@ describe('LifecycleActionComponent', () => { ImageId: '', IsDeactivated: false, IsMAllTable: false, - IsMNTable: false - }) + IsMNTable: false, + }), ); const moduleDef = { @@ -107,12 +120,11 @@ describe('LifecycleActionComponent', () => { MockDataSourceToolbarComponent, MockDataTableComponent, MockDataTableColumnComponent, - MockLdsReplacePipe + MockLdsReplacePipe, ], imports: [ EuiCoreModule, FormsModule, - HttpClientModule, LoggerTestingModule, MatButtonModule, MatCardModule, @@ -124,43 +136,52 @@ describe('LifecycleActionComponent', () => { TranslateModule.forRoot({ loader: { provide: TranslateLoader, - useClass: TranslateFakeLoader - } - }) + useClass: TranslateFakeLoader, + }, + }), + DataViewModule, + MatTableModule, ], providers: [ { provide: MatDialogRef, - useValue: mockMatDialogRef + useValue: mockMatDialogRef, }, { provide: MAT_DIALOG_DATA, - useValue: { action: LifecycleAction.Publish, elements: [], shops: [], type: 'AobApplication' } + useValue: { action: LifecycleAction.Publish, elements: [], shops: [], type: 'AobApplication' }, }, { provide: LdsReplacePipe, - useValue: mockLdsReplacePipe + useValue: mockLdsReplacePipe, }, { provide: AobApiService, useValue: { typedClient: { PortalApplication: { - GetSchema: () => PortalApplication.GetEntitySchema() + GetSchema: () => PortalApplication.GetEntitySchema(), }, PortalEntitlement: { - GetSchema: () => PortalEntitlement.GetEntitySchema() - } - } - } + GetSchema: () => PortalEntitlement.GetEntitySchema(), + }, + }, + }, }, { provide: MetadataService, useClass: class { GetTableMetadata = getTableMetadataSpy; - } - } - ] + }, + }, + { provide: MessageDialogService, useValue: {} }, + { provide: ClassloggerService, useValue: { debug: () => {}, info: () => {} } }, + { provide: SqlWizardApiService, useValue: {} }, + { provide: ConfirmationService, useValue: {} }, + { provide: DataViewSource, useValue: FakeDataViewSource }, + provideHttpClient(withInterceptorsFromDi()), + provideHttpClientTesting(), + ], }; beforeEach(waitForAsync(() => { @@ -185,98 +206,105 @@ describe('LifecycleActionComponent', () => { tick(); - expect(component.dstSettings.dataSource).toBeTruthy(); + expect(component.dataSource).toBeTruthy(); })); [ { whenToPublish: 'now', - expectedIsInActive: false + expectedIsInActive: false, }, { whenToPublish: 'future', - expectedIsInActive: false - } - ].forEach(testcase => + expectedIsInActive: false, + }, + ].forEach((testcase) => it('should return publishData of entitlements in publish mode', () => { initComponent(); component.data.action = LifecycleAction.Publish; component.data.elements = []; - component.whenToPublish = testcase.whenToPublish as ('now' | 'future'); + component.whenToPublish = testcase.whenToPublish as 'now' | 'future'; component.submitData(); - expect(mockMatDialogRef.close).toHaveBeenCalledWith({ publishFuture: testcase.expectedIsInActive, date: component.datepickerFormControl.value.toDate() }); - })); + expect(mockMatDialogRef.close).toHaveBeenCalledWith({ + publishFuture: testcase.expectedIsInActive, + date: component.datepickerFormControl.value.toDate(), + }); + }), + ); [ { action: LifecycleAction.Unassign, - mode: 'Unassign' + mode: 'Unassign', }, { action: LifecycleAction.Unpublish, - mode: 'Unpublish' - } - ].forEach(testcase => + mode: 'Unpublish', + }, + ].forEach((testcase) => it(`should return true in ${testcase.mode} mode`, () => { initComponent(); component.data.action = testcase.action; component.data.elements = []; component.submitData(); expect(mockMatDialogRef.close).toHaveBeenCalledWith(true); - }) + }), ); - [ { - type: 'AobApplication' as 'AobEntitlement' | 'AobApplication' + type: 'AobApplication' as 'AobEntitlement' | 'AobApplication', }, { - type: 'AobEntitlement' as 'AobEntitlement' | 'AobApplication' - } - ].forEach(maintestcase => + type: 'AobEntitlement' as 'AobEntitlement' | 'AobApplication', + }, + ].forEach((maintestcase) => [ { action: LifecycleAction.Unassign, mode: 'Unassign', - entitlementsCount: 1 + entitlementsCount: 1, }, { action: LifecycleAction.Unassign, mode: 'Unassign', - entitlementsCount: 2 + entitlementsCount: 2, }, { action: LifecycleAction.Publish, mode: 'Publish', - entitlementsCount: 1 + entitlementsCount: 1, }, { action: LifecycleAction.Publish, mode: 'Publish', - entitlementsCount: 2 + entitlementsCount: 2, }, { action: LifecycleAction.Unpublish, mode: 'Unpublish', - entitlementsCount: 1 + entitlementsCount: 1, }, { action: LifecycleAction.Unpublish, mode: 'Unpublish', - entitlementsCount: 2 - } - ].forEach(testcase => + entitlementsCount: 2, + }, + ].forEach((testcase) => it(`should init the component with mode=${testcase.mode} and ${testcase.entitlementsCount} entitlements`, () => { - - let dummyEntitlements = []; + let dummyEntitlements: any[] = []; for (let n = 0; n < testcase.entitlementsCount; n++) { dummyEntitlements.push({}); } moduleDef.providers[1] = { provide: MAT_DIALOG_DATA, - useValue: { action: testcase.action, elements: dummyEntitlements, shops: [], type: (maintestcase.type as 'AobEntitlement' | 'AobApplication') } + useValue: { + action: testcase.action, + elements: dummyEntitlements, + shops: [], + type: maintestcase.type as 'AobEntitlement' | 'AobApplication', + }, }; TestBed.configureTestingModule(moduleDef).compileComponents(); @@ -293,7 +321,7 @@ describe('LifecycleActionComponent', () => { expect(component.isUnassign()).toBe(testcase.action === LifecycleAction.Unassign); expect(component.isPublish()).toBe(testcase.action === LifecycleAction.Publish); expect(component.isUnpublish()).toBe(testcase.action === LifecycleAction.Unpublish); - }))); - - + }), + ), + ); }); diff --git a/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-action.component.ts b/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-action.component.ts index 203acd483..eeb0ae923 100644 --- a/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-action.component.ts +++ b/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-action.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,20 +24,26 @@ * */ +import { Platform } from '@angular/cdk/platform'; import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; -import { UntypedFormControl, ValidatorFn, AbstractControl, UntypedFormGroup } from '@angular/forms'; -import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { AbstractControl, FormGroup, UntypedFormControl, ValidatorFn } from '@angular/forms'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { TranslateService } from '@ngx-translate/core'; -import { Subscription } from 'rxjs'; -import { Platform } from '@angular/cdk/platform'; import moment from 'moment-timezone'; +import { Subscription } from 'rxjs'; -import { LdsReplacePipe, ClassloggerService, DataSourceToolbarSettings, MetadataService } from 'qbm'; -import { MetaTableData } from 'imx-api-qbm'; +import { + DisplayColumns, + EntitySchema, + IClientProperty, + MetaTableData, + TypedEntity, + TypedEntityCollectionData, +} from '@imx-modules/imx-qbm-dbts'; +import { ClassloggerService, DataViewSource, LdsReplacePipe, MetadataService } from 'qbm'; +import { AobApiService } from '../aob-api-client.service'; import { LifecycleActionParameter } from './lifecycle-action-parameter.interface'; import { LifecycleAction } from './lifecycle-action.enum'; -import { EntitySchema, DisplayColumns, TypedEntityCollectionData, TypedEntity, IClientProperty } from 'imx-qbm-dbts'; -import { AobApiService } from '../aob-api-client.service'; /** * A component that represents a dialog for submitting an {@link LifecycleAction|action} on the @@ -47,12 +53,11 @@ import { AobApiService } from '../aob-api-client.service'; @Component({ selector: 'imx-entitlements-action', templateUrl: './lifecycle-action.component.html', - styleUrls: ['./lifecycle-action.component.scss'] + styleUrls: ['./lifecycle-action.component.scss'], + providers: [DataViewSource], }) export class LifecycleActionComponent implements OnDestroy, OnInit { - public readonly DisplayColumns = DisplayColumns; // Enables use of this static class in Angular Templates. - public dstSettings: DataSourceToolbarSettings; public entitySchema: EntitySchema; @@ -70,7 +75,7 @@ export class LifecycleActionComponent implements OnDestroy, OnInit { public publishFuture: boolean; - public readonly publishDateForm: UntypedFormGroup; + public readonly publishDateForm: FormGroup<{ datepickerFormControl: UntypedFormControl; whenToPublish: UntypedFormControl }>; public readonly datepickerFormControl: UntypedFormControl; public readonly browserCulture: string; @@ -85,8 +90,6 @@ export class LifecycleActionComponent implements OnDestroy, OnInit { return this.datepickerFormControl.value.toDate().toLocaleTimeString(this.browserCulture); } - private dataSource: TypedEntityCollectionData; - /** * @ignore * List of subscriptions. @@ -104,7 +107,9 @@ export class LifecycleActionComponent implements OnDestroy, OnInit { private readonly translateService: TranslateService, private readonly metadataProvider: MetadataService, private readonly aobApiService: AobApiService, - private readonly platform: Platform) { + private readonly platform: Platform, + public dataSource: DataViewSource, + ) { this.browserCulture = this.translateService.currentLang; this.initCaptions(); @@ -114,53 +119,47 @@ export class LifecycleActionComponent implements OnDestroy, OnInit { // set date to tomorrow this.minDate = moment(curDate); - this.datepickerFormControl = new UntypedFormControl(this.minDate, this.validateDate(this.minDate, () => this.publishFuture)); + this.datepickerFormControl = new UntypedFormControl( + this.minDate, + this.validateDate(this.minDate, () => this.publishFuture), + ); - this.publishDateForm = new UntypedFormGroup({ + this.publishDateForm = new FormGroup<{ datepickerFormControl: UntypedFormControl; whenToPublish: UntypedFormControl }>({ datepickerFormControl: this.datepickerFormControl, whenToPublish: new UntypedFormControl('now'), }); - this.subscriptions.push(this.publishDateForm.get('whenToPublish').valueChanges - .subscribe((when: 'now' | 'future') => { + this.subscriptions.push( + this.publishDateForm.controls.whenToPublish.valueChanges.subscribe((when: 'now' | 'future') => { this.whenToPublish = when; this.publishFuture = this.whenToPublish === 'future'; - })); + }), + ); this.publishFuture = false; - - this.dataSource = { - Data: data.elements, - totalCount: data.elements.length - }; } /** * @ignore Used internally. */ public async ngOnInit(): Promise { - this.entitySchema = this.isApplication() ? this.aobApiService.typedClient.PortalApplication.GetSchema() : this.aobApiService.typedClient.PortalEntitlement.GetSchema(); - this.displayedColumns = [ - this.entitySchema.Columns[DisplayColumns.DISPLAY_PROPERTYNAME], - this.entitySchema.Columns.UID_AERoleOwner - ]; - - this.dstSettings = { - dataSource: this.dataSource, - entitySchema: this.entitySchema, - displayedColumns: this.displayedColumns, - navigationState: { - StartIndex: 0 - } - }; + this.displayedColumns = [this.entitySchema.Columns[DisplayColumns.DISPLAY_PROPERTYNAME], this.entitySchema.Columns.UID_AERoleOwner]; + + this.dataSource.init({ + execute: (): Promise> => + Promise.resolve({ Data: this.data.elements, totalCount: this.data.elements.length }), + schema: this.entitySchema, + columnsToDisplay: this.displayedColumns, + localSource: true, + }); await this.metadataProvider - .GetTableMetadata(this.entitySchema.TypeName) - .then((metadata: MetaTableData) => (this.tableDisplay = metadata.DisplaySingular)); + .GetTableMetadata(this.entitySchema.TypeName || '') + .then((metadata: MetaTableData) => (this.tableDisplay = metadata.DisplaySingular || '')); } /** @@ -168,7 +167,7 @@ export class LifecycleActionComponent implements OnDestroy, OnInit { * Unsubscribes all listeners. */ public ngOnDestroy(): void { - this.subscriptions.forEach(subscription => subscription.unsubscribe()); + this.subscriptions.forEach((subscription) => subscription.unsubscribe()); } /** @@ -176,9 +175,10 @@ export class LifecycleActionComponent implements OnDestroy, OnInit { */ public submitData(): void { this.logger.debug(this, `Close the ${this.data.action} dialog for ${this.data.elements.length} ${this.data.type}(s).`); - const result = this.data.action === LifecycleAction.Publish - ? { publishFuture: this.publishFuture, date: this.datepickerFormControl.value.toDate() } - : true; + const result = + this.data.action === LifecycleAction.Publish + ? { publishFuture: this.publishFuture, date: this.datepickerFormControl.value.toDate() } + : true; this.dialogRef.close(result); } @@ -219,7 +219,9 @@ export class LifecycleActionComponent implements OnDestroy, OnInit { this.logger.debug(this, `Init captions for the ${this.data.action.toString()}-mode depending on the application count.`); const multipleEntitlements = this.data != null && this.data.elements != null && this.data.elements.length > 1; - let dialogTitle = multipleEntitlements ? '#LDS#Heading Unassign {0} Application Entitlements' : '#LDS#Heading Unassign Application Entitlement'; + let dialogTitle = multipleEntitlements + ? '#LDS#Heading Unassign {0} Application Entitlements' + : '#LDS#Heading Unassign Application Entitlement'; let buttonText = multipleEntitlements ? '#LDS#Unassign' : '#LDS#Unassign'; if (this.data.type === 'AobApplication') { @@ -232,7 +234,9 @@ export class LifecycleActionComponent implements OnDestroy, OnInit { dialogTitle = multipleEntitlements ? '#LDS#Heading Publish {0} Applications' : '#LDS#Heading Publish Application'; buttonText = multipleEntitlements ? '#LDS#Publish' : '#LDS#Publish'; } else { - dialogTitle = multipleEntitlements ? '#LDS#Heading Publish {0} Application Entitlements' : '#LDS#Heading Publish Application Entitlement'; + dialogTitle = multipleEntitlements + ? '#LDS#Heading Publish {0} Application Entitlements' + : '#LDS#Heading Publish Application Entitlement'; buttonText = multipleEntitlements ? '#LDS#Publish' : '#LDS#Publish'; } } else if (this.isUnpublish()) { @@ -240,16 +244,18 @@ export class LifecycleActionComponent implements OnDestroy, OnInit { dialogTitle = multipleEntitlements ? '#LDS#Heading Unpublish {0} Applications' : '#LDS#Heading Unpublish Application'; buttonText = multipleEntitlements ? '#LDS#Unpublish' : '#LDS#Unpublish'; } else { - dialogTitle = multipleEntitlements ? '#LDS#Heading Unpublish {0} Application Entitlements' : '#LDS#Heading Unpublish Application Entitlement'; + dialogTitle = multipleEntitlements + ? '#LDS#Heading Unpublish {0} Application Entitlements' + : '#LDS#Heading Unpublish Application Entitlement'; buttonText = multipleEntitlements ? '#LDS#Unpublish' : '#LDS#Unpublish'; } } - this.translateService.get(dialogTitle) - .subscribe((trans: string) => this.dialogTitle = this.ldsReplace.transform(trans, this.data.elements.length)); + this.translateService + .get(dialogTitle) + .subscribe((trans: string) => (this.dialogTitle = this.ldsReplace.transform(trans, this.data.elements.length))); - this.translateService.get(buttonText). - subscribe((trans: string) => this.actionButtonText = trans); + this.translateService.get(buttonText).subscribe((trans: string) => (this.actionButtonText = trans)); } private validateDate(minDate: any, publishFuture: () => boolean): ValidatorFn { diff --git a/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-action.enum.ts b/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-action.enum.ts index 35cd2b5bd..f9a124126 100644 --- a/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-action.enum.ts +++ b/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-action.enum.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,5 +30,5 @@ export enum LifecycleAction { Publish, Unpublish, - Unassign + Unassign, } diff --git a/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-actions.module.ts b/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-actions.module.ts index 8c31f8a22..6d8fbcb57 100644 --- a/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-actions.module.ts +++ b/imxweb/projects/aob/src/lib/lifecycle-actions/lifecycle-actions.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,9 +24,9 @@ * */ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { LifecycleActionComponent } from './lifecycle-action.component'; +import { NgModule } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatDatepickerModule } from '@angular/material/datepicker'; @@ -34,10 +34,11 @@ import { MatDialogModule } from '@angular/material/dialog'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { MatRadioModule } from '@angular/material/radio'; +import { MatTableModule } from '@angular/material/table'; import { EuiCoreModule } from '@elemental-ui/core'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { TranslateModule } from '@ngx-translate/core'; -import { QbmModule, DataSourceToolbarModule, DataTableModule, LdsReplaceModule } from 'qbm'; +import { DataSourceToolbarModule, DataTableModule, DataViewModule, LdsReplaceModule, QbmModule } from 'qbm'; +import { LifecycleActionComponent } from './lifecycle-action.component'; @NgModule({ declarations: [LifecycleActionComponent], imports: [ @@ -56,7 +57,9 @@ import { QbmModule, DataSourceToolbarModule, DataTableModule, LdsReplaceModule } MatRadioModule, ReactiveFormsModule, QbmModule, - TranslateModule + TranslateModule, + DataViewModule, + MatTableModule, ], }) -export class LifecycleActionsModule { } +export class LifecycleActionsModule {} diff --git a/imxweb/projects/aob/src/lib/permissions/aob-permissions-helper.ts b/imxweb/projects/aob/src/lib/permissions/aob-permissions-helper.ts index 353224cfe..c24194e76 100644 --- a/imxweb/projects/aob/src/lib/permissions/aob-permissions-helper.ts +++ b/imxweb/projects/aob/src/lib/permissions/aob-permissions-helper.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,9 +25,9 @@ */ export function isAobApplicationOwner(features: string[]): boolean { - return features.find(item => item === 'Portal_UI_ApplicationOwner') != null; + return features.find((item) => item === 'Portal_UI_ApplicationOwner') != null; } export function isAobApplicationAdmin(features: string[]): boolean { - return features.find(item => item === 'Portal_UI_ApplicationAdmin') != null; + return features.find((item) => item === 'Portal_UI_ApplicationAdmin') != null; } diff --git a/imxweb/projects/aob/src/lib/permissions/aob-permissions.service.ts b/imxweb/projects/aob/src/lib/permissions/aob-permissions.service.ts index 2bc69324e..fd0731d53 100644 --- a/imxweb/projects/aob/src/lib/permissions/aob-permissions.service.ts +++ b/imxweb/projects/aob/src/lib/permissions/aob-permissions.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,16 +30,21 @@ import { UserModelService } from 'qer'; import { isAobApplicationAdmin, isAobApplicationOwner } from './aob-permissions-helper'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class AobPermissionsService { - constructor(private readonly userService: UserModelService) { } + constructor(private readonly userService: UserModelService) {} public async isAobApplicationOwner(): Promise { - return isAobApplicationOwner((await this.userService.getFeatures()).Features); + return isAobApplicationOwner((await this.userService.getFeatures()).Features || []); } public async isAobApplicationAdmin(): Promise { - return isAobApplicationAdmin((await this.userService.getFeatures()).Features); + return isAobApplicationAdmin((await this.userService.getFeatures()).Features || []); + } + + public async isAobApplicationOwnerOrAdmin(): Promise { + const features = (await this.userService.getFeatures()).Features || []; + return isAobApplicationAdmin(features) || isAobApplicationOwner(features); } } diff --git a/imxweb/projects/aob/src/lib/service-items/service-items.module.ts b/imxweb/projects/aob/src/lib/service-items/service-items.module.ts index e31542951..929f95faa 100644 --- a/imxweb/projects/aob/src/lib/service-items/service-items.module.ts +++ b/imxweb/projects/aob/src/lib/service-items/service-items.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,8 +28,6 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; @NgModule({ - imports: [ - CommonModule - ] + imports: [CommonModule], }) -export class ServiceItemsModule { } +export class ServiceItemsModule {} diff --git a/imxweb/projects/aob/src/lib/service-items/service-items.service.ts b/imxweb/projects/aob/src/lib/service-items/service-items.service.ts index 49c38003e..1d54e75a1 100644 --- a/imxweb/projects/aob/src/lib/service-items/service-items.service.ts +++ b/imxweb/projects/aob/src/lib/service-items/service-items.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,24 +26,29 @@ import { Injectable } from '@angular/core'; +import { PortalEntitlement, PortalEntitlementServiceitem } from '@imx-modules/imx-api-aob'; +import { CollectionLoadParameters, ExtendedTypedEntityCollection } from '@imx-modules/imx-qbm-dbts'; import { ApiClientService } from 'qbm'; -import { PortalEntitlement, PortalEntitlementServiceitem } from 'imx-api-aob'; -import { CollectionLoadParameters, TypedEntityCollectionData } from 'imx-qbm-dbts'; import { AobApiService } from '../aob-api-client.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ServiceItemsService { - constructor(private readonly aobClient: AobApiService, private readonly apiProvider: ApiClientService) { } + constructor( + private readonly aobClient: AobApiService, + private readonly apiProvider: ApiClientService, + ) {} public async get( entitlement: PortalEntitlement, - parameters: CollectionLoadParameters = { } - ): Promise> { - return this.apiProvider.request(() => this.aobClient.typedClient.PortalEntitlementServiceitem.Get({ - ...{ uid_aobentitlement: entitlement.UID_AOBEntitlement.value }, - ...parameters - })); + parameters: CollectionLoadParameters = {}, + ): Promise | undefined> { + return this.apiProvider.request(() => + this.aobClient.typedClient.PortalEntitlementServiceitem.Get({ + ...{ uid_aobentitlement: entitlement.UID_AOBEntitlement.value }, + ...parameters, + }), + ); } } diff --git a/imxweb/projects/aob/src/lib/shops/shops.service.ts b/imxweb/projects/aob/src/lib/shops/shops.service.ts index 5159a4639..68525edd0 100644 --- a/imxweb/projects/aob/src/lib/shops/shops.service.ts +++ b/imxweb/projects/aob/src/lib/shops/shops.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,65 +26,77 @@ import { Injectable } from '@angular/core'; -import { ClassloggerService, ApiClientService } from 'qbm'; -import { CollectionLoadParameters, TypedEntity, TypedEntityCollectionData } from 'imx-qbm-dbts'; -import { PortalShops, PortalApplicationinshop, PortalApplication } from 'imx-api-aob'; +import { PortalApplication, PortalApplicationinshop, PortalShops } from '@imx-modules/imx-api-aob'; +import { CollectionLoadParameters, TypedEntity, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; +import { ApiClientService, ClassloggerService } from 'qbm'; import { AobApiService } from '../aob-api-client.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ShopsService { public get display(): string { - return this.aobClient.typedClient.PortalShops.GetSchema().Display; + return this.aobClient.typedClient.PortalShops.GetSchema().Display || ''; } constructor( private readonly aobClient: AobApiService, private readonly logger: ClassloggerService, - private readonly apiProvider: ApiClientService - ) { } + private readonly apiProvider: ApiClientService, + ) {} - public async get(parameters: CollectionLoadParameters = { ParentKey: '' }): Promise> { + public async get(parameters: CollectionLoadParameters = { ParentKey: '' }): Promise | undefined> { this.logger.debug(this, 'get'); - return this.apiProvider.request(() => this.aobClient.typedClient.PortalShops.Get(parameters)); + return this.apiProvider.request( + () => this.aobClient.typedClient.PortalShops.Get(parameters) as Promise>, + ); } - public async getApplicationInShop(application: string, parameters: CollectionLoadParameters = {}): - Promise> { + public async getApplicationInShop( + application: string, + parameters: CollectionLoadParameters = {}, + ): Promise | undefined> { this.logger.debug(this, 'getApplicationInShop'); if (application) { - return this.apiProvider.request(() => - this.aobClient.typedClient.PortalApplicationinshop.Get(application, parameters)); + return this.apiProvider.request(() => this.aobClient.typedClient.PortalApplicationinshop.Get(application, parameters)); } } - public async getFirstAndCount(uidApplication: string): Promise<{ first: TypedEntity, count: number }> { - + public async getFirstAndCount(uidApplication: string): Promise<{ first: TypedEntity | undefined; count: number }> { const shops = await this.getApplicationInShop(uidApplication, { PageSize: 1 }); return { - first: shops.Data.length === 0 ? undefined : shops.Data[0], - count: shops.totalCount + first: shops?.Data.length === 0 ? undefined : shops?.Data[0], + count: shops?.totalCount || 0, }; } - public async updateApplicationInShops(application: PortalApplication, changeSet: { add: PortalShops[], remove: PortalShops[] }): Promise { + public async updateApplicationInShops( + application: PortalApplication, + changeSet: { add: PortalShops[]; remove: PortalShops[] }, + ): Promise { this.logger.debug(this, 'updateApplicationInShops'); const applicationInShop = await this.getApplicationInShop(application.UID_AOBApplication.value); - return applicationInShop && - await this.addShops(application, changeSet.add.filter(shop => !ShopsService.isAssigned(shop, applicationInShop.Data))) && - this.removeShops(application, changeSet.remove.filter(shop => ShopsService.isAssigned(shop, applicationInShop.Data))); + return ( + !!applicationInShop && + (await this.addShops( + application, + changeSet.add.filter((shop) => !ShopsService.isAssigned(shop, applicationInShop.Data)), + )) && + (await this.removeShops( + application, + changeSet.remove.filter((shop) => ShopsService.isAssigned(shop, applicationInShop.Data)), + )) + ); } private async addShops(application: PortalApplication, shops: PortalShops[]): Promise { let count = 0; await this.apiProvider.request(async () => { for (const shop of shops) { - await this.aobClient.client. - portal_applicationinshop_put(application.UID_AOBApplication.value, shop.UID_ITShopOrg.value, []); + await this.aobClient.client.portal_applicationinshop_put(application.UID_AOBApplication.value, shop.UID_ITShopOrg.value, []); count++; } }); @@ -95,8 +107,11 @@ export class ShopsService { let count = 0; await this.apiProvider.request(async () => { for (const shop of shops) { - await this.aobClient.client - .portal_applicationinshop_delete(application.UID_AOBApplication.value, shop.UID_ITShopOrg.value, undefined); + await this.aobClient.client.portal_applicationinshop_delete( + application.UID_AOBApplication.value, + shop.UID_ITShopOrg.value, + '', + ); count++; } }); @@ -104,6 +119,6 @@ export class ShopsService { } public static isAssigned(shop: PortalShops, shopsAssigned: PortalApplicationinshop[]): boolean { - return shopsAssigned && shopsAssigned.find(shopAssigned => shopAssigned.UID_ITShopOrg.value === shop.UID_ITShopOrg.value) != null; + return shopsAssigned && shopsAssigned.find((shopAssigned) => shopAssigned.UID_ITShopOrg.value === shop.UID_ITShopOrg.value) != null; } } diff --git a/imxweb/projects/aob/src/lib/start-page/start-page.component.html b/imxweb/projects/aob/src/lib/start-page/start-page.component.html index 75084f4a5..f03ed29fd 100644 --- a/imxweb/projects/aob/src/lib/start-page/start-page.component.html +++ b/imxweb/projects/aob/src/lib/start-page/start-page.component.html @@ -1,19 +1,19 @@
- - account_circle + + account_circle
-
{{ '#LDS#Welcome'| translate }}, 
+
{{ '#LDS#Welcome' | translate }}, 
{{ name }}
- {{ '#LDS#Applications' | translate }} + {{ '#LDS#Applications' | translate }} - {{ '#LDS#{0} Applications' | translate | ldsReplace:numberOfApplications }} + {{ '#LDS#{0} Applications' | translate | ldsReplace: numberOfApplications }} @@ -21,11 +21,23 @@ - - + + diff --git a/imxweb/projects/aob/src/lib/start-page/start-page.component.scss b/imxweb/projects/aob/src/lib/start-page/start-page.component.scss index 044196a80..ce1524134 100644 --- a/imxweb/projects/aob/src/lib/start-page/start-page.component.scss +++ b/imxweb/projects/aob/src/lib/start-page/start-page.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; // Host :host { @@ -13,7 +13,7 @@ img { } // material classes -.mat-card { +.mat-mdc-card { width: 365px; height: 179px; margin: 50px 20px 0px; @@ -22,34 +22,30 @@ img { flex: 0 0 auto; } - - -.mat-card-title { +.mat-mdc-card-title { padding: 20px 33px 0px; - font-size: 24px; font-weight: 600; margin-bottom: 0; } - -.mat-card > .mat-card-actions:last-child { +.mat-mdc-card > .mat-mdc-card-actions:last-child { padding-bottom: 25px; } -.mat-card-content { +.mat-mdc-card-content { font-size: 16px; padding: 5px 33px 10px; margin-bottom: 0; flex-grow: 1; } -.mat-card-actions { +.mat-mdc-card-actions { display: flex; flex-direction: row; padding: 0 25px; } -.mat-button { +.mat-mdc-button { font-size: 16px; } @@ -79,10 +75,3 @@ img { text-overflow: ellipsis; text-align: left; } - -.imx-user-icon { - font-size: 100px; - text-align: center; - display: inline; - color: $iris-blue; -} diff --git a/imxweb/projects/aob/src/lib/start-page/start-page.component.ts b/imxweb/projects/aob/src/lib/start-page/start-page.component.ts index 6d9b5b0a5..557f2e9ad 100644 --- a/imxweb/projects/aob/src/lib/start-page/start-page.component.ts +++ b/imxweb/projects/aob/src/lib/start-page/start-page.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,6 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; import { Component, OnDestroy, OnInit } from '@angular/core'; import { SafeUrl } from '@angular/platform-browser'; import { EuiLoadingService } from '@elemental-ui/core'; @@ -39,13 +38,13 @@ import { AobPermissionsService } from '../permissions/aob-permissions.service'; @Component({ selector: 'imx-start', templateUrl: './start-page.component.html', - styleUrls: ['./start-page.component.scss'] + styleUrls: ['./start-page.component.scss'], }) export class StartPageComponent implements OnInit, OnDestroy { public numberOfApplications = 0; public isAdmin: boolean; public name: string; - public imageUrl: SafeUrl; + public imageUrl: SafeUrl | undefined; private authSubscription: Subscription; @@ -58,11 +57,10 @@ export class StartPageComponent implements OnInit, OnDestroy { private readonly aobPermissionsService: AobPermissionsService, authentication: AuthenticationService, ) { - this.authSubscription = authentication.onSessionResponse?.subscribe(async sessionState => { - this.name = sessionState.Username; - this.imageUrl = await this.imageProvider.getPersonImageUrl(sessionState.UserUid); - } - ); + this.authSubscription = authentication.onSessionResponse?.subscribe(async (sessionState) => { + this.name = sessionState.Username || ''; + this.imageUrl = await this.imageProvider.getPersonImageUrl(this.name); + }); } public ngOnDestroy(): void { @@ -70,8 +68,9 @@ export class StartPageComponent implements OnInit, OnDestroy { } public async ngOnInit(): Promise { - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } try { this.logger.debug(this, 'get number of applications...'); const apps = await this.applicationsProvider.get(); @@ -82,9 +81,8 @@ export class StartPageComponent implements OnInit, OnDestroy { } this.isAdmin = await this.aobPermissionsService.isAobApplicationAdmin(); - } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } } } diff --git a/imxweb/projects/aob/src/lib/start-page/start-page.module.ts b/imxweb/projects/aob/src/lib/start-page/start-page.module.ts index b30574409..2e8973a03 100644 --- a/imxweb/projects/aob/src/lib/start-page/start-page.module.ts +++ b/imxweb/projects/aob/src/lib/start-page/start-page.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -37,7 +37,6 @@ import { StartPageComponent } from './start-page.component'; import { AobUserModule } from '../user/user.module'; import { GlobalKpiModule } from '../global-kpi/global-kpi.module'; - @NgModule({ declarations: [StartPageComponent], imports: [ @@ -51,10 +50,8 @@ import { GlobalKpiModule } from '../global-kpi/global-kpi.module'; AobUserModule, QbmModule, RouterModule, - GlobalKpiModule - ], - exports: [ - StartPageComponent + GlobalKpiModule, ], + exports: [StartPageComponent], }) -export class StartPageModule { } +export class StartPageModule {} diff --git a/imxweb/projects/aob/src/lib/user/user.component.html b/imxweb/projects/aob/src/lib/user/user.component.html index 36c691313..6864cff87 100644 --- a/imxweb/projects/aob/src/lib/user/user.component.html +++ b/imxweb/projects/aob/src/lib/user/user.component.html @@ -1,5 +1,5 @@ -
- person +
+ person
{{ primaryValue }} {{ secondaryValue }} diff --git a/imxweb/projects/aob/src/lib/user/user.component.scss b/imxweb/projects/aob/src/lib/user/user.component.scss index 62df0e484..c59521191 100644 --- a/imxweb/projects/aob/src/lib/user/user.component.scss +++ b/imxweb/projects/aob/src/lib/user/user.component.scss @@ -29,14 +29,14 @@ flex: 1 1 auto; overflow: hidden; - >span { + > span { font-weight: 600; font-size: 12px; overflow: hidden; text-overflow: ellipsis; } - >span:last-child { + > span:last-child { font-size: 10px; color: $black-b; } @@ -50,11 +50,11 @@ } .imx-user-properties { - >span { + > span { font-size: 16px; } - >span:last-child { + > span:last-child { font-size: 12px; } } @@ -66,14 +66,14 @@ background-color: $corbin-pastel; } - .imx-user-properties>span { + .imx-user-properties > span { font-size: 12px; } } .imx-user.large.noUser { .imx-user-properties { - >span { + > span { font-size: 14px; } } @@ -86,7 +86,7 @@ background-color: $color-gray-60; } .imx-user-properties { - >span:last-child { + > span:last-child { color: $color-gray-60; } } @@ -100,7 +100,7 @@ background-color: $color-gray-80; } .imx-user-properties { - >span:last-child { + > span:last-child { color: $color-gray-80; } } diff --git a/imxweb/projects/aob/src/lib/user/user.component.ts b/imxweb/projects/aob/src/lib/user/user.component.ts index 9d73ad046..42e1c0362 100644 --- a/imxweb/projects/aob/src/lib/user/user.component.ts +++ b/imxweb/projects/aob/src/lib/user/user.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,13 +28,13 @@ import { Component, Input, OnChanges } from '@angular/core'; import { SafeUrl } from '@angular/platform-browser'; import { TranslateService } from '@ngx-translate/core'; -import { IReadValue, IWriteValue } from 'imx-qbm-dbts'; +import { IReadValue, IWriteValue } from '@imx-modules/imx-qbm-dbts'; import { ClassloggerService } from 'qbm'; @Component({ selector: 'imx-user', templateUrl: './user.component.html', - styleUrls: ['./user.component.scss'] + styleUrls: ['./user.component.scss'], }) export class UserComponent implements OnChanges { @Input() public uid: IReadValue | IWriteValue; @@ -49,8 +49,8 @@ export class UserComponent implements OnChanges { constructor( private readonly translateService: TranslateService, - private readonly logger: ClassloggerService) { - } + private readonly logger: ClassloggerService, + ) {} public ngOnChanges(): void { if (!this.noUserText || this.noUserText.length < 1) { @@ -60,7 +60,7 @@ export class UserComponent implements OnChanges { if (!this.hasValidUser()) { this.logger.debug(this, 'Show noUserText because of an empty value.'); - this.translateService.get(this.noUserText).subscribe((trans: string) => this.secondaryValue = trans); + this.translateService.get(this.noUserText).subscribe((trans: string) => (this.secondaryValue = trans)); this.primaryValue = ''; } else { this.primaryValue = this.uid.Column.GetDisplayValue(); diff --git a/imxweb/projects/aob/src/lib/user/user.module.ts b/imxweb/projects/aob/src/lib/user/user.module.ts index ee2618721..34047927f 100644 --- a/imxweb/projects/aob/src/lib/user/user.module.ts +++ b/imxweb/projects/aob/src/lib/user/user.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -32,16 +32,8 @@ import { UserComponent } from './user.component'; import { UserModule } from 'qer'; @NgModule({ - declarations: [ - UserComponent - ], - imports: [ - CommonModule, - MatIconModule, - UserModule - ], - exports: [ - UserComponent - ], + declarations: [UserComponent], + imports: [CommonModule, MatIconModule, UserModule], + exports: [UserComponent], }) -export class AobUserModule { } +export class AobUserModule {} diff --git a/imxweb/projects/aob/src/public-api.ts b/imxweb/projects/aob/src/public-api.ts index aed73106c..522ce3139 100644 --- a/imxweb/projects/aob/src/public-api.ts +++ b/imxweb/projects/aob/src/public-api.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,7 +28,7 @@ * Hier kommen die zu exportierenden Komponenten hin */ -export { AobConfigModule } from './lib/aob-config.module' +export { AobConfigModule } from './lib/aob-config.module'; export { StartPageComponent } from './lib/start-page/start-page.component'; export { ApplicationsComponent } from './lib/applications/applications.component'; export { ApplicationsModule } from './lib/applications/applications.module'; diff --git a/imxweb/projects/aob/src/test.ts b/imxweb/projects/aob/src/test.ts index f53ca02e8..287a3f2d5 100644 --- a/imxweb/projects/aob/src/test.ts +++ b/imxweb/projects/aob/src/test.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,23 +26,13 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'core-js/es7/reflect'; -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; -import { getTestBed } from '@angular/core/testing'; -import { - BrowserDynamicTestingModule, - platformBrowserDynamicTesting -} from '@angular/platform-browser-dynamic/testing'; +// Zone must be imported first. Be careful with prettier import organizing +import 'zone.js'; +import 'zone.js/testing'; -declare const require: any; +import { getTestBed } from '@angular/core/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; +import 'core-js/es/reflect'; // First, initialize the Angular testing environment. -getTestBed().initTestEnvironment( - BrowserDynamicTestingModule, - platformBrowserDynamicTesting() -); -// Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); -// And load the modules. -context.keys().map(context); +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); diff --git a/imxweb/projects/aob/tsconfig.lib.json b/imxweb/projects/aob/tsconfig.lib.json index 6bc419f84..9786008a7 100644 --- a/imxweb/projects/aob/tsconfig.lib.json +++ b/imxweb/projects/aob/tsconfig.lib.json @@ -7,10 +7,11 @@ "declarationMap": true, "sourceMap": true, "inlineSources": true, - "types": [] + "types": [], + "strictNullChecks": true }, - "exclude": [ - "src/test.ts", - "**/*.spec.ts" - ] + "angularCompilerOptions": { + "strictTemplates": true + }, + "exclude": ["src/test.ts", "**/*.spec.ts"] } diff --git a/imxweb/projects/aob/tsconfig.lib.prod.json b/imxweb/projects/aob/tsconfig.lib.prod.json index 61c7592e7..fb40dbbb0 100644 --- a/imxweb/projects/aob/tsconfig.lib.prod.json +++ b/imxweb/projects/aob/tsconfig.lib.prod.json @@ -3,8 +3,5 @@ "extends": "./tsconfig.lib.json", "compilerOptions": { "declarationMap": false - }, - "angularCompilerOptions": { - "enableIvy": true } } diff --git a/imxweb/projects/aob/tsconfig.spec.json b/imxweb/projects/aob/tsconfig.spec.json index 16da33db0..6bd74e1f7 100644 --- a/imxweb/projects/aob/tsconfig.spec.json +++ b/imxweb/projects/aob/tsconfig.spec.json @@ -1,17 +1,10 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "strictNullChecks": false, "outDir": "../../out-tsc/spec", - "types": [ - "jasmine", - "node" - ] + "types": ["jasmine", "node"] }, - "files": [ - "src/test.ts" - ], - "include": [ - "**/*.spec.ts", - "**/*.d.ts" - ] + "files": ["src/test.ts"], + "include": ["**/*.spec.ts", "**/*.d.ts"] } diff --git a/imxweb/projects/aob/tslint.json b/imxweb/projects/aob/tslint.json deleted file mode 100644 index 8bab15f53..000000000 --- a/imxweb/projects/aob/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "imx", - "camelCase" - ], - "component-selector": [ - true, - "element", - "imx", - "kebab-case" - ] - } -} diff --git a/imxweb/projects/apc/.compodocrc.json b/imxweb/projects/apc/.compodocrc.json index 442e29f03..e4eccd87e 100644 --- a/imxweb/projects/apc/.compodocrc.json +++ b/imxweb/projects/apc/.compodocrc.json @@ -1,6 +1,6 @@ { "name": "IMX Web - APC Library", - "output": "../../documentation/v92/apc", + "output": "../../documentation/v93/apc", "theme": "material", "assetsFolder": "../../compodoc/assets", "customLogo": "../../compodoc/assets/images/oneidentity-logo.png", diff --git a/imxweb/projects/apc/.eslintrc.json b/imxweb/projects/apc/.eslintrc.json new file mode 100644 index 000000000..a6da13b8c --- /dev/null +++ b/imxweb/projects/apc/.eslintrc.json @@ -0,0 +1,35 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*", "**/*.spec.ts"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": ["imxweb/projects/apc/tsconfig.lib.json", "imxweb/projects/apc/tsconfig.spec.json"], + "createDefaultProgram": true + }, + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "imx", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "imx", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "rules": {} + } + ] +} diff --git a/imxweb/projects/apc/imx-plugin-config.json b/imxweb/projects/apc/imx-plugin-config.json index 0d6786142..c17d294bc 100644 --- a/imxweb/projects/apc/imx-plugin-config.json +++ b/imxweb/projects/apc/imx-plugin-config.json @@ -5,4 +5,4 @@ "Name": "ApcConfigModule" } ] -} \ No newline at end of file +} diff --git a/imxweb/projects/apc/karma.conf.js b/imxweb/projects/apc/karma.conf.js index 211193c9d..3e53a3ac8 100644 --- a/imxweb/projects/apc/karma.conf.js +++ b/imxweb/projects/apc/karma.conf.js @@ -15,7 +15,7 @@ module.exports = function (config) { require('karma-junit-reporter'), ], client: { - clearContext: false // leave Jasmine Spec Runner output visible in browser + clearContext: false, // leave Jasmine Spec Runner output visible in browser }, coverageIstanbulReporter: { dir: require('path').join(__dirname, 'results'), @@ -23,7 +23,7 @@ module.exports = function (config) { fixWebpackSourcePaths: true, 'report-config': { html: { - subdir: 'coverage-html' + subdir: 'coverage-html', }, }, thresholds: { @@ -40,13 +40,14 @@ module.exports = function (config) { port: 9876, captureTimeout: 210000, browserDisconnectTolerance: 3, - browserDisconnectTimeout : 210000, - browserNoActivityTimeout : 210000, + browserDisconnectTimeout: 210000, + browserNoActivityTimeout: 210000, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], singleRun: false, - restartOnFileChange: true + failOnEmptyTestSuite: false, + restartOnFileChange: true, }); }; diff --git a/imxweb/projects/apc/ng-package.dynamic.json b/imxweb/projects/apc/ng-package.dynamic.json index d03b39616..b039b9514 100644 --- a/imxweb/projects/apc/ng-package.dynamic.json +++ b/imxweb/projects/apc/ng-package.dynamic.json @@ -16,12 +16,11 @@ "@elemental-ui/core", "@ngx-translate/core", "@ngx-translate/http-loader", - "applicationinsights-js", "billboard.js" ], - "dest": "../../html/apc", + "dest": "../../html/qer-app-portal/apc", "lib": { "entryFile": "src/public_api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] } } diff --git a/imxweb/projects/apc/ng-package.json b/imxweb/projects/apc/ng-package.json index 18180a00a..c9cc87bfc 100644 --- a/imxweb/projects/apc/ng-package.json +++ b/imxweb/projects/apc/ng-package.json @@ -3,6 +3,6 @@ "dest": "../../dist/apc", "lib": { "entryFile": "src/public_api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] } } diff --git a/imxweb/projects/apc/package.json b/imxweb/projects/apc/package.json index 344a4c47d..640a40043 100644 --- a/imxweb/projects/apc/package.json +++ b/imxweb/projects/apc/package.json @@ -1,6 +1,6 @@ { "name": "apc", - "version": "9.2.1", + "version": "9.3.0", "private": true, "bundledDependencies": [ "imx-api-apc" diff --git a/imxweb/projects/apc/project.json b/imxweb/projects/apc/project.json new file mode 100644 index 000000000..c44bda316 --- /dev/null +++ b/imxweb/projects/apc/project.json @@ -0,0 +1,58 @@ +{ + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "name": "apc", + "sourceRoot": "projects/apc/src", + "implicitDependencies": ["qer"], + "projectType": "library", + "prefix": "imx", + "generators": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:ng-packagr", + "options": { + "tsConfig": "projects/apc/tsconfig.lib.json", + "project": "projects/apc/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "projects/apc/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "projects/apc/tsconfig.lib.json" + }, + "dynamic": { + "project": "projects/apc/ng-package.dynamic.json" + } + }, + "outputs": ["{workspaceRoot}/dist/apc"] + }, + "test": { + "executor": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/apc/src/test.ts", + "tsConfig": "projects/apc/tsconfig.spec.json", + "karmaConfig": "projects/apc/karma.conf.js", + "stylePreprocessorOptions": { + "includePaths": [ + "./shared/assets", + "./shared/scss", + "./node_modules", + "./node_modules/@elemental-ui/cadence-icon", + "./node_modules/@elemental-ui/core" + ] + } + } + }, + "lint": { + "executor": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": ["projects/apc/tsconfig.lib.json", "projects/apc/tsconfig.spec.json"], + "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + } + } + } +} diff --git a/imxweb/projects/apc/src/lib/apc-api-client.service.ts b/imxweb/projects/apc/src/lib/apc-api-client.service.ts index 89e8bd1b5..5bef90164 100644 --- a/imxweb/projects/apc/src/lib/apc-api-client.service.ts +++ b/imxweb/projects/apc/src/lib/apc-api-client.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,11 +25,11 @@ */ import { Injectable } from '@angular/core'; -import { V2Client, TypedClient } from 'imx-api-apc'; +import { V2Client, TypedClient } from '@imx-modules/imx-api-apc'; import { AppConfigService, ClassloggerService, ImxTranslationProviderService } from 'qbm'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ApcApiService { private tc: TypedClient; @@ -45,7 +45,8 @@ export class ApcApiService { constructor( config: AppConfigService, private readonly logger: ClassloggerService, - private readonly translationProvider: ImxTranslationProviderService) { + private readonly translationProvider: ImxTranslationProviderService, + ) { try { // Use schema loaded by QBM client const schemaProvider = config.client; diff --git a/imxweb/projects/apc/src/lib/apc-config.module.ts b/imxweb/projects/apc/src/lib/apc-config.module.ts index 9e858f4d9..778710f1b 100644 --- a/imxweb/projects/apc/src/lib/apc-config.module.ts +++ b/imxweb/projects/apc/src/lib/apc-config.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -32,19 +32,17 @@ import { SoftwareComponent } from './software/software.component'; import { InitService } from './init.service'; import { SoftwareModule } from './software/software.module'; -const routes: Routes = [{ - path: 'resp/Application', - component: SoftwareComponent, - canActivate: [RouteGuardService], - resolve: [RouteGuardService], -}, +const routes: Routes = [ + { + path: 'resp/Application', + component: SoftwareComponent, + canActivate: [RouteGuardService], + resolve: [RouteGuardService], + }, ]; @NgModule({ - imports: [ - RouterModule.forChild(routes), - SoftwareModule - ] + imports: [RouterModule.forChild(routes), SoftwareModule], }) export class ApcConfigModule { constructor(private readonly initializer: InitService) { diff --git a/imxweb/projects/apc/src/lib/app-info.model.ts b/imxweb/projects/apc/src/lib/app-info.model.ts index 0c8ec00b1..e93c577fa 100644 --- a/imxweb/projects/apc/src/lib/app-info.model.ts +++ b/imxweb/projects/apc/src/lib/app-info.model.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { EntitySchema, DataModel, FilterData } from 'imx-qbm-dbts'; +import { EntitySchema, DataModel, FilterData } from '@imx-modules/imx-qbm-dbts'; export interface ResourceDataModel { getModel(filter: FilterData[]): Promise; @@ -32,7 +32,7 @@ export interface ResourceDataModel { export interface ResourceInfo { table: string; - caption?:string; + caption?: string; admin?: ResourceInfoApiWrapper; resp?: ResourceInfoApiWrapper; accProduct?: any; diff --git a/imxweb/projects/apc/src/lib/init.service.ts b/imxweb/projects/apc/src/lib/init.service.ts index 3617591ee..45769bd4e 100644 --- a/imxweb/projects/apc/src/lib/init.service.ts +++ b/imxweb/projects/apc/src/lib/init.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,11 +30,12 @@ import { MyResponsibilitiesRegistryService } from 'qer'; import { SoftwareComponent } from './software/software.component'; import { HELP_CONTEXTUAL } from 'qbm'; - - @Injectable({ providedIn: 'root' }) export class InitService { - constructor(private readonly router: Router, private readonly myResponsibilitiesRegistryService: MyResponsibilitiesRegistryService) {} + constructor( + private readonly router: Router, + private readonly myResponsibilitiesRegistryService: MyResponsibilitiesRegistryService, + ) {} public onInit(routes: Route[]): void { this.addRoutes(routes); @@ -59,7 +60,7 @@ export class InitService { TableName: 'Application', Count: 0, }, - contextId: HELP_CONTEXTUAL.MyResponsibilitiesApplication + contextId: HELP_CONTEXTUAL.MyResponsibilitiesApplication, })); } } diff --git a/imxweb/projects/apc/src/lib/software/software-sidesheet/software-memberships/software-memberships.component.html b/imxweb/projects/apc/src/lib/software/software-sidesheet/software-memberships/software-memberships.component.html index 2dd1dd15a..6e40d22a2 100644 --- a/imxweb/projects/apc/src/lib/software/software-sidesheet/software-memberships/software-memberships.component.html +++ b/imxweb/projects/apc/src/lib/software/software-sidesheet/software-memberships/software-memberships.component.html @@ -1,77 +1,99 @@ -
- +
+ {{ membershipHint | translate }} - {{ - LdsNotUnsubscribableHint | translate - }} + {{ LdsNotUnsubscribableHint | translate }}
- -
- - - - - - - +
+ + + + + {{ entitySchema?.Columns?.[DisplayColumns.DISPLAY_PROPERTYNAME]?.Display }} + + +
{{ item.GetEntity().GetDisplay() }}
+ +
+ + + +
- {{ item?.GetEntity()?.GetDisplay() }} + + {{ entitySchema.Columns.XMarkedForDeletion.Display }} +
- - - - -
-
- - {{ entitySchema.Columns.XMarkedForDeletion.Display }} - -
-
- - {{ '#LDS#Membership is ineffective' | translate }} - -
+
+ + {{ '#LDS#Membership is ineffective' | translate }} +
- - - - - - - - -
- +
+ +
+ + + {{ entitySchema?.Columns?.UID_Person?.Display }} + + +
{{ item.entity.columns.UID_Person.GetDisplayValue() }}
+ +
+ + + {{ entitySchema?.Columns?.XOrigin?.Display }} + + +
{{ item.entity.columns.XOrigin.GetDisplayValue() }}
+ +
+ + + {{ entitySchema?.Columns?.XDateInserted?.Display }} + + +
{{ item.entity.columns.XDateInserted.GetDisplayValue() }}
+ +
+ + + {{ entitySchema?.Columns?.ValidUntil?.Display }} + + +
{{ item.entity.columns.ValidUntil.GetDisplayValue() }}
+ +
+
+ +
+
- -
- - diff --git a/imxweb/projects/apc/src/lib/software/software-sidesheet/software-memberships/software-memberships.component.scss b/imxweb/projects/apc/src/lib/software/software-sidesheet/software-memberships/software-memberships.component.scss index 99132f227..1c333575c 100644 --- a/imxweb/projects/apc/src/lib/software/software-sidesheet/software-memberships/software-memberships.component.scss +++ b/imxweb/projects/apc/src/lib/software/software-sidesheet/software-memberships/software-memberships.component.scss @@ -1,32 +1,15 @@ +@import 'base/mixins'; + :host { - display: flex; - flex-direction: column; - flex: 1 1 auto; - overflow: hidden; - - eui-icon { - font-size: 14px; - margin-right: 4px; - } + @include flex-column-container-fill(); } .hidden { display: none; } -.mat-card { - flex: 1 1 auto; - display: flex; - flex-direction: column; - overflow: hidden; - margin: 20px; -} - .imx-table-content { - flex: 1 1 auto; - display: flex; - flex-direction: column; - overflow: hidden; + @include flex-column-container-fill(); } imx-busy-indicator { @@ -35,38 +18,6 @@ imx-busy-indicator { align-items: center; } -.helper-alert { - margin: 12px; -} - -.helper-alert-container { - margin: 0px 20px; -} - imx-selected-elements { margin-left: 10px; } - -.imx-menu-spacer{ - flex: 1 1 auto; -} - -.eui-sidesheet-actions { - button:not(:last-of-type) { - margin-right: 12px; - } -} - -.imx-badge-container { - > div { - display: table-cell; - - > .eui-badge { - margin: 0 5px; - - ::ng-deep .eui-badge-content { - white-space: nowrap; - } - } - } -} diff --git a/imxweb/projects/apc/src/lib/software/software-sidesheet/software-memberships/software-memberships.component.ts b/imxweb/projects/apc/src/lib/software/software-sidesheet/software-memberships/software-memberships.component.ts index 33550b97a..b6a655ade 100644 --- a/imxweb/projects/apc/src/lib/software/software-sidesheet/software-memberships/software-memberships.component.ts +++ b/imxweb/projects/apc/src/lib/software/software-sidesheet/software-memberships/software-memberships.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,9 +28,27 @@ import { Component, Input, OnChanges, SimpleChanges, ViewChild } from '@angular/ import { EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { PortalResourcesApplicationsMembership } from 'imx-api-apc'; -import { CollectionLoadParameters, DbObjectKey, DisplayColumns, IClientProperty, ValType, XOrigin } from 'imx-qbm-dbts'; -import { BusyService, ConfirmationService, DataSourceToolbarSettings, DataTableComponent, SnackBarService } from 'qbm'; +import { PortalResourcesApplicationsMembership } from '@imx-modules/imx-api-apc'; +import { + CollectionLoadParameters, + DbObjectKey, + DisplayColumns, + EntitySchema, + IClientProperty, + TypedEntity, + TypedEntityCollectionData, + ValType, + XOrigin, +} from '@imx-modules/imx-qbm-dbts'; +import { + BusyService, + calculateSidesheetWidth, + ConfirmationService, + DataTableComponent, + DataViewInitParameters, + DataViewSource, + SnackBarService, +} from 'qbm'; import { SourceDetectiveSidesheetComponent, SourceDetectiveSidesheetData, SourceDetectiveType } from 'qer'; import { SoftwareService } from '../../software.service'; @@ -38,11 +56,11 @@ import { SoftwareService } from '../../software.service'; selector: 'imx-software-memberships', templateUrl: './software-memberships.component.html', styleUrls: ['./software-memberships.component.scss'], + providers: [DataViewSource], }) export class SoftwareMembershipsComponent implements OnChanges { - public dstSettings: DataSourceToolbarSettings; public DisplayColumns = DisplayColumns; - public entitySchema = this.softwareService.membershipSchema; + public entitySchema: EntitySchema; public selections: PortalResourcesApplicationsMembership[] = []; public showUnsubscribeWarning = false; @@ -62,8 +80,8 @@ export class SoftwareMembershipsComponent implements OnChanges { @ViewChild('membersTable') public membersTable: DataTableComponent; public membershipHint = - '#LDS#Here you can manage the memberships of the software application. You can remove memberships and view the assignment analysis for each membership.'; - public LdsNotUnsubscribableHint = + '#LDS#Here you can specify which identities have access to the software application. You can remove memberships and view the assignment analysis for each membership.'; + public LdsNotUnsubscribableHint = '#LDS#There is at least one membership you cannot unsubscribe. You can only unsubscribe memberships you have requested.'; public get canDeleteSelected(): boolean { @@ -71,7 +89,7 @@ export class SoftwareMembershipsComponent implements OnChanges { } public get canUnsubscribeSelected(): boolean { - return this.selections.length > 0 && this.selections.every((item) => (item.UID_PersonWantsOrg?.value ?? '') !== ''); + return this.selections.length > 0 && this.selections.every((item) => (item.UID_PersonWantsOrg?.value ?? '') !== ''); } constructor( @@ -79,11 +97,13 @@ export class SoftwareMembershipsComponent implements OnChanges { private readonly sideSheet: EuiSidesheetService, private readonly translate: TranslateService, private readonly confirmationService: ConfirmationService, - private readonly snackBarService: SnackBarService + private readonly snackBarService: SnackBarService, + public dataSource: DataViewSource, ) { + this.entitySchema = this.softwareService.membershipSchema; this.displayColumns = [ this.entitySchema.Columns.UID_Person, - this.entitySchema.Columns.XOrigin, + this.entitySchema.Columns?.XOrigin, this.entitySchema.Columns.XDateInserted, this.entitySchema.Columns.ValidUntil, { ColumnName: 'badges', Type: ValType.String }, @@ -92,22 +112,18 @@ export class SoftwareMembershipsComponent implements OnChanges { public async ngOnChanges(changes: SimpleChanges): Promise { if (changes.uidSoftware && changes.uidSoftware.currentValue) { - return this.getData({}); + return this.getData(); } } - public async onSearch(keyword: string): Promise { - this.getData({ search: keyword, StartIndex: 0 }); + public onSelectionChanged(selection: readonly TypedEntity[]): void { + this.selections = selection as PortalResourcesApplicationsMembership[]; } - public onSelectionChanged(selection: PortalResourcesApplicationsMembership[]): void { - this.selections = selection; - } - - public async showDetails(item: PortalResourcesApplicationsMembership): Promise { - const uidPerson = item.UID_Person.value; + public async showDetails(item: TypedEntity): Promise { + const uidPerson = item.GetEntity().GetColumn('UID_Person').GetValue(); - const objectKey = DbObjectKey.FromXml(item.XObjectKey.value); + const objectKey = DbObjectKey.FromXml(item.GetEntity().GetColumn('XObjectKey').GetValue()); const data: SourceDetectiveSidesheetData = { UID_Person: uidPerson, @@ -119,27 +135,26 @@ export class SoftwareMembershipsComponent implements OnChanges { title: await this.translate.get('#LDS#Heading View Assignment Analysis').toPromise(), subTitle: item.GetEntity().GetDisplay(), padding: '0px', - width: 'max(60%,600px)', + width: calculateSidesheetWidth(), disableClose: false, testId: 'software-memberships-assingment-analysis', data, }); } - public async getData(navigationState: CollectionLoadParameters): Promise { - this.navigationState = navigationState; - - const isBusy = this.busyService.beginBusy(); - try { - this.dstSettings = { - dataSource: await this.softwareService.getMemberShips(this.uidSoftware, this.navigationState), - entitySchema: this.entitySchema, - navigationState: this.navigationState, - displayedColumns: this.displayColumns, - }; - } finally { - isBusy.endBusy(); - } + public async getData(): Promise { + this.dataSource.itemStatus = this.status; + const dataViewInitParameters: DataViewInitParameters = { + execute: (params: CollectionLoadParameters): Promise> => + this.softwareService.getMemberShips(this.uidSoftware, params), + schema: this.entitySchema, + columnsToDisplay: this.displayColumns, + highlightEntity: (entity: TypedEntity) => { + this.showDetails(entity); + }, + selectionChange: (selection) => this.onSelectionChanged(selection), + }; + this.dataSource.init(dataViewInitParameters); } public async deleteSelected(): Promise { @@ -155,11 +170,11 @@ export class SoftwareMembershipsComponent implements OnChanges { try { await this.softwareService.deleteGroupMembers( this.uidSoftware, - this.selections.map((i) => i.GetEntity().GetColumn('UID_Person').GetValue()) + this.selections.map((i) => i.GetEntity().GetColumn('UID_Person').GetValue()), ); this.membersTable.clearSelection(); this.snackBarService.open({ key: '#LDS#The memberships have been successfully removed.' }, '#LDS#Close'); - await this.getData({ search: this.navigationState.search }); + await this.dataSource.updateState(); } finally { isBusy.endBusy(); } @@ -194,7 +209,7 @@ export class SoftwareMembershipsComponent implements OnChanges { } finally { isBusy.endBusy(); this.membersTable.clearSelection(); - await this.getData({ search: this.navigationState.search }); + await this.dataSource.updateState(); } } } diff --git a/imxweb/projects/apc/src/lib/software/software-sidesheet/software-sidesheet.component.html b/imxweb/projects/apc/src/lib/software/software-sidesheet/software-sidesheet.component.html index bb0ff2074..09183770f 100644 --- a/imxweb/projects/apc/src/lib/software/software-sidesheet/software-sidesheet.component.html +++ b/imxweb/projects/apc/src/lib/software/software-sidesheet/software-sidesheet.component.html @@ -1,23 +1,39 @@ - +
#LDS#Heading Main Data  - +
- +
- +
-
@@ -37,16 +53,27 @@
#LDS#Heading Service Item  - +
- +
- +
diff --git a/imxweb/projects/apc/src/lib/software/software-sidesheet/software-sidesheet.component.scss b/imxweb/projects/apc/src/lib/software/software-sidesheet/software-sidesheet.component.scss index fbaf7c85f..5c2d1fc5a 100644 --- a/imxweb/projects/apc/src/lib/software/software-sidesheet/software-sidesheet.component.scss +++ b/imxweb/projects/apc/src/lib/software/software-sidesheet/software-sidesheet.component.scss @@ -1,117 +1,14 @@ -@import '@elemental-ui/core/src/styles/_eui_palette.scss'; - +@import 'base/mixins'; :host { - display: flex; - flex-direction: column; - overflow: hidden; - height: 100%; + @include flex-column-container($height: 100%, $overflow: hidden); - .mat-tab-group { - flex-grow: 1; + .imx-cdr-group { + flex: 1 1 auto; overflow: auto; - } - - ::ng-deep .mat-tab-body-wrapper { - height: 100%; - } - - ::ng-deep .mat-tab-body-content { - display: flex; - flex-direction: column; - } - - ::ng-deep .mat-tab-group { - height: 100%; - - .mat-tab-header { - padding: 0 32px; - } - } - - .imx-tab-content { - display: flex; - flex-direction: column; - height: 100%; - - .imx-tab-content-body { - flex: 1 1 auto; - overflow: hidden; - display: flex; - flex-direction: column; - - .mat-card { - height: 100%; - display: flex; - flex-direction: column; - overflow: hidden; - padding: 16px; - - > form { - padding-right: 10px; - overflow: auto; - } - } - } - - .imx-cdr-group { - flex: 1 1 auto; - overflow: auto; - padding-right: 10px; - } + padding-right: 10px; } .hidden { display: none; } - - .imx-content-card { - margin: 20px; - display: flex; - flex-direction: column; - overflow: hidden; - - .imx-service-item-form { - display: flex; - flex-direction: column; - flex: 1 1 auto; - overflow: auto; - } - } -} - -:host { - .imx-tab-content { - .imx-action-buttons { - background-color: $color-gray-0; - } - } - - .eui-sidesheet-content { - ::ng-deep .mat-tab-group { - .mat-tab-header { - background-color: $color-gray-0; - } - } - } -} - -.eui-dark-theme { - :host { - .imx-tab-content { - .imx-action-buttons { - background-color: $color-gray-70; - } - } - - .eui-sidesheet-content { - ::ng-deep .mat-tab-group { - .mat-tab-header { - background-color: $color-gray-80; - } - } - } - .imx-tab-content .imx-action-buttons { - background-color: $color-gray-70; - } - } } diff --git a/imxweb/projects/apc/src/lib/software/software-sidesheet/software-sidesheet.component.ts b/imxweb/projects/apc/src/lib/software/software-sidesheet/software-sidesheet.component.ts index 16f33c1a4..c9a78f6c5 100644 --- a/imxweb/projects/apc/src/lib/software/software-sidesheet/software-sidesheet.component.ts +++ b/imxweb/projects/apc/src/lib/software/software-sidesheet/software-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,12 +25,12 @@ */ import { ChangeDetectorRef, Component, Inject, OnDestroy, OnInit } from '@angular/core'; -import { UntypedFormArray, FormGroup, FormArray } from '@angular/forms'; +import { FormGroup, UntypedFormArray } from '@angular/forms'; +import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; import { Subscription } from 'rxjs'; -import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; -import { IEntityColumn, TypedEntity } from 'imx-qbm-dbts'; -import { PortalRespApplication } from 'imx-api-apc'; +import { PortalRespApplication } from '@imx-modules/imx-api-apc'; +import { IEntityColumn, TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { BusyService, CdrFactoryService, ColumnDependentReference, ConfirmationService } from 'qbm'; import { SoftwareService } from '../software.service'; @@ -44,8 +44,8 @@ interface ServiceItemGroup { /** * internal interface for the service item form */ -interface SoftwareFormGroup{ - array: UntypedFormArray +interface SoftwareFormGroup { + array: UntypedFormArray; } @Component({ @@ -53,14 +53,13 @@ interface SoftwareFormGroup{ styleUrls: ['./software-sidesheet.component.scss'], }) export class SoftwareSidesheetComponent implements OnInit, OnDestroy { - public cdrList: ColumnDependentReference[]; public softwareFormGroup = new FormGroup({ array: new UntypedFormArray([]) }); - public serviceItemGroup = new FormGroup({ serviceItemParameter: new FormArray([]) }); + public serviceItemGroup = new FormGroup({ serviceItemParameter: new UntypedFormArray([]) }); public isLoading = false; - public serviceItem: TypedEntity; + public serviceItem: TypedEntity | null; public get withProduct(): boolean { const value = this.productColumn?.GetValue(); @@ -74,7 +73,7 @@ export class SoftwareSidesheetComponent implements OnInit, OnDestroy { private subscriptions: Subscription[] = []; private entity: PortalRespApplication; private editableFields: string[]; - private busyService = new BusyService(); + public busyService = new BusyService(); constructor( @Inject(EUI_SIDESHEET_DATA) @@ -85,7 +84,7 @@ export class SoftwareSidesheetComponent implements OnInit, OnDestroy { private readonly changeDetector: ChangeDetectorRef, private readonly softwareService: SoftwareService, sidesheetRef: EuiSidesheetRef, - confirmation: ConfirmationService + confirmation: ConfirmationService, ) { this.subscriptions.push( sidesheetRef.closeClicked().subscribe(async () => { @@ -93,14 +92,14 @@ export class SoftwareSidesheetComponent implements OnInit, OnDestroy { return; } sidesheetRef.close(false); - }) + }), ); this.subscriptions.push( this.busyService.busyStateChanged.subscribe((state: boolean) => { this.isLoading = state; this.changeDetector.detectChanges(); - }) + }), ); } @@ -146,6 +145,9 @@ export class SoftwareSidesheetComponent implements OnInit, OnDestroy { * Saves the service item and reloads the form for further editing */ public async saveServiceItem(): Promise { + if (!this.serviceItem) { + return; + } const isBusy = this.busyService.beginBusy(); try { await this.serviceItem.GetEntity().Commit(); diff --git a/imxweb/projects/apc/src/lib/software/software.component.html b/imxweb/projects/apc/src/lib/software/software.component.html index 271eaba84..5282e0f0e 100644 --- a/imxweb/projects/apc/src/lib/software/software.component.html +++ b/imxweb/projects/apc/src/lib/software/software.component.html @@ -1,33 +1,30 @@ - - -
-
- {{ tablenameDisplay }} + +
+
+

{{ tablenameDisplay }}

-
- - - - - -
- {{ item?.GetEntity()?.GetDisplay() }} -
-
-
- -
- +
+ + + + + {{ entitySchema?.Columns?.[DisplayColumns.DISPLAY_PROPERTYNAME]?.Display }} + + +
{{ item.GetEntity().GetDisplay() }}
+ +
+ + + {{ entitySchema?.Columns?.Requestable?.Display }} + + +
{{ item.entity.columns.Requestable.GetDisplayValue() }}
+ +
+
+
diff --git a/imxweb/projects/apc/src/lib/software/software.component.scss b/imxweb/projects/apc/src/lib/software/software.component.scss index 55e1a13ed..03e367108 100644 --- a/imxweb/projects/apc/src/lib/software/software.component.scss +++ b/imxweb/projects/apc/src/lib/software/software.component.scss @@ -16,37 +16,6 @@ imx-data-table { color: $color-gray-30; } -.data-explorer-card-header { - background-color: $color-gray-0; - border-top-left-radius: 4px; - border-top-right-radius: 4px; - z-index: 100; - border: 1px solid rgba( $color-blue-60, 0.6); - margin-bottom: 20px; - - .data-explorer-card-header-bg { - border-top-left-radius: 4px; - border-top-right-radius: 4px; - background-color: $color-blue-20; - padding: 10px 24px; - display: flex; - align-items: center; - justify-content: flex-start; - height:45px; - - & > span { - font-size: 20px; - } - } -} - -.imx-page-card { - display: flex; - flex-direction: column; - flex: 1 1 auto; - overflow: hidden; -} - .imx-data-tree-container { overflow: hidden; background-color: $color-gray-0; @@ -65,13 +34,6 @@ imx-data-table { margin-top: 16px; } -.imx-card-content { - display: flex; - flex-direction: column; - flex: 1 1 auto; - overflow: hidden; -} - .data-explorer-bottom-button-row { display: flex; flex-direction: row; @@ -84,22 +46,11 @@ imx-data-table { } } -.imx-row-icon { - align-self: center; - margin-right: 15px; - text-align: right; - display: block; -} - .eui-dark-theme { :host { - .imx-data-tree-container, - .data-explorer-card-header-bg { + .imx-data-tree-container { background: $color-gray-70; } - .data-explorer-card-header .data-explorer-card-header-bg { - background: $color-blue-80; - } } } diff --git a/imxweb/projects/apc/src/lib/software/software.component.ts b/imxweb/projects/apc/src/lib/software/software.component.ts index cb659972d..bcd68499b 100644 --- a/imxweb/projects/apc/src/lib/software/software.component.ts +++ b/imxweb/projects/apc/src/lib/software/software.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,14 +28,25 @@ import { Component, Input, OnInit } from '@angular/core'; import { EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { CollectionLoadParameters, DataModel, DisplayColumns, EntitySchema, IClientProperty, TypedEntity } from 'imx-qbm-dbts'; +import { + CollectionLoadParameters, + DataModel, + DisplayColumns, + EntitySchema, + IClientProperty, + TypedEntity, + TypedEntityCollectionData, +} from '@imx-modules/imx-qbm-dbts'; import { BusyService, DataSourceToolbarSettings, + DataViewInitParameters, + DataViewSource, HelpContextualValues, LdsReplacePipe, MetadataService, SideNavigationComponent, + calculateSidesheetWidth, } from 'qbm'; import { SoftwareSidesheetComponent } from './software-sidesheet/software-sidesheet.component'; import { SoftwareService } from './software.service'; @@ -43,6 +54,7 @@ import { SoftwareService } from './software.service'; @Component({ templateUrl: './software.component.html', styleUrls: ['./software.component.scss'], + providers: [DataViewSource], }) export class SoftwareComponent implements OnInit, SideNavigationComponent { @Input() public contextId: HelpContextualValues; @@ -64,6 +76,7 @@ export class SoftwareComponent implements OnInit, SideNavigationComponent { private readonly sidesheet: EuiSidesheetService, private readonly ldsReplace: LdsReplacePipe, private readonly translate: TranslateService, + public dataSource: DataViewSource, ) {} public async ngOnInit(): Promise { @@ -76,35 +89,31 @@ export class SoftwareComponent implements OnInit, SideNavigationComponent { isBusy.endBusy(); } - this.tablenameDisplay = this.metadata.tables['Application'].Display; + this.tablenameDisplay = this.metadata.tables?.['Application']?.Display ?? ''; this.displayColumns = [this.entitySchema.Columns[DisplayColumns.DISPLAY_PROPERTYNAME]]; if (this.entitySchema.Columns.Requestable) { this.displayColumns.splice(1, 0, this.entitySchema.Columns.Requestable); } - this.navigate(); - } - - public async onNavigationStateChanged(newState?: CollectionLoadParameters): Promise { - if (newState) { - this.navigationState = newState; - } - await this.navigate(); - } - - public async onSearch(keywords: string): Promise { - this.navigationState.StartIndex = 0; - this.navigationState.search = keywords; - await this.navigate(); + const dataViewInitParameters: DataViewInitParameters = { + execute: (params: CollectionLoadParameters): Promise> => this.resourceProvider.get(params), + schema: this.entitySchema, + columnsToDisplay: this.displayColumns, + dataModel: this.dataModel, + highlightEntity: (entity: TypedEntity) => { + this.showDetails(entity); + }, + }; + this.dataSource.init(dataViewInitParameters); } public async showDetails(item: TypedEntity): Promise { const sidesheetRef = this.sidesheet.open(SoftwareSidesheetComponent, { title: this.ldsReplace.transform(await this.translate.get('#LDS#Heading Edit {0}').toPromise(), this.tablenameDisplay), - headerColour: 'blue', + subTitle: item.GetEntity().GetDisplay(), padding: '0px', - width: 'max(600px, 60%)', + width: calculateSidesheetWidth(), disableClose: true, testId: 'software-sidesheet', data: { @@ -114,24 +123,8 @@ export class SoftwareComponent implements OnInit, SideNavigationComponent { sidesheetRef.afterClosed().subscribe(async (result) => { if (result) { - await this.navigate(); + await this.dataSource.updateState(); } }); } - - private async navigate(): Promise { - const isBusy = this.busyService.beginBusy(); - try { - this.dstSettings = { - dataSource: await this.resourceProvider.get(this.navigationState), - entitySchema: this.entitySchema, - navigationState: this.navigationState, - displayedColumns: this.displayColumns, - filters: this.dataModel.Filters, - dataModel: this.dataModel, - }; - } finally { - isBusy.endBusy(); - } - } } diff --git a/imxweb/projects/apc/src/lib/software/software.module.ts b/imxweb/projects/apc/src/lib/software/software.module.ts index 081d210e5..08435a657 100644 --- a/imxweb/projects/apc/src/lib/software/software.module.ts +++ b/imxweb/projects/apc/src/lib/software/software.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,21 +24,27 @@ * */ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { SoftwareComponent } from './software.component'; -import { SoftwareSidesheetComponent } from './software-sidesheet/software-sidesheet.component'; -import { BusyIndicatorModule, CdrModule, DataSourceToolbarModule, DataTableModule, HelpContextualModule, SelectedElementsModule } from 'qbm'; -import { TranslateModule } from '@ngx-translate/core'; +import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MatCardModule } from '@angular/material/card'; import { MatButtonModule } from '@angular/material/button'; -import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; +import { MatCardModule } from '@angular/material/card'; import { MatTabsModule } from '@angular/material/tabs'; -import { SoftwareMembershipsComponent } from './software-sidesheet/software-memberships/software-memberships.component'; +import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; +import { TranslateModule } from '@ngx-translate/core'; +import { + BusyIndicatorModule, + CdrModule, + DataSourceToolbarModule, + DataTableModule, + DataViewModule, + HelpContextualModule, + SelectedElementsModule, +} from 'qbm'; import { ServiceItemsEditFormModule } from 'qer'; - - +import { SoftwareMembershipsComponent } from './software-sidesheet/software-memberships/software-memberships.component'; +import { SoftwareSidesheetComponent } from './software-sidesheet/software-sidesheet.component'; +import { SoftwareComponent } from './software.component'; @NgModule({ declarations: [SoftwareComponent, SoftwareSidesheetComponent, SoftwareMembershipsComponent], @@ -57,7 +63,8 @@ import { ServiceItemsEditFormModule } from 'qer'; BusyIndicatorModule, SelectedElementsModule, ServiceItemsEditFormModule, - HelpContextualModule + HelpContextualModule, + DataViewModule, ], }) export class SoftwareModule {} diff --git a/imxweb/projects/apc/src/lib/software/software.service.ts b/imxweb/projects/apc/src/lib/software/software.service.ts index 2c187fec1..8bba419d6 100644 --- a/imxweb/projects/apc/src/lib/software/software.service.ts +++ b/imxweb/projects/apc/src/lib/software/software.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,8 +26,12 @@ import { Injectable } from '@angular/core'; -import { PortalResourcesApplicationsMembership, PortalRespApplication } from 'imx-api-apc'; -import { ProjectConfig, QerProjectConfig } from 'imx-api-qer'; +import { + PortalResourcesApplicationServiceitemWrapper, + PortalResourcesApplicationsMembership, + PortalRespApplication, +} from '@imx-modules/imx-api-apc'; +import { ProjectConfig, QerProjectConfig } from '@imx-modules/imx-api-qer'; import { CollectionLoadParameters, CompareOperator, @@ -40,7 +44,7 @@ import { TypedEntity, TypedEntityBuilder, TypedEntityCollectionData, -} from 'imx-qbm-dbts'; +} from '@imx-modules/imx-qbm-dbts'; import { ProjectConfigurationService, QerApiService } from 'qer'; import { ApcApiService } from '../apc-api-client.service'; import { ResourceInfo } from '../app-info.model'; @@ -56,26 +60,31 @@ export class SoftwareService { constructor( protected readonly project: ProjectConfigurationService, private readonly api: ApcApiService, - private readonly qerClient: QerApiService - ) {} - - private appInfo = { - table: 'Application', - caption: '#LDS#Software', - accProduct: this.api.typedClient.PortalResourcesApplicationServiceitem, - resp: { - type: PortalRespApplication, - get: async (parameter: any) => this.api.client.portal_resp_application_get(parameter), - schema: this.api.typedClient.PortalRespApplication.GetSchema(), - dataModel: async (filter?: FilterData[]) => this.api.client.portal_resp_application_datamodel_get({ filter }), - interactive: this.api.typedClient.PortalRespApplicationInteractive, - }, + private readonly qerClient: QerApiService, + ) { + this.appInfo = { + table: 'Application', + caption: '#LDS#Software', + accProduct: this.api.typedClient.PortalResourcesApplicationServiceitem, + resp: { + type: PortalRespApplication, + get: async (parameter: any) => this.api.client.portal_resp_application_get(parameter), + schema: this.api.typedClient.PortalRespApplication.GetSchema(), + dataModel: async (filter?: FilterData[]) => this.api.client.portal_resp_application_datamodel_get({ filter }), + interactive: this.api.typedClient.PortalRespApplicationInteractive, + }, + }; + } + + private appInfo: { + table: string; + caption: string; + accProduct: PortalResourcesApplicationServiceitemWrapper; + resp: any; }; public async get(parameter: CollectionLoadParameters): Promise> { const conf = this.appInfo.resp; - - if (!conf) return null; const builder = new TypedEntityBuilder(conf.type); const data = await conf.get(parameter); @@ -102,11 +111,11 @@ export class SoftwareService { this.config = await this.project.getConfig(); } - const list = primary ? this.config.OwnershipConfig.PrimaryFields : this.config.OwnershipConfig.EditableFields; - return list[objectType].filter((name) => entity.GetSchema().Columns[name]); + const list = primary ? this.config?.OwnershipConfig?.PrimaryFields : this.config?.OwnershipConfig?.EditableFields; + return list && list[objectType] ? list[objectType].filter((name) => entity.GetSchema().Columns[name]) : []; } - public async getServiceItem(uidAccProduct: string): Promise { + public async getServiceItem(uidAccProduct: string): Promise { const filter: FilterData[] = [ { Type: FilterType.Compare, @@ -118,12 +127,12 @@ export class SoftwareService { const item = await this.appInfo.accProduct.Get({ filter }); - return item.Data.length > 0 ? item.Data[0] : undefined; + return item.Data.length > 0 ? item.Data[0] : null; } public async getMemberShips( uidApplication: string, - parameter: CollectionLoadParameters + parameter: CollectionLoadParameters, ): Promise> { return this.api.typedClient.PortalResourcesApplicationsMembership.Get(uidApplication, parameter); } @@ -131,8 +140,9 @@ export class SoftwareService { public async deleteGroupMembers(uidSoftware: string, uidPersonList: string[]): Promise { return await Promise.all( uidPersonList.map(async (item) => { - (await this.api.typedClient.PortalResourcesApplicationsMembership.Delete(uidSoftware,item)).Data[0] - })); + (await this.api.typedClient.PortalResourcesApplicationsMembership.Delete(uidSoftware, item)).Data[0]; + }), + ); } public async unsubscribeMembership(item: TypedEntity): Promise { diff --git a/imxweb/projects/apc/src/public_api.ts b/imxweb/projects/apc/src/public_api.ts index 43d0ad019..5a8d374b3 100644 --- a/imxweb/projects/apc/src/public_api.ts +++ b/imxweb/projects/apc/src/public_api.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/apc/src/test.ts b/imxweb/projects/apc/src/test.ts index f7ac7d548..159c87d7d 100644 --- a/imxweb/projects/apc/src/test.ts +++ b/imxweb/projects/apc/src/test.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,24 +26,14 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'core-js/es7/reflect'; -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +// Zone must be imported first. Be careful with prettier import organizing +import 'zone.js'; +import 'zone.js/testing'; + import { getTestBed } from '@angular/core/testing'; -import { - BrowserDynamicTestingModule, - platformBrowserDynamicTesting -} from '@angular/platform-browser-dynamic/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; +import 'core-js/es/reflect'; import { TestHelperModule } from 'qbm'; -declare const require: any; - // First, initialize the Angular testing environment. -getTestBed().initTestEnvironment( - [BrowserDynamicTestingModule, TestHelperModule], - platformBrowserDynamicTesting() -); -// Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); -// And load the modules. -context.keys().map(context); +getTestBed().initTestEnvironment([BrowserDynamicTestingModule, TestHelperModule], platformBrowserDynamicTesting()); diff --git a/imxweb/projects/apc/tsconfig.lib.json b/imxweb/projects/apc/tsconfig.lib.json index 6bc419f84..534c734a0 100644 --- a/imxweb/projects/apc/tsconfig.lib.json +++ b/imxweb/projects/apc/tsconfig.lib.json @@ -1,16 +1,17 @@ /* To learn more about this file see: https://angular.io/config/tsconfig. */ { "extends": "../../tsconfig.json", + "angularCompilerOptions": { + "strictTemplates": true + }, "compilerOptions": { "outDir": "../../out-tsc/lib", + "strictNullChecks": true, "declaration": true, "declarationMap": true, "sourceMap": true, "inlineSources": true, "types": [] }, - "exclude": [ - "src/test.ts", - "**/*.spec.ts" - ] + "exclude": ["src/test.ts", "**/*.spec.ts"] } diff --git a/imxweb/projects/apc/tsconfig.lib.prod.json b/imxweb/projects/apc/tsconfig.lib.prod.json index 61c7592e7..fb40dbbb0 100644 --- a/imxweb/projects/apc/tsconfig.lib.prod.json +++ b/imxweb/projects/apc/tsconfig.lib.prod.json @@ -3,8 +3,5 @@ "extends": "./tsconfig.lib.json", "compilerOptions": { "declarationMap": false - }, - "angularCompilerOptions": { - "enableIvy": true } } diff --git a/imxweb/projects/apc/tsconfig.spec.json b/imxweb/projects/apc/tsconfig.spec.json index 16da33db0..6bd74e1f7 100644 --- a/imxweb/projects/apc/tsconfig.spec.json +++ b/imxweb/projects/apc/tsconfig.spec.json @@ -1,17 +1,10 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "strictNullChecks": false, "outDir": "../../out-tsc/spec", - "types": [ - "jasmine", - "node" - ] + "types": ["jasmine", "node"] }, - "files": [ - "src/test.ts" - ], - "include": [ - "**/*.spec.ts", - "**/*.d.ts" - ] + "files": ["src/test.ts"], + "include": ["**/*.spec.ts", "**/*.d.ts"] } diff --git a/imxweb/projects/apc/tslint.json b/imxweb/projects/apc/tslint.json deleted file mode 100644 index 8bab15f53..000000000 --- a/imxweb/projects/apc/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "imx", - "camelCase" - ], - "component-selector": [ - true, - "element", - "imx", - "kebab-case" - ] - } -} diff --git a/imxweb/projects/att/.compodocrc.json b/imxweb/projects/att/.compodocrc.json index 14353b30b..fb57684b2 100644 --- a/imxweb/projects/att/.compodocrc.json +++ b/imxweb/projects/att/.compodocrc.json @@ -1,6 +1,6 @@ { "name": "IMX Web - ATT Library", - "output": "../../documentation/v92/att", + "output": "../../documentation/v93/att", "theme": "material", "assetsFolder": "../../compodoc/assets", "customLogo": "../../compodoc/assets/images/oneidentity-logo.png", diff --git a/imxweb/projects/att/.eslintrc.json b/imxweb/projects/att/.eslintrc.json new file mode 100644 index 000000000..105acb604 --- /dev/null +++ b/imxweb/projects/att/.eslintrc.json @@ -0,0 +1,35 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*", "**/*.spec.ts"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": ["imxweb/projects/att/tsconfig.lib.json", "imxweb/projects/att/tsconfig.spec.json"], + "createDefaultProgram": true + }, + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "imx", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "imx", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "rules": {} + } + ] +} diff --git a/imxweb/projects/att/imx-plugin-config.json b/imxweb/projects/att/imx-plugin-config.json index 933ae1799..cdad7725e 100644 --- a/imxweb/projects/att/imx-plugin-config.json +++ b/imxweb/projects/att/imx-plugin-config.json @@ -1,14 +1,14 @@ { - "qer-app-portal": [ - { - "Container": "att", - "Name": "AttConfigModule" - } - ], - "qer-app-pwdportal": [ - { - "Container": "att", - "Name": "NewUserModule" - } - ] -} \ No newline at end of file + "qer-app-portal": [ + { + "Container": "att", + "Name": "AttConfigModule" + } + ], + "qer-app-pwdportal": [ + { + "Container": "att", + "Name": "NewUserModule" + } + ] +} diff --git a/imxweb/projects/att/karma.conf.js b/imxweb/projects/att/karma.conf.js index d7a9db062..3e53a3ac8 100644 --- a/imxweb/projects/att/karma.conf.js +++ b/imxweb/projects/att/karma.conf.js @@ -15,7 +15,7 @@ module.exports = function (config) { require('karma-junit-reporter'), ], client: { - clearContext: false // leave Jasmine Spec Runner output visible in browser + clearContext: false, // leave Jasmine Spec Runner output visible in browser }, coverageIstanbulReporter: { dir: require('path').join(__dirname, 'results'), @@ -23,14 +23,14 @@ module.exports = function (config) { fixWebpackSourcePaths: true, 'report-config': { html: { - subdir: 'coverage-html' + subdir: 'coverage-html', }, }, thresholds: { statements: 0, branches: 0, functions: 0, - lines: 0 + lines: 0, }, }, junitReporter: { @@ -40,13 +40,14 @@ module.exports = function (config) { port: 9876, captureTimeout: 210000, browserDisconnectTolerance: 3, - browserDisconnectTimeout : 210000, - browserNoActivityTimeout : 210000, + browserDisconnectTimeout: 210000, + browserNoActivityTimeout: 210000, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], singleRun: false, - restartOnFileChange: true + failOnEmptyTestSuite: false, + restartOnFileChange: true, }); }; diff --git a/imxweb/projects/o3t/ng-package.dynamic.json b/imxweb/projects/att/ng-package.dynamic-pwd.json similarity index 84% rename from imxweb/projects/o3t/ng-package.dynamic.json rename to imxweb/projects/att/ng-package.dynamic-pwd.json index 13fdffef2..3599d8f82 100644 --- a/imxweb/projects/o3t/ng-package.dynamic.json +++ b/imxweb/projects/att/ng-package.dynamic-pwd.json @@ -16,12 +16,11 @@ "@elemental-ui/core", "@ngx-translate/core", "@ngx-translate/http-loader", - "applicationinsights-js", "billboard.js" ], - "dest": "../../html/o3t", + "dest": "../../html/qer-app-pwdportal/att", "lib": { "entryFile": "src/public_api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] } } diff --git a/imxweb/projects/att/ng-package.dynamic.json b/imxweb/projects/att/ng-package.dynamic.json index addb5f02e..4d95ec994 100644 --- a/imxweb/projects/att/ng-package.dynamic.json +++ b/imxweb/projects/att/ng-package.dynamic.json @@ -16,12 +16,11 @@ "@elemental-ui/core", "@ngx-translate/core", "@ngx-translate/http-loader", - "applicationinsights-js", "billboard.js" ], - "dest": "../../html/att", + "dest": "../../html/qer-app-portal/att", "lib": { "entryFile": "src/public_api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] } } diff --git a/imxweb/projects/att/ng-package.json b/imxweb/projects/att/ng-package.json index fbcdd099d..afa86ad20 100644 --- a/imxweb/projects/att/ng-package.json +++ b/imxweb/projects/att/ng-package.json @@ -4,6 +4,6 @@ "allowedNonPeerDependencies": ["imx-api-att"], "lib": { "entryFile": "src/public_api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] } } diff --git a/imxweb/projects/att/package.json b/imxweb/projects/att/package.json index b6ccecc96..8194febc6 100644 --- a/imxweb/projects/att/package.json +++ b/imxweb/projects/att/package.json @@ -1,6 +1,6 @@ { "name": "att", - "version": "9.2.1", + "version": "9.3.0", "private": true, "bundledDependencies": [ "imx-api-att" diff --git a/imxweb/projects/att/project.json b/imxweb/projects/att/project.json new file mode 100644 index 000000000..032a8a2fb --- /dev/null +++ b/imxweb/projects/att/project.json @@ -0,0 +1,78 @@ +{ + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "name": "att", + "sourceRoot": "projects/att/src", + "implicitDependencies": [ + "qer" + ], + "projectType": "library", + "prefix": "imx", + "generators": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:ng-packagr", + "options": { + "tsConfig": "projects/att/tsconfig.lib.json", + "project": "projects/att/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "projects/att/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "projects/att/tsconfig.lib.json" + }, + "dynamic": { + "project": "projects/att/ng-package.dynamic.json" + }, + "dynamic-pwd": { + "project": "projects/att/ng-package.dynamic-pwd.json" + }, + "remote-dev": { + "tsConfig": "projects/att/tsconfig.lib.json" + }, + "remote-qs": { + "tsConfig": "projects/att/tsconfig.lib.json" + } + }, + "outputs": [ + "{workspaceRoot}/dist/att" + ] + }, + "test": { + "executor": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/att/src/test.ts", + "tsConfig": "projects/att/tsconfig.spec.json", + "karmaConfig": "projects/att/karma.conf.js", + "stylePreprocessorOptions": { + "includePaths": [ + "./shared/assets", + "./shared/scss", + "./node_modules", + "./node_modules/@elemental-ui/cadence-icon", + "./node_modules/@elemental-ui/core" + ] + } + } + }, + "lint": { + "executor": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": [ + "projects/att/tsconfig.lib.json", + "projects/att/tsconfig.spec.json" + ], + "exclude": [ + "**/node_modules/**", + "**/*.spec.ts", + "**/*.json" + ] + } + } + } +} diff --git a/imxweb/projects/att/src/default-mocks.spec.ts b/imxweb/projects/att/src/default-mocks.spec.ts index 344736214..3c5dc3413 100644 --- a/imxweb/projects/att/src/default-mocks.spec.ts +++ b/imxweb/projects/att/src/default-mocks.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,7 +28,7 @@ import { of, Subject } from 'rxjs'; import { ngMocks } from 'ng-mocks'; import { EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { ISessionState,AuthenticationService,RouteGuardService } from 'qbm'; +import { ISessionState, AuthenticationService, RouteGuardService } from 'qbm'; export class AttDefaultMocks { public static readonly afterClosedSubject = new Subject(); diff --git a/imxweb/projects/att/src/lib/admin/permissions-helper.ts b/imxweb/projects/att/src/lib/admin/permissions-helper.ts index 098826dbb..c30662f52 100644 --- a/imxweb/projects/att/src/lib/admin/permissions-helper.ts +++ b/imxweb/projects/att/src/lib/admin/permissions-helper.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,13 +25,13 @@ */ export function canSeeAttestationPolicies(features: string[]): boolean { - return features.find(item => item === 'Portal_UI_PolicyAdmin' || - item === 'Portal_UI_PolicyStatistics' || - item === 'Portal_UI_PolicyOwner') != null; + return ( + features.find( + (item) => item === 'Portal_UI_PolicyAdmin' || item === 'Portal_UI_PolicyStatistics' || item === 'Portal_UI_PolicyOwner', + ) != null + ); } export function isAttestationAdmin(features: string[]): boolean { - return features.find(item => item === 'Portal_UI_PolicyAdmin') != null; + return features.find((item) => item === 'Portal_UI_PolicyAdmin') != null; } - - diff --git a/imxweb/projects/att/src/lib/admin/permissions.service.ts b/imxweb/projects/att/src/lib/admin/permissions.service.ts index 8f75395d5..a483f7288 100644 --- a/imxweb/projects/att/src/lib/admin/permissions.service.ts +++ b/imxweb/projects/att/src/lib/admin/permissions.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,16 +30,16 @@ import { UserModelService } from 'qer'; import { canSeeAttestationPolicies, isAttestationAdmin } from './permissions-helper'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class PermissionsService { - constructor(private readonly userService: UserModelService) { } + constructor(private readonly userService: UserModelService) {} public async canSeeAttestationPolicies(): Promise { - return canSeeAttestationPolicies((await this.userService.getFeatures()).Features); + return canSeeAttestationPolicies((await this.userService.getFeatures()).Features || []); } public async isAttestationAdmin(): Promise { - return isAttestationAdmin((await this.userService.getFeatures()).Features); + return isAttestationAdmin((await this.userService.getFeatures()).Features || []); } } diff --git a/imxweb/projects/att/src/lib/api.service.ts b/imxweb/projects/att/src/lib/api.service.ts index 181d7de82..50630024a 100644 --- a/imxweb/projects/att/src/lib/api.service.ts +++ b/imxweb/projects/att/src/lib/api.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,12 +26,12 @@ import { Injectable } from '@angular/core'; -import { V2Client, TypedClient } from 'imx-api-att'; -import { ApiClient } from 'imx-qbm-dbts'; +import { V2Client, TypedClient } from '@imx-modules/imx-api-att'; +import { ApiClient } from '@imx-modules/imx-qbm-dbts'; import { AppConfigService, ClassloggerService, ImxTranslationProviderService } from 'qbm'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ApiService { private tc: TypedClient; @@ -51,7 +51,8 @@ export class ApiService { constructor( private readonly config: AppConfigService, private readonly logger: ClassloggerService, - private readonly translationProvider: ImxTranslationProviderService) { + private readonly translationProvider: ImxTranslationProviderService, + ) { try { this.logger.debug(this, 'Initializing API service'); diff --git a/imxweb/projects/att/src/lib/att-config.module.ts b/imxweb/projects/att/src/lib/att-config.module.ts index 40e8b54e1..2d5ed5ef5 100644 --- a/imxweb/projects/att/src/lib/att-config.module.ts +++ b/imxweb/projects/att/src/lib/att-config.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -126,9 +126,7 @@ const routes: Routes = [ ]; @NgModule({ - declarations: [ - DashboardPluginComponent - ], + declarations: [DashboardPluginComponent], imports: [ CommonModule, TilesModule, @@ -139,11 +137,13 @@ const routes: Routes = [ MatListModule, TranslateModule, EuiCoreModule, - ] + ], }) export class AttConfigModule { constructor( - private readonly initializer: InitService, private readonly logger: ClassloggerService) { + private readonly initializer: InitService, + private readonly logger: ClassloggerService, + ) { this.logger.info(this, '🔥 ATT loaded'); this.initializer.onInit(routes); this.logger.info(this, '▶️ ATT initialized'); diff --git a/imxweb/projects/att/src/lib/attestation-action/attestation-action.component.html b/imxweb/projects/att/src/lib/attestation-action/attestation-action.component.html index 97c031299..de2875fb3 100644 --- a/imxweb/projects/att/src/lib/attestation-action/attestation-action.component.html +++ b/imxweb/projects/att/src/lib/attestation-action/attestation-action.component.html @@ -1,5 +1,5 @@ -
- +
+ {{ data.description | translate }} @@ -32,7 +32,7 @@ (controlCreated)="formGroup.addControl('attestation-parameters', $event)" > - +
-
+
@@ -20,18 +23,38 @@
- + +
+ + +
+ + {{ '#LDS#Related objects' | translate }} + + + {{ relatedOption.Display }} + + + + + +
+
+
- - @@ -58,7 +98,7 @@ - - -
diff --git a/imxweb/projects/att/src/lib/attestation-history/attestation-history-wrapper.component.scss b/imxweb/projects/att/src/lib/attestation-history/attestation-history-wrapper.component.scss index f71091d5f..4318c9d88 100644 --- a/imxweb/projects/att/src/lib/attestation-history/attestation-history-wrapper.component.scss +++ b/imxweb/projects/att/src/lib/attestation-history/attestation-history-wrapper.component.scss @@ -1,4 +1,4 @@ -@import '../../../../../shared/scss/common-table.scss'; +@import 'base/mixins'; :host { display: flex; @@ -7,18 +7,10 @@ height: inherit; } -.imx-history-card { - @include imx-flex-fill-control-hidden-overflow(); - - imx-attestation-history { - flex: 1 1 auto; - } -} - -.mat-stroked-button { - @include imx-icon-for-image-button(); +imx-attestation-history { + height: 100%; } -.imx-button-bar { - @include imx-button-bar(); +.mat-mdc-outlined-button { + @include image-button-icon(); } diff --git a/imxweb/projects/att/src/lib/attestation-history/attestation-history-wrapper.component.ts b/imxweb/projects/att/src/lib/attestation-history/attestation-history-wrapper.component.ts index 1d1a9077b..680e5d4c4 100644 --- a/imxweb/projects/att/src/lib/attestation-history/attestation-history-wrapper.component.ts +++ b/imxweb/projects/att/src/lib/attestation-history/attestation-history-wrapper.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,27 +24,32 @@ * */ -import { Component, OnDestroy } from '@angular/core'; +import { AfterViewInit, ChangeDetectorRef, Component, OnDestroy, ViewChild } from '@angular/core'; import { Subscription } from 'rxjs'; -import { AuthenticationService } from 'qbm'; +import { TypedEntity } from '@imx-modules/imx-qbm-dbts'; +import { AuthenticationService, DataViewSource } from 'qbm'; import { AttestationHistoryActionService } from './attestation-history-action.service'; import { AttestationHistoryCase } from './attestation-history-case'; +import { AttestationHistoryComponent } from './attestation-history.component'; @Component({ selector: 'imx-attestation-history-wrapper', templateUrl: './attestation-history-wrapper.component.html', - styleUrls: ['./attestation-history-wrapper.component.scss'] + styleUrls: ['./attestation-history-wrapper.component.scss'], }) -export class AttestationHistoryWrapperComponent implements OnDestroy { +export class AttestationHistoryWrapperComponent implements OnDestroy, AfterViewInit { + @ViewChild('attestationHistoryComponent', { static: false }) public attestationHistoryComponent: AttestationHistoryComponent; + public dataSource: DataViewSource; public get canPerformActions(): boolean { - return this.selectedCases.length > 0 && ( - this.canRecallDecision - || this.canWithdrawDelegation - ); + return this.selectedCases.length > 0 && (this.canRecallDecision || this.canWithdrawDelegation); + } + public get canWithdrawDelegation(): boolean { + return this.selectedCases.every((item) => item.canWithdrawDelegation(this.userUid)); + } + public get canRecallDecision(): boolean { + return this.selectedCases.every((item) => item.canRecallDecision); } - public get canWithdrawDelegation(): boolean { return this.selectedCases.every(item => item.canWithdrawDelegation(this.userUid)); } - public get canRecallDecision(): boolean { return this.selectedCases.every(item => item.canRecallDecision); } public selectedCases: AttestationHistoryCase[] = []; @@ -52,15 +57,24 @@ export class AttestationHistoryWrapperComponent implements OnDestroy { private readonly subscriptions: Subscription[] = []; - constructor(public readonly attestationAction: AttestationHistoryActionService, authentication: AuthenticationService) { - this.subscriptions.push(authentication.onSessionResponse?.subscribe(sessionState => this.userUid = sessionState?.UserUid)); + constructor( + public readonly attestationAction: AttestationHistoryActionService, + authentication: AuthenticationService, + public changeDetectionRef: ChangeDetectorRef, + ) { + this.subscriptions.push(authentication.onSessionResponse?.subscribe((sessionState) => (this.userUid = sessionState?.UserUid || ''))); } public ngOnDestroy(): void { - this.subscriptions.forEach(s => s.unsubscribe()); + this.subscriptions.forEach((s) => s.unsubscribe()); + } + + ngAfterViewInit(): void { + this.dataSource = this.attestationHistoryComponent?.dataSource; + this.changeDetectionRef.detectChanges(); } - public onSelectionChanged(items: AttestationHistoryCase[]): void { - this.selectedCases = items; + public onSelectionChanged(items: TypedEntity[]): void { + this.selectedCases = items as AttestationHistoryCase[]; } } diff --git a/imxweb/projects/att/src/lib/attestation-history/attestation-history.component.html b/imxweb/projects/att/src/lib/attestation-history/attestation-history.component.html index 41ddb23d6..02b42ecaf 100644 --- a/imxweb/projects/att/src/lib/attestation-history/attestation-history.component.html +++ b/imxweb/projects/att/src/lib/attestation-history/attestation-history.component.html @@ -1,63 +1,58 @@ - -
- -
-
- - - - - - - - - -
-
- - {{ item.AttestationState.Column.GetDisplayValue() }} +
+

+ {{ title | translate }} + +

+ +
+ + + + {{ entitySchema?.Columns?.UiText?.Display }} + + + + + + + +
+
+ + {{ item.AttestationState.Column.GetDisplayValue() }} +
+
+ + {{ item.AttestationState.Column.GetDisplayValue() }} +
+
+ + {{ item.AttestationState.Column.GetDisplayValue() }} +
+
+ + {{ item.RiskIndex.GetMetadata().GetDisplay() + ': ' + item.RiskIndex.Column.GetDisplayValue() }} +
-
- - {{ item.AttestationState.Column.GetDisplayValue() }} -
-
- - {{ item.AttestationState.Column.GetDisplayValue() }} -
-
- - {{ item.RiskIndex.GetMetadata().GetDisplay() + ': ' + item.RiskIndex.Column.GetDisplayValue() }} -
-
- - - - - - - - - + + + + + + + + + + + diff --git a/imxweb/projects/att/src/lib/attestation-history/attestation-history.component.scss b/imxweb/projects/att/src/lib/attestation-history/attestation-history.component.scss index 086102e88..335e190a4 100644 --- a/imxweb/projects/att/src/lib/attestation-history/attestation-history.component.scss +++ b/imxweb/projects/att/src/lib/attestation-history/attestation-history.component.scss @@ -1,98 +1,20 @@ -@import '../../../../../shared/scss/common-table.scss'; -@import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/mixins'; :host { - overflow: hidden; - display: flex; - flex-direction: column; -} - -.imx-icons-container { - padding-right: 15px; - .table-icon { - display: flex; - align-items: center; - color: $color-gray-60; - font-weight: 600; - height: 14px; - &--warning { - .eui-icon { - color: $color-orange-60; - } - } - &--new { - .eui-icon { - color: $color-green-60; - } - } - &--alert { - .eui-icon, - span { - color: $color-red-60; - } - } - &--primary { - .eui-icon, - span { - color: $color-blue-60; - } - } - &:not(:first-of-type) { - margin-left: 8px; - border-left: 1px solid $color-gray-20; - padding-left: 8px; - } - .mat-icon, - .eui-icon { - padding-right: 6px; - } - } + @include flex-column-container($overflow: hidden); } imx-data-table { flex-grow: 2; } +.imx-history-card { + @include flex-column-container-fill(); +} .imx-display-column { display: flex; :last-child { flex-grow: 1; - } -} - -.imx-button-column { - @include imx-button-column-right(); -} - -.eui-dark-theme, -.eui-contrast-theme { - :host { - .imx-icons-container { - .table-icon { - color: $color-gray-10; - &--warning { - .eui-icon { - color: $color-orange-40; - } - } - &--new { - .mat-icon { - color: $color-green-40; - } - } - &--alert { - .eui-icon, - span { - color: $color-red-40; - } - } - &--primary { - .eui-icon, - span { - color: $color-blue-60; - } - } - } - } + display:block; } } diff --git a/imxweb/projects/att/src/lib/attestation-history/attestation-history.component.ts b/imxweb/projects/att/src/lib/attestation-history/attestation-history.component.ts index 24f7ddb05..87c119b02 100644 --- a/imxweb/projects/att/src/lib/attestation-history/attestation-history.component.ts +++ b/imxweb/projects/att/src/lib/attestation-history/attestation-history.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,80 +24,68 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; -import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'; +import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; -import { TranslateService } from '@ngx-translate/core'; -import { Subscription } from 'rxjs'; +import { ViewConfigData } from '@imx-modules/imx-api-qer'; import { + CollectionLoadParameters, CompareOperator, + DataModel, + DbObjectKey, DisplayColumns, EntitySchema, + FilterData, FilterType, TypedEntity, - FilterData, - DataModel, + TypedEntityCollectionData, ValType, - DbObjectKey, -} from 'imx-qbm-dbts'; +} from '@imx-modules/imx-qbm-dbts'; +import { TranslateService } from '@ngx-translate/core'; import { + BusyService, + calculateSidesheetWidth, + ClientPropertyForTableColumns, DataSourceItemStatus, - DataSourceToolbarFilter, - DataSourceToolbarGroupData, - DataSourceToolbarSettings, - DataTableComponent, + DataSourceToolbarViewConfig, DataTableGroupedData, - SettingsService, + DataViewInitParameters, + DataViewSource, UserMessageService, - ClientPropertyForTableColumns, - DataSourceToolbarViewConfig, - BusyService, } from 'qbm'; -import { AttestationHistoryFilterComponent } from './attestation-history-filter/attestation-history-filter.component'; -import { AttestationHistoryCase } from './attestation-history-case'; -import { AttestationCaseLoadParameters } from './attestation-case-load-parameters.interface'; -import { AttestationHistoryService } from './attestation-history.service'; -import { AttestationHistoryDetailsComponent } from './attestation-history-details/attestation-history-details.component'; +import { SourceDetectiveSidesheetComponent, SourceDetectiveSidesheetData, SourceDetectiveType, ViewConfigService } from 'qer'; +import { Subscription } from 'rxjs'; import { Approvers } from '../decision/approvers.interface'; -import { AttestationHistoryActionService } from './attestation-history-action.service'; import { AttestationCasesService } from '../decision/attestation-cases.service'; -import { createGroupData } from '../datamodel/datamodel-helper'; -import { SourceDetectiveSidesheetComponent, SourceDetectiveSidesheetData, SourceDetectiveType, ViewConfigService } from 'qer'; -import { ViewConfigData } from 'imx-api-qer'; +import { AttestationHistoryActionService } from './attestation-history-action.service'; +import { AttestationHistoryCase } from './attestation-history-case'; +import { AttestationHistoryDetailsComponent } from './attestation-history-details/attestation-history-details.component'; +import { AttestationHistoryService } from './attestation-history.service'; @Component({ selector: 'imx-attestation-history', templateUrl: './attestation-history.component.html', styleUrls: ['./attestation-history.component.scss'], + providers: [DataViewSource], }) export class AttestationHistoryComponent implements OnInit, OnDestroy { @Input() public parameters: { objecttable: string; objectuid: string; filter?: FilterData[] }; @Input() public itemStatus: DataSourceItemStatus = { enabled: (__) => true }; @Input() public withAssignmentAnalysis: boolean = false; - @Input() public selectable : boolean = true; + @Input() public selectable: boolean = true; + @Input() title: string; - @ViewChild('attestorFilter', { static: false }) public attestorFilter: AttestationHistoryFilterComponent; - - public dstSettings: DataSourceToolbarSettings; public readonly DisplayColumns = DisplayColumns; public readonly entitySchema: EntitySchema; public groupedData: { [key: string]: DataTableGroupedData } = {}; - @Output() public selectionChanged = new EventEmitter(); + @Output() public selectionChanged = new EventEmitter(); public busyService = new BusyService(); - private filterOptions: DataSourceToolbarFilter[] = []; private dataModel: DataModel; - - private navigationState: AttestationCaseLoadParameters; - - private groupData: DataSourceToolbarGroupData; private displayedColumns: ClientPropertyForTableColumns[]; private readonly subscriptions: Subscription[] = []; - - @ViewChild(DataTableComponent) private readonly table: DataTableComponent; private viewConfig: DataSourceToolbarViewConfig; private viewConfigPath = 'attestation/case'; @@ -109,22 +97,21 @@ export class AttestationHistoryComponent implements OnInit, OnDestroy { private readonly busyServiceElemental: EuiLoadingService, private readonly sideSheet: EuiSidesheetService, private readonly translator: TranslateService, - private readonly settingsService: SettingsService, - private readonly messageService: UserMessageService + private readonly messageService: UserMessageService, + public dataSource: DataViewSource, ) { this.entitySchema = attestationCaseService.attestationCaseSchema; this.subscriptions.push( this.attestationAction.applied.subscribe(async () => { - await this.getData(); - this.table?.clearSelection(); - }) + this.dataSource.updateState(); + this.dataSource.selection.clear(); + }), ); } public async ngOnInit(): Promise { this.displayedColumns = [this.entitySchema.Columns.UiText, this.entitySchema.Columns.AttestationState]; - if (this.withAssignmentAnalysis) { this.displayedColumns.push({ ColumnName: 'actions', @@ -133,50 +120,19 @@ export class AttestationHistoryComponent implements OnInit, OnDestroy { untranslatedDisplay: '#LDS#View assignment analysis', }); } - this.navigationState = { ...this.parameters, ...{ PageSize: this.settingsService.DefaultPageSize, StartIndex: 0 } }; const isBusy = this.busyService.beginBusy(); try { this.dataModel = await this.historyService.getDataModel( this.parameters?.objecttable, this.parameters?.objectuid, - this.parameters?.filter - ); - this.filterOptions = this.dataModel.Filters; - this.groupData = createGroupData( - this.dataModel, - (parameters) => { - const uidpolicy = this.filterOptions.find((elem) => elem.Name === 'uidpolicy')?.CurrentValue; - const risk = this.filterOptions.find((elem) => elem.Name === 'risk')?.CurrentValue; - const state = this.filterOptions.find((elem) => elem.Name === 'state')?.CurrentValue; - return this.historyService.getGroupInfo({ - ...{ - PageSize: this.navigationState.PageSize, - StartIndex: 0, - objecttable: this.parameters?.objecttable, - objectuid: this.parameters?.objectuid, - groupFilter: this.parameters?.filter, - risk, - state, - uidpolicy, - }, - ...parameters, - }); - }, - [] + this.parameters?.filter, ); this.viewConfig = await this.viewConfigService.getInitialDSTExtension(this.dataModel, this.viewConfigPath); - // We will check the configs for default state only on ini - if (!this.viewConfigService.isDefaultConfigSet()) { - // If we have no default settings, we will use the due date to sort - this.navigationState.OrderBy = 'ToSolveTill'; - } - await this.getData(); + await this.initTable(); } finally { - setTimeout(() => { - isBusy.endBusy(); - }); + isBusy.endBusy(); } } @@ -187,84 +143,63 @@ export class AttestationHistoryComponent implements OnInit, OnDestroy { public async updateConfig(config: ViewConfigData): Promise { await this.viewConfigService.putViewConfig(config); this.viewConfig = await this.viewConfigService.getDSTExtensionChanges(this.viewConfigPath); - this.dstSettings.viewConfig = this.viewConfig; + this.dataSource.viewConfig.set(this.viewConfig); } public async deleteConfigById(id: string): Promise { await this.viewConfigService.deleteViewConfig(id); this.viewConfig = await this.viewConfigService.getDSTExtensionChanges(this.viewConfigPath); - this.dstSettings.viewConfig = this.viewConfig; - } - - public async onSearch(search: string): Promise { - return this.getData({ search }); + this.dataSource.viewConfig.set(this.viewConfig); } - public async onGroupingChange(groupKey: string): Promise { - const isBusy = this.busyService.beginBusy(); - - try { - const groupedData = this.groupedData[groupKey]; - let filter = groupedData.navigationState?.filter; - if (this.parameters?.filter) { - filter = [...(groupedData.navigationState?.filter ?? []), ...(this.parameters?.filter ?? [])].filter((elem) => elem != null); - } - const navigationState = { ...groupedData.navigationState, filter }; - groupedData.data = await this.historyService.getAttestations(navigationState); - groupedData.settings = { - displayedColumns: this.dstSettings.displayedColumns, - dataModel: this.dstSettings.dataModel, - dataSource: groupedData.data, - entitySchema: this.dstSettings.entitySchema, - navigationState: groupedData.navigationState, - }; - } finally { - isBusy.endBusy(); - } - } - - public async getData(newState?: AttestationCaseLoadParameters): Promise { - this.navigationState = { - ...(this.dstSettings?.navigationState ?? this.navigationState), - uid_persondecision: this.attestorFilter?.selectedUid, - ...newState, + public async initTable(): Promise { + this.dataSource.itemStatus = this.itemStatus; + const dataViewInitParameters: DataViewInitParameters = { + execute: (params: CollectionLoadParameters, signal: AbortSignal): Promise> => + this.historyService.getAttestations({ ...this.parameters, ...params }), + schema: this.entitySchema, + columnsToDisplay: this.displayedColumns, + dataModel: this.dataModel, + groupExecute: (column: string, params: CollectionLoadParameters, signal: AbortSignal) => + Promise.resolve( + this.historyService + .getGroupInfo({ + ...params, + ...this.parameters, + StartIndex: 0, + def: column, + }) + .then((groupInfoData) => { + groupInfoData.Groups?.map((item) => { + item.Display?.forEach((display) => + item.Filters?.forEach((filter) => { + if (filter.Value1 != null) { + display.Display = display.Display?.replace(`%${filter.ColumnName}%`, filter.Value1); + } + }), + ); + return item; + }); + return groupInfoData; + }), + ), + viewConfig: this.viewConfig, + highlightEntity: (entity: AttestationHistoryCase) => { + this.viewDetails(entity); + }, + selectionChange: (selection: Array) => this.selectionChanged.emit(selection), + exportFunction: this.historyService.exportAttestation(this.dataSource.state()), }; - - const isBusy = this.busyService.beginBusy(); - - try { - const data = await this.historyService.getAttestations(this.navigationState); - const exportMethod = this.historyService.exportAttestation(this.navigationState); - exportMethod.initialColumns = this.displayedColumns.map((col) => col.ColumnName); - if (data) { - this.dstSettings = { - dataSource: { - totalCount: data.totalCount, - Data: data.Data ? data.Data : undefined, - }, - filters: this.filterOptions, - groupData: this.groupData, - displayedColumns: this.displayedColumns, - entitySchema: this.entitySchema, - navigationState: this.navigationState, - dataModel: this.dataModel, - viewConfig: this.viewConfig, - exportMethod, - }; - } else { - this.dstSettings = undefined; - } - } finally { - isBusy.endBusy(); - } + this.dataSource.init(dataViewInitParameters); } - public async viewDetails(attestationCase: AttestationHistoryCase): Promise { + public async viewDetails(entity: AttestationHistoryCase): Promise { + const attestationCase = entity as AttestationHistoryCase; let attestationCaseWithPolicy: AttestationHistoryCase; - let approvers: Approvers; - - let busyIndicator: OverlayRef; - setTimeout(() => (busyIndicator = this.busyServiceElemental.show())); + let approvers: Approvers | undefined; + if (this.busyServiceElemental.overlayRefs.length === 0) { + this.busyServiceElemental.show(); + } try { attestationCaseWithPolicy = ( @@ -290,7 +225,7 @@ export class AttestationHistoryComponent implements OnInit, OnDestroy { approvers = await this.attestationCaseService.getApprovers(attestationCaseWithPolicy); } } finally { - setTimeout(() => this.busyServiceElemental.hide(busyIndicator)); + this.busyServiceElemental.hide(); } if (attestationCaseWithPolicy) { @@ -298,7 +233,7 @@ export class AttestationHistoryComponent implements OnInit, OnDestroy { title: await this.translator.get('#LDS#Heading View Attestation Case Details').toPromise(), subTitle: attestationCaseWithPolicy.GetEntity().GetDisplay(), padding: '0', - width: '600px', + width: calculateSidesheetWidth(), testId: 'attestation-history-case-sidesheet', data: { case: attestationCaseWithPolicy, @@ -314,7 +249,6 @@ export class AttestationHistoryComponent implements OnInit, OnDestroy { } public async viewAssignmentAnalysis(event: Event, attestationCase: AttestationHistoryCase): Promise { - event.stopPropagation(); const uidPerson = attestationCase.UID_Person.value; @@ -330,7 +264,7 @@ export class AttestationHistoryComponent implements OnInit, OnDestroy { title: await this.translator.get('#LDS#Heading View Assignment Analysis').toPromise(), subTitle: attestationCase.GetEntity().GetDisplay(), padding: '0px', - width: 'max(50%,500px)', + width: calculateSidesheetWidth(800, 0.5), disableClose: false, testId: 'attestation-history-details-assignment-analysis', data, diff --git a/imxweb/projects/att/src/lib/attestation-history/attestation-history.module.ts b/imxweb/projects/att/src/lib/attestation-history/attestation-history.module.ts index abfb545af..b3018f6e1 100644 --- a/imxweb/projects/att/src/lib/attestation-history/attestation-history.module.ts +++ b/imxweb/projects/att/src/lib/attestation-history/attestation-history.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,25 +24,27 @@ * */ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { TranslateModule } from '@ngx-translate/core'; -import { MatButtonModule} from '@angular/material/button'; -import { EuiCoreModule } from '@elemental-ui/core'; -import { MatMenuModule } from '@angular/material/menu'; +import { NgModule } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; +import { MatMenuModule } from '@angular/material/menu'; import { MatTabsModule } from '@angular/material/tabs'; +import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; +import { TranslateModule } from '@ngx-translate/core'; -import { DataSourceToolbarModule, DataTableModule, CdrModule, SelectedElementsModule, HelpContextualModule } from 'qbm'; -import { AttestationHistoryComponent } from './attestation-history.component'; -import { AttestationHistoryService } from './attestation-history.service'; -import { AttestationHistoryDetailsComponent } from './attestation-history-details/attestation-history-details.component'; -import { AttestationDecisionModule } from '../decision/attestation-decision.module'; -import { AttestationHistoryWrapperComponent } from './attestation-history-wrapper.component'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatIconModule } from '@angular/material/icon'; +import { CdrModule, DataSourceToolbarModule, DataTableModule, DataViewModule, HelpContextualModule, SelectedElementsModule } from 'qbm'; +import { ObjectHyperviewModule } from 'qer'; import { AttestationDisplayModule } from '../attestation-display/attestation-display.module'; import { AttestationSnapshotModule } from '../attestation-snapshot/attestation-snapshot.module'; -import {AttestationHistoryFilterComponent} from './attestation-history-filter/attestation-history-filter.component'; -import { MatIconModule } from '@angular/material/icon'; +import { AttestationDecisionModule } from '../decision/attestation-decision.module'; +import { AttestationHistoryDetailsComponent } from './attestation-history-details/attestation-history-details.component'; +import { AttestationHistoryFilterComponent } from './attestation-history-filter/attestation-history-filter.component'; +import { AttestationHistoryWrapperComponent } from './attestation-history-wrapper.component'; +import { AttestationHistoryComponent } from './attestation-history.component'; +import { AttestationHistoryService } from './attestation-history.service'; import { MyAttestationCasesComponent } from './my-attestation-cases/my-attestation-cases.component'; @NgModule({ @@ -65,13 +67,18 @@ import { MyAttestationCasesComponent } from './my-attestation-cases/my-attestati MatMenuModule, MatTabsModule, MatIconModule, + FormsModule, + ReactiveFormsModule, TranslateModule, EuiCoreModule, + EuiMaterialModule, AttestationDecisionModule, AttestationDisplayModule, AttestationSnapshotModule, SelectedElementsModule, HelpContextualModule, + ObjectHyperviewModule, + DataViewModule, ], providers: [AttestationHistoryService], }) diff --git a/imxweb/projects/att/src/lib/attestation-history/attestation-history.service.ts b/imxweb/projects/att/src/lib/attestation-history/attestation-history.service.ts index 09e5f0ffc..9a4e2e07b 100644 --- a/imxweb/projects/att/src/lib/attestation-history/attestation-history.service.ts +++ b/imxweb/projects/att/src/lib/attestation-history/attestation-history.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,6 +26,7 @@ import { Injectable } from '@angular/core'; +import { AttCaseDataRead, PortalAttestationCase, V2ApiClientMethodFactory } from '@imx-modules/imx-api-att'; import { DataModel, EntityCollectionData, @@ -36,22 +37,24 @@ import { MethodDefinition, MethodDescriptor, TypedEntityCollectionData, -} from 'imx-qbm-dbts'; -import { AttCaseDataRead, PortalAttestationCase, V2ApiClientMethodFactory } from 'imx-api-att'; -import { ParameterDataService, ParameterDataLoadParameters } from 'qer'; +} from '@imx-modules/imx-qbm-dbts'; +import { DataSourceToolbarExportMethod } from 'qbm'; +import { ParameterDataLoadParameters, ParameterDataService } from 'qer'; import { ApiService } from '../api.service'; -import { AttestationHistoryCase } from './attestation-history-case'; import { AttestationCaseLoadParameters } from './attestation-case-load-parameters.interface'; -import { DataSourceToolbarExportMethod } from 'qbm'; +import { AttestationHistoryCase } from './attestation-history-case'; @Injectable({ providedIn: 'root', }) export class AttestationHistoryService { - constructor(private readonly attClient: ApiService, private readonly parameterDataService: ParameterDataService) {} + constructor( + private readonly attClient: ApiService, + private readonly parameterDataService: ParameterDataService, + ) {} public async get( - parameters: AttestationCaseLoadParameters + parameters?: AttestationCaseLoadParameters, ): Promise> { return this.attClient.typedClient.PortalAttestationCase.Get(parameters); } @@ -66,7 +69,7 @@ export class AttestationHistoryService { item.GetEntity(), { ...collection.extendedData, ...{ index } }, (parameters) => this.getParameterCandidates(parameters), - (treefilterparameter) => this.getFilterTree(treefilterparameter) + (treefilterparameter) => this.getFilterTree(treefilterparameter), ); return new AttestationHistoryCase(item, parameterDataContainer, { ...collection.extendedData, ...{ index } }); @@ -80,13 +83,13 @@ export class AttestationHistoryService { getMethod: (withProperties: string, PageSize?: number) => { let method: MethodDescriptor; if (PageSize) { - method = factory.portal_attestation_case_get({...loadParameters, withProperties, PageSize, StartIndex: 0}) + method = factory.portal_attestation_case_get({ ...loadParameters, withProperties, PageSize, StartIndex: 0 }); } else { - method = factory.portal_attestation_case_get({...loadParameters, withProperties}) + method = factory.portal_attestation_case_get({ ...loadParameters, withProperties }); } return new MethodDefinition(method); - } - } + }, + }; } public async getDataModel(objecttable?: string, objectuid?: string, groupFilter?: FilterData[]): Promise { @@ -99,31 +102,32 @@ export class AttestationHistoryService { public getGroupInfo(parameters: AttestationCaseLoadParameters = {}): Promise { // remove groupFilter from parameters - const {withProperties, groupFilter, search, OrderBy, ...paramsWithoutGroupFilter } = parameters; + const { withProperties, groupFilter, search, OrderBy, ...paramsWithoutGroupFilter } = parameters; return this.attClient.client.portal_attestation_case_group_get({ ...paramsWithoutGroupFilter, - ...{ withcount: true, filter: parameters.groupFilter }, + withcount: true, + filter: [...(parameters.groupFilter || []), ...(parameters.filter || [])], }); } private async getParameterCandidates(parameters: ParameterDataLoadParameters): Promise { return this.attClient.client.portal_attestation_case_parameter_candidates_post( parameters.columnName, - parameters.fkTableName, - parameters.diffData, - parameters + parameters.fkTableName || '', + parameters.diffData || {}, + parameters, ); } private async getFilterTree(parameters: ParameterDataLoadParameters): Promise { return this.attClient.client.portal_attestation_case_parameter_candidates_filtertree_post( parameters.columnName, - parameters.fkTableName, - parameters.diffData, + parameters.fkTableName || '', + parameters.diffData || {}, { ...parameters, parentkey: parameters.ParentKey, - } + }, ); } } diff --git a/imxweb/projects/att/src/lib/attestation-history/my-attestation-cases/my-attestation-cases.component.html b/imxweb/projects/att/src/lib/attestation-history/my-attestation-cases/my-attestation-cases.component.html index 296a7f3ac..5bfe89f28 100644 --- a/imxweb/projects/att/src/lib/attestation-history/my-attestation-cases/my-attestation-cases.component.html +++ b/imxweb/projects/att/src/lib/attestation-history/my-attestation-cases/my-attestation-cases.component.html @@ -1,7 +1,8 @@ -

- {{ '#LDS#Heading My Attestations' | translate }} - -

- - - + + + diff --git a/imxweb/projects/att/src/lib/attestation-history/my-attestation-cases/my-attestation-cases.component.scss b/imxweb/projects/att/src/lib/attestation-history/my-attestation-cases/my-attestation-cases.component.scss index c624cb40a..964be29d8 100644 --- a/imxweb/projects/att/src/lib/attestation-history/my-attestation-cases/my-attestation-cases.component.scss +++ b/imxweb/projects/att/src/lib/attestation-history/my-attestation-cases/my-attestation-cases.component.scss @@ -8,10 +8,3 @@ flex: 1 1 auto; } } - -.mat-card { - display: flex; - flex: 1 1 auto; - overflow: hidden; - margin: 3px; -} diff --git a/imxweb/projects/att/src/lib/attestation-history/my-attestation-cases/my-attestation-cases.component.ts b/imxweb/projects/att/src/lib/attestation-history/my-attestation-cases/my-attestation-cases.component.ts index da7e48b6a..77a42133f 100644 --- a/imxweb/projects/att/src/lib/attestation-history/my-attestation-cases/my-attestation-cases.component.ts +++ b/imxweb/projects/att/src/lib/attestation-history/my-attestation-cases/my-attestation-cases.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -42,7 +42,7 @@ export class MyAttestationCasesComponent implements OnDestroy { this.attestationParameters = session.IsLoggedIn ? { objecttable: 'Person', - objectuid: session.UserUid, + objectuid: session.UserUid || '', } : undefined; }); diff --git a/imxweb/projects/att/src/lib/attestation-snapshot/attestation-snapshot.component.html b/imxweb/projects/att/src/lib/attestation-snapshot/attestation-snapshot.component.html index 073c75862..5c6042a16 100644 --- a/imxweb/projects/att/src/lib/attestation-snapshot/attestation-snapshot.component.html +++ b/imxweb/projects/att/src/lib/attestation-snapshot/attestation-snapshot.component.html @@ -1,9 +1,15 @@ - + - {{ obj.Data.Display }} - {{'#LDS#Object type' | translate}}: {{ obj.TableDisplay }} + {{ obj.Data?.Display }} - {{ '#LDS#Object type' | translate }}: {{ obj.TableDisplay }}
@@ -19,8 +25,8 @@
  • - {{ col.value.Caption }} - {{ col.value.DisplayValue }} + {{ getColumnCaption(col.value) }} + {{ getColumnDisplayValue(col.value) }}
diff --git a/imxweb/projects/att/src/lib/attestation-snapshot/attestation-snapshot.component.scss b/imxweb/projects/att/src/lib/attestation-snapshot/attestation-snapshot.component.scss index ae29e8c1c..f0f1461cf 100644 --- a/imxweb/projects/att/src/lib/attestation-snapshot/attestation-snapshot.component.scss +++ b/imxweb/projects/att/src/lib/attestation-snapshot/attestation-snapshot.component.scss @@ -1,19 +1,11 @@ @import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/mixins'; :host { margin-top: 20px; display: block; } -.helper-alert { - display: flex; - margin-bottom: 20px; - - span { - display: block; - } -} - .property { margin-bottom: 20px; span { @@ -27,13 +19,10 @@ } } -.mat-card { +.mat-mdc-card { + @include flex-column-container-fill(); margin-bottom: 1em; - height: 100%; padding: 20px; - overflow: hidden; - display: flex; - flex-direction: column; } .imx-snapshot-content { diff --git a/imxweb/projects/att/src/lib/attestation-snapshot/attestation-snapshot.component.ts b/imxweb/projects/att/src/lib/attestation-snapshot/attestation-snapshot.component.ts index d463221d2..3dfd34b49 100644 --- a/imxweb/projects/att/src/lib/attestation-snapshot/attestation-snapshot.component.ts +++ b/imxweb/projects/att/src/lib/attestation-snapshot/attestation-snapshot.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,7 +27,9 @@ import { Component, Input, OnInit } from '@angular/core'; import { EuiLoadingService } from '@elemental-ui/core'; -import { AttestationSnapshotData } from 'imx-api-att'; +import { AttestationSnapshotData } from '@imx-modules/imx-api-att'; +import { EntityColumnData } from '@imx-modules/imx-qbm-dbts'; +import { TranslateService } from '@ngx-translate/core'; import { ApiService } from '../api.service'; @Component({ @@ -39,10 +41,11 @@ export class AttestationSnapshotComponent implements OnInit { public snapshot: AttestationSnapshotData; @Input() public uidCase: string; - @Input() public date :string; + @Input() public date: string; constructor( private readonly attApi: ApiService, - private readonly busy: EuiLoadingService + private readonly busy: EuiLoadingService, + private translate: TranslateService, ) {} public async ngOnInit(): Promise { @@ -54,6 +57,13 @@ export class AttestationSnapshotComponent implements OnInit { this.busy.hide(overlay); } } + public getColumnDisplayValue(column: any): string { + return (column as EntityColumnData).DisplayValue || this.translate.instant('#LDS#Not set'); + } + + public getColumnCaption(column: any): string { + return (column as EntityColumnData).Caption || ''; + } public get attestationDate(): string { return this.date; @@ -63,11 +73,11 @@ export class AttestationSnapshotComponent implements OnInit { * Ordering Objects array by Display (if Displays are equal, then by TableDisplay) in ascending order */ private sortObjectsByDisplay(): void { - this.snapshot.Objects.sort((obj1, obj2) => { - const display1 = obj1.Data.Display; - const display2 = obj2.Data.Display; - const tableDisplay1 = obj1.TableDisplay; - const tableDisplay2 = obj2.TableDisplay; + this.snapshot.Objects?.sort((obj1, obj2) => { + const display1 = obj1.Data?.Display || ''; + const display2 = obj2.Data?.Display || ''; + const tableDisplay1 = obj1.TableDisplay || ''; + const tableDisplay2 = obj2.TableDisplay || ''; if (display1 === display2) return tableDisplay1 < tableDisplay2 ? -1 : 1; diff --git a/imxweb/projects/att/src/lib/attestation-snapshot/attestation-snapshot.module.ts b/imxweb/projects/att/src/lib/attestation-snapshot/attestation-snapshot.module.ts index 806291d64..5d2396ab1 100644 --- a/imxweb/projects/att/src/lib/attestation-snapshot/attestation-snapshot.module.ts +++ b/imxweb/projects/att/src/lib/attestation-snapshot/attestation-snapshot.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,33 +24,18 @@ * */ -import { CommonModule } from "@angular/common"; -import { NgModule } from "@angular/core"; -import { MatButtonModule } from "@angular/material/button"; -import { MatCardModule } from "@angular/material/card"; -import { EuiCoreModule, EuiMaterialModule } from "@elemental-ui/core"; -import { TranslateModule } from "@ngx-translate/core"; -import { CdrModule, LdsReplaceModule } from "qbm"; -import { AttestationSnapshotComponent } from "./attestation-snapshot.component"; +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; +import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; +import { TranslateModule } from '@ngx-translate/core'; +import { CdrModule, LdsReplaceModule } from 'qbm'; +import { AttestationSnapshotComponent } from './attestation-snapshot.component'; @NgModule({ - declarations: [ - AttestationSnapshotComponent - ], - imports: [ - CommonModule, - CdrModule, - EuiCoreModule, - EuiMaterialModule, - LdsReplaceModule, - TranslateModule, - MatCardModule, - MatButtonModule, - ], - exports: [ - AttestationSnapshotComponent - ] + declarations: [AttestationSnapshotComponent], + imports: [CommonModule, CdrModule, EuiCoreModule, EuiMaterialModule, LdsReplaceModule, TranslateModule, MatCardModule, MatButtonModule], + exports: [AttestationSnapshotComponent], }) -export class AttestationSnapshotModule { - -} \ No newline at end of file +export class AttestationSnapshotModule {} diff --git a/imxweb/projects/att/src/lib/claim-device/claim-device.component.html b/imxweb/projects/att/src/lib/claim-device/claim-device.component.html index 662f79252..e601b3ad6 100644 --- a/imxweb/projects/att/src/lib/claim-device/claim-device.component.html +++ b/imxweb/projects/att/src/lib/claim-device/claim-device.component.html @@ -1,10 +1,21 @@ -

+

{{ '#LDS#Heading Assign an Owner for a Device' | translate }} -

+ - - + +
@@ -12,7 +23,7 @@

+

diff --git a/imxweb/projects/att/src/lib/claim-device/claim-device.component.scss b/imxweb/projects/att/src/lib/claim-device/claim-device.component.scss index eeb7cee7a..1c1cc3b58 100644 --- a/imxweb/projects/att/src/lib/claim-device/claim-device.component.scss +++ b/imxweb/projects/att/src/lib/claim-device/claim-device.component.scss @@ -3,11 +3,6 @@ max-width: 100%; } -mat-radio-group { - display: flex; - flex-direction: column; -} - .imx-button-previous { margin-right: 10px; } diff --git a/imxweb/projects/att/src/lib/claim-device/claim-device.component.spec.ts b/imxweb/projects/att/src/lib/claim-device/claim-device.component.spec.ts index 17a6ebb96..daf24126d 100644 --- a/imxweb/projects/att/src/lib/claim-device/claim-device.component.spec.ts +++ b/imxweb/projects/att/src/lib/claim-device/claim-device.component.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,15 +30,15 @@ import { MatRadioChange } from '@angular/material/radio'; import { EuiLoadingService } from '@elemental-ui/core'; import { BehaviorSubject } from 'rxjs'; -import { AuthenticationService, clearStylesFromDOM, ISessionState, LdsReplaceModule } from 'qbm'; +import { MockBuilder, MockedComponentFixture, MockRender } from 'ng-mocks'; +import { AuthenticationService, clearStylesFromDOM, ISessionState } from 'qbm'; import { PersonService } from 'qer'; import { ClaimDeviceComponent } from './claim-device.component'; import { ClaimDeviceService } from './claim-device.service'; -import { MockBuilder, MockedComponentFixture, MockRender } from 'ng-mocks'; @Component({ selector: 'imx-cdr-editor', - template: '

MockCdrEditor

' + template: '

MockCdrEditor

', }) class MockCdrEditor { @Input() public cdr: any; @@ -48,7 +48,7 @@ describe('ClaimDeviceComponent', () => { let component: ClaimDeviceComponent; let fixture: MockedComponentFixture; - const claimGroupServiceStub = new class { + const claimGroupServiceStub = new (class { numberOfSuggestedOwners = 0; hasSuggestedOwners = jasmine.createSpy('hasSuggestedOwners').and.callFake(() => Promise.resolve(this.numberOfSuggestedOwners > 0)); @@ -57,13 +57,13 @@ describe('ClaimDeviceComponent', () => { column: { ColumnName: 'UID_Hardware', GetValue: () => 'device-key', - GetDisplayValue: () => 'displayvalue for some device' - } + GetDisplayValue: () => 'displayvalue for some device', + }, }); createColumnSuggestedOwner = jasmine.createSpy('createColumnSuggestedOwner').and.returnValue({ ColumnName: 'some column name', - GetValue: () => 'some value' + GetValue: () => 'some value', }); reset() { @@ -73,38 +73,39 @@ describe('ClaimDeviceComponent', () => { this.createColumnSuggestedOwner.calls.reset(); this.numberOfSuggestedOwners = 0; } - }(); + })(); const personServiceStub = { createColumnCandidatesPerson: jasmine.createSpy('createColumnCandidatesPerson').and.returnValue({ ColumnName: 'personColumn', - GetValue: () => 'some value' - }) + GetValue: () => 'some value', + }), }; const authStub = { onSessionResponse: new BehaviorSubject({ - UserUid: "", - Username: "" - }) - } + UserUid: '', + Username: '', + }), + }; + + const euiLoadingServiceStud = { + overlayRefs: [], + }; beforeEach(() => { return MockBuilder(ClaimDeviceComponent) .mock(ClaimDeviceService, claimGroupServiceStub) .mock(PersonService, personServiceStub) - .mock(EuiLoadingService) + .mock(EuiLoadingService, euiLoadingServiceStud) .mock(AuthenticationService, authStub) - .beforeCompileComponents(testBed => { + .beforeCompileComponents((testBed) => { testBed.configureTestingModule({ - declarations: [ - ClaimDeviceComponent, - MockCdrEditor - ], - schemas: [CUSTOM_ELEMENTS_SCHEMA] + declarations: [ClaimDeviceComponent, MockCdrEditor], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + }); }); - }); - }) + }); beforeEach(() => { claimGroupServiceStub.reset(); @@ -123,16 +124,13 @@ describe('ClaimDeviceComponent', () => { const cdr = { display: 'some display' }; component.ownerCandidateListChange({ - value: { createOwnerCdr: () => cdr } + value: { createOwnerCdr: () => cdr }, } as MatRadioChange); expect(component.ownerCdr.display).toEqual(cdr.display); }); - for (const method of [ - () => component.loadSuggestedOwners(), - () => component.stepChange({ selectedIndex: 2 } as StepperSelectionEvent) - ]) { + for (const method of [() => component.loadSuggestedOwners(), () => component.stepChange({ selectedIndex: 2 } as StepperSelectionEvent)]) { it('loads owner candidates', async () => { await method(); expect(component.ownershipOptions.length).toEqual(2); diff --git a/imxweb/projects/att/src/lib/claim-device/claim-device.component.ts b/imxweb/projects/att/src/lib/claim-device/claim-device.component.ts index 62fa2f933..b765f374b 100644 --- a/imxweb/projects/att/src/lib/claim-device/claim-device.component.ts +++ b/imxweb/projects/att/src/lib/claim-device/claim-device.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,7 +27,6 @@ import { StepperSelectionEvent } from '@angular/cdk/stepper'; import { Component, ErrorHandler, OnDestroy, OnInit } from '@angular/core'; import { UntypedFormGroup } from '@angular/forms'; -import { OverlayRef } from '@angular/cdk/overlay'; import { MatRadioChange } from '@angular/material/radio'; import { EuiLoadingService } from '@elemental-ui/core'; import { Subscription } from 'rxjs'; @@ -38,14 +37,16 @@ import { ClaimDeviceService } from './claim-device.service'; @Component({ styleUrls: ['./claim-device.component.scss'], - templateUrl: './claim-device.component.html' + templateUrl: './claim-device.component.html', }) export class ClaimDeviceComponent implements OnDestroy, OnInit { - public get ownerDisplay(): string { return this.ownerCdr ? this.ownerCdr.column.GetDisplayValue() : this.user.name; } + public get ownerDisplay(): string { + return this.ownerCdr ? this.ownerCdr.column.GetDisplayValue() : this.user.name || ''; + } public deviceCdr: BaseCdr; public ownerCdr: BaseCdr; - public ownershipOptions: { title: string; createOwnerCdr: () => BaseCdr; }[]; + public ownershipOptions: { title: string; createOwnerCdr: () => BaseCdr | undefined }[] = []; public ownerAssigned = false; public canClaimDevice = false; @@ -53,7 +54,7 @@ export class ClaimDeviceComponent implements OnDestroy, OnInit { public readonly ownerForm = new UntypedFormGroup({}); private deviceKey: string; - private readonly user: { uid?: string; name?: string; } = {}; + private readonly user: { uid?: string; name?: string } = {}; private readonly subscriptions: Subscription[] = []; constructor( @@ -61,30 +62,33 @@ export class ClaimDeviceComponent implements OnDestroy, OnInit { private readonly personService: PersonService, private readonly busyService: EuiLoadingService, private readonly authentication: AuthenticationService, - private readonly errorHandler: ErrorHandler + private readonly errorHandler: ErrorHandler, ) { this.initDeviceCdr(); - this.subscriptions.push(this.authentication.onSessionResponse.subscribe(async (sessionState: ISessionState) => { - if (sessionState) { - this.user.uid = sessionState.UserUid; - this.user.name = sessionState.Username; - } - })); + this.subscriptions.push( + this.authentication.onSessionResponse.subscribe(async (sessionState: ISessionState) => { + if (sessionState) { + this.user.uid = sessionState.UserUid || ''; + this.user.name = sessionState.Username || ''; + } + }), + ); } public async ngOnInit(): Promise { - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } try { - this.canClaimDevice = await this.claimDeviceService.canClaimDevice(); + this.canClaimDevice = await this.claimDeviceService.canClaimDevice(); } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } } public ngOnDestroy(): void { - this.subscriptions.forEach(subscription => subscription.unsubscribe()); + this.subscriptions.forEach((subscription) => subscription.unsubscribe()); } public resetForms(): void { @@ -107,7 +111,7 @@ export class ClaimDeviceComponent implements OnDestroy, OnInit { } public async loadSuggestedOwners(): Promise { - const fkValue = this.deviceCdr.column.GetValue(); + const fkValue = this.deviceCdr.column.GetValue(); if (this.deviceKey === fkValue) { return; @@ -118,46 +122,42 @@ export class ClaimDeviceComponent implements OnDestroy, OnInit { this.ownershipOptions = []; this.ownershipOptions.push({ title: '#LDS#I want to take ownership of this device', - createOwnerCdr: () => undefined + createOwnerCdr: () => undefined, }); this.ownershipOptions.push({ title: '#LDS#Select another owner', - createOwnerCdr: () => new BaseCdr( - this.personService.createColumnCandidatesPerson(), - display - ) + createOwnerCdr: () => new BaseCdr(this.personService.createColumnCandidatesPerson(), display), }); this.initOwnerCdr(this.ownershipOptions[0].createOwnerCdr()); } public async assignOwner(): Promise { - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } try { - await this.claimDeviceService.assignOwner( - this.deviceKey, - this.ownerCdr ? this.ownerCdr.column.GetValue() : this.user.uid - ); + await this.claimDeviceService.assignOwner(this.deviceKey, this.ownerCdr ? this.ownerCdr.column.GetValue() : this.user.uid); this.ownerAssigned = true; } catch (error) { this.errorHandler.handleError(error); } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } } private initDeviceCdr(): void { - Object.keys(this.deviceForm.controls).forEach(name => this.deviceForm.removeControl(name)); + Object.keys(this.deviceForm.controls).forEach((name) => this.deviceForm.removeControl(name)); this.deviceCdr = this.claimDeviceService.createCdrForDevice(); } - private initOwnerCdr(cdr: BaseCdr): void { - Object.keys(this.ownerForm.controls).forEach(name => this.ownerForm.removeControl(name)); - - this.ownerCdr = cdr; + private initOwnerCdr(cdr?: BaseCdr): void { + if (cdr) { + Object.keys(this.ownerForm.controls).forEach((name) => this.ownerForm.removeControl(name)); + this.ownerCdr = cdr; + } } } diff --git a/imxweb/projects/att/src/lib/claim-device/claim-device.module.ts b/imxweb/projects/att/src/lib/claim-device/claim-device.module.ts index b8cf5b6f8..f89b8d63c 100644 --- a/imxweb/projects/att/src/lib/claim-device/claim-device.module.ts +++ b/imxweb/projects/att/src/lib/claim-device/claim-device.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -37,9 +37,7 @@ import { CdrModule, HelpContextualModule, LdsReplaceModule } from 'qbm'; import { ClaimDeviceComponent } from './claim-device.component'; @NgModule({ - declarations: [ - ClaimDeviceComponent - ], + declarations: [ClaimDeviceComponent], imports: [ CdrModule, CommonModule, @@ -52,7 +50,6 @@ import { ClaimDeviceComponent } from './claim-device.component'; ReactiveFormsModule, TranslateModule, HelpContextualModule, - ] + ], }) -export class ClaimDeviceModule { -} +export class ClaimDeviceModule {} diff --git a/imxweb/projects/att/src/lib/claim-device/claim-device.service.ts b/imxweb/projects/att/src/lib/claim-device/claim-device.service.ts index 1a2ea7453..65ab2dd48 100644 --- a/imxweb/projects/att/src/lib/claim-device/claim-device.service.ts +++ b/imxweb/projects/att/src/lib/claim-device/claim-device.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,15 +26,18 @@ import { Injectable } from '@angular/core'; -import { CollectionLoadParameters, EntityCollectionData, IEntityColumn, MetaTableRelationData, ValType } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, EntityCollectionData, IEntityColumn, MetaTableRelationData, ValType } from '@imx-modules/imx-qbm-dbts'; import { BaseCdr, EntityService } from 'qbm'; import { ApiService } from '../api.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ClaimDeviceService { - constructor(private readonly apiService: ApiService, private readonly entityService: EntityService) { } + constructor( + private readonly apiService: ApiService, + private readonly entityService: EntityService, + ) {} public async canClaimDevice(): Promise { return (await this.apiService.client.portal_attestation_userconfig_get()).CanClaimDevice; @@ -50,9 +53,9 @@ export class ClaimDeviceService { ChildColumnName: 'UID_Hardware', IsMemberRelation: false, ParentTableName: 'Hardware', - ParentColumnName: 'XObjectKey' + ParentColumnName: 'XObjectKey', }, - parameters => this.apiService.client.portal_claimdevice_devices_get(parameters), + (parameters) => this.apiService.client.portal_claimdevice_devices_get(parameters), ); return new BaseCdr(column, '#LDS#Device' /* TODO: globalize */); @@ -67,30 +70,22 @@ export class ClaimDeviceService { ColumnName: fkRelation.ChildColumnName, Type: ValType.String, MinLen: 1, - FkRelation: fkRelation + FkRelation: fkRelation, }, - [{ - columnName: fkRelation.ChildColumnName, - fkTableName: fkRelation.ParentTableName, - parameterNames: [ - 'OrderBy', - 'StartIndex', - 'PageSize', - 'filter', - 'search', - ], - load: async (_, parameters: CollectionLoadParameters = {}) => loadFkCandidates(parameters), - getDataModel: async () => ({ Filters: [] }), - getFilterTree: async () => ({ Elements: [] }) - }] + [ + { + columnName: fkRelation.ChildColumnName || '', + fkTableName: fkRelation.ParentTableName || '', + parameterNames: ['OrderBy', 'StartIndex', 'PageSize', 'filter', 'search'], + load: async (_, parameters: CollectionLoadParameters = {}) => loadFkCandidates(parameters), + getDataModel: async () => ({ Filters: [] }), + getFilterTree: async () => ({ Elements: [] }), + }, + ], ); } - - - private async getOwnerCandidates( - parameters: CollectionLoadParameters = {} - ): Promise { + private async getOwnerCandidates(parameters: CollectionLoadParameters = {}): Promise { const collection = await this.apiService.client.portal_candidates_Person_get(parameters); return collection; diff --git a/imxweb/projects/att/src/lib/dashboard-plugin/dashboard-plugin.component.html b/imxweb/projects/att/src/lib/dashboard-plugin/dashboard-plugin.component.html index 41e5513e2..81c31cf46 100644 --- a/imxweb/projects/att/src/lib/dashboard-plugin/dashboard-plugin.component.html +++ b/imxweb/projects/att/src/lib/dashboard-plugin/dashboard-plugin.component.html @@ -1,8 +1,8 @@ @@ -10,8 +10,8 @@ diff --git a/imxweb/projects/att/src/lib/dashboard-plugin/dashboard-plugin.component.ts b/imxweb/projects/att/src/lib/dashboard-plugin/dashboard-plugin.component.ts index eac2a020d..07f3c5489 100644 --- a/imxweb/projects/att/src/lib/dashboard-plugin/dashboard-plugin.component.ts +++ b/imxweb/projects/att/src/lib/dashboard-plugin/dashboard-plugin.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,10 +31,9 @@ import { DashboardService, PendingItemsType, UserModelService } from 'qer'; import { AttestationFeatureGuardService } from '../attestation-feature-guard.service'; @Component({ - templateUrl: './dashboard-plugin.component.html' + templateUrl: './dashboard-plugin.component.html', }) export class DashboardPluginComponent implements OnInit { - public pendingItems: PendingItemsType; public attEnabled: boolean; @@ -42,11 +41,10 @@ export class DashboardPluginComponent implements OnInit { public readonly router: Router, private readonly dashboardSvc: DashboardService, private readonly userModelSvc: UserModelService, - private readonly attFeatureGuard: AttestationFeatureGuardService - ) { } + private readonly attFeatureGuard: AttestationFeatureGuardService, + ) {} public async ngOnInit(): Promise { - const busy = this.dashboardSvc.beginBusy(); try { @@ -58,6 +56,6 @@ export class DashboardPluginComponent implements OnInit { } public goToAttestationInquiries(): void { - this.router.navigate(['attestation', 'decision'], {queryParams: {inquiries:true}}); + this.router.navigate(['attestation', 'decision'], { queryParams: { inquiries: true } }); } } diff --git a/imxweb/projects/att/src/lib/datamodel/datamodel-helper.ts b/imxweb/projects/att/src/lib/datamodel/datamodel-helper.ts index 6246f915c..51fe80293 100644 --- a/imxweb/projects/att/src/lib/datamodel/datamodel-helper.ts +++ b/imxweb/projects/att/src/lib/datamodel/datamodel-helper.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,55 +31,55 @@ import { DataModelGroupInfo, DataModelProperty, GroupInfoData, -} from 'imx-qbm-dbts'; -import { DataSourceToolbarGroupData } from 'qbm'; +} from '@imx-modules/imx-qbm-dbts'; +import { DataSourceToolBarGroup, DataSourceToolbarGroupData, DataSourceToolBarGroupingCategory } from 'qbm'; export function createGroupData( dataModel: DataModel, getGroupInfo: (parameters: { by?: string; def?: string } & CollectionLoadParameters) => Promise, - disableGroupingFor: string[] + disableGroupingFor: string[], ): DataSourceToolbarGroupData { - const groups = []; - const groupingCategories = []; + const groups: DataSourceToolBarGroup[] = []; + const groupingCategories: DataSourceToolBarGroupingCategory[] = []; if (dataModel.Properties) { for (const property of dataModel.Properties.filter( - (p) => p.IsGroupable && disableGroupingFor.every((elem) => elem !== p.Property?.ColumnName) + (p) => p.IsGroupable && disableGroupingFor.every((elem) => elem !== p.Property?.ColumnName), )) { groups.push({ property, - getData: async (parameters) => await getGroupInfo({ ...{ by: property.Property.ColumnName }, ...parameters }), + getData: async (parameters) => await getGroupInfo({ ...{ by: property.Property?.ColumnName }, ...parameters }), }); } } - const getGroupInfoGroups = (options: DataModelFilterOption[]) => - options.map((property) => ({ + const getGroupInfoGroups = (options?: DataModelFilterOption[]): DataSourceToolBarGroup[] => + options?.map((property) => ({ property: property as DataModelProperty & DataModelGroupInfo, getData: async (parameters) => { const original = await getGroupInfo({ ...{ def: property.Value }, ...parameters }); - const groupDisplay = original.Groups.map((item) => { - item.Display.forEach((display) => - item.Filters.forEach((filter) => { + const groupDisplay = original.Groups?.map((item) => { + item.Display?.forEach((display) => + item.Filters?.forEach((filter) => { if (filter.Value1 != null) { - display.Display = display.Display.replace(`%${filter.ColumnName}%`, filter.Value1); + display.Display = display.Display?.replace(`%${filter.ColumnName}%`, filter.Value1); } - }) + }), ); return item; }); return { Groups: groupDisplay, TotalCount: original.TotalCount }; }, - })); + })) || []; if (dataModel.GroupInfo?.length === 1) { - getGroupInfoGroups(dataModel.GroupInfo[0].Options).forEach((group) => groups.push(group)); + getGroupInfoGroups(dataModel.GroupInfo?.[0].Options)?.forEach((group) => groups.push(group)); } else { dataModel.GroupInfo?.forEach((dataModelGroupInfo) => groupingCategories.push({ property: dataModelGroupInfo, - groups: getGroupInfoGroups(dataModelGroupInfo.Options), - }) + groups: getGroupInfoGroups(dataModelGroupInfo?.Options), + }), ); } @@ -87,5 +87,5 @@ export function createGroupData( return { groups, groupingCategories }; } - return undefined; + return {}; } diff --git a/imxweb/projects/att/src/lib/decision/approvers.interface.ts b/imxweb/projects/att/src/lib/decision/approvers.interface.ts index fb048cf2f..df086c2f9 100644 --- a/imxweb/projects/att/src/lib/decision/approvers.interface.ts +++ b/imxweb/projects/att/src/lib/decision/approvers.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,10 +24,10 @@ * */ - -import { EntityData } from 'imx-qbm-dbts'; +import { EntityData } from '@imx-modules/imx-qbm-dbts'; export interface Approvers { current: EntityData[]; future: EntityData[]; + canSeeSteps: boolean; } diff --git a/imxweb/projects/att/src/lib/decision/approvers/approvers.component.html b/imxweb/projects/att/src/lib/decision/approvers/approvers.component.html index 14e16ae81..cbbe73f2a 100644 --- a/imxweb/projects/att/src/lib/decision/approvers/approvers.component.html +++ b/imxweb/projects/att/src/lib/decision/approvers/approvers.component.html @@ -1,36 +1,38 @@ -
  • +
  • - {{'#LDS#The next approval step is currently being calculated.' | translate}} + {{ + (approvers.canSeeSteps + ? '#LDS#The next approval step is currently being calculated.' + : '#LDS#You do not have permission to see the next approval step.' + ) | translate + }}
  • -
  • +
  • - {{'#LDS#The following identities are currently entitled to approve this attestation case.' | translate}} + {{ '#LDS#The following identities are currently entitled to approve this attestation case.' | translate }}
    - {{ approver.Columns.UID_PersonHead.DisplayValue }} + {{ approver.Columns?.UID_PersonHead?.DisplayValue }}
  • -
  • +
  • - {{'#LDS#The following identities are entitled to approve this attestation case after the current workflow step.' | translate}} + {{ '#LDS#The following identities are entitled to approve this attestation case after the current workflow step.' | translate }}
    - {{ approver.Columns.UID_PersonHead.DisplayValue }} + {{ approver.Columns?.UID_PersonHead?.DisplayValue }}
    -
  • \ No newline at end of file + diff --git a/imxweb/projects/att/src/lib/decision/approvers/approvers.component.scss b/imxweb/projects/att/src/lib/decision/approvers/approvers.component.scss index 73de9af8b..bac5a01cd 100644 --- a/imxweb/projects/att/src/lib/decision/approvers/approvers.component.scss +++ b/imxweb/projects/att/src/lib/decision/approvers/approvers.component.scss @@ -15,7 +15,7 @@ li.imx-event::before { background-color: $color-gray-0; height: 24px; width: 28px; - content: ""; + content: ''; float: left; margin-top: -2px; } @@ -29,15 +29,14 @@ li.imx-event.imx-pending::before { font-family: Cadence-Icon; font-size: 24px; color: $color-gray-30; - content: "\e009"; + content: '\e009'; } -.mat-card-header { - margin-left: -16px; +.mat-mdc-card-header { margin-bottom: 10px; } -.mat-card-header .mat-card-title { +.mat-mdc-card-header .mat-mdc-card-title { font-size: 1.05em; margin-bottom: 0; } @@ -48,13 +47,12 @@ li.imx-event.imx-pending::before { .eui-dark-theme { :host { - li.imx-event::before{ + li.imx-event::before { background-color: $color-gray-70; } - li{ - .mat-card - { + li { + .mat-mdc-card { background-color: $color-gray-60; } } @@ -63,13 +61,12 @@ li.imx-event.imx-pending::before { .eui-contrast-theme { :host { - li.imx-event::before{ + li.imx-event::before { background-color: $color-gray-90; } - li{ - .mat-card - { + li { + .mat-mdc-card { background-color: $color-gray-80; } } diff --git a/imxweb/projects/att/src/lib/decision/approvers/approvers.component.ts b/imxweb/projects/att/src/lib/decision/approvers/approvers.component.ts index c762de8a3..857ac1d84 100644 --- a/imxweb/projects/att/src/lib/decision/approvers/approvers.component.ts +++ b/imxweb/projects/att/src/lib/decision/approvers/approvers.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,7 +31,7 @@ import { Approvers } from '../approvers.interface'; @Component({ selector: 'imx-approvers', templateUrl: './approvers.component.html', - styleUrls: ['./approvers.component.scss'] + styleUrls: ['./approvers.component.scss'], }) export class ApproversComponent { @Input() public approvers: Approvers; diff --git a/imxweb/projects/att/src/lib/decision/attestation-case.component.html b/imxweb/projects/att/src/lib/decision/attestation-case.component.html index e5721a072..39228badd 100644 --- a/imxweb/projects/att/src/lib/decision/attestation-case.component.html +++ b/imxweb/projects/att/src/lib/decision/attestation-case.component.html @@ -1,8 +1,8 @@ - +
    - + #LDS#Based on the peer group analysis, it is recommended that you approve this attestation case. @@ -23,7 +23,10 @@
    - +
    @@ -31,7 +34,11 @@
      - + +
    @@ -40,34 +47,40 @@ -
    +
    - -
    + +
    - +
    - -
    + +
    - +
    - +
    - + {{ '#LDS#Related objects' | translate }} @@ -75,7 +88,8 @@ - + +
    @@ -83,7 +97,7 @@
    - - - - @@ -183,11 +222,16 @@ - diff --git a/imxweb/projects/att/src/lib/decision/attestation-case.component.scss b/imxweb/projects/att/src/lib/decision/attestation-case.component.scss index 066678e2a..71d9822b8 100644 --- a/imxweb/projects/att/src/lib/decision/attestation-case.component.scss +++ b/imxweb/projects/att/src/lib/decision/attestation-case.component.scss @@ -1,43 +1,9 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; :host { display: flex; flex-direction: column; justify-content: space-between; - - ::ng-deep .mat-tab-body-wrapper { - flex: 1 1 auto; - - .mat-tab-body { - display: flex; - flex-direction: column; - flex: 1 1 auto; - } - } - - ::ng-deep .mat-tab-body-content { - display: flex; - flex-direction: column; - } - - .mat-expansion-panel { - margin-bottom: 10px; - } -} -.mat-tab-group { - overflow-y: auto; - height: 100%; -} - -.imx-mat-tab-container { - padding: 20px; -} - -.imx-mat-tab-container-full { - height: 100%; - flex: 1 1 auto; - display: flex; - flex-direction: column; } .imx-attestation-case-information, @@ -53,62 +19,10 @@ margin: 10px; } } -.imx-attestation-case-hyperview{ +.imx-attestation-case-hyperview { display: flex; flex-direction: column; } .imx-attestation-case-loss-preview { margin: 0 15px; } - -.imx-helper-alert { - width: 100%; - - ::ng-deep .eui-alert { - margin-bottom: 20px; - } -} - -.eui-sidesheet-actions { - button:not(.imx-justify-right) { - margin-left: 16px; - .mat-icon { - margin-right: 5px; - font-size: 24px; - } - } - .imx-justify-right - { - margin-right: auto; - } -} - -:host { - ::ng-deep .mat-tab-labels { - background-color: $color-gray-0; - } -} - -.eui-dark-theme { - :host { - ::ng-deep .mat-tab-labels { - background-color: $color-gray-80; - } - - .imx-button-bar { - background-color: $color-gray-70; - } - } -} - -.eui-contrast-theme { - :host { - ::ng-deep .mat-tab-labels { - background-color: $color-gray-100; - } - - .imx-button-bar { - background-color: $color-gray-90; - } - } -} diff --git a/imxweb/projects/att/src/lib/decision/attestation-case.component.ts b/imxweb/projects/att/src/lib/decision/attestation-case.component.ts index 6b10c4673..7ec8f444a 100644 --- a/imxweb/projects/att/src/lib/decision/attestation-case.component.ts +++ b/imxweb/projects/att/src/lib/decision/attestation-case.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,17 +25,18 @@ */ import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; -import { EuiDownloadOptions, EuiLoadingService, EuiSidesheetRef, EuiSidesheetService, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; -import { Subscription } from 'rxjs'; -import { MatTabChangeEvent } from '@angular/material/tabs'; import { MatDialog } from '@angular/material/dialog'; +import { MatTabChangeEvent } from '@angular/material/tabs'; +import { EUI_SIDESHEET_DATA, EuiDownloadOptions, EuiLoadingService, EuiSidesheetRef, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; +import { Subscription } from 'rxjs'; -import { DbObjectKey } from 'imx-qbm-dbts'; -import { AttestationRelatedObject, PortalAttestationCaseHistory } from 'imx-api-att'; +import { AttestationRelatedObject, PortalAttestationCaseHistory } from '@imx-modules/imx-api-att'; +import { DbObjectKey } from '@imx-modules/imx-qbm-dbts'; import { AuthenticationService, BaseReadonlyCdr, + calculateSidesheetWidth, ClassloggerService, ColumnDependentReference, MetadataService, @@ -50,9 +51,9 @@ import { TermsOfUseViewerComponent, } from 'qer'; import { AttestationActionService } from '../attestation-action/attestation-action.service'; +import { Approvers } from './approvers.interface'; import { AttestationCase } from './attestation-case'; import { AttestationCasesService } from './attestation-cases.service'; -import { Approvers } from './approvers.interface'; import { LossPreview } from './loss-preview.interface'; import { MitigatingControlsComponent } from './mitigating-controls/mitigating-controls.component'; @@ -131,7 +132,7 @@ export class AttestationCaseComponent implements OnDestroy, OnInit { this.reportDownload = this.attestationCasesService.getReportDownloadOptions(this.case); this.subscriptions$.push(this.attestationAction.applied.subscribe(() => this.sidesheetRef.close())); - this.subscriptions$.push(authentication.onSessionResponse.subscribe((sessionState) => (this.userUid = sessionState?.UserUid))); + this.subscriptions$.push(authentication.onSessionResponse.subscribe((sessionState) => (this.userUid = sessionState?.UserUid || ''))); } public async ngOnInit(): Promise { @@ -141,7 +142,7 @@ export class AttestationCaseComponent implements OnDestroy, OnInit { this.complianceTabTitle = await this.translate.get('#LDS#Heading Rule Violations').toPromise(); this.policyTabTitle = await this.translate.get('#LDS#Heading Policy Violations').toPromise(); const info = await this.systemInfoService.get(); - this.canAnalyzeRisk = info.PreProps.includes('RISKINDEX') && this.case.RiskIndex.value > 0; + this.canAnalyzeRisk = !!info.PreProps?.includes('RISKINDEX') && this.case.RiskIndex.value > 0; } finally { this.busyService.hide(overlay); } @@ -156,7 +157,7 @@ export class AttestationCaseComponent implements OnDestroy, OnInit { title: await this.translate.get('#LDS#Heading View Terms of Use').toPromise(), subTitle: this.case.GetEntity().GetDisplay(), padding: '0px', - width: '60%', + width: calculateSidesheetWidth(), icon: 'accesscertification', testId: 'attestation-view-terms-of-use', data: this.case.UID_QERTermsOfUse, @@ -169,7 +170,7 @@ export class AttestationCaseComponent implements OnDestroy, OnInit { title: await this.translate.get('#LDS#Heading Analyze Risk').toPromise(), subTitle: this.case.GetEntity().GetDisplay(), padding: '0px', - width: 'max(600px,60%)', + width: calculateSidesheetWidth(), data: { objectKey: key }, }); } @@ -223,7 +224,7 @@ export class AttestationCaseComponent implements OnDestroy, OnInit { title: await this.translate.get('#LDS#Heading View Assignment Analysis').toPromise(), subTitle: this.case.GetEntity().GetDisplay(), padding: '0px', - width: 'max(60%,600px)', + width: calculateSidesheetWidth(), disableClose: false, testId: 'attestation-history-details-assignment-analysis', data, @@ -233,21 +234,23 @@ export class AttestationCaseComponent implements OnDestroy, OnInit { public async setRelatedOptions(): Promise { this.relatedOptions = (await Promise.all( - this.data.case.data?.RelatedObjects.map(async (relatedObject) => { - const objectType = DbObjectKey.FromXml(relatedObject.ObjectKey); - if (!this.metadataService.tables[objectType.TableName]) { - await this.metadataService.updateNonExisting([objectType.TableName]); - } + this.data.case.data?.RelatedObjects?.map(async (relatedObject) => { + const objectType = DbObjectKey.FromXml(relatedObject.ObjectKey || ''); + await this.metadataService.updateNonExisting([objectType.TableName]); return { - ObjectKey: relatedObject.ObjectKey, - Display: `${relatedObject.Display} - ${this.metadataService.tables[objectType.TableName].DisplaySingular}`, + ObjectKey: relatedObject.ObjectKey || '', + Display: `${relatedObject.Display} - ${this.metadataService.tables[objectType.TableName]?.DisplaySingular}`, }; - }), + }) || [], )) || []; + if (this.relatedOptions?.length > 0) { + this.selectedOption = this.relatedOptions[0]; + this.setHyperviewObject(this.selectedOption); + } } public setHyperviewObject(selectedRelatedObject: AttestationRelatedObject): void { - const dbKey = DbObjectKey.FromXml(selectedRelatedObject.ObjectKey); + const dbKey = DbObjectKey.FromXml(selectedRelatedObject.ObjectKey || ''); this.selectedHyperviewType = dbKey.TableName; this.selectedHyperviewUID = dbKey.Keys.join(','); } diff --git a/imxweb/projects/att/src/lib/decision/attestation-case.spec.ts b/imxweb/projects/att/src/lib/decision/attestation-case.spec.ts index c06b94b87..0569926ff 100644 --- a/imxweb/projects/att/src/lib/decision/attestation-case.spec.ts +++ b/imxweb/projects/att/src/lib/decision/attestation-case.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,8 +24,8 @@ * */ -import { AttCaseDataRead, PortalAttestationApprove } from 'imx-api-att'; -import { IClientProperty, IEntity, IEntityColumn } from 'imx-qbm-dbts'; +import { AttCaseDataRead, PortalAttestationApprove } from '@imx-modules/imx-api-att'; +import { IClientProperty, IEntity, IEntityColumn } from '@imx-modules/imx-qbm-dbts'; import { AttestationCase } from './attestation-case'; describe('AttestationCase', () => { @@ -33,45 +33,44 @@ describe('AttestationCase', () => { return { GetMetadata: () => ({ CanEdit: () => true }), GetValue: () => value, - GetDisplayValue: () => '' + GetDisplayValue: () => '', } as IEntityColumn; } function createEntitySchema(columnNames) { const clientProperties = {}; - columnNames.forEach(name => - clientProperties[name] = {} as IClientProperty - ); + columnNames.forEach((name) => (clientProperties[name] = {} as IClientProperty)); return { Columns: clientProperties }; } function createEntity(columns: { [name: string]: IEntityColumn } = {}, key?) { return { GetDisplay: () => '', - GetColumn: name => columns[name] || createColumn(), + GetColumn: (name) => columns[name] || createColumn(), GetKeys: () => [key], - GetSchema: () => createEntitySchema(Object.keys(columns ?? {})) + GetSchema: () => createEntitySchema(Object.keys(columns ?? {})), } as IEntity; } [ { value: true, expected: 1 }, { value: false, expected: 0 }, - { value: undefined, expected: 0 } - ].forEach(testcase => - it('adds IsCrossFunctional to propertyInfo only if it is "true"', () => { - const entity = createEntity({ - IsCrossFunctional: createColumn(testcase.value), - UiText: createColumn('some ui text') - }); + { value: undefined, expected: 0 }, + ].forEach((testcase) => + it('adds IsCrossFunctional to propertyInfo only if it is "true"', () => { + const entity = createEntity({ + IsCrossFunctional: createColumn(testcase.value), + UiText: createColumn('some ui text'), + }); - const approval = new AttestationCase( - { GetEntity: () => entity } as PortalAttestationApprove, - false, // no chief approval - undefined, - {} as { index: number } & AttCaseDataRead - ); + const approval = new AttestationCase( + { GetEntity: () => entity } as PortalAttestationApprove, + false, // no chief approval + undefined, + {} as { index: number } & AttCaseDataRead, + ); - expect(approval.propertyInfo.length).toEqual(testcase.expected); - })); + expect(approval.propertyInfo.length).toEqual(testcase.expected); + }), + ); }); diff --git a/imxweb/projects/att/src/lib/decision/attestation-case.ts b/imxweb/projects/att/src/lib/decision/attestation-case.ts index 4b1fdf9f0..566db9c5b 100644 --- a/imxweb/projects/att/src/lib/decision/attestation-case.ts +++ b/imxweb/projects/att/src/lib/decision/attestation-case.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,10 +24,10 @@ * */ -import { AttCaseDataRead, AttestationCaseData, AttestationCaseUiData, PortalAttestationApprove } from 'imx-api-att'; -import { IEntity, IEntityColumn, TypedEntity } from 'imx-qbm-dbts'; -import { ParameterDataContainer, WorkflowDataWrapper } from 'qer'; +import { AttCaseDataRead, AttestationCaseData, AttestationCaseUiData, PortalAttestationApprove } from '@imx-modules/imx-api-att'; +import { IEntity, IEntityColumn, TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { BaseCdr, ColumnDependentReference } from 'qbm'; +import { ParameterDataContainer, WorkflowDataWrapper } from 'qer'; import { AttestationCaseAction } from '../attestation-action/attestation-case-action.interface'; export class AttestationCase extends PortalAttestationApprove implements AttestationCaseAction { @@ -41,18 +41,18 @@ export class AttestationCase extends PortalAttestationApprove implements Attesta return this.parameterDataContainer.columns; } public get canAskAQuestion(): boolean { - return this.data.CanAskForHelp; + return !!this.data?.CanAskForHelp; } public readonly propertyInfo: ColumnDependentReference[]; public readonly key: string; - public readonly data: AttestationCaseData; + public readonly data: AttestationCaseData | undefined; public readonly typedEntity: TypedEntity; // from interface AttestationAction public readonly propertiesForAction: IEntityColumn[]; // from interface AttestationAction - public readonly uiData: AttestationCaseUiData; + public readonly uiData: AttestationCaseUiData | undefined; - public get hasPolicyViolation() { - return this.data?.PolicyViolations?.length > 0; + public get hasPolicyViolation(): boolean { + return !!this.data?.PolicyViolations?.length; } private directDecisionTarget = 0; @@ -62,7 +62,7 @@ export class AttestationCase extends PortalAttestationApprove implements Attesta private readonly baseObject: PortalAttestationApprove, private readonly isChiefApproval: boolean, private readonly parameterDataContainer: ParameterDataContainer, - extendedCollectionData: { index: number } & AttCaseDataRead + extendedCollectionData: { index: number } & AttCaseDataRead, ) { super(baseObject.GetEntity()); @@ -92,11 +92,13 @@ export class AttestationCase extends PortalAttestationApprove implements Attesta .filter((property) => property.value != null && property.value !== '') .map((property) => new BaseCdr(property.Column, extendedCollectionData[property.Column.ColumnName])); - this.data = extendedCollectionData.Data ? extendedCollectionData.Data[extendedCollectionData.index] : undefined; + this.data = extendedCollectionData.Data + ? { ...extendedCollectionData.Data[extendedCollectionData.index], WorkflowSteps: extendedCollectionData.WorkflowSteps } + : undefined; this.uiData = extendedCollectionData.UiData ? extendedCollectionData.UiData[extendedCollectionData.index] : undefined; if (this.data) { - this.workflowWrapper = new WorkflowDataWrapper(this.data); + this.workflowWrapper = new WorkflowDataWrapper({ ...this.data, WorkflowSteps: extendedCollectionData.WorkflowSteps }); } } @@ -106,12 +108,12 @@ export class AttestationCase extends PortalAttestationApprove implements Attesta public async commit(reload = true): Promise { this.baseObject.extendedData = this.parameterDataContainer.getEntityWriteDataColumns(); - if (this.baseObject.extendedData.DialogParameter[0].length > 0 || this.baseObject.GetEntity().GetDiffData().Data.length > 0) { + if (this.baseObject.extendedData.DialogParameter[0].length > 0 || !!this.baseObject.GetEntity().GetDiffData().Data?.length) { try { await this.baseObject.GetEntity().Commit(reload); } catch (error) { await this.baseObject.GetEntity().DiscardChanges(); - this.baseObject.extendedData = undefined; + this.baseObject.extendedData = {}; throw error; } } diff --git a/imxweb/projects/att/src/lib/decision/attestation-cases.service.ts b/imxweb/projects/att/src/lib/decision/attestation-cases.service.ts index 3f7d4a8f3..2a9b5dfe5 100644 --- a/imxweb/projects/att/src/lib/decision/attestation-cases.service.ts +++ b/imxweb/projects/att/src/lib/decision/attestation-cases.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,20 +27,6 @@ import { Injectable } from '@angular/core'; import { EuiDownloadOptions } from '@elemental-ui/core'; -import { - CollectionLoadParameters, - EntityCollectionData, - GroupInfoData, - DataModel, - MethodDefinition, - TypedEntity, - TypedEntityBuilder, - TypedEntityCollectionData, - EntitySchema, - IReadValue, - FilterTreeData, - MethodDescriptor, -} from 'imx-qbm-dbts'; import { ApiClientMethodFactory, AttestationCaseData, @@ -54,14 +40,29 @@ import { PwoQueryInput, ReasonInput, V2ApiClientMethodFactory, -} from 'imx-api-att'; +} from '@imx-modules/imx-api-att'; +import { + CollectionLoadParameters, + DataModel, + EntityCollectionData, + EntitySchema, + FilterTreeData, + GroupInfoData, + IReadValue, + MethodDefinition, + MethodDescriptor, + TypedEntity, + TypedEntityBuilder, + TypedEntityCollectionData, +} from '@imx-modules/imx-qbm-dbts'; +import { AppConfigService, DataSourceToolbarExportMethod, ElementalUiConfigService } from 'qbm'; +import { ApproverContainer, ParameterDataLoadParameters, ParameterDataService } from 'qer'; import { ApiService } from '../api.service'; +import { AttestationCaseLoadParameters } from '../attestation-history/attestation-case-load-parameters.interface'; +import { AttestationHistoryCase } from '../attestation-history/attestation-history-case'; +import { Approvers } from './approvers.interface'; import { AttestationCase } from './attestation-case'; -import { ParameterDataService, ParameterDataLoadParameters, ApproverContainer } from 'qer'; -import { AppConfigService, DataSourceToolbarExportMethod, ElementalUiConfigService, ParameterizedTextComponent } from 'qbm'; import { AttestationDecisionLoadParameters } from './attestation-decision-load-parameters'; -import { Approvers } from './approvers.interface'; -import { AttestationCaseLoadParameters } from '../attestation-history/attestation-case-load-parameters.interface'; @Injectable({ providedIn: 'root', @@ -75,7 +76,7 @@ export class AttestationCasesService { private readonly attClient: ApiService, private readonly parameterDataService: ParameterDataService, private readonly elementalUiConfigService: ElementalUiConfigService, - private readonly config: AppConfigService + private readonly config: AppConfigService, ) {} public get attestationApproveSchema(): EntitySchema { @@ -86,11 +87,18 @@ export class AttestationCasesService { return this.attClient.typedClient.PortalAttestationCase.GetSchema(); } - public async get(attDecisionParameters?: AttestationDecisionLoadParameters, isUserEscalationApprover= false): Promise> { + public async get( + attDecisionParameters?: AttestationDecisionLoadParameters, + isUserEscalationApprover = false, + signal?: AbortSignal, + ): Promise> { + const navigationState = { + ...attDecisionParameters, + Escalation: + ((attDecisionParameters?.uid_attestationcase ?? '') !== '' && isUserEscalationApprover) || attDecisionParameters?.Escalation, + }; - const navigationState = { ...attDecisionParameters, Escalation: (attDecisionParameters.uid_attestationcase !== '' && isUserEscalationApprover) || attDecisionParameters.Escalation }; - - const collection = await this.attClient.typedClient.PortalAttestationApprove.Get(navigationState); + const collection = await this.attClient.typedClient.PortalAttestationApprove.Get(navigationState, { signal }); return { tableName: collection.tableName, totalCount: collection.totalCount, @@ -99,7 +107,7 @@ export class AttestationCasesService { item.GetEntity(), { ...collection.extendedData, ...{ index } }, (parameters) => this.getParameterCandidates(parameters), - (treefilterparameter) => this.getFilterTree(treefilterparameter) + (treefilterparameter) => this.getFilterTree(treefilterparameter), ); return new AttestationCase(item, this.isChiefApproval, parameterDataContainer, { ...collection.extendedData, ...{ index } }); @@ -113,13 +121,13 @@ export class AttestationCasesService { getMethod: (withProperties: string, PageSize?: number) => { let method: MethodDescriptor; if (PageSize) { - method = factory.portal_attestation_approve_get({...attDecisionParameters, withProperties, PageSize, StartIndex: 0}) + method = factory.portal_attestation_approve_get({ ...attDecisionParameters, withProperties, PageSize, StartIndex: 0 }); } else { - method = factory.portal_attestation_approve_get({...attDecisionParameters, withProperties}) + method = factory.portal_attestation_approve_get({ ...attDecisionParameters, withProperties }); } return new MethodDefinition(method); - } - } + }, + }; } public async getNumberOfPending(parameters: AttestationCaseLoadParameters): Promise { @@ -141,31 +149,41 @@ export class AttestationCasesService { } public async getApprovers( - attestationCase: TypedEntity & { - DecisionLevel: IReadValue; - UID_QERWorkingMethod: IReadValue; - data: AttestationCaseData; - } + attestationCase: + | (TypedEntity & { + DecisionLevel: IReadValue; + UID_QERWorkingMethod: IReadValue; + data: AttestationCaseData; + }) + | AttestationHistoryCase + | AttestationCase, ): Promise { + const pwoData = { + WorkflowHistory: attestationCase.data?.WorkflowHistory, + WorkflowData: attestationCase.data?.WorkflowData, + WorkflowSteps: attestationCase.data?.WorkflowSteps, + }; const approverContainer = new ApproverContainer({ decisionLevel: attestationCase.DecisionLevel.value, qerWorkingMethod: attestationCase.UID_QERWorkingMethod.value, - pwoData: attestationCase.data, - approvers: (await this.attClient.client.portal_attestation_persondecision_get(this.getKey(attestationCase))).Entities.map( - (item) => item.Columns.UID_Person.Value - ), + pwoData, + approvers: + (await this.attClient.client.portal_attestation_persondecision_get(this.getKey(attestationCase))).Entities?.map( + (item) => item.Columns?.UID_Person.Value, + ) || [], }); return { current: approverContainer.approverNow, future: approverContainer.approverFuture, + canSeeSteps: approverContainer.canSeeSteps, }; } - public createHistoryTypedEntities(data: AttestationCaseData): TypedEntityCollectionData { + public createHistoryTypedEntities(data?: AttestationCaseData): TypedEntityCollectionData { return this.historyBuilder.buildReadWriteEntities( - data.WorkflowHistory, - this.attClient.typedClient.PortalAttestationCaseHistory.GetSchema() + data?.WorkflowHistory || { TotalCount: 0 }, + this.attClient.typedClient.PortalAttestationCaseHistory.GetSchema(), ); } @@ -295,9 +313,9 @@ export class AttestationCasesService { }; return this.attClient.client.portal_attestation_approve_parameter_candidates_post( parameters.columnName, - parameters.fkTableName, - parameters.diffData, - parameter + parameters.fkTableName || '', + parameters.diffData || {}, + parameter, ); } @@ -308,9 +326,9 @@ export class AttestationCasesService { }; return this.attClient.client.portal_attestation_approve_parameter_candidates_filtertree_post( parameters.columnName, - parameters.fkTableName, - parameters.diffData, - parameter + parameters.fkTableName || '', + parameters.diffData || {}, + parameter, ); } } diff --git a/imxweb/projects/att/src/lib/decision/attestation-decision-load-parameters.ts b/imxweb/projects/att/src/lib/decision/attestation-decision-load-parameters.ts index 5e5ce769e..48eb6782d 100644 --- a/imxweb/projects/att/src/lib/decision/attestation-decision-load-parameters.ts +++ b/imxweb/projects/att/src/lib/decision/attestation-decision-load-parameters.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { CollectionLoadParameters } from 'imx-qbm-dbts'; +import { CollectionLoadParameters } from '@imx-modules/imx-qbm-dbts'; export interface AttestationDecisionLoadParameters extends CollectionLoadParameters { Escalation?: boolean; @@ -38,5 +38,5 @@ export enum AttestationDecisionAction { approve, deny, denydecision, - showcase + showcase, } diff --git a/imxweb/projects/att/src/lib/decision/attestation-decision.component.html b/imxweb/projects/att/src/lib/decision/attestation-decision.component.html index e252c7fd4..02a25f215 100644 --- a/imxweb/projects/att/src/lib/decision/attestation-decision.component.html +++ b/imxweb/projects/att/src/lib/decision/attestation-decision.component.html @@ -1,10 +1,18 @@ -

    - {{ '#LDS#Heading Pending Attestations' | translate }} - -

    +
    +

    + {{ '#LDS#Heading Pending Attestations' | translate }} + +

    + +
    - + @@ -24,132 +32,130 @@

    - -
    - - {{ '#LDS#Show attestation cases to be approved by chief approval team' | translate }} - -
    - + - -
    - - - - - - - - -
    -
    - - {{ '#LDS#Policy violation' | translate }} -
    -
    - add_circle - {{ '#LDS#New' | translate }} -
    -
    - - {{ item.RiskIndex.Column.GetDisplayValue() }} -
    -
    - - {{ '#LDS#Overdue' | translate }} -
    -
    - - {{ '#LDS#Rule violation' | translate }} -
    -
    - - {{ '#LDS#Reserved' | translate }} -
    -
    -
    -
    - - -
    - - -
    -
    -
    - - -
    - -
    -
    -
    -
    -
    - -
    + {{ '#LDS#Show attestation cases to be approved by chief approval team' | translate }} + +

    + + + + {{ entitySchema?.Columns?.UiText?.Display }} + + + + + + + + +
    +
    + + {{ '#LDS#Policy violation' | translate }} +
    +
    + add_circle + {{ '#LDS#New' | translate }} +
    +
    + + {{ item.RiskIndex.Column.GetDisplayValue() }} +
    +
    + + {{ '#LDS#Overdue' | translate }} +
    +
    + + {{ '#LDS#Rule violation' | translate }} +
    +
    + + {{ '#LDS#Reserved' | translate }} +
    +
    + +
    + + + +
    + + +
    + +
    + + + +
    + +
    + +
    +
    + -
    - -
    +
    + + + + + diff --git a/imxweb/projects/att/src/lib/decision/attestation-decision.component.scss b/imxweb/projects/att/src/lib/decision/attestation-decision.component.scss index aa00c1cd3..e2d2ac0b0 100644 --- a/imxweb/projects/att/src/lib/decision/attestation-decision.component.scss +++ b/imxweb/projects/att/src/lib/decision/attestation-decision.component.scss @@ -1,5 +1,6 @@ @import '@elemental-ui/core/src/styles/_eui_palette.scss'; -@import '../../../../../shared/scss/common-table.scss'; +@import 'base/mixins'; + :host { display: flex; flex-direction: column; @@ -14,51 +15,27 @@ overflow: hidden; } - .hidden { - display: none; - } - - .mat-tab-group { - height: 100%; - overflow: hidden; - - ::ng-deep .mat-tab-body-wrapper { - flex: 1 1 auto; - - .mat-tab-body-content { - height: 100%; - overflow: hidden; - display: flex; - flex-direction: column; - } - } - } - - .mat-stroked-button, - .mat-raised-button { - @include imx-icon-for-image-button(); - } - - .imx-table-container { - @include imx-flex-fill-control-hidden-overflow(); + .mat-mdc-outlined-button, + .mat-mdc-unelevated-button { + @include image-button-icon(); } > * { margin-right: 16px; } +} - .mat-icon { - margin-right: 5px; - font-size: 24px; - } +.hidden { + display: none; } + .imx-attestationcase-table { flex-grow: 1; overflow: auto; } .imx-decision { - @include imx-button-column-right(); + @include button-column-right(); > button:not(:last-of-type) { margin-right: 5px; } @@ -71,51 +48,6 @@ justify-content: center; } -.imx-button-bar { - @include imx-button-bar(); -} - -.imx-icons-container { - padding-right: 15px; - .table-icon { - display: flex; - align-items: center; - color: $color-gray-60; - font-weight: 600; - height: 14px; - &--warning { - .eui-icon { - color: $color-orange-60; - } - } - &--new { - .mat-icon { - color: $color-green-60; - } - } - &--info { - .mat-icon { - color: $color-gray-60; - } - } - &--alert { - .eui-icon, - span { - color: $color-red-60; - } - } - &:not(:first-of-type) { - margin-left: 8px; - border-left: 1px solid $color-gray-20; - padding-left: 8px; - } - .mat-icon, - .eui-icon { - padding-right: 6px; - } - } -} - .toggle-wrapper { margin-bottom: 1em; } @@ -125,82 +57,3 @@ display: none; } } - -// Theming -:host { - ::ng-deep .mat-tab-labels { - background-color: $color-gray-0; - } - - .recommendation-approve-icon { - color: $color-green-60; - } - - .recommendation-deny-icon { - color: $color-red-60; - } -} - -.eui-dark-theme { - :host { - ::ng-deep .mat-tab-labels { - background-color: $color-gray-80; - } - - .recommendation-approve-icon { - color: $color-green-40; - } - - .recommendation-deny-icon { - color: $color-red-40; - } - .imx-icons-container { - .table-icon { - color: $color-gray-10; - &--warning { - .eui-icon { - color: $color-orange-40; - } - } - &--new { - .mat-icon { - color: $color-green-40; - } - } - &--info { - .mat-icon { - color: $color-gray-10; - } - } - &--alert { - .eui-icon, - span { - color: $color-red-40; - } - } - } - } - } -} - -.eui-contrast-theme { - :host { - ::ng-deep .mat-tab-labels { - background-color: $color-gray-100; - } - - .recommendation-approve-icon { - color: $color-green-40; - } - - .recommendation-deny-icon { - color: $color-red-40; - } - - .imx-icons-container { - .table-icon { - color: inherit; - } - } - } -} diff --git a/imxweb/projects/att/src/lib/decision/attestation-decision.component.ts b/imxweb/projects/att/src/lib/decision/attestation-decision.component.ts index 0c8d00108..fa588bf9c 100644 --- a/imxweb/projects/att/src/lib/decision/attestation-decision.component.ts +++ b/imxweb/projects/att/src/lib/decision/attestation-decision.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,53 +24,61 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { ActivatedRoute } from '@angular/router'; import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; +import { TranslateService } from '@ngx-translate/core'; import { Subscription } from 'rxjs'; import { first } from 'rxjs/operators'; -import { TranslateService } from '@ngx-translate/core'; -import { CollectionLoadParameters, CompareOperator, DataModel, FilterType, TypedEntity, ValType } from 'imx-qbm-dbts'; +import { EntitlementLossDto, RecommendationEnum } from '@imx-modules/imx-api-att'; +import { ViewConfigData } from '@imx-modules/imx-api-qer'; +import { + CollectionLoadParameters, + CompareOperator, + DataModel, + EntitySchema, + FilterType, + TypedEntity, + TypedEntityCollectionData, + ValType, +} from '@imx-modules/imx-qbm-dbts'; import { AuthenticationService, BusyService, - DataSourceToolbarFilter, - DataSourceToolbarGroupData, - DataSourceToolbarSettings, DataSourceToolbarViewConfig, DataTableComponent, - DataTableGroupedData, + DataViewInitParameters, + DataViewSource, MessageDialogComponent, - SettingsService, + QueuedActionState, UserMessageService, + calculateSidesheetWidth, + isMobile, } from 'qbm'; -import { AttestationCasesService } from './attestation-cases.service'; -import { AttestationCaseComponent } from './attestation-case.component'; +import { PendingItemsType, RecommendationSidesheetComponent, UserModelService, ViewConfigService } from 'qer'; +import { ApiService } from '../api.service'; import { AttestationActionService } from '../attestation-action/attestation-action.service'; -import { AttestationCase } from './attestation-case'; +import { AttestationFeatureGuardService } from '../attestation-feature-guard.service'; import { Approvers } from './approvers.interface'; +import { AttestationCase } from './attestation-case'; +import { AttestationCaseComponent } from './attestation-case.component'; +import { AttestationCasesService } from './attestation-cases.service'; import { AttestationDecisionAction, AttestationDecisionLoadParameters } from './attestation-decision-load-parameters'; -import { ApiService } from '../api.service'; -import { createGroupData } from '../datamodel/datamodel-helper'; -import { AttestationFeatureGuardService } from '../attestation-feature-guard.service'; -import { EntitlementLossDto, RecommendationEnum } from 'imx-api-att'; import { LossPreviewDialogComponent } from './loss-preview-dialog/loss-preview-dialog.component'; import { LossPreview } from './loss-preview.interface'; -import { PendingItemsType, RecommendationSidesheetComponent, UserModelService, ViewConfigService } from 'qer'; -import { ViewConfigData } from 'imx-api-qer'; @Component({ templateUrl: './attestation-decision.component.html', styleUrls: ['./attestation-decision.component.scss'], + providers: [DataViewSource], }) export class AttestationDecisionComponent implements OnInit, OnDestroy { - public dstSettings: DataSourceToolbarSettings; + public stateOptions = QueuedActionState; public selectedCases: AttestationCase[] = []; public userUid: string; - public hideToolbar:boolean = false; + public hideToolbar: boolean = false; public recApprove = RecommendationEnum.Approve; public recDeny = RecommendationEnum.Deny; @@ -90,35 +98,50 @@ export class AttestationDecisionComponent implements OnInit, OnDestroy { public get canDenyApproval(): boolean { return this.selectedCases.every((item) => item.canDenyApproval(this.userUid)); } - public get canEscalateDecisions(): boolean { + public get canEscalateDecision(): boolean { return this.selectedCases.every((item) => item.canEscalateDecision(this.userUid)); } + public get canSendInquiry(): boolean { + return this.selectedCases.every((item) => item.canAskAQuestion); + } + public get canRecallInquiry(): boolean { + return this.selectedCases.every((item) => item.IsReserved.value && item.hasAskedLastQuestion(this.userUid)); + } + public get canCancelReservation(): boolean { + return this.selectedCases.every( + (item) => item.IsReserved.value && (item.hasAskedLastQuestion(this.userUid) || this.isUserEscalationApprover), + ); + } public get canPerformActions(): boolean { return ( this.selectedCases.length > 0 && - (this.canWithdrawAddApprover || this.canAddApprover || this.canDelegateDecision || this.canDenyApproval || this.canReRouteDecision) + (this.canWithdrawAddApprover || + this.canAddApprover || + this.canDelegateDecision || + this.canDenyApproval || + this.canReRouteDecision || + this.canEscalateDecision || + this.canRecallInquiry || + this.canSendInquiry || + this.canCancelReservation) ); } public isUserEscalationApprover = false; public mitigatingControlsPerViolation: boolean; - public groupedData: { [key: string]: DataTableGroupedData } = {}; public allLossPreviewItems: EntitlementLossDto[]; public lossPreview: LossPreview; public hasInquiries: boolean; public tabIndex = 0; public busyService = new BusyService(); - public entitySchema = this.attestationCases.attestationApproveSchema; + public entitySchema: EntitySchema; - private approvalThreshold: number; + private approvalThreshold: number | undefined; private autoRemovalScope: boolean; - private navigationState: AttestationDecisionLoadParameters; - private filterOptions: DataSourceToolbarFilter[] = []; private dataModel: DataModel; - private groupData: DataSourceToolbarGroupData; private decisionAction: AttestationDecisionAction = AttestationDecisionAction.none; - private readonly collectionLoadParameters; + private additionalParameter: { uid_attestationhelper?: string; uid_attestationcase?: string } = {}; private readonly subscriptions: Subscription[] = []; @ViewChild(DataTableComponent) private readonly table: DataTableComponent; @@ -139,22 +162,21 @@ export class AttestationDecisionComponent implements OnInit, OnDestroy { private viewConfigService: ViewConfigService, private dialog: MatDialog, private readonly usermodelService: UserModelService, - settingsService: SettingsService, - authentication: AuthenticationService + authentication: AuthenticationService, + public dataSource: DataViewSource, ) { - this.collectionLoadParameters = { PageSize: settingsService.DefaultPageSize, StartIndex: 0 }; - this.navigationState = this.collectionLoadParameters; + this.entitySchema = this.attestationCases.attestationApproveSchema; this.subscriptions.push( this.attestationAction.applied.subscribe(() => { - this.getData(); - this.table?.clearSelection(); - }) + this.dataSource.updateState(); + this.dataSource.selection.clear(); + }), ); this.subscriptions.push( authentication.onSessionResponse.subscribe((sessionState) => { - this.userUid = sessionState?.UserUid; + this.userUid = sessionState?.UserUid || ''; this.attestationCases.isChiefApproval = false; - }) + }), ); this.attFeatureService.getAttestationConfig().then((config) => { @@ -163,10 +185,6 @@ export class AttestationDecisionComponent implements OnInit, OnDestroy { }); } - get isMobile(): boolean { - return document.body.offsetWidth <= 768; - } - public get viewEscalation(): boolean { return this.attestationCases.isChiefApproval; } @@ -174,18 +192,12 @@ export class AttestationDecisionComponent implements OnInit, OnDestroy { this.attestationCases.isChiefApproval = val; // reload data model for changed filter options when the user toggles escalation mode - this.initDataModel(); - } - - public switchEscalation(): Promise { - return this.getData(); + this.getData(); } public async ngOnInit(): Promise { - let isBusy: any; - setTimeout(() => { - isBusy = this.busyService.beginBusy(); - }); + const isBusy = this.busyService.beginBusy(); + try { const config = await this.attService.client.portal_attestation_config_get(); const pendingItems: PendingItemsType = await this.usermodelService.getPendingItems(); @@ -202,19 +214,16 @@ export class AttestationDecisionComponent implements OnInit, OnDestroy { Person: '#LDS#Affected identity', }, }; - if (params.inquiries) { + if (params?.inquiries) { this.tabIndex = 1; this.hasInquiries = true; } - await this.initDataModel(true); await this.parseParams(); await this.getData(); this.handleDecision(); } finally { - setTimeout(() => { - isBusy.endBusy(); - }); + isBusy.endBusy(); } } @@ -225,13 +234,13 @@ export class AttestationDecisionComponent implements OnInit, OnDestroy { public async updateConfig(config: ViewConfigData): Promise { await this.viewConfigService.putViewConfig(config); this.viewConfig = await this.viewConfigService.getDSTExtensionChanges(this.viewConfigPath); - this.dstSettings.viewConfig = this.viewConfig; + this.dataSource.viewConfig.set(this.viewConfig); } public async deleteConfigById(id: string): Promise { await this.viewConfigService.deleteViewConfig(id); this.viewConfig = await this.viewConfigService.getDSTExtensionChanges(this.viewConfigPath); - this.dstSettings.viewConfig = this.viewConfig; + this.dataSource.viewConfig.set(this.viewConfig); } public isNewLoss(loss: EntitlementLossDto): boolean { @@ -244,8 +253,9 @@ export class AttestationDecisionComponent implements OnInit, OnDestroy { this.attestationAction[func](cases); return; } - let busyIndicator: OverlayRef; - setTimeout(() => (busyIndicator = this.busyServiceElemental.show())); + if (this.busyServiceElemental.overlayRefs.length === 0) { + this.busyServiceElemental.show(); + } try { // Accumulate all losses this.allLossPreviewItems = []; @@ -257,10 +267,10 @@ export class AttestationDecisionComponent implements OnInit, OnDestroy { this.allLossPreviewItems.push(loss); } }); - }) + }), ); } finally { - setTimeout(() => this.busyServiceElemental.hide(busyIndicator)); + this.busyServiceElemental.hide(); } if (this.allLossPreviewItems.length === 0) { // There are no losses, go ahead with handle @@ -271,8 +281,8 @@ export class AttestationDecisionComponent implements OnInit, OnDestroy { this.lossPreview.LossPreviewItems = this.allLossPreviewItems; const selection = await this.dialog .open(LossPreviewDialogComponent, { - width: this.isMobile ? '90vw' : '60vw', - maxWidth: this.isMobile ? '90vw' : '80vw', + width: isMobile() ? '90vw' : '60vw', + maxWidth: isMobile() ? '90vw' : '80vw', height: '70vh', maxHeight: '70vh', data: this.lossPreview, @@ -286,76 +296,63 @@ export class AttestationDecisionComponent implements OnInit, OnDestroy { } } - public async search(search: string): Promise { - return this.getData({ ...this.navigationState, ...{ search } }); - } - - public async getData(newState?: CollectionLoadParameters): Promise { - if (newState) { - this.navigationState = newState; - } - - const isBusy = this.busyService.beginBusy(); - + public async getData(): Promise { + const displayedColumns = [ + this.entitySchema.Columns.UiText, + { + ColumnName: 'badges', + Type: ValType.String, + untranslatedDisplay: '#LDS#Badges', + }, + { + ColumnName: 'decision', + Type: ValType.String, + afterAdditionals: true, + untranslatedDisplay: '#LDS#Decision', + }, + { + ColumnName: 'recommendations', + Type: ValType.String, + afterAdditionals: true, + untranslatedDisplay: '#LDS#Recommendation', + }, + ]; + let busyIndicator = this.busyServiceElemental.show(); try { - const params: AttestationDecisionLoadParameters = { - Escalation: this.attestationCases.isChiefApproval, - ...this.navigationState, - }; - const dataSource = await this.attestationCases.get(params,this.isUserEscalationApprover); - const exportMethod = this.attestationCases.exportData(params); - this.dstSettings = { - dataSource, - entitySchema: this.entitySchema, - navigationState: this.navigationState, - filters: this.filterOptions, - dataModel: this.dataModel, - groupData: this.groupData, - displayedColumns: [ - this.entitySchema.Columns.UiText, - { - ColumnName: 'badges', - Type: ValType.String, - untranslatedDisplay: '#LDS#Badges', - }, - { - ColumnName: 'decision', - Type: ValType.String, - afterAdditionals: true, - untranslatedDisplay: '#LDS#Decision', - }, - { - ColumnName: 'recommendations', - Type: ValType.String, - afterAdditionals: true, - untranslatedDisplay: '#LDS#Recommendation', - }, - ], - exportMethod, - viewConfig: this.viewConfig, - }; + this.dataModel = await this.attestationCases.getDataModel(); + this.viewConfig = await this.viewConfigService.getInitialDSTExtension(this.dataModel, this.viewConfigPath); } finally { - isBusy.endBusy(); + this.busyServiceElemental.hide(busyIndicator); } - } - - public async onGroupingChange(groupKey: string): Promise { - const isBusy = this.busyService.beginBusy(); - try { - const groupedData = this.groupedData[groupKey]; - const navigationState = { ...groupedData.navigationState, Escalation: this.viewEscalation }; - groupedData.data = await this.attestationCases.get(navigationState,this.isUserEscalationApprover); - groupedData.settings = { - displayedColumns: this.dstSettings.displayedColumns, - dataModel: this.dstSettings.dataModel, - dataSource: groupedData.data, - entitySchema: this.dstSettings.entitySchema, - navigationState, - }; - } finally { - isBusy.endBusy(); - } + const dataViewInitParameters: DataViewInitParameters = { + execute: (params: CollectionLoadParameters, signal: AbortSignal): Promise> => { + const newParams: AttestationDecisionLoadParameters = { + Escalation: this.viewEscalation, + ...params, + }; + if (this.additionalParameter.uid_attestationcase) { + newParams.uid_attestationcase = this.additionalParameter.uid_attestationcase; + } + if (this.additionalParameter.uid_attestationhelper) { + newParams.uid_attestationhelper = this.additionalParameter.uid_attestationhelper; + } + return this.attestationCases.get(newParams, this.isUserEscalationApprover, signal); + }, + schema: this.entitySchema, + columnsToDisplay: displayedColumns, + dataModel: this.dataModel, + exportFunction: this.attestationCases.exportData(this.dataSource.state()), + viewConfig: this.viewConfig, + highlightEntity: (identity: AttestationCase) => { + this.edit(identity); + }, + groupExecute: (column: string, params: CollectionLoadParameters, signal: AbortSignal) => { + return this.attestationCases.getGroupInfo(this.getGroupParams(column, params)); + }, + selectionChange: (selection: AttestationCase[]) => this.onSelectionChanged(selection), + }; + await this.dataSource.init(dataViewInitParameters); } public onSelectionChanged(cases: AttestationCase[]): void { @@ -364,38 +361,44 @@ export class AttestationDecisionComponent implements OnInit, OnDestroy { public async edit(attestationCase: AttestationCase): Promise { let attestationCaseWithPolicy: AttestationCase; - let approvers: Approvers; - - let busyIndicator: OverlayRef; - setTimeout(() => (busyIndicator = this.busyServiceElemental.show())); + let approvers: Approvers | undefined; + if (this.busyServiceElemental.overlayRefs.length === 0) { + this.busyServiceElemental.show(); + } try { attestationCaseWithPolicy = ( - await this.attestationCases.get({ - Escalation: this.viewEscalation, - uidpolicy: attestationCase.UID_AttestationPolicy.value, - filter: [ - { - ColumnName: 'UID_AttestationCase', - Type: FilterType.Compare, - CompareOp: CompareOperator.Equal, - Value1: attestationCase.GetEntity().GetKeys()[0], - }, - ], - },this.isUserEscalationApprover) + await this.attestationCases.get( + { + Escalation: this.isUserEscalationApprover, + uidpolicy: attestationCase.UID_AttestationPolicy.value, + filter: [ + { + ColumnName: 'UID_AttestationCase', + Type: FilterType.Compare, + CompareOp: CompareOperator.Equal, + Value1: attestationCase.GetEntity().GetKeys()[0], + }, + ], + }, + this.isUserEscalationApprover, + ) ).Data[0]; // Add additional violation data to this case - attestationCaseWithPolicy.data.CanSeeComplianceViolations = attestationCase.data.CanSeeComplianceViolations; - attestationCaseWithPolicy.data.ComplianceViolations = attestationCase.data.ComplianceViolations; - attestationCaseWithPolicy.data.CanSeePolicyViolations = attestationCase.data.CanSeePolicyViolations; - attestationCaseWithPolicy.data.PolicyViolations = attestationCase.data.PolicyViolations; + if (attestationCaseWithPolicy?.data) { + attestationCaseWithPolicy.data.CanSeeComplianceViolations = !!attestationCase.data?.CanSeeComplianceViolations; + attestationCaseWithPolicy.data.ComplianceViolations = attestationCase.data?.ComplianceViolations || []; + attestationCaseWithPolicy.data.CanSeePolicyViolations = !!attestationCase.data?.CanSeePolicyViolations; + attestationCaseWithPolicy.data.PolicyViolations = attestationCase.data?.PolicyViolations || []; + attestationCaseWithPolicy.data.WorkflowSteps = attestationCase.data?.WorkflowSteps; + } if (attestationCaseWithPolicy && !['approved', 'denied'].includes(attestationCaseWithPolicy.AttestationState.value)) { approvers = await this.attestationCases.getApprovers(attestationCaseWithPolicy); } this.lossPreview.Case = attestationCase; } finally { - setTimeout(() => this.busyServiceElemental.hide(busyIndicator)); + this.busyServiceElemental.hide(); } if (attestationCaseWithPolicy) { @@ -403,7 +406,7 @@ export class AttestationDecisionComponent implements OnInit, OnDestroy { title: await this.translate.get('#LDS#Heading View Attestation Case Details').toPromise(), subTitle: attestationCaseWithPolicy.GetEntity().GetDisplay(), padding: '0px', - width: 'max(70%,768px)', + width: calculateSidesheetWidth(1000), testId: 'attestation-case-sidesheet', data: { case: attestationCaseWithPolicy, @@ -430,9 +433,17 @@ export class AttestationDecisionComponent implements OnInit, OnDestroy { subTitle: attestationCase.GetEntity().GetDisplay(), panelClass: 'imx-sidesheet', padding: '0', - width: 'max(700px, 60%)', + width: calculateSidesheetWidth(1000), testId: 'attestation-recommendation-sidesheet', - data: attestationCase.data.Recommendation, + data: { + recommendations: attestationCase.data?.Recommendation, + informationTexts: { + approve: '#LDS#Based on an analysis of currently available data, it is recommended that you approve this attestation case.', + reject: '#LDS#Based on an analysis of currently available data, it is recommended that you deny this attestation case.', + noRecord: + '#LDS#Based on an analysis of currently available data, no definitive recommendation can be made for this attestation case.', + }, + }, }) .afterClosed() .toPromise(); @@ -444,40 +455,12 @@ export class AttestationDecisionComponent implements OnInit, OnDestroy { } } - private async initDataModel(checkConfigs = false): Promise { - this.dataModel = await this.attestationCases.getDataModel(); - this.viewConfig = await this.viewConfigService.getInitialDSTExtension(this.dataModel, this.viewConfigPath); - if (checkConfigs) { - // We will check the configs for default state only on ini - if (!this.viewConfigService.isDefaultConfigSet()) { - // If we have no default settings, we will use the due date to sort - this.navigationState.OrderBy = 'ToSolveTill'; - } - } - - this.filterOptions = this.dataModel?.Filters ?? []; - - this.groupData = createGroupData( - this.dataModel, - (parameters) => - this.attestationCases.getGroupInfo({ - ...{ - PageSize: this.collectionLoadParameters.PageSize, - StartIndex: 0, - }, - ...parameters, - }), - [] - ); - } - private async parseParams(): Promise { - const queryParams = await this.activatedRoute.queryParams.pipe(first()).toPromise(); + const queryParams = (await this.activatedRoute.queryParams.pipe(first()).toPromise()) || []; // Cases: VI_BuildAttestationLink_Approve, VI_BuildAttestationLink_Deny, VI_BuildAttestationLink_Reject if (queryParams['uid_attestationhelper'] && queryParams['decision']) { - this.navigationState.uid_attestationhelper = queryParams['uid_attestationhelper']; - + this.additionalParameter.uid_attestationhelper = queryParams['uid_attestationhelper']; // Will otherwise result in a string this.decisionAction = AttestationDecisionAction[queryParams['decision'].toLowerCase()] as unknown as AttestationDecisionAction; return; @@ -485,13 +468,13 @@ export class AttestationDecisionComponent implements OnInit, OnDestroy { // Case: VI_BuildAttestationLink_Show if (queryParams['uid_attestationhelper']) { - this.navigationState.uid_attestationhelper = queryParams['uid_attestationhelper']; + this.additionalParameter.uid_attestationhelper = queryParams['uid_attestationhelper']; return; } // Case: VI_BuildAttestationLink_ViewDetails if (queryParams['uid_attestationcase']) { - this.navigationState.uid_attestationcase = queryParams['uid_attestationcase']; + this.additionalParameter.uid_attestationcase = queryParams['uid_attestationcase']; this.decisionAction = AttestationDecisionAction.showcase; this.hideToolbar = true; return; @@ -506,16 +489,14 @@ export class AttestationDecisionComponent implements OnInit, OnDestroy { return; } - if (this.dstSettings.dataSource.Data == null || this.dstSettings.dataSource.Data.length === 0) { + if (this.dataSource.data.length === 0) { const dialogRef = this.noCaseDialog.open(MessageDialogComponent, { data: { ShowOk: true, - Title: await this.translate.get('#LDS#Heading Cannot Find Attestation Case').toPromise(), - Message: await this.translate - .get( - '#LDS#The attestation case does not exist (anymore). To view all attestation cases, close this page and reopen the Pending Attestions page.' - ) - .toPromise(), + Title: await this.translate.instant('#LDS#Heading Cannot Find Attestation Case'), + Message: await this.translate.instant( + '#LDS#The attestation case does not exist (anymore). To view all attestation cases, close this page and reopen the Pending Attestions page.', + ), }, }); return; @@ -523,17 +504,25 @@ export class AttestationDecisionComponent implements OnInit, OnDestroy { switch (this.decisionAction) { case AttestationDecisionAction.approve: - this.attestationAction.approve(this.dstSettings.dataSource.Data as AttestationCase[]); + this.attestationAction.approve(this.dataSource.data); break; case AttestationDecisionAction.deny: - this.attestationAction.deny(this.dstSettings.dataSource.Data as AttestationCase[]); + this.attestationAction.deny(this.dataSource.data); break; case AttestationDecisionAction.denydecision: - this.attestationAction.denyDecisions(this.dstSettings.dataSource.Data as AttestationCase[]); + this.attestationAction.denyDecisions(this.dataSource.data); break; case AttestationDecisionAction.showcase: - this.edit(this.dstSettings.dataSource.Data[0] as AttestationCase); + this.edit(this.dataSource.data[0]); break; } } + + private getGroupParams(column: string, params: CollectionLoadParameters): { by?: string; def?: string } & CollectionLoadParameters { + if (this.dataModel.Properties?.find((property) => property.Property?.ColumnName === column)) { + return { ...params, by: column }; + } else { + return { ...params, def: column }; + } + } } diff --git a/imxweb/projects/att/src/lib/decision/attestation-decision.module.ts b/imxweb/projects/att/src/lib/decision/attestation-decision.module.ts index e274c5ea1..56056c8f3 100644 --- a/imxweb/projects/att/src/lib/decision/attestation-decision.module.ts +++ b/imxweb/projects/att/src/lib/decision/attestation-decision.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,31 +28,41 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; -import { MatExpansionModule } from '@angular/material/expansion'; import { MatCardModule } from '@angular/material/card'; +import { MatExpansionModule } from '@angular/material/expansion'; import { RouterModule } from '@angular/router'; import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; import { TranslateModule } from '@ngx-translate/core'; -import { DataSourceToolbarModule, DataTableModule, CdrModule, EntityModule, BulkPropertyEditorModule, DateModule, SelectedElementsModule, HelpContextualModule } from 'qbm'; -import { AttestationDecisionComponent } from './attestation-decision.component'; -import { AttestationCaseComponent } from './attestation-case.component'; -import { AttestationActionComponent } from '../attestation-action/attestation-action.component'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { + BulkPropertyEditorModule, + CdrModule, + DataSourceToolbarModule, + DataTableModule, + DataViewModule, + DateModule, + EntityModule, + HelpContextualModule, + SelectedElementsModule, +} from 'qbm'; import { JustificationModule, ObjectHyperviewModule, TermsOfUseModule } from 'qer'; -import { DecisionHistoryItemComponent } from './decision-history-item/decision-history-item.component'; -import { ApproversComponent } from './approvers/approvers.component'; -import { EntityPropertyEditorComponent } from '../entity-property-editor/entity-property-editor.component'; +import { AttestationActionComponent } from '../attestation-action/attestation-action.component'; import { AttestationDisplayModule } from '../attestation-display/attestation-display.module'; -import { MatTooltipModule } from '@angular/material/tooltip'; import { AttestationSnapshotModule } from '../attestation-snapshot/attestation-snapshot.module'; -import { LossPreviewDialogComponent } from './loss-preview-dialog/loss-preview-dialog.component'; -import { LossPreviewTableComponent } from './loss-preview-table/loss-preview-table.component'; +import { EntityPropertyEditorComponent } from '../entity-property-editor/entity-property-editor.component'; +import { ApproversComponent } from './approvers/approvers.component'; +import { AttestationCaseComponent } from './attestation-case.component'; +import { AttestationDecisionComponent } from './attestation-decision.component'; +import { AttestationInquiriesComponent } from './attestation-inquiries/attestation-inquiries.component'; import { DecisionComplianceViolationComponent } from './decision-compliance-violation/decision-compliance-violation.component'; +import { DecisionHistoryItemComponent } from './decision-history-item/decision-history-item.component'; import { DecisionPolicyViolationComponent } from './decision-policy-violation/decision-policy-violation.component'; +import { LossPreviewDialogComponent } from './loss-preview-dialog/loss-preview-dialog.component'; +import { LossPreviewTableComponent } from './loss-preview-table/loss-preview-table.component'; import { MitigatingControlsComponent } from './mitigating-controls/mitigating-controls.component'; -import { AttestationInquiriesComponent } from './attestation-inquiries/attestation-inquiries.component'; -import { MatDividerModule } from '@angular/material/divider'; -import { MatIconModule } from '@angular/material/icon'; @NgModule({ declarations: [ AttestationCaseComponent, @@ -66,7 +76,7 @@ import { MatIconModule } from '@angular/material/icon'; DecisionComplianceViolationComponent, DecisionPolicyViolationComponent, MitigatingControlsComponent, - AttestationInquiriesComponent + AttestationInquiriesComponent, ], imports: [ AttestationSnapshotModule, @@ -96,11 +106,9 @@ import { MatIconModule } from '@angular/material/icon'; SelectedElementsModule, MatIconModule, HelpContextualModule, - ObjectHyperviewModule + ObjectHyperviewModule, + DataViewModule, ], - exports: [ - DecisionHistoryItemComponent, - ApproversComponent - ] + exports: [DecisionHistoryItemComponent, ApproversComponent], }) -export class AttestationDecisionModule { } +export class AttestationDecisionModule {} diff --git a/imxweb/projects/att/src/lib/decision/attestation-inquiries/attestation-inquiries.component.html b/imxweb/projects/att/src/lib/decision/attestation-inquiries/attestation-inquiries.component.html index ba445dc1d..6144eed7c 100644 --- a/imxweb/projects/att/src/lib/decision/attestation-inquiries/attestation-inquiries.component.html +++ b/imxweb/projects/att/src/lib/decision/attestation-inquiries/attestation-inquiries.component.html @@ -1,53 +1,59 @@ - - + - - -
    - - - - - - - - - {{ getInquiryText(item) }} - - - - - {{ getInquirer(item) }} - - - - + [showGrouping]="false" + > + + + {{ entitySchema?.Columns?.UiText?.Display }} + + + + + + + {{ AttestationInquiry.queryCaption | translate }} + + +
    {{ getInquiryText(item) }}
    + +
    + + + {{ AttestationInquiry.headCaption | translate }} + + +
    {{ getInquirer(item) }}
    + +
    + + + {{ AttestationInquiry.queryDate | translate }} + + +
    {{ getQueryDate(item) | shortDate }} - - - - - -
    -
    + + + + + +
    + -
    -
    -
    - -
    - +
    + + + +
    diff --git a/imxweb/projects/att/src/lib/decision/attestation-inquiries/attestation-inquiries.component.scss b/imxweb/projects/att/src/lib/decision/attestation-inquiries/attestation-inquiries.component.scss index 2ac72973f..0e1751f10 100644 --- a/imxweb/projects/att/src/lib/decision/attestation-inquiries/attestation-inquiries.component.scss +++ b/imxweb/projects/att/src/lib/decision/attestation-inquiries/attestation-inquiries.component.scss @@ -1,28 +1,9 @@ -@import '../../../../../../shared/scss/common-table.scss'; - +@import 'base/mixins'; :host { - display: flex; - flex-direction: column; - overflow: hidden; + @include flex-column-container($overflow: hidden); flex-grow: 1; } -.imx-table-container { - flex-grow: 1; - overflow: auto; -} - -.imx-mat-card-container { - overflow: hidden; - display: flex; - flex-direction: column; - height: 100%; -} - -.imx-button-column { - @include imx-button-column-right(); -} - -.mat-stroked-button { - @include imx-icon-for-image-button(); -} +.mat-mdc-card.imx-card-fill{ + margin: 0 0 3px 0; +} \ No newline at end of file diff --git a/imxweb/projects/att/src/lib/decision/attestation-inquiries/attestation-inquiries.component.ts b/imxweb/projects/att/src/lib/decision/attestation-inquiries/attestation-inquiries.component.ts index af5474ef2..a9e8e4d7a 100644 --- a/imxweb/projects/att/src/lib/decision/attestation-inquiries/attestation-inquiries.component.ts +++ b/imxweb/projects/att/src/lib/decision/attestation-inquiries/attestation-inquiries.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,52 +24,51 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; -import { Subscription } from 'rxjs'; import { TranslateService } from '@ngx-translate/core'; +import { Subscription } from 'rxjs'; -import { PwoExtendedData, ViewConfigData } from 'imx-api-qer'; +import { PwoExtendedData, ViewConfigData } from '@imx-modules/imx-api-qer'; import { - ValType, - ExtendedTypedEntityCollection, - TypedEntity, - EntitySchema, - DataModel, CollectionLoadParameters, - FilterType, CompareOperator, -} from 'imx-qbm-dbts'; + DataModel, + EntitySchema, + ExtendedTypedEntityCollection, + FilterType, + TypedEntity, + TypedEntityCollectionData, + ValType, +} from '@imx-modules/imx-qbm-dbts'; import { - DataSourceToolbarSettings, - ClassloggerService, AuthenticationService, - DataTableComponent, - SettingsService, - SnackBarService, - UserMessageService, - ClientPropertyForTableColumns, BusyService, + calculateSidesheetWidth, + ClientPropertyForTableColumns, DataSourceToolbarViewConfig, + DataTableComponent, + DataViewInitParameters, + DataViewSource, + UserMessageService, } from 'qbm'; +import { ViewConfigService } from 'qer'; +import { AttestationActionService } from '../../attestation-action/attestation-action.service'; +import { AttestationFeatureGuardService } from '../../attestation-feature-guard.service'; +import { Approvers } from '../approvers.interface'; import { AttestationCase } from '../attestation-case'; import { AttestationCaseComponent } from '../attestation-case.component'; import { AttestationCasesService } from '../attestation-cases.service'; -import { AttestationActionService } from '../../attestation-action/attestation-action.service'; -import { Approvers } from '../approvers.interface'; -import { AttestationFeatureGuardService } from '../../attestation-feature-guard.service'; import { LossPreview } from '../loss-preview.interface'; import { AttestationInquiry } from './attestation-inquiry.model'; -import { ViewConfigService } from 'qer'; @Component({ templateUrl: './attestation-inquiries.component.html', selector: 'imx-attestation-inquiries', styleUrls: ['./attestation-inquiries.component.scss'], + providers: [DataViewSource], }) export class AttestationInquiriesComponent implements OnInit, OnDestroy { - public dstSettings: DataSourceToolbarSettings; public readonly entitySchema: EntitySchema; public attestationCasesCollection: ExtendedTypedEntityCollection; public hasData = false; @@ -77,8 +76,6 @@ export class AttestationInquiriesComponent implements OnInit, OnDestroy { public mitigatingControlsPerViolation: boolean; @ViewChild(DataTableComponent) private readonly table: DataTableComponent; public lossPreview: LossPreview; - - private navigationState: CollectionLoadParameters; private displayedColumns: ClientPropertyForTableColumns[]; private readonly subscriptions: Subscription[] = []; private dataModel: DataModel; @@ -95,14 +92,11 @@ export class AttestationInquiriesComponent implements OnInit, OnDestroy { private viewConfigService: ViewConfigService, private readonly sidesheet: EuiSidesheetService, private readonly messageService: UserMessageService, - private readonly logger: ClassloggerService, private readonly busyServiceElemental: EuiLoadingService, private readonly translate: TranslateService, - snackbar: SnackBarService, - settingsService: SettingsService, - authentication: AuthenticationService + authentication: AuthenticationService, + public dataSource: DataViewSource, ) { - this.navigationState = { PageSize: settingsService.DefaultPageSize, StartIndex: 0 }; this.entitySchema = attestationCasesService.attestationApproveSchema; (this.displayedColumns = [ { @@ -130,18 +124,16 @@ export class AttestationInquiriesComponent implements OnInit, OnDestroy { this.subscriptions.push( this.actionService.applied.subscribe(async () => { this.getData(); - this.table.clearSelection(); - }) + }), ); this.attFeatureService.getAttestationConfig().then((config) => { this.isUserEscalationApprover = config.IsUserInChiefApprovalTeam; this.mitigatingControlsPerViolation = config.MitigatingControlsPerViolation; }); - this.subscriptions.push(authentication.onSessionResponse.subscribe((session) => (this.userUid = session.UserUid))); + this.subscriptions.push(authentication.onSessionResponse.subscribe((session) => (this.userUid = session.UserUid || ''))); } public async ngOnInit(): Promise { - this.navigationState.forinquiry = true; const isBusy = this.busyService.beginBusy(); try { @@ -168,32 +160,32 @@ export class AttestationInquiriesComponent implements OnInit, OnDestroy { this.subscriptions.forEach((s) => s.unsubscribe()); } - public async getData(parameters?: CollectionLoadParameters): Promise { - if (parameters) { - this.navigationState = parameters; - } - - const isBusy = this.busyService.beginBusy(); - - try { - this.attestationCasesCollection = await this.attestationCasesService.get(this.navigationState); - this.hasData = this.attestationCasesCollection.totalCount > 0 || (this.navigationState.search ?? '') !== ''; - this.updateTable(); - } finally { - isBusy.endBusy(); - } + public async getData(): Promise { + const dataViewInitParameters: DataViewInitParameters = { + execute: (params: CollectionLoadParameters, signal: AbortSignal): Promise> => + this.attestationCasesService.get({ ...params, forinquiry: true }), + schema: this.entitySchema, + columnsToDisplay: this.displayedColumns, + dataModel: this.dataModel, + exportFunction: this.attestationCasesService.exportData(this.dataSource.state()), + viewConfig: this.viewConfig, + highlightEntity: (identity: AttestationCase) => { + this.editCase(identity); + }, + }; + await this.dataSource.init(dataViewInitParameters); } public async updateConfig(config: ViewConfigData): Promise { await this.viewConfigService.putViewConfig(config); this.viewConfig = await this.viewConfigService.getDSTExtensionChanges(this.viewConfigPath); - this.dstSettings.viewConfig = this.viewConfig; + this.dataSource.viewConfig.set(this.viewConfig); } public async deleteConfigById(id: string): Promise { await this.viewConfigService.deleteViewConfig(id); this.viewConfig = await this.viewConfigService.getDSTExtensionChanges(this.viewConfigPath); - this.dstSettings.viewConfig = this.viewConfig; + this.dataSource.viewConfig.set(this.viewConfig); } /** @@ -203,10 +195,10 @@ export class AttestationInquiriesComponent implements OnInit, OnDestroy { */ public async editCase(attestationCase: AttestationCase): Promise { let attestationCaseWithPolicy: AttestationCase; - let approvers: Approvers; - - let busyIndicator: OverlayRef; - setTimeout(() => (busyIndicator = this.busyServiceElemental.show())); + let approvers: Approvers | undefined; + if (this.busyServiceElemental.overlayRefs.length === 0) { + this.busyServiceElemental.show(); + } try { attestationCaseWithPolicy = ( @@ -226,17 +218,19 @@ export class AttestationInquiriesComponent implements OnInit, OnDestroy { ).Data[0]; // Add additional violation data to this case - attestationCaseWithPolicy.data.CanSeeComplianceViolations = attestationCase.data.CanSeeComplianceViolations; - attestationCaseWithPolicy.data.ComplianceViolations = attestationCase.data.ComplianceViolations; - attestationCaseWithPolicy.data.CanSeePolicyViolations = attestationCase.data.CanSeePolicyViolations; - attestationCaseWithPolicy.data.PolicyViolations = attestationCase.data.PolicyViolations; + if (attestationCaseWithPolicy.data) { + attestationCaseWithPolicy.data.CanSeeComplianceViolations = !!attestationCase.data?.CanSeeComplianceViolations; + attestationCaseWithPolicy.data.ComplianceViolations = attestationCase.data?.ComplianceViolations || []; + attestationCaseWithPolicy.data.CanSeePolicyViolations = !!attestationCase.data?.CanSeePolicyViolations; + attestationCaseWithPolicy.data.PolicyViolations = attestationCase.data?.PolicyViolations || []; + } if (attestationCaseWithPolicy && !['approved', 'denied'].includes(attestationCaseWithPolicy.AttestationState.value)) { approvers = await this.attestationCasesService.getApprovers(attestationCaseWithPolicy); } this.lossPreview.LossPreviewItems = await this.attestationCasesService.getLossPreviewEntities(attestationCase); } finally { - setTimeout(() => this.busyServiceElemental.hide(busyIndicator)); + this.busyServiceElemental.hide(); } if (attestationCaseWithPolicy) { @@ -244,7 +238,7 @@ export class AttestationInquiriesComponent implements OnInit, OnDestroy { title: await this.translate.get('#LDS#Heading View Attestation Case Details').toPromise(), subTitle: attestationCaseWithPolicy.GetEntity().GetDisplay(), padding: '0px', - width: 'max(60%,700px)', + width: calculateSidesheetWidth(1000), testId: 'attestation-case-sidesheet', data: { case: attestationCaseWithPolicy, @@ -264,43 +258,12 @@ export class AttestationInquiriesComponent implements OnInit, OnDestroy { } public getInquiryText(pwo: AttestationCase): string { - return this.actionService.getCaseData(pwo).Columns.ReasonHead.Value; + return this.actionService.getCaseData(pwo)?.Columns?.ReasonHead.Value || ''; } public getInquirer(pwo: AttestationCase): string { - return this.actionService.getCaseData(pwo).Columns.DisplayPersonHead.Value; - } - public getQueryDate(pwo: AttestationCase): Date { - return new Date(this.actionService.getCaseData(pwo).Columns.DateHead.Value); + return this.actionService.getCaseData(pwo)?.Columns?.DisplayPersonHead.Value || ''; } - - public onSearch(keywords: string): Promise { - const navigationState = { - ...this.navigationState, - ...{ - StartIndex: 0, - search: keywords, - }, - }; - - return this.getData(navigationState); - } - - private updateTable(): void { - if (this.attestationCasesCollection) { - const exportMethod = this.attestationCasesService.exportData(this.navigationState); - exportMethod.initialColumns = this.displayedColumns.map((col) => col.ColumnName); - this.dstSettings = { - dataSource: this.attestationCasesCollection, - extendedData: this.attestationCasesCollection?.extendedData?.Data, - entitySchema: this.entitySchema, - navigationState: this.navigationState, - displayedColumns: this.displayedColumns, - dataModel: this.dataModel, - viewConfig: this.viewConfig, - exportMethod, - }; - } else { - this.dstSettings = undefined; - } + public getQueryDate(pwo: AttestationCase): string { + return this.actionService.getCaseData(pwo)?.Columns?.DateHead.Value || ''; } } diff --git a/imxweb/projects/att/src/lib/decision/attestation-inquiries/attestation-inquiry.model.ts b/imxweb/projects/att/src/lib/decision/attestation-inquiries/attestation-inquiry.model.ts index 1470de200..66e9f472c 100644 --- a/imxweb/projects/att/src/lib/decision/attestation-inquiries/attestation-inquiry.model.ts +++ b/imxweb/projects/att/src/lib/decision/attestation-inquiries/attestation-inquiry.model.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,8 +24,8 @@ * */ -export class AttestationInquiry{ +export class AttestationInquiry { public static headCaption = '#LDS#Inquiry made by'; public static queryCaption = '#LDS#Inquiry'; public static queryDate = '#LDS#Inquiry made on'; -} \ No newline at end of file +} diff --git a/imxweb/projects/att/src/lib/decision/decision-compliance-violation/decision-compliance-violation.component.html b/imxweb/projects/att/src/lib/decision/decision-compliance-violation/decision-compliance-violation.component.html index 4e8a7f71a..d769e2e50 100644 --- a/imxweb/projects/att/src/lib/decision/decision-compliance-violation/decision-compliance-violation.component.html +++ b/imxweb/projects/att/src/lib/decision/decision-compliance-violation/decision-compliance-violation.component.html @@ -1,95 +1,103 @@
    -
    - - - {{'#LDS#Here you can get an overview of all rule violations of this attestation case.' | translate }} - - -
    + + + {{ '#LDS#Here you can get an overview of all rule violations of this attestation case.' | translate }} + +
    - - - - - {{complianceViolation.DisplayRule}} - - {{'#LDS#Exception allowed' | translate}} - - - {{'#LDS#Exception forbidden' | translate}} - - - -
    -
    -
    {{'#LDS#Affected identity' | translate}}
    -
    {{complianceViolation.DisplayPerson}}
    -
    -
    -
    {{'#LDS#Contributing entitlements' | translate}}
    -
    -
    {{entitlement?.Display}}
    -
    {{entitlement?.Type}}
    -
    -
    {{'#LDS#Related SAP entitlements' | translate}}
    -
      -
    • -
      {{source?.Display}}
      -
      {{source?.Type}}
      -
    • -
    - {{'#LDS#Not set' | translate}} -
    + + + + + {{ complianceViolation.DisplayRule }} + + {{ '#LDS#Exception allowed' | translate }} + + + {{ '#LDS#Exception forbidden' | translate }} + + + +
    +
    +
    {{ '#LDS#Affected identity' | translate }}
    +
    {{ complianceViolation.DisplayPerson }}
    +
    +
    +
    {{ '#LDS#Contributing entitlements' | translate }}
    +
    +
    {{ entitlement?.Display }}
    +
    {{ entitlement?.Type }}
    +
    +
    {{ '#LDS#Related SAP entitlements' | translate }}
    +
      +
    • +
      {{ source?.Display }}
      +
      {{ source?.Type }}
      +
    • +
    + {{ '#LDS#Not set' | translate }}
    -
    -
    {{ '#LDS#The following mitigating controls are available for the rule violation:' | translate }}
    -
      -
    • - {{control.Display}} -
    • -
    -
    - +
    +
    {{ '#LDS#The following mitigating controls are available for the rule violation:' | translate }}
    +
      +
    • + {{ control.Display }} +
    • +
    +
    +
    +
    - - - - - {{complianceViolation.DisplayRule}} - - {{'#LDS#Exception allowed' | translate}} + + + {{ complianceViolation.DisplayRule }} + + {{ '#LDS#Exception allowed' | translate }} - - {{'#LDS#Exception forbidden' | translate}} + + {{ '#LDS#Exception forbidden' | translate }} - - -
    -
    {{'#LDS#Affected object' | translate}}
    -
    {{complianceViolation.DisplayObject}}
    -
    -
    -
    {{ '#LDS#The following mitigating controls are available for the rule violation:' | translate }}
    -
      -
    • - {{control.Display}} -
    • -
    -
    -
    -
    +
    + +
    +
    {{ '#LDS#Affected object' | translate }}
    +
    {{ complianceViolation.DisplayObject }}
    +
    +
    +
    {{ '#LDS#The following mitigating controls are available for the rule violation:' | translate }}
    +
      +
    • + {{ control.Display }} +
    • +
    +
    +
    +
    - - {{'#LDS#There are currently no mitigating controls assigned to the rule violation.' |translate}} + + {{ '#LDS#There are currently no mitigating controls assigned to the rule violation.' | translate }} diff --git a/imxweb/projects/att/src/lib/decision/decision-compliance-violation/decision-compliance-violation.component.scss b/imxweb/projects/att/src/lib/decision/decision-compliance-violation/decision-compliance-violation.component.scss index 34b7b59e9..658494b6e 100644 --- a/imxweb/projects/att/src/lib/decision/decision-compliance-violation/decision-compliance-violation.component.scss +++ b/imxweb/projects/att/src/lib/decision/decision-compliance-violation/decision-compliance-violation.component.scss @@ -1,49 +1,17 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; - -@mixin imx-flex-row-container { - display: flex; - flex-direction: row; -} +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/mixins'; .heading-wrapper { - @include imx-flex-row-container(); + @include flex-row-container(); flex: 0 0 auto; - - .helper-alert { - display: flex; - margin-bottom: 15px; - } - - .alert-wrapper { - width: 100%; - margin: 0 0 0 auto; - align-self: flex-end; - } } -.mat-card-title { +.mat-mdc-card-title { display: flex; align-items: center; margin-bottom: 17px; } -.mat-expansion-panel { - margin-bottom: 20px; - - &-header-title { - font-size: 24px; - font-weight: 500; - } - - ::ng-deep .eui-alert { - width: auto; - } -} - -.eui-badge { - padding-left: 10px; -} - .imx-container > * { margin-bottom: 10px; } @@ -53,11 +21,13 @@ margin-top: 5px; margin-bottom: 5px; } -.imx-entitlement-subtitle, .imx-source-subtitle { +.imx-entitlement-subtitle, +.imx-source-subtitle { color: $color-gray-40; } -.imx-risk-reduction-item, .imx-source-item { +.imx-risk-reduction-item, +.imx-source-item { list-style-type: circle; margin-left: 20px; } diff --git a/imxweb/projects/att/src/lib/decision/decision-compliance-violation/decision-compliance-violation.component.ts b/imxweb/projects/att/src/lib/decision/decision-compliance-violation/decision-compliance-violation.component.ts index 466a3e58c..1206aa83e 100644 --- a/imxweb/projects/att/src/lib/decision/decision-compliance-violation/decision-compliance-violation.component.ts +++ b/imxweb/projects/att/src/lib/decision/decision-compliance-violation/decision-compliance-violation.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,12 +25,12 @@ */ import { Component, Input } from '@angular/core'; -import { ComplianceViolation } from 'imx-api-att'; +import { ComplianceViolation } from '@imx-modules/imx-api-att'; @Component({ selector: 'imx-decision-compliance-violation', templateUrl: './decision-compliance-violation.component.html', - styleUrls: ['./decision-compliance-violation.component.scss'] + styleUrls: ['./decision-compliance-violation.component.scss'], }) export class DecisionComplianceViolationComponent { @Input() public complianceViolations: ComplianceViolation[]; diff --git a/imxweb/projects/att/src/lib/decision/decision-history-item/decision-history-item.component.html b/imxweb/projects/att/src/lib/decision/decision-history-item/decision-history-item.component.html index c0848a6c1..bcfb15ed0 100644 --- a/imxweb/projects/att/src/lib/decision/decision-history-item/decision-history-item.component.html +++ b/imxweb/projects/att/src/lib/decision/decision-history-item/decision-history-item.component.html @@ -1,45 +1,55 @@ -
  • +
  • - {{ - workflowHistoryEntity.DecisionType?.Column?.GetDisplayValue() }} -  - {{ - workflowHistoryEntity.XDateInserted.value | localizedDate }} + {{ workflowHistoryEntity.DecisionType?.Column?.GetDisplayValue() }} +  - {{ workflowHistoryEntity.XDateInserted.value | localizedDate }} - {{workflowHistoryEntity.Ident_PWODecisionStep?.Column?.GetDisplayValue()}} + {{ workflowHistoryEntity.Ident_PWODecisionStep?.Column?.GetDisplayValue() }} + - {{'#LDS#Approval decision by chief approval team'|translate}} + {{ '#LDS#Approval decision by chief approval team' | translate }} - - + +
    {{ - decisionHistory.getColumnDescriptionForDisplayPersonHead(workflowHistoryEntity.DecisionType?.value) | translate }} - {{workflowHistoryEntity.DisplayPersonHead?.Column?.GetDisplayValue()}} + decisionHistory.getColumnDescriptionForDisplayPersonHead(workflowHistoryEntity.DecisionType?.value || '') | translate + }} + {{ workflowHistoryEntity.DisplayPersonHead?.Column?.GetDisplayValue() }}
    -
    +
    - {{ "#LDS#Query"| translate }} - {{workflowHistoryEntity.ReasonHead?.Column?.GetDisplayValue()}} + {{ '#LDS#Query' | translate }} + {{ workflowHistoryEntity.ReasonHead?.Column?.GetDisplayValue() }}
    -
    +
    - {{ "#LDS#Answer"| translate }} - {{workflowHistoryEntity.ReasonHead?.Column?.GetDisplayValue()}} + {{ '#LDS#Answer' | translate }} + {{ workflowHistoryEntity.ReasonHead?.Column?.GetDisplayValue() }}
    @@ -47,20 +57,23 @@ - + - +
    - {{ "#LDS#Recipient"| translate }} - {{workflowHistoryEntity.UID_PersonRelated?.Column?.GetDisplayValue()}} + {{ '#LDS#Recipient' | translate }} + {{ workflowHistoryEntity.UID_PersonRelated?.Column?.GetDisplayValue() }}
    -
  • \ No newline at end of file + diff --git a/imxweb/projects/att/src/lib/decision/decision-history-item/decision-history-item.component.scss b/imxweb/projects/att/src/lib/decision/decision-history-item/decision-history-item.component.scss index a5aea9e8b..590c9827c 100644 --- a/imxweb/projects/att/src/lib/decision/decision-history-item/decision-history-item.component.scss +++ b/imxweb/projects/att/src/lib/decision/decision-history-item/decision-history-item.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; :host { display: flex; @@ -18,7 +18,7 @@ } } -.mat-card-title { +.mat-mdc-card-title { margin-bottom: 15px; :first-child { font-weight: bold; @@ -29,16 +29,15 @@ } } -.mat-card-header { - margin-left: -16px; +.mat-mdc-card-header { margin-bottom: 10px; - .mat-card-title { + .mat-mdc-card-title { font-size: 1.05em; margin-bottom: 0; } - .mat-card-subtitle.imx-workflowhistory-pwodecisionstep { + .mat-mdc-card-subtitle.imx-workflowhistory-pwodecisionstep { margin: 5px 0; } } @@ -66,7 +65,7 @@ li.imx-event::before { background-color: $color-gray-0; height: 24px; width: 28px; - content: ""; + content: ''; float: left; margin-top: -2px; } @@ -80,7 +79,7 @@ li.imx-event.imx-positive::before { font-family: Cadence-Icon; font-size: 24px; color: $color-green-60; - content: "\e049"; + content: '\e049'; } li.imx-event.imx-negative { @@ -92,7 +91,7 @@ li.imx-event.imx-negative::before { font-family: Cadence-Icon; font-size: 24px; color: $color-red-60; - content: "\e05c"; + content: '\e05c'; } li.imx-event.imx-question { @@ -104,7 +103,7 @@ li.imx-event.imx-question::before { font-family: Cadence-Icon; font-size: 24px; color: $color-blue-60; - content: "\e009"; + content: '\e009'; } li.imx-event.imx-info { @@ -116,7 +115,7 @@ li.imx-event.imx-info::before { font-family: Cadence-Icon; font-size: 24px; color: $color-blue-60; - content: "\e010"; + content: '\e010'; } li.imx-event.imx-pending { @@ -128,22 +127,17 @@ li.imx-event.imx-pending::before { font-family: Cadence-Icon; font-size: 24px; color: $color-gray-30; - content: "\e009"; -} - -.chief-approval-badge { - margin-bottom: .5em; + content: '\e009'; } .eui-dark-theme { :host { - li.imx-event::before{ + li.imx-event::before { background-color: $color-gray-70; } - li{ - .mat-card - { + li { + .mat-mdc-card { background-color: $color-gray-60; } } @@ -152,13 +146,12 @@ li.imx-event.imx-pending::before { .eui-contrast-theme { :host { - li.imx-event::before{ + li.imx-event::before { background-color: $color-gray-90; } - li{ - .mat-card - { + li { + .mat-mdc-card { background-color: $color-gray-80; } } diff --git a/imxweb/projects/att/src/lib/decision/decision-history-item/decision-history-item.component.ts b/imxweb/projects/att/src/lib/decision/decision-history-item/decision-history-item.component.ts index 2954b1f02..d1ac55899 100644 --- a/imxweb/projects/att/src/lib/decision/decision-history-item/decision-history-item.component.ts +++ b/imxweb/projects/att/src/lib/decision/decision-history-item/decision-history-item.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,12 +26,12 @@ import { Component, Input } from '@angular/core'; -import { PortalAttestationCaseHistory } from 'imx-api-att'; +import { PortalAttestationCaseHistory } from '@imx-modules/imx-api-att'; import { DecisionHistoryService } from 'qer'; @Component({ selector: 'imx-decision-history-item', templateUrl: './decision-history-item.component.html', - styleUrls: ['./decision-history-item.component.scss'] + styleUrls: ['./decision-history-item.component.scss'], }) export class DecisionHistoryItemComponent { @Input() public workflowHistoryEntity: PortalAttestationCaseHistory; diff --git a/imxweb/projects/att/src/lib/decision/decision-policy-violation/decision-policy-violation.component.html b/imxweb/projects/att/src/lib/decision/decision-policy-violation/decision-policy-violation.component.html index b93b8f6b9..3b4079755 100644 --- a/imxweb/projects/att/src/lib/decision/decision-policy-violation/decision-policy-violation.component.html +++ b/imxweb/projects/att/src/lib/decision/decision-policy-violation/decision-policy-violation.component.html @@ -1,76 +1,92 @@
    -
    - - - {{'#LDS#Here you can get an overview of all policy violations of this attestation case.' | translate }} - - -
    + + + {{ '#LDS#Here you can get an overview of all policy violations of this attestation case.' | translate }} + +
    - - - - - {{policyViolation.DisplayPolicy}} - - {{'#LDS#Exception allowed' | translate}} - - - {{'#LDS#Exception forbidden' | translate}} - - - -
    -
    {{'#LDS#Affected object' | translate}}
    -
    {{policyViolation.DisplayObject}}
    -
    -
    -
    {{ '#LDS#The following mitigating controls are available for the policy violation:' | translate }}
    -
      -
    • - {{control.Display}} -
    • -
    -
    -
    + + + + + {{ policyViolation.DisplayPolicy }} + + +
    +
    {{ '#LDS#State' | translate }}
    + + {{ stateDisplay(policyViolation) | translate }} + +
    +
    +
    {{ '#LDS#Exception allowed' | translate }}
    +
    {{ (policyViolation.IsExceptionAllowed ? '#LDS#Company policy allows exceptions' : '#LDS#Company policy forbids exceptions') | translate }}
    +
    +
    +
    {{ '#LDS#Affected object' | translate }}
    +
    {{ policyViolation.DisplayObject }}
    +
    +
    +
    {{ '#LDS#The following mitigating controls are available for the policy violation:' | translate }}
    +
      +
    • + {{ control.Display }} +
    • +
    +
    +
    - - - - - {{policyViolation.DisplayPolicy}} - - {{'#LDS#Exception allowed' | translate}} + + + {{ policyViolation.DisplayPolicy }} + +
    +
    {{ '#LDS#State' | translate }}
    + + {{ stateDisplay(policyViolation) | translate }} - - {{'#LDS#Exception forbidden' | translate}} - - - -
    -
    {{'#LDS#Affected object' | translate}}
    -
    {{policyViolation.DisplayObject}}
    -
    -
    {{ '#LDS#The following mitigating controls are available for the policy violation:' | translate }}
    -
      -
    • - {{control.Display}} -
    • -
    +
    +
    {{ '#LDS#Exception allowed' | translate }}
    +
    {{ (policyViolation.IsExceptionAllowed ? '#LDS#Company policy allows exceptions' : '#LDS#Company policy forbids exceptions') | translate }}
    - - +
    +
    {{ '#LDS#Affected object' | translate }}
    +
    {{ policyViolation.DisplayObject }}
    +
    + +
    +
    {{ '#LDS#The following mitigating controls are available for the policy violation:' | translate }}
    +
      +
    • + {{ control.Display }} +
    • +
    +
    +
    + - - {{'#LDS#There are currently no mitigating controls assigned to the policy violation.' |translate}} + + {{ '#LDS#There are currently no mitigating controls assigned to the policy violation.' | translate }} diff --git a/imxweb/projects/att/src/lib/decision/decision-policy-violation/decision-policy-violation.component.scss b/imxweb/projects/att/src/lib/decision/decision-policy-violation/decision-policy-violation.component.scss index f3b6073a7..433c77c9b 100644 --- a/imxweb/projects/att/src/lib/decision/decision-policy-violation/decision-policy-violation.component.scss +++ b/imxweb/projects/att/src/lib/decision/decision-policy-violation/decision-policy-violation.component.scss @@ -1,26 +1,11 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; - -@mixin imx-flex-row-container { - display: flex; - flex-direction: row; -} +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/mixins'; .heading-wrapper { - @include imx-flex-row-container(); + @include flex-row-container(); flex: 0 0 auto; - - .helper-alert { - display: flex; - margin-bottom: 15px; - } - - .alert-wrapper { - width: 100%; - margin: 0 0 0 auto; - align-self: flex-end; - } } -.mat-card { +.mat-mdc-card { padding-left: 24px; padding-right: 24px; @@ -31,23 +16,6 @@ } } -.mat-expansion-panel { - margin-bottom: 20px; - - &-header-title { - font-size: 24px; - font-weight: 500; - } - - ::ng-deep .eui-alert { - width: auto; - } -} - -.eui-badge { - padding-left: 10px; -} - .imx-container > * { margin-bottom: 10px; } @@ -63,9 +31,3 @@ list-style-type: circle; margin-left: 20px; } - -.imx-top-alert { - display: block; - margin: 1em 0; -} - diff --git a/imxweb/projects/att/src/lib/decision/decision-policy-violation/decision-policy-violation.component.ts b/imxweb/projects/att/src/lib/decision/decision-policy-violation/decision-policy-violation.component.ts index 6ec3a5272..21e84b617 100644 --- a/imxweb/projects/att/src/lib/decision/decision-policy-violation/decision-policy-violation.component.ts +++ b/imxweb/projects/att/src/lib/decision/decision-policy-violation/decision-policy-violation.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,16 +25,25 @@ */ import { Component, Input } from '@angular/core'; -import { PolicyViolation } from 'imx-api-att'; +import { PolicyViolation, ViolationState } from '@imx-modules/imx-api-att'; @Component({ selector: 'imx-decision-policy-violation', templateUrl: './decision-policy-violation.component.html', - styleUrls: ['./decision-policy-violation.component.scss'] + styleUrls: ['./decision-policy-violation.component.scss'], }) export class DecisionPolicyViolationComponent { @Input() public policyViolations: PolicyViolation[]; @Input() public mitigatingControlsPerViolation: boolean; constructor() {} + + public stateDisplay(violation: PolicyViolation) { + switch (violation.State){ + case ViolationState.ExceptionApproved: return '#LDS#Exception granted'; + case ViolationState.ExceptionDenied: return '#LDS#Exception denied'; + } + + return '#LDS#Approval decision pending'; + } } diff --git a/imxweb/projects/att/src/lib/decision/loss-preview-dialog/loss-preview-dialog.component.html b/imxweb/projects/att/src/lib/decision/loss-preview-dialog/loss-preview-dialog.component.html index 802b23c72..94c117f37 100644 --- a/imxweb/projects/att/src/lib/decision/loss-preview-dialog/loss-preview-dialog.component.html +++ b/imxweb/projects/att/src/lib/decision/loss-preview-dialog/loss-preview-dialog.component.html @@ -2,11 +2,11 @@ -
    +
    -
    diff --git a/imxweb/projects/att/src/lib/decision/loss-preview-dialog/loss-preview-dialog.component.scss b/imxweb/projects/att/src/lib/decision/loss-preview-dialog/loss-preview-dialog.component.scss index 5956a2b77..859c6f75f 100644 --- a/imxweb/projects/att/src/lib/decision/loss-preview-dialog/loss-preview-dialog.component.scss +++ b/imxweb/projects/att/src/lib/decision/loss-preview-dialog/loss-preview-dialog.component.scss @@ -5,26 +5,22 @@ flex-direction: column; height: 100%; - .mat-card { + .mat-mdc-card { margin: 0 -10px; flex: 1; } - - .mat-dialog-actions { - gap: 10px; - } } // Theming :host { - .mat-card { + .mat-mdc-card { background-color: $color-gray-10; } } .eui-dark-theme { :host { - .mat-card { + .mat-mdc-card { background-color: $color-gray-80; } } @@ -32,7 +28,7 @@ .eui-contrast-theme { :host { - .mat-card { + .mat-mdc-card { background-color: $color-gray-90; } } diff --git a/imxweb/projects/att/src/lib/decision/loss-preview-dialog/loss-preview-dialog.component.ts b/imxweb/projects/att/src/lib/decision/loss-preview-dialog/loss-preview-dialog.component.ts index 62923695c..dcc6daa7a 100644 --- a/imxweb/projects/att/src/lib/decision/loss-preview-dialog/loss-preview-dialog.component.ts +++ b/imxweb/projects/att/src/lib/decision/loss-preview-dialog/loss-preview-dialog.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,7 +31,7 @@ import { LossPreview } from '../loss-preview.interface'; @Component({ selector: 'imx-loss-preview-dialog', templateUrl: './loss-preview-dialog.component.html', - styleUrls: ['./loss-preview-dialog.component.scss'] + styleUrls: ['./loss-preview-dialog.component.scss'], }) export class LossPreviewDialogComponent implements OnInit { public lossPreview: LossPreview; @@ -39,10 +39,9 @@ export class LossPreviewDialogComponent implements OnInit { constructor( public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public readonly data: LossPreview, - ) { - } + ) {} public ngOnInit(): void { - this.lossPreview = this.data; + this.lossPreview = this.data; } public onDeny(): void { diff --git a/imxweb/projects/att/src/lib/decision/loss-preview-table/loss-preview-table.component.html b/imxweb/projects/att/src/lib/decision/loss-preview-table/loss-preview-table.component.html index 9843d893d..4780ff513 100644 --- a/imxweb/projects/att/src/lib/decision/loss-preview-table/loss-preview-table.component.html +++ b/imxweb/projects/att/src/lib/decision/loss-preview-table/loss-preview-table.component.html @@ -1,14 +1,19 @@
    -

    - {{'#LDS#Heading Entitlement Loss' | translate}} -

    -
    - - - {{'#LDS#Here you can get an overview of all entitlements that will be withdrawn if the attestation case is denied.' | translate }} - - -
    +

    + {{ '#LDS#Heading Entitlement Loss' | translate }} +

    + + + {{ '#LDS#Here you can get an overview of all entitlements that will be withdrawn if the attestation case is denied.' | translate }} + +
    @@ -19,28 +24,28 @@

    - + - + - + - +
    {{ lossPreviewDisplayKeys.Display | translate }}{{ lossPreviewDisplayKeys.Display || '' | translate }}
    {{ row?.Display }}
    - {{row?.Table}} + {{ row?.Table }}
    {{ lossPreviewDisplayKeys.ObjectDisplay | translate }}{{ lossPreviewDisplayKeys.ObjectDisplay || '' | translate }} - {{row?.ObjectDisplay}} + {{ row?.ObjectDisplay }} {{ lossPreviewDisplayKeys.Person | translate }}{{ lossPreviewDisplayKeys.Person || '' | translate }} {{ row?.Person }}
    diff --git a/imxweb/projects/att/src/lib/decision/loss-preview-table/loss-preview-table.component.scss b/imxweb/projects/att/src/lib/decision/loss-preview-table/loss-preview-table.component.scss index fc32cc929..93badb901 100644 --- a/imxweb/projects/att/src/lib/decision/loss-preview-table/loss-preview-table.component.scss +++ b/imxweb/projects/att/src/lib/decision/loss-preview-table/loss-preview-table.component.scss @@ -28,32 +28,6 @@ flex: 0 0 auto; margin-top: 15px; align-items: center; - - .helper-alert { - display: flex; - } - - .alert-wrapper { - width: 100%; - margin: 0 0 0 auto; - align-self: flex-end; - } - - .alert-wrapper--shortened { - width: 70%; - } -} - -th.mat-header-cell:not(:last-child), td.mat-cell:not(:last-child) { - padding-right: 10px; -} - -table.mat-table { - box-shadow: none; - padding: 1px; -} -.table-row-tall { - padding: 5px 0; } .table-subtitle { diff --git a/imxweb/projects/att/src/lib/decision/loss-preview-table/loss-preview-table.component.ts b/imxweb/projects/att/src/lib/decision/loss-preview-table/loss-preview-table.component.ts index a8d2a19cb..e4716422e 100644 --- a/imxweb/projects/att/src/lib/decision/loss-preview-table/loss-preview-table.component.ts +++ b/imxweb/projects/att/src/lib/decision/loss-preview-table/loss-preview-table.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,9 +25,9 @@ */ import { Component, Input, OnInit } from '@angular/core'; -import { EntitlementLossDto } from 'imx-api-att'; -import { LossPreview } from '../loss-preview.interface'; +import { EntitlementLossDto } from '@imx-modules/imx-api-att'; import { AttestationCasesService } from '../attestation-cases.service'; +import { LossPreview } from '../loss-preview.interface'; @Component({ selector: 'imx-loss-preview-table', @@ -57,7 +57,9 @@ export class LossPreviewTableComponent implements OnInit { public async loadData(): Promise { this.isLoading = true; try { - this.lossPreview.LossPreviewItems = await this.caseService.getLossPreviewEntities(this.lossPreview.Case); + if (!!this.lossPreview.Case) { + this.lossPreview.LossPreviewItems = await this.caseService.getLossPreviewEntities(this.lossPreview.Case); + } } finally { this.isLoading = false; } diff --git a/imxweb/projects/att/src/lib/decision/loss-preview.interface.ts b/imxweb/projects/att/src/lib/decision/loss-preview.interface.ts index 406a865a7..95838e383 100644 --- a/imxweb/projects/att/src/lib/decision/loss-preview.interface.ts +++ b/imxweb/projects/att/src/lib/decision/loss-preview.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { EntitlementLossDto } from 'imx-api-att'; +import { EntitlementLossDto } from '@imx-modules/imx-api-att'; import { AttestationCase } from './attestation-case'; export interface LossPreview { diff --git a/imxweb/projects/att/src/lib/decision/mitigating-controls/mitigating-controls.component.html b/imxweb/projects/att/src/lib/decision/mitigating-controls/mitigating-controls.component.html index 10db3c147..3c9a136e9 100644 --- a/imxweb/projects/att/src/lib/decision/mitigating-controls/mitigating-controls.component.html +++ b/imxweb/projects/att/src/lib/decision/mitigating-controls/mitigating-controls.component.html @@ -1,16 +1,27 @@ -

    {{ '#LDS#Heading Assign Mitigating Controls' | translate}}

    +

    {{ '#LDS#Heading Assign Mitigating Controls' | translate }}

    - +
    - - - - \ No newline at end of file + + diff --git a/imxweb/projects/att/src/lib/decision/mitigating-controls/mitigating-controls.component.scss b/imxweb/projects/att/src/lib/decision/mitigating-controls/mitigating-controls.component.scss index 418290f98..a23c84eea 100644 --- a/imxweb/projects/att/src/lib/decision/mitigating-controls/mitigating-controls.component.scss +++ b/imxweb/projects/att/src/lib/decision/mitigating-controls/mitigating-controls.component.scss @@ -1 +1 @@ -// You can add css code here \ No newline at end of file +// You can add css code here diff --git a/imxweb/projects/att/src/lib/decision/mitigating-controls/mitigating-controls.component.ts b/imxweb/projects/att/src/lib/decision/mitigating-controls/mitigating-controls.component.ts index 69690b750..af787d88a 100644 --- a/imxweb/projects/att/src/lib/decision/mitigating-controls/mitigating-controls.component.ts +++ b/imxweb/projects/att/src/lib/decision/mitigating-controls/mitigating-controls.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,25 +28,24 @@ import { Component, Inject } from '@angular/core'; import { UntypedFormGroup } from '@angular/forms'; import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; -import { IEntityColumn } from 'imx-qbm-dbts'; +import { IEntityColumn } from '@imx-modules/imx-qbm-dbts'; import { BaseCdr, ColumnDependentReference } from 'qbm'; @Component({ selector: 'imx-mitigating-controls', templateUrl: './mitigating-controls.component.html', - styleUrls: ['./mitigating-controls.component.scss'] + styleUrls: ['./mitigating-controls.component.scss'], }) export class MitigatingControlsComponent { - public mitigatingControlCdr: ColumnDependentReference; public formGroup = new UntypedFormGroup({}); constructor( - @Inject(MAT_DIALOG_DATA) public readonly data: { - column: IEntityColumn + @Inject(MAT_DIALOG_DATA) + public readonly data: { + column: IEntityColumn; }, - public dialogRef: MatDialogRef + public dialogRef: MatDialogRef, ) { this.mitigatingControlCdr = new BaseCdr(this.data.column); } - } diff --git a/imxweb/projects/att/src/lib/entity-property-editor/entity-property-editor.component.html b/imxweb/projects/att/src/lib/entity-property-editor/entity-property-editor.component.html index 660035667..e75995806 100644 --- a/imxweb/projects/att/src/lib/entity-property-editor/entity-property-editor.component.html +++ b/imxweb/projects/att/src/lib/entity-property-editor/entity-property-editor.component.html @@ -1 +1 @@ - \ No newline at end of file + diff --git a/imxweb/projects/att/src/lib/entity-property-editor/entity-property-editor.component.scss b/imxweb/projects/att/src/lib/entity-property-editor/entity-property-editor.component.scss index e77cdc598..965d16769 100644 --- a/imxweb/projects/att/src/lib/entity-property-editor/entity-property-editor.component.scss +++ b/imxweb/projects/att/src/lib/entity-property-editor/entity-property-editor.component.scss @@ -1 +1 @@ -// TODO: Styles \ No newline at end of file +// TODO: Styles diff --git a/imxweb/projects/att/src/lib/entity-property-editor/entity-property-editor.component.ts b/imxweb/projects/att/src/lib/entity-property-editor/entity-property-editor.component.ts index dba19db4f..c39fc6338 100644 --- a/imxweb/projects/att/src/lib/entity-property-editor/entity-property-editor.component.ts +++ b/imxweb/projects/att/src/lib/entity-property-editor/entity-property-editor.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,16 +26,16 @@ import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; -import { EntityValue } from 'imx-qbm-dbts'; +import { EntityValue } from '@imx-modules/imx-qbm-dbts'; import { BaseCdr, ColumnDependentReference } from 'qbm'; @Component({ selector: 'imx-entity-property-editor', templateUrl: './entity-property-editor.component.html', - styleUrls: ['./entity-property-editor.component.scss'] + styleUrls: ['./entity-property-editor.component.scss'], }) export class EntityPropertyEditorComponent implements OnChanges { - public cdr: ColumnDependentReference; + public cdr: ColumnDependentReference | undefined; @Input() public property: EntityValue; @Input() public hideIfEmpty = true; @@ -43,8 +43,10 @@ export class EntityPropertyEditorComponent implements OnChanges { public ngOnChanges(changes: SimpleChanges): void { if (changes.property) { - this.cdr = this.property && (!this.hideIfEmpty || (this.property.value != null && this.property.value !== '')) ? - new BaseCdr(this.property.Column, this.caption) : undefined; + this.cdr = + this.property && (!this.hideIfEmpty || (this.property.value != null && this.property.value !== '')) + ? new BaseCdr(this.property.Column, this.caption) + : undefined; } } } diff --git a/imxweb/projects/att/src/lib/guards/attestation-admin-guard.service.ts b/imxweb/projects/att/src/lib/guards/attestation-admin-guard.service.ts index d235b918a..1e642e47c 100644 --- a/imxweb/projects/att/src/lib/guards/attestation-admin-guard.service.ts +++ b/imxweb/projects/att/src/lib/guards/attestation-admin-guard.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { Injectable, OnDestroy } from '@angular/core'; -import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router'; +import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; import { Observable, Subscription } from 'rxjs'; import { AppConfigService, AuthenticationService, ISessionState } from 'qbm'; @@ -34,14 +34,14 @@ import { PermissionsService } from '../admin/permissions.service'; @Injectable({ providedIn: 'root', }) -export class AttestionAdminGuardService implements CanActivate, OnDestroy { +export class AttestionAdminGuardService implements OnDestroy { private onSessionResponse: Subscription; constructor( private readonly attPermissionService: PermissionsService, private readonly authentication: AuthenticationService, private readonly appConfig: AppConfigService, - private readonly router: Router + private readonly router: Router, ) {} public canActivate(route: ActivatedRouteSnapshot, _: RouterStateSnapshot): Observable { @@ -49,8 +49,8 @@ export class AttestionAdminGuardService implements CanActivate, OnDestroy { this.onSessionResponse = this.authentication.onSessionResponse.subscribe(async (sessionState: ISessionState) => { if (sessionState.IsLoggedIn) { const userIsAttestationAdmin = await this.attPermissionService.isAttestationAdmin(); - if (!userIsAttestationAdmin) { - this.router.navigate([this.appConfig.Config.routeConfig.start], { queryParams: {} } ); + if (!userIsAttestationAdmin) { + this.router.navigate([this.appConfig.Config.routeConfig?.start], { queryParams: {} }); } observer.next(userIsAttestationAdmin ? true : false); observer.complete(); diff --git a/imxweb/projects/att/src/lib/guards/attestation-policies-guard.service.ts b/imxweb/projects/att/src/lib/guards/attestation-policies-guard.service.ts index a6bc0720a..32815dbb1 100644 --- a/imxweb/projects/att/src/lib/guards/attestation-policies-guard.service.ts +++ b/imxweb/projects/att/src/lib/guards/attestation-policies-guard.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { Injectable } from '@angular/core'; -import { CanActivate, Router } from '@angular/router'; +import { Router } from '@angular/router'; import { AppConfigService } from 'qbm'; import { PermissionsService } from '../admin/permissions.service'; @@ -33,17 +33,17 @@ import { PermissionsService } from '../admin/permissions.service'; @Injectable({ providedIn: 'root', }) -export class AttestationPoliciesGuardService implements CanActivate { +export class AttestationPoliciesGuardService { constructor( private readonly permissionService: PermissionsService, private readonly appConfig: AppConfigService, - private readonly router: Router - ) { } + private readonly router: Router, + ) {} public async canActivate(): Promise { const userCanSeeAttestationPolicies = await this.permissionService.canSeeAttestationPolicies(); if (!userCanSeeAttestationPolicies) { - this.router.navigate([this.appConfig.Config.routeConfig.start], { queryParams: {} }); + this.router.navigate([this.appConfig.Config.routeConfig?.start], { queryParams: {} }); return false; } return userCanSeeAttestationPolicies; diff --git a/imxweb/projects/att/src/lib/hardware-guard.service.ts b/imxweb/projects/att/src/lib/hardware-guard.service.ts index b323cdee4..14789d368 100644 --- a/imxweb/projects/att/src/lib/hardware-guard.service.ts +++ b/imxweb/projects/att/src/lib/hardware-guard.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,7 +26,7 @@ import { Injectable } from '@angular/core'; import { CanActivate, Router } from '@angular/router'; -import { SystemInfoService } from 'qbm'; +import { AppConfigService, SystemInfoService } from 'qbm'; import { ProjectConfigurationService } from 'qer'; @Injectable({ @@ -34,19 +34,20 @@ import { ProjectConfigurationService } from 'qer'; }) export class HardwareGuardService implements CanActivate { constructor( + private readonly config: AppConfigService, private readonly projectConfig: ProjectConfigurationService, private readonly systemInfo: SystemInfoService, - private readonly router: Router + private readonly router: Router, ) {} public async canActivate(): Promise { - const preprops = (await this.systemInfo.get()).PreProps; - const hardware = (await this.projectConfig.getConfig()).DeviceConfig.VI_Hardware_Enabled; + const preprops = (await this.systemInfo.get()).PreProps || []; + const hardware = !!(await this.projectConfig.getConfig()).DeviceConfig?.VI_Hardware_Enabled; if (hardware && preprops.includes('MAC')) { return true; } - this.router.navigate(['']); + this.router.navigate([this.config.Config.routeConfig?.start], { queryParams: {} }); return false; } } diff --git a/imxweb/projects/att/src/lib/identity-attestation.service.ts b/imxweb/projects/att/src/lib/identity-attestation.service.ts index 47f46d828..38bf4b5f7 100644 --- a/imxweb/projects/att/src/lib/identity-attestation.service.ts +++ b/imxweb/projects/att/src/lib/identity-attestation.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,7 +27,7 @@ import { Injectable } from '@angular/core'; import { Subject } from 'rxjs'; -import { CompareOperator, FilterType } from 'imx-qbm-dbts'; +import { CompareOperator, FilterType } from '@imx-modules/imx-qbm-dbts'; import { AttestationHistoryService } from './attestation-history/attestation-history.service'; import { AttestationCasesService } from './decision/attestation-cases.service'; import { AttestationHistoryActionService } from './attestation-history/attestation-history-action.service'; @@ -35,40 +35,44 @@ import { AttestationCaseLoadParameters } from './attestation-history/attestation import { ObjectAttestationStatistics } from 'qer'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class IdentityAttestationService { - public get applied(): Subject { return this.attestationAction.applied; } + public get applied(): Subject { + return this.attestationAction.applied; + } constructor( private readonly attestationHistory: AttestationHistoryService, private readonly attestationApprove: AttestationCasesService, - private readonly attestationAction: AttestationHistoryActionService - ) { } + private readonly attestationAction: AttestationHistoryActionService, + ) {} public async getNumberOfPendingForUser(parameters: AttestationCaseLoadParameters): Promise { const attestations = await this.attestationHistory.get({ ...parameters, - ...{ PageSize: -1 } + ...{ PageSize: -1 }, }); - const filter = [{ - ColumnName: 'IsClosed', - Type: FilterType.Compare, - CompareOp: CompareOperator.Equal, - Value1: 0 - }]; + const filter = [ + { + ColumnName: 'IsClosed', + Type: FilterType.Compare, + CompareOp: CompareOperator.Equal, + Value1: 0, + }, + ]; const pendingAttestations = await this.attestationHistory.get({ ...parameters, ...{ filter }, - ...{ PageSize: -1 } + ...{ PageSize: -1 }, }); return { total: attestations?.totalCount ?? 0, pendingTotal: pendingAttestations?.totalCount ?? 0, - pendingForUser: await this.attestationApprove.getNumberOfPending(parameters) + pendingForUser: await this.attestationApprove.getNumberOfPending(parameters), }; } } diff --git a/imxweb/projects/att/src/lib/init.service.ts b/imxweb/projects/att/src/lib/init.service.ts index b3a0963b8..1381727d3 100644 --- a/imxweb/projects/att/src/lib/init.service.ts +++ b/imxweb/projects/att/src/lib/init.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,15 +25,15 @@ */ import { Injectable } from '@angular/core'; -import { Router, Route } from '@angular/router'; +import { Route, Router } from '@angular/router'; +import { ProjectConfig, QerProjectConfig } from '@imx-modules/imx-api-qer'; import { ExtService, MenuItem, MenuService, TabItem } from 'qbm'; import { NotificationRegistryService } from 'qer'; import { canSeeAttestationPolicies, isAttestationAdmin } from './admin/permissions-helper'; import { PermissionsService } from './admin/permissions.service'; import { DashboardPluginComponent } from './dashboard-plugin/dashboard-plugin.component'; import { AttestationWrapperComponent } from './runs/attestation/attestation-wrapper/attestation-wrapper.component'; -import { ProjectConfig, QerProjectConfig } from 'imx-api-qer'; @Injectable({ providedIn: 'root' }) export class InitService { @@ -42,7 +42,7 @@ export class InitService { private readonly router: Router, private readonly menuService: MenuService, private readonly notificationService: NotificationRegistryService, - private readonly permissions: PermissionsService + private readonly permissions: PermissionsService, ) { this.setupMenu(); } @@ -65,7 +65,17 @@ export class InitService { inputData: { id: 'attestations', label: '#LDS#Attestation', - checkVisibility: async (_) => (await this.permissions.isAttestationAdmin()), + checkVisibility: async (_) => await this.permissions.isAttestationAdmin(), + }, + sortOrder: 0, + } as TabItem); + + this.extService.register('dugSidesheet',{ + instance: AttestationWrapperComponent, + inputData: { + id: 'attestations', + label: '#LDS#Attestation', + checkVisibility: async (_) => true, }, sortOrder: 0, } as TabItem); @@ -94,10 +104,11 @@ export class InitService { } private setupMenu(): void { - this.menuService.addMenuFactories((preProps: string[], features: string[]) => { - if (!preProps.includes('ATTESTATION')) { - return null; - } + this.menuService.addMenuFactories( + (preProps: string[], features: string[]) => { + if (!preProps.includes('ATTESTATION')) { + return undefined; + } const menu: MenuItem = { id: 'ROOT_Attestation', @@ -126,7 +137,7 @@ export class InitService { }; if (canSeeAttestationPolicies(features)) { - menu.items.push({ + menu.items?.push({ id: 'ATT_Attestation_AttestationRuns', route: 'attestation/runs', title: '#LDS#Menu Entry Attestation runs', @@ -134,7 +145,7 @@ export class InitService { sorting: '20-40', }); - menu.items.push({ + menu.items?.push({ id: 'ATT_Attestation_AttestationPolicies', route: 'attestation/policies', title: '#LDS#Menu Entry Attestation policies', @@ -142,7 +153,7 @@ export class InitService { sorting: '20-50', }); - menu.items.push({ + menu.items?.push({ id: 'ATT_Attestation_AttestationPolicyGroup', route: 'attestation/policy-group', title: '#LDS#Menu Entry Policy collections', @@ -151,22 +162,22 @@ export class InitService { }); } - if (isAttestationAdmin(features)) { - menu.items.push({ - id: 'ATT_Attestation_AttestationPreselection', - route: 'attestation/preselection', - title: '#LDS#Menu Entry Sample data', - description: '#LDS#Shows an overview of samples.', - sorting: '20-50', - }); - } + if (isAttestationAdmin(features)) { + menu.items?.push({ + id: 'ATT_Attestation_AttestationPreselection', + route: 'attestation/preselection', + title: '#LDS#Menu Entry Sampling data', + description: '#LDS#Shows an overview of samples.', + sorting: '20-50', + }); + } return menu; }, function (preProps: string[], __: string[], projectConfig: QerProjectConfig & ProjectConfig) { - const deviceEnabled = projectConfig.DeviceConfig.VI_Hardware_Enabled; - if (!preProps.includes('MAC') || !preProps.includes('ITSHOP') || ! deviceEnabled) { - return null; + const deviceEnabled = !!projectConfig.DeviceConfig?.VI_Hardware_Enabled; + if (!preProps.includes('MAC') || !preProps.includes('ITSHOP') || !deviceEnabled) { + return undefined; } return { @@ -182,7 +193,7 @@ export class InitService { }, ], }; - } + }, ); } } diff --git a/imxweb/projects/att/src/lib/new-user/confirm-dialog.component.html b/imxweb/projects/att/src/lib/new-user/confirm-dialog.component.html index bf924f244..eed5173d3 100644 --- a/imxweb/projects/att/src/lib/new-user/confirm-dialog.component.html +++ b/imxweb/projects/att/src/lib/new-user/confirm-dialog.component.html @@ -1,9 +1,9 @@ -

    {{'#LDS#Success' | translate }}

    +

    {{ '#LDS#Success' | translate }}

    -
    +
    -
    \ No newline at end of file +
    diff --git a/imxweb/projects/att/src/lib/new-user/confirm-dialog.component.ts b/imxweb/projects/att/src/lib/new-user/confirm-dialog.component.ts index 82c38d9b6..242fbd4f0 100644 --- a/imxweb/projects/att/src/lib/new-user/confirm-dialog.component.ts +++ b/imxweb/projects/att/src/lib/new-user/confirm-dialog.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,11 +29,8 @@ import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { ParameterizedText } from 'qbm'; @Component({ - templateUrl: './confirm-dialog.component.html' + templateUrl: './confirm-dialog.component.html', }) export class ConfirmDialogComponent { - - constructor( - @Inject(MAT_DIALOG_DATA) public ptext: ParameterizedText - ) { } + constructor(@Inject(MAT_DIALOG_DATA) public ptext: ParameterizedText) {} } diff --git a/imxweb/projects/aob/src/lib/applications/application-hyperview/application-hyperview.service.ts b/imxweb/projects/att/src/lib/new-user/new-user-metadata.service.ts similarity index 64% rename from imxweb/projects/aob/src/lib/applications/application-hyperview/application-hyperview.service.ts rename to imxweb/projects/att/src/lib/new-user/new-user-metadata.service.ts index fb1e8204e..16e406f07 100644 --- a/imxweb/projects/aob/src/lib/applications/application-hyperview/application-hyperview.service.ts +++ b/imxweb/projects/att/src/lib/new-user/new-user-metadata.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,21 +25,23 @@ */ import { Injectable } from '@angular/core'; +import { MetaTableData } from '@imx-modules/imx-qbm-dbts'; -import { ApiClientService } from 'qbm'; -import { ShapeData } from 'imx-api-qer'; -import { QerApiService } from 'qer'; +import { ApiClientService, MetadataService } from 'qbm'; +import { ApiService } from '../api.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) -export class ApplicationHyperviewService { - +export class NewUserMetadataService extends MetadataService { constructor( - private readonly apiClient: QerApiService, - private readonly apiProvider: ApiClientService) { } + private readonly apiClient: ApiService, + private readonly apiProvider: ApiClientService, + ) { + super(); + } - public async get(uidApplication: string): Promise { - return this.apiProvider.request(() => this.apiClient.client.portal_hyperview_get('AOBApplication', uidApplication)); + protected async getTable(tableName: string, options?: any): Promise { + return this.apiProvider.request(() => this.apiClient.client.register_metadata_table_get(tableName, options)); } } diff --git a/imxweb/projects/att/src/lib/new-user/new-user.component.html b/imxweb/projects/att/src/lib/new-user/new-user.component.html index f5e3283fb..48ed66823 100644 --- a/imxweb/projects/att/src/lib/new-user/new-user.component.html +++ b/imxweb/projects/att/src/lib/new-user/new-user.component.html @@ -1,39 +1,27 @@
    -

    #LDS#Fill out the following fields.

    - +
    - -
    - - {{ ldsCaptchaInfo | translate }} - -
    - -
    - - - -
    - +
    -
    - -
    diff --git a/imxweb/projects/att/src/lib/new-user/new-user.component.scss b/imxweb/projects/att/src/lib/new-user/new-user.component.scss index 7a7729443..672c46cc9 100644 --- a/imxweb/projects/att/src/lib/new-user/new-user.component.scss +++ b/imxweb/projects/att/src/lib/new-user/new-user.component.scss @@ -1,7 +1,3 @@ -.alert-container { - margin-bottom: 1em; -} - .captcha-container { display: flex; @@ -16,21 +12,6 @@ } } -.eui-sidesheet-content { - display: flex; - flex-direction: column; -} - -.eui-sidesheet-actions { - .justify-start { - margin-right: auto; - } - - button:not(:last-of-type):not(.justify-start) { - margin-right: 10px; - } -} - .errormessages { display: block; margin: 1em 0 1em 0; diff --git a/imxweb/projects/att/src/lib/new-user/new-user.component.ts b/imxweb/projects/att/src/lib/new-user/new-user.component.ts index 6be17eba9..36494b378 100644 --- a/imxweb/projects/att/src/lib/new-user/new-user.component.ts +++ b/imxweb/projects/att/src/lib/new-user/new-user.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,6 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; @@ -32,26 +31,43 @@ import { EuiLoadingService, EuiSidesheetRef } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; import { Subscription } from 'rxjs'; -import { RegisterPerson } from 'imx-api-att'; -import { CaptchaService, ColumnDependentReference, ConfirmationService, ErrorService, ParameterizedText, UserMessageService, CdrFactoryService } from 'qbm'; +import { RegisterPerson } from '@imx-modules/imx-api-att'; +import { + CaptchaService, + CdrFactoryService, + ColumnDependentReference, + ConfirmationService, + ErrorService, + MetadataService, + ParameterizedText, + UserMessageService, +} from 'qbm'; import { ApiService } from '../api.service'; import { ConfirmDialogComponent } from './confirm-dialog.component'; +import { NewUserMetadataService } from './new-user-metadata.service'; @Component({ templateUrl: './new-user.component.html', - styleUrls: ['./new-user.component.scss'] + styleUrls: ['./new-user.component.scss'], + providers: [ + { + provide: MetadataService, + useClass: NewUserMetadataService, + }, + CaptchaService, + ], }) export class NewUserComponent implements OnInit, OnDestroy { - public readonly profileForm: UntypedFormGroup; public busy = false; public person: RegisterPerson; - public cdrList: ColumnDependentReference[] = []; + public cdrList: (ColumnDependentReference | undefined)[] = []; - public ldsCaptchaInfo = '#LDS#Please enter the characters from the image.'; - public ldsAccountInfo = '#LDS#Your account "%CentralAccount%" has been successfully created and the email address "%DefaultEmailAddress%" has been assigned to the account. Once your account is approved, it will be activated.'; + public ldsCaptchaInfo = '#LDS#Enter the characters from the image.'; + public ldsAccountInfo = + '#LDS#Your account "%CentralAccount%" has been successfully created and the email address "%DefaultEmailAddress%" has been assigned to the account. Once your account is approved, it will be activated.'; private disposable: () => void; private readonly subscriptions: Subscription[] = []; @@ -68,25 +84,28 @@ export class NewUserComponent implements OnInit, OnDestroy { private readonly cdrFactoryService: CdrFactoryService, errorService: ErrorService, formBuilder: UntypedFormBuilder, - confirmation: ConfirmationService + confirmation: ConfirmationService, ) { + this.captchaSvc.captchaImageUrl = 'register/captchaimage'; this.profileForm = new UntypedFormGroup({ formArray: formBuilder.array([]) }); this.disposable = errorService.setTarget('sidesheet'); - this.subscriptions.push(this.sidesheetRef.closeClicked().subscribe(async (result) => { - if (!this.profileForm.pristine) { - const close = await confirmation.confirmLeaveWithUnsavedChanges(); - if (close) { - this.sidesheetRef.close(); + this.subscriptions.push( + this.sidesheetRef.closeClicked().subscribe(async (result) => { + if (!this.profileForm.pristine) { + const close = await confirmation.confirmLeaveWithUnsavedChanges(); + if (close) { + this.sidesheetRef.close(); + } + } else { + this.sidesheetRef.close(result); } - } else { - this.sidesheetRef.close(result); - } - })); + }), + ); } public ngOnDestroy(): void { - this.subscriptions.forEach(s => s.unsubscribe()); + this.subscriptions.forEach((s) => s.unsubscribe()); this.disposable(); } public async ngOnInit(): Promise { @@ -94,7 +113,10 @@ export class NewUserComponent implements OnInit, OnDestroy { try { const data = await this.attApiClient.client.register_config_get(); - this.cdrList =this.cdrFactoryService.buildCdrFromColumnList(this.person.GetEntity(),data.WritablePropertiesForUnregisteredUsers); + this.cdrList = this.cdrFactoryService.buildCdrFromColumnList( + this.person.GetEntity(), + data.WritablePropertiesForUnregisteredUsers || [], + ); } finally { this.busy = false; this.cd.detectChanges(); @@ -103,10 +125,11 @@ export class NewUserComponent implements OnInit, OnDestroy { public async save(): Promise { // reset the error message - this.messageSvc.subject.next(undefined); + this.messageSvc.subject.next({ text: '' }); - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } try { // use response code this.person.extendedData = { Code: this.captchaSvc.Response }; @@ -118,17 +141,16 @@ export class NewUserComponent implements OnInit, OnDestroy { const parameterizedText: ParameterizedText = { value: template, marker: { start: '"%', end: '%"' }, - getParameterValue: columnName => this.person.GetEntity().GetColumn(columnName).GetDisplayValue() + getParameterValue: (columnName) => this.person.GetEntity().GetColumn(columnName).GetDisplayValue(), }; this.sidesheetRef.close(); this.disposable(); this.dialog.open(ConfirmDialogComponent, { data: parameterizedText }); - } catch (e) { throw e; } finally { this.captchaSvc.ReinitCaptcha(); - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } } } diff --git a/imxweb/projects/att/src/lib/new-user/new-user.module.ts b/imxweb/projects/att/src/lib/new-user/new-user.module.ts index 6a677a047..5dc32f3d1 100644 --- a/imxweb/projects/att/src/lib/new-user/new-user.module.ts +++ b/imxweb/projects/att/src/lib/new-user/new-user.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -78,7 +78,12 @@ const routes: Routes = [ declarations: [OpenSidesheetComponent, NewUserComponent, UserActivationComponent, ConfirmDialogComponent], }) export class NewUserModule { - constructor(authService: AuthenticationService, router: Router, apiService: ApiService, private readonly logger: ClassloggerService) { + constructor( + authService: AuthenticationService, + router: Router, + apiService: ApiService, + private readonly logger: ClassloggerService, + ) { const newUserAuthProvider: AuthConfigProvider = { display: '#LDS#Create new account', name: 'NewUser', diff --git a/imxweb/projects/att/src/lib/new-user/open-sidesheet.component.scss b/imxweb/projects/att/src/lib/new-user/open-sidesheet.component.scss deleted file mode 100644 index dac3e7996..000000000 --- a/imxweb/projects/att/src/lib/new-user/open-sidesheet.component.scss +++ /dev/null @@ -1,5 +0,0 @@ -@import "variables.scss"; - -.mat-raised-button { - background-color: $VI_Common_Color_Blue_3; -} \ No newline at end of file diff --git a/imxweb/projects/att/src/lib/new-user/open-sidesheet.component.ts b/imxweb/projects/att/src/lib/new-user/open-sidesheet.component.ts index 8f25f3e61..057e2d5f4 100644 --- a/imxweb/projects/att/src/lib/new-user/open-sidesheet.component.ts +++ b/imxweb/projects/att/src/lib/new-user/open-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,27 +28,26 @@ import { Component, OnInit } from '@angular/core'; import { EuiSidesheetConfig, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; +import { calculateSidesheetWidth } from 'qbm'; import { NewUserComponent } from './new-user.component'; @Component({ template: '', - styleUrls: ['./open-sidesheet.component.scss'] }) -export class OpenSidesheetComponent implements OnInit{ - +export class OpenSidesheetComponent implements OnInit { constructor( private readonly sidesheet: EuiSidesheetService, - private readonly translate: TranslateService) { - } - public async ngOnInit(): Promise { - const config: EuiSidesheetConfig = { - title: await this.translate.get('#LDS#Heading Create New Account').toPromise(), - width: 'max(650px, 60%)', - panelClass: 'imx-sidesheet', - padding: '0px', - disableClose: true, - testId: 'register-new-user', - }; - this.sidesheet.open(NewUserComponent, config); + private readonly translate: TranslateService, + ) {} + public async ngOnInit(): Promise { + const config: EuiSidesheetConfig = { + title: await this.translate.get('#LDS#Heading Create New Account').toPromise(), + width: calculateSidesheetWidth(1000), + panelClass: 'imx-sidesheet', + padding: '0px', + disableClose: true, + testId: 'register-new-user', + }; + this.sidesheet.open(NewUserComponent, config); } } diff --git a/imxweb/projects/att/src/lib/new-user/user-activation.component.html b/imxweb/projects/att/src/lib/new-user/user-activation.component.html index 6caf4ffdf..68cf5eb6f 100644 --- a/imxweb/projects/att/src/lib/new-user/user-activation.component.html +++ b/imxweb/projects/att/src/lib/new-user/user-activation.component.html @@ -1,23 +1,20 @@
    +

    {{ '#LDS#Heading Confirm Email Address' | translate }}

    -

    {{ '#LDS#Heading Confirm Email Address' | translate }}

    + - + + {{ ldsText | translate }} +
    + - - - - {{ldsText| translate}} -
    - - - -
    -
    + +
    +
    diff --git a/imxweb/projects/att/src/lib/new-user/user-activation.component.scss b/imxweb/projects/att/src/lib/new-user/user-activation.component.scss index ab44607d5..c8eb92fe9 100644 --- a/imxweb/projects/att/src/lib/new-user/user-activation.component.scss +++ b/imxweb/projects/att/src/lib/new-user/user-activation.component.scss @@ -6,14 +6,6 @@ left: 0; } -.button-bar { - margin-top: 1em; - - button:not(:last-child) { - margin-right: 1em; - } -} - .pagecontent { margin: 1em; } diff --git a/imxweb/projects/att/src/lib/new-user/user-activation.component.ts b/imxweb/projects/att/src/lib/new-user/user-activation.component.ts index a451e84c3..ae1049a9c 100644 --- a/imxweb/projects/att/src/lib/new-user/user-activation.component.ts +++ b/imxweb/projects/att/src/lib/new-user/user-activation.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,9 +27,9 @@ import { Component, OnDestroy } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { EuiLoadingService } from '@elemental-ui/core'; +import { PersonActivationDto } from '@imx-modules/imx-api-att'; import { TranslateService } from '@ngx-translate/core'; -import { PersonActivationDto } from 'imx-api-att'; -import { AuthenticationService, SessionState, SnackBarService, SplashService } from 'qbm'; +import { AuthenticationService, ConfirmationService, Message, SessionState, SnackBarService, SplashService, UserMessageService } from 'qbm'; import { Subscription } from 'rxjs'; import { ApiService } from '../api.service'; @@ -43,9 +43,10 @@ export class UserActivationComponent implements OnDestroy { public ldsText: string; public hasProblems = false; - private readonly subscription: Subscription; + private readonly subscriptions: Subscription[] = []; private passcode: string; private uidCase: string; + public message: Message | undefined; constructor( private readonly attApiService: ApiService, @@ -55,28 +56,43 @@ export class UserActivationComponent implements OnDestroy { private readonly translateService: TranslateService, private readonly busyService: EuiLoadingService, private readonly splashService: SplashService, - private readonly router: Router + private readonly router: Router, + private readonly confirmationService: ConfirmationService, + private readonly userMessageService: UserMessageService, ) { - this.subscription = route.queryParamMap.subscribe(async (params) => { - this.busy = true; - try { - this.uidCase = params.get('aeweb_UID_AttestationCase'); - this.passcode = params.get('aeweb_PassCode'); - if (!this.hasFormat) { - return; + this.subscriptions.push( + route.queryParamMap.subscribe(async (params) => { + this.busy = true; + try { + this.uidCase = params.get('aeweb_UID_AttestationCase') || ''; + this.passcode = params.get('aeweb_PassCode') || ''; + if (!this.hasFormat) { + return; + } + this.data = await this.attApiService.client.passwordreset_activation_init_post(this.uidCase); + + // Apply culture + this.data?.Culture ? this.useCulture(this.data.Culture) : null; + } catch { + this.hasProblems = true; + } finally { + this.determineText(); + this.splashService.close(); + this.busy = false; } - this.data = await this.attApiService.client.passwordreset_activation_init_post(this.uidCase); + }), + ); - // Apply culture - this.data?.Culture ? this.useCulture(this.data.Culture) : null; - } catch { - this.hasProblems = true; - } finally { - this.determineText(); - this.splashService.close(); - this.busy = false; - } - }); + this.subscriptions.push( + this.userMessageService.subject.subscribe((message) => { + this.message = message; + if (!!this.message) { + this.confirmationService.showErrorMessage({ + Message: this.message?.text, + }); + } + }), + ); } public get hasFormat(): boolean { @@ -84,7 +100,7 @@ export class UserActivationComponent implements OnDestroy { } public ngOnDestroy(): void { - this.subscription?.unsubscribe(); + this.subscriptions.forEach((subscription) => subscription.unsubscribe()); } public async resendMail(): Promise { @@ -134,8 +150,7 @@ export class UserActivationComponent implements OnDestroy { this.ldsText = '#LDS#Your registration was denied.'; break; default: - this.ldsText = - '#LDS#There was an unexpected error. Please try again.'; + this.ldsText = '#LDS#There was an unexpected error. Please try again.'; } return; } diff --git a/imxweb/projects/att/src/lib/pick-category/pick-category-common.scss b/imxweb/projects/att/src/lib/pick-category/pick-category-common.scss index e46bdd2ad..402d7ab9e 100644 --- a/imxweb/projects/att/src/lib/pick-category/pick-category-common.scss +++ b/imxweb/projects/att/src/lib/pick-category/pick-category-common.scss @@ -1,30 +1,7 @@ -@mixin imx-flex-column-container { - display: flex; - flex-direction: column; -} - -@mixin imx-flex-row-container { - display: flex; - flex-direction: row; -} - -.eui-sidesheet-content { - @include imx-flex-column-container(); -} +@import 'base/mixins'; .heading-wrapper { - @include imx-flex-row-container(); + @include flex-row-container(); flex: 0 0 auto; align-items: flex-start; - - .helper-alert { - display: flex; - margin-bottom: 15px; - } - - .alert-wrapper { - margin: 0 0 0 auto; - align-self: flex-end; - width: 50%; - } } diff --git a/imxweb/projects/att/src/lib/pick-category/pick-category-create/pick-category-create.component.html b/imxweb/projects/att/src/lib/pick-category/pick-category-create/pick-category-create.component.html index f216c170a..09dd85e9a 100644 --- a/imxweb/projects/att/src/lib/pick-category/pick-category-create/pick-category-create.component.html +++ b/imxweb/projects/att/src/lib/pick-category/pick-category-create/pick-category-create.component.html @@ -1,6 +1,6 @@
    - - + +
    -
    - -
    @@ -40,28 +46,28 @@ {{ '#LDS#The following sample will be created:' | translate }} - +
    {{ '#LDS#Assigned identities:' | translate }}
    - - - - - - + + + + {{ entitySchema?.Columns?.[DisplayColumns.DISPLAY_PROPERTYNAME]?.Display }} + + {{ item.GetEntity().GetDisplay() }} + +
    - +
    -
    diff --git a/imxweb/projects/att/src/lib/pick-category/pick-category-create/pick-category-create.component.scss b/imxweb/projects/att/src/lib/pick-category/pick-category-create/pick-category-create.component.scss index ff4eba68b..6538ce992 100644 --- a/imxweb/projects/att/src/lib/pick-category/pick-category-create/pick-category-create.component.scss +++ b/imxweb/projects/att/src/lib/pick-category/pick-category-create/pick-category-create.component.scss @@ -1,110 +1,25 @@ -@import "../pick-category-common.scss"; -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; +@import 'base/mixins'; +@import '../pick-category-common.scss'; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; :host { background-color: $color-gray-2; .eui-sidesheet-content { - > .mat-card { - display: flex; - flex-direction: column; - overflow: hidden; - } - .imx-dst-selected-identities { display: none; } - - div.imx-table-container { - display: flex; - flex-direction: column; - overflow: hidden; - height: inherit; - margin-bottom: 20px; - } - - .mat-stepper-vertical { - @include imx-flex-column-container(); - max-height: 100%; - - ::ng-deep .mat-step { - @include imx-flex-column-container(); - max-height: calc(100% - 144px); - flex: 1 1 auto; - - .mat-vertical-content-container { - @include imx-flex-column-container(); - height: calc(100% - 72px); - - .mat-vertical-content { - @include imx-flex-column-container(); - height: 100%; - flex: 1 1 auto; - - imx-pick-category-select-identities { - @include imx-flex-column-container(); - max-height: calc(100% - 36px); - - imx-data-source-toolbar { - background-color: $color-gray-0; - } - } - - .imx-stepper-button-container { - @include imx-flex-row-container(); - gap: 20px; - } - } - } - } - - .imx-summary-intro { - margin: 15px 0; - } - } } } .eui-dark-theme { :host { background-color: $color-gray-80; - - .eui-sidesheet-content { - .mat-stepper-vertical { - ::ng-deep .mat-step { - .mat-vertical-content-container { - .mat-vertical-content { - imx-pick-category-select-identities { - imx-data-source-toolbar { - background-color: $color-gray-80; - } - } - } - } - } - } - } } } .eui-contrast-theme { :host { background-color: $color-gray-100; - - .eui-sidesheet-content { - .mat-stepper-vertical { - ::ng-deep .mat-step { - .mat-vertical-content-container { - .mat-vertical-content { - imx-pick-category-select-identities { - imx-data-source-toolbar { - background-color: $color-gray-100; - } - } - } - } - } - } - } } } diff --git a/imxweb/projects/att/src/lib/pick-category/pick-category-create/pick-category-create.component.spec.ts b/imxweb/projects/att/src/lib/pick-category/pick-category-create/pick-category-create.component.spec.ts index e79243f27..aa21ec0d2 100644 --- a/imxweb/projects/att/src/lib/pick-category/pick-category-create/pick-category-create.component.spec.ts +++ b/imxweb/projects/att/src/lib/pick-category/pick-category-create/pick-category-create.component.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,24 +27,25 @@ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; +import { PortalPickcategoryItems } from '@imx-modules/imx-api-qer'; import { of } from 'rxjs'; -import { PortalPickcategoryItems } from 'imx-api-qer'; -import { ConfirmationService } from 'qbm'; +import { PortalPersonAll } from '@imx-modules/imx-api-qer'; +import { ClassloggerService, ConfirmationService, MessageDialogService, SqlWizardApiService } from 'qbm'; import { PickCategoryService } from '../pick-category.service'; import { PickCategoryCreateComponent } from './pick-category-create.component'; +import { IdentitiesService } from 'qer'; + describe('PickCategoryCreateComponent', () => { let component: PickCategoryCreateComponent; let fixture: ComponentFixture; - let confirm = true; const mockConfirmationService = { - confirm: jasmine.createSpy('confirm') - .and.callFake(() => Promise.resolve(confirm)) - } + confirm: jasmine.createSpy('confirm').and.callFake(() => Promise.resolve(confirm)), + }; const sidesheetData = { pickCategory: { @@ -59,16 +60,9 @@ describe('PickCategoryCreateComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ - PickCategoryCreateComponent - ], - schemas: [ - CUSTOM_ELEMENTS_SCHEMA - ], - imports: [ - FormsModule, - ReactiveFormsModule - ], + declarations: [PickCategoryCreateComponent], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + imports: [FormsModule, ReactiveFormsModule], providers: [ { provide: EUI_SIDESHEET_DATA, @@ -77,13 +71,13 @@ describe('PickCategoryCreateComponent', () => { { provide: EuiSidesheetRef, useValue: { - close: jasmine.createSpy('close'), + close: jasmine.createSpy('close'), closeClicked: jasmine.createSpy('closeClicked').and.returnValue(of(undefined)), - } + }, }, { provide: ConfirmationService, - useValue: mockConfirmationService + useValue: mockConfirmationService, }, { provide: PickCategoryService, @@ -92,12 +86,20 @@ describe('PickCategoryCreateComponent', () => { deletePickCategoryItems: jasmine.createSpy('deletePickCategoryItems').and.returnValue({}), getPickCategoryItems: jasmine.createSpy('getPickCategoryItems').and.returnValue({}), handleOpenLoader: jasmine.createSpy('handleOpenLoader').and.callThrough(), - handleCloseLoader: jasmine.createSpy('handleCloseLoader').and.callThrough() - } - } - ] - }) - .compileComponents(); + handleCloseLoader: jasmine.createSpy('handleCloseLoader').and.callThrough(), + }, + }, + { + provide: IdentitiesService, + useValue: { + personAllSchema: PortalPersonAll.GetEntitySchema(), + }, + }, + { provide: ClassloggerService, useValue: { debug: () => {} } }, + { provide: MessageDialogService, useValue: {} }, + { provide: SqlWizardApiService, useValue: {} }, + ], + }).compileComponents(); }); beforeEach(() => { diff --git a/imxweb/projects/att/src/lib/pick-category/pick-category-create/pick-category-create.component.ts b/imxweb/projects/att/src/lib/pick-category/pick-category-create/pick-category-create.component.ts index 8706d411d..7e1131188 100644 --- a/imxweb/projects/att/src/lib/pick-category/pick-category-create/pick-category-create.component.ts +++ b/imxweb/projects/att/src/lib/pick-category/pick-category-create/pick-category-create.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,69 +27,56 @@ import { StepperSelectionEvent } from '@angular/cdk/stepper'; import { Component, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { UntypedFormGroup } from '@angular/forms'; -import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; import { Subscription } from 'rxjs'; -import { PortalPickcategory, PortalPickcategoryItems } from 'imx-api-qer'; -import { DisplayColumns } from 'imx-qbm-dbts'; +import { PortalPersonAll, PortalPickcategory, PortalPickcategoryItems } from '@imx-modules/imx-api-qer'; +import { DisplayColumns, EntitySchema, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; -import { - BaseCdr, - ColumnDependentReference, - ConfirmationService, - DataSourceToolbarSettings, - DataSourceWrapper, -} from 'qbm'; +import { BaseCdr, ColumnDependentReference, ConfirmationService, DataViewInitParameters, DataViewSource } from 'qbm'; +import { IdentitiesService } from 'qer'; import { PickCategorySelectIdentitiesComponent } from '../pick-category-select-identities/pick-category-select-identities.component'; import { PickCategoryService } from '../pick-category.service'; @Component({ selector: 'imx-pick-category-create', templateUrl: './pick-category-create.component.html', - styleUrls: ['./pick-category-create.component.scss'] + styleUrls: ['./pick-category-create.component.scss'], + providers: [DataViewSource], }) export class PickCategoryCreateComponent implements OnInit, OnDestroy { - public readonly displayNameForm = new UntypedFormGroup({}); - - public readonly dstWrapper: DataSourceWrapper; - public dstSettings: DataSourceToolbarSettings; - + public readonly DisplayColumns = DisplayColumns; public selectedItems: PortalPickcategoryItems[] = []; - public displayNameCdr: ColumnDependentReference; public displayNameReadonlyCdr: ColumnDependentReference; - + public entitySchema: EntitySchema; + private readonly subscriptions: Subscription[] = []; @ViewChild(PickCategorySelectIdentitiesComponent) public selectIndentities: PickCategorySelectIdentitiesComponent; constructor( - @Inject(EUI_SIDESHEET_DATA) public data: { - pickCategory: PortalPickcategory + @Inject(EUI_SIDESHEET_DATA) + public data: { + pickCategory: PortalPickcategory; }, private readonly sidesheetRef: EuiSidesheetRef, - readonly pickCategoryService: PickCategoryService, - confirmation: ConfirmationService + readonly pickCategoryService: PickCategoryService, + confirmation: ConfirmationService, + private readonly identityService: IdentitiesService, + public dataSource: DataViewSource, ) { - const entitySchema = pickCategoryService.pickcategoryItemsSchema; - - this.dstWrapper = new DataSourceWrapper( - () => Promise.resolve({ - totalCount: this.selectIndentities.selection?.length, - Data: this.selectIndentities.selection + this.entitySchema = this.identityService.personAllSchema; + this.subscriptions.push( + sidesheetRef.closeClicked().subscribe(async () => { + if (!this.displayNameForm.pristine && !(await confirmation.confirmLeaveWithUnsavedChanges())) { + return; + } + + sidesheetRef.close(false); }), - [entitySchema.Columns[DisplayColumns.DISPLAY_PROPERTYNAME]], - entitySchema ); - - this.subscriptions.push(sidesheetRef.closeClicked().subscribe(async () => { - if (!this.displayNameForm.pristine && !(await confirmation.confirmLeaveWithUnsavedChanges())) { - return; - } - - sidesheetRef.close(false); - })); } public async ngOnInit(): Promise { @@ -99,7 +86,7 @@ export class PickCategoryCreateComponent implements OnInit, OnDestroy { } public ngOnDestroy(): void { - this.subscriptions.forEach(s => s.unsubscribe()); + this.subscriptions.forEach((s) => s.unsubscribe()); } public async stepChange(change: StepperSelectionEvent): Promise { @@ -117,11 +104,18 @@ export class PickCategoryCreateComponent implements OnInit, OnDestroy { this.sidesheetRef.close({ create: true, pickCategory: this.data.pickCategory, - pickedItems: this.selectIndentities?.selection + pickedItems: this.selectIndentities?.selection, }); } private async getData(): Promise { - this.dstSettings = await this.dstWrapper.getDstSettings(); + const dataViewInitParameters: DataViewInitParameters = { + execute: (): Promise> => + Promise.resolve({ Data: this.selectIndentities.selection, totalCount: this.selectIndentities.selection.length }), + schema: this.entitySchema, + columnsToDisplay: [this.entitySchema.Columns[DisplayColumns.DISPLAY_PROPERTYNAME]], + localSource: true, + }; + this.dataSource.init(dataViewInitParameters); } } diff --git a/imxweb/projects/att/src/lib/pick-category/pick-category-select-identities/pick-category-select-identities.component.html b/imxweb/projects/att/src/lib/pick-category/pick-category-select-identities/pick-category-select-identities.component.html index a73245314..892e992e7 100644 --- a/imxweb/projects/att/src/lib/pick-category/pick-category-select-identities/pick-category-select-identities.component.html +++ b/imxweb/projects/att/src/lib/pick-category/pick-category-select-identities/pick-category-select-identities.component.html @@ -1,45 +1,36 @@ - - +
    - - +
    -
    -

    - - {{'#LDS#Selected identities' | translate}} - - - {{selection?.length > 0 ? selection.length : 0}} - -

    -
    - - - - - - - - - - + + + + + + {{ entitySchema?.Columns?.[DisplayColumns.DISPLAY_PROPERTYNAME]?.Display }} + + {{ item.GetEntity().GetDisplay() }} + + + diff --git a/imxweb/projects/att/src/lib/pick-category/pick-category-select-identities/pick-category-select-identities.component.scss b/imxweb/projects/att/src/lib/pick-category/pick-category-select-identities/pick-category-select-identities.component.scss index 74ca43dc5..9c7bc0c1f 100644 --- a/imxweb/projects/att/src/lib/pick-category/pick-category-select-identities/pick-category-select-identities.component.scss +++ b/imxweb/projects/att/src/lib/pick-category/pick-category-select-identities/pick-category-select-identities.component.scss @@ -1,43 +1,11 @@ -@import "../pick-category-common.scss"; -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; +@import '../pick-category-common.scss'; :host { - background-color: $color-gray-2; - ::ng-deep .cdk-column-select { width: 50px; } - .imx-selected-items { - margin: 0; - line-height: 36px; - display: flex; - flex: 1 0 auto; - margin: auto; - align-items: center; - - .imx-badge { - background-color: transparent; - ::ng-deep .eui-badge-content { - font-size: 14px; - line-height: 14px; - } - } - } - - button { - margin-left: 10px; - } -} - -.eui-dark-theme { - :host { - background-color: $color-gray-80; - } -} - -.eui-contrast-theme { - :host { - background-color: $color-gray-100; + .justify-start { + margin-right: auto; } } diff --git a/imxweb/projects/att/src/lib/pick-category/pick-category-select-identities/pick-category-select-identities.component.spec.ts b/imxweb/projects/att/src/lib/pick-category/pick-category-select-identities/pick-category-select-identities.component.spec.ts index b4a4149dc..594c1cea9 100644 --- a/imxweb/projects/att/src/lib/pick-category/pick-category-select-identities/pick-category-select-identities.component.spec.ts +++ b/imxweb/projects/att/src/lib/pick-category/pick-category-select-identities/pick-category-select-identities.component.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,10 +26,10 @@ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; -import { PortalPersonAll } from 'imx-api-qer'; +import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; +import { PortalPersonAll } from '@imx-modules/imx-api-qer'; -import { MetadataService } from 'qbm'; +import { ClassloggerService, ConfirmationService, MessageDialogService, MetadataService, SqlWizardApiService } from 'qbm'; import { IdentitiesService } from 'qer'; import { PickCategoryService } from '../pick-category.service'; import { PickCategorySelectIdentitiesComponent } from './pick-category-select-identities.component'; @@ -51,17 +51,13 @@ describe('PickCategorySelectIdentitiesComponent', () => { const metadataServiceStub = { tables: {}, - update: jasmine.createSpy('update') + update: jasmine.createSpy('update'), }; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - declarations: [ - PickCategorySelectIdentitiesComponent - ], - schemas: [ - CUSTOM_ELEMENTS_SCHEMA - ], + declarations: [PickCategorySelectIdentitiesComponent], + schemas: [CUSTOM_ELEMENTS_SCHEMA], providers: [ { provide: EUI_SIDESHEET_DATA, @@ -69,40 +65,41 @@ describe('PickCategorySelectIdentitiesComponent', () => { }, { provide: EuiSidesheetRef, - useValue: {} + useValue: {}, }, { provide: MetadataService, - useValue: metadataServiceStub + useValue: metadataServiceStub, }, { provide: PickCategoryService, useValue: { handleOpenLoader: jasmine.createSpy('handleOpenLoader').and.callThrough(), - handleCloseLoader: jasmine.createSpy('handleCloseLoader').and.callThrough() - } + handleCloseLoader: jasmine.createSpy('handleCloseLoader').and.callThrough(), + }, }, { provide: IdentitiesService, useValue: { personAllSchema: PortalPersonAll.GetEntitySchema(), - getAllPerson: jasmine.createSpy('getAllPerson').and.returnValue(Promise.resolve( - { totalCount: 100, Data: ['1', '2', '3'] } - )), - } + getAllPerson: jasmine.createSpy('getAllPerson').and.returnValue(Promise.resolve({ totalCount: 100, Data: ['1', '2', '3'] })), + }, }, - ] - }) - .compileComponents(); + { provide: ClassloggerService, useValue: { debug: () => {} } }, + { provide: MessageDialogService, useValue: {} }, + { provide: ConfirmationService, useValue: {} }, + { provide: SqlWizardApiService, useValue: {} }, + ], + }).compileComponents(); })); -beforeEach(() => { - fixture = TestBed.createComponent(PickCategorySelectIdentitiesComponent); - component = fixture.componentInstance; - fixture.detectChanges(); -}); + beforeEach(() => { + fixture = TestBed.createComponent(PickCategorySelectIdentitiesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); -it('should create', () => { - expect(component).toBeTruthy(); -}); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/imxweb/projects/att/src/lib/pick-category/pick-category-select-identities/pick-category-select-identities.component.ts b/imxweb/projects/att/src/lib/pick-category/pick-category-select-identities/pick-category-select-identities.component.ts index 3f64c9fdf..d808d6ffa 100644 --- a/imxweb/projects/att/src/lib/pick-category/pick-category-select-identities/pick-category-select-identities.component.ts +++ b/imxweb/projects/att/src/lib/pick-category/pick-category-select-identities/pick-category-select-identities.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,36 +25,35 @@ */ import { Component, Inject, Input, OnInit } from '@angular/core'; -import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; -import { PortalPersonAll, PortalPickcategoryItems } from 'imx-api-qer'; +import { PortalPersonAll, PortalPickcategoryItems } from '@imx-modules/imx-api-qer'; import { CollectionLoadParameters, CompareOperator, DbObjectKey, DisplayColumns, + EntitySchema, FilterData, FilterType, IClientProperty, - TypedEntity -} from 'imx-qbm-dbts'; + TypedEntityCollectionData, +} from '@imx-modules/imx-qbm-dbts'; -import { DataSourceToolbarSettings, DataSourceWrapper, MetadataService } from 'qbm'; +import { DataViewInitParameters, DataViewSource, MetadataService } from 'qbm'; import { IdentitiesService } from 'qer'; -import { PickCategoryService } from '../pick-category.service'; @Component({ selector: 'imx-pick-category-select-identities', templateUrl: './pick-category-select-identities.component.html', styleUrls: ['./pick-category-select-identities.component.scss'], + providers: [DataViewSource], }) - export class PickCategorySelectIdentitiesComponent implements OnInit { - - public readonly dstWrapper: DataSourceWrapper; - public dstSettings: DataSourceToolbarSettings; public displayColumns: IClientProperty[]; - public selection: TypedEntity[]; + public selection: PortalPersonAll[]; + public entitySchema: EntitySchema; + public DisplayColumns = DisplayColumns; @Input() public embeddedMode = false; @@ -62,37 +61,30 @@ export class PickCategorySelectIdentitiesComponent implements OnInit { @Inject(EUI_SIDESHEET_DATA) public selectedItems: PortalPickcategoryItems[], private readonly sidesheetRef: EuiSidesheetRef, private readonly identityService: IdentitiesService, - private readonly pickCategoryService: PickCategoryService, private readonly metadataService: MetadataService, + public dataSource: DataViewSource, ) { - const entitySchema = this.identityService.personAllSchema; - - this.dstWrapper = new DataSourceWrapper( - state => this.identityService.getAllPerson(state), - [entitySchema.Columns[DisplayColumns.DISPLAY_PROPERTYNAME]], - entitySchema - ); + this.entitySchema = this.identityService.personAllSchema; } public async ngOnInit(): Promise { await this.getData(); } - public async getData(navigationState?: CollectionLoadParameters): Promise { - this.pickCategoryService.handleOpenLoader(); - - try { - navigationState = { - ...navigationState, - ... { filter: await this.getFilter() } - }; - this.dstSettings = await this.dstWrapper.getDstSettings(navigationState); - } finally { - this.pickCategoryService.handleCloseLoader(); - } + public async getData(): Promise { + const dataViewInitParameters: DataViewInitParameters = { + execute: async (params: CollectionLoadParameters, signal: AbortSignal): Promise> => { + const filters = await this.getFilter(); + return this.identityService.getAllPerson({ ...params, filter: [...(params.filter || []), ...filters] }, signal); + }, + schema: this.entitySchema, + columnsToDisplay: [this.entitySchema.Columns[DisplayColumns.DISPLAY_PROPERTYNAME]], + selectionChange: (selection: Array) => this.onSelectionChanged(selection), + }; + this.dataSource.init(dataViewInitParameters); } - public onSelectionChanged(selection: TypedEntity[]): void { + public onSelectionChanged(selection: PortalPersonAll[]): void { this.selection = selection; } @@ -102,21 +94,20 @@ export class PickCategorySelectIdentitiesComponent implements OnInit { private async getFilter(): Promise { if (this.selectedItems && this.selectedItems.length > 0) { - const tableName = DbObjectKey.FromXml(this.selectedItems[0].ObjectKeyItem.value).TableName; await this.metadataService.updateNonExisting([tableName]); const tableMetadata = this.metadataService.tables[tableName]; - return this.selectedItems.map(item => { + return this.selectedItems.map((item) => { return { - ColumnName: tableMetadata.PrimaryKeyColumns[0], + ColumnName: tableMetadata?.PrimaryKeyColumns?.[0] || '', Type: FilterType.Compare, CompareOp: CompareOperator.NotEqual, - Value1: DbObjectKey.FromXml(item.ObjectKeyItem.value).Keys[0] + Value1: DbObjectKey.FromXml(item.ObjectKeyItem.value).Keys[0], }; }); } + return []; } - } diff --git a/imxweb/projects/att/src/lib/pick-category/pick-category-sidesheet/pick-category-sidesheet.component.html b/imxweb/projects/att/src/lib/pick-category/pick-category-sidesheet/pick-category-sidesheet.component.html index 7ea95b1a8..44a110f4d 100644 --- a/imxweb/projects/att/src/lib/pick-category/pick-category-sidesheet/pick-category-sidesheet.component.html +++ b/imxweb/projects/att/src/lib/pick-category/pick-category-sidesheet/pick-category-sidesheet.component.html @@ -1,40 +1,53 @@
    - + - - {{'#LDS#Assigned identities' | translate}} - - - - - - - - -
    - -
    -
    - +
    +
    diff --git a/imxweb/projects/att/src/lib/pick-category/pick-category-sidesheet/pick-category-sidesheet.component.scss b/imxweb/projects/att/src/lib/pick-category/pick-category-sidesheet/pick-category-sidesheet.component.scss index 3bdae3d1e..bd1d96cb0 100644 --- a/imxweb/projects/att/src/lib/pick-category/pick-category-sidesheet/pick-category-sidesheet.component.scss +++ b/imxweb/projects/att/src/lib/pick-category/pick-category-sidesheet/pick-category-sidesheet.component.scss @@ -1,31 +1,12 @@ -@import "../pick-category-common.scss"; -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; +@import 'base/mixins'; +@import '../pick-category-common.scss'; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; :host { background-color: $color-gray-2; - - .imx-table-container { - @include imx-flex-column-container(); - overflow: hidden; - - > span { - margin-bottom: 10px; - display: block; - } - - .buttons { - @include imx-flex-row-container(); - justify-content: flex-end; - - .justify-start { - margin-right: auto; - - eui-icon { - font-size: 14px; - margin-right: 4px; - } - } - } + .eui-sidesheet-content{ + display: flex; + flex-direction: column; } } diff --git a/imxweb/projects/att/src/lib/pick-category/pick-category-sidesheet/pick-category-sidesheet.component.spec.ts b/imxweb/projects/att/src/lib/pick-category/pick-category-sidesheet/pick-category-sidesheet.component.spec.ts index 3a9e8a5d2..44e7abd65 100644 --- a/imxweb/projects/att/src/lib/pick-category/pick-category-sidesheet/pick-category-sidesheet.component.spec.ts +++ b/imxweb/projects/att/src/lib/pick-category/pick-category-sidesheet/pick-category-sidesheet.component.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,18 +24,17 @@ * */ - import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { EuiSidesheetService, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { EUI_SIDESHEET_DATA, EuiSidesheetService } from '@elemental-ui/core'; -import { IClientProperty } from 'imx-qbm-dbts'; -import { ClassloggerService, ConfirmationService } from 'qbm'; +import { IClientProperty } from '@imx-modules/imx-qbm-dbts'; +import { ClassloggerService, ConfirmationService, SqlWizardApiService } from 'qbm'; -import { PickCategorySidesheetComponent } from './pick-category-sidesheet.component'; import { PickCategoryService } from '../pick-category.service'; +import { PickCategorySidesheetComponent } from './pick-category-sidesheet.component'; describe('PickCategorySidesheetComponent', () => { let component: PickCategorySidesheetComponent; @@ -49,30 +48,21 @@ describe('PickCategorySidesheetComponent', () => { GetColumn: () => ({ GetValue: () => ({}) }), }), }, - isNew: false + isNew: false, }; let confirm = true; const mockConfirmationService = { - confirm: jasmine.createSpy('confirm') - .and.callFake(() => Promise.resolve(confirm)) - } + confirm: jasmine.createSpy('confirm').and.callFake(() => Promise.resolve(confirm)), + }; const deletePickedItemsSpy = jasmine.createSpy('deletePickedItems').and.returnValue(Promise.resolve({})); beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ - PickCategorySidesheetComponent - ], - schemas: [ - CUSTOM_ELEMENTS_SCHEMA - ], - imports: [ - FormsModule, - ReactiveFormsModule, - MatSnackBarModule - ], + declarations: [PickCategorySidesheetComponent], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + imports: [FormsModule, ReactiveFormsModule, MatSnackBarModule], providers: [ { provide: EUI_SIDESHEET_DATA, @@ -80,18 +70,18 @@ describe('PickCategorySidesheetComponent', () => { }, { provide: EuiSidesheetService, - useValue: {} + useValue: {}, }, { provide: ConfirmationService, - useValue: mockConfirmationService + useValue: mockConfirmationService, }, { provide: ClassloggerService, useValue: { debug: jasmine.createSpy('debug').and.callThrough(), - trace: jasmine.createSpy('trace').and.callThrough() - } + trace: jasmine.createSpy('trace').and.callThrough(), + }, }, { provide: PickCategoryService, @@ -100,12 +90,12 @@ describe('PickCategorySidesheetComponent', () => { deletePickedItems: deletePickedItemsSpy, getPickCategoryItems: jasmine.createSpy('getPickCategoryItems').and.returnValue({}), handleOpenLoader: jasmine.createSpy('handleOpenLoader').and.callThrough(), - handleCloseLoader: jasmine.createSpy('handleCloseLoader').and.callThrough() - } - } - ] - }) - .compileComponents(); + handleCloseLoader: jasmine.createSpy('handleCloseLoader').and.callThrough(), + }, + }, + { provide: SqlWizardApiService, useValue: {} }, + ], + }).compileComponents(); }); beforeEach(() => { @@ -120,15 +110,12 @@ describe('PickCategorySidesheetComponent', () => { }); describe('removePickedItems() tests', () => { - for (const testcase of [ - { confirm: true }, - { confirm: false } - ]) { + for (const testcase of [{ confirm: true }, { confirm: false }]) { it('should make a call to delete the picked items, if the user confirm the dialog', async () => { confirm = testcase.confirm; await component.removePickedItems(); - if(testcase.confirm) { + if (testcase.confirm) { expect(deletePickedItemsSpy).toHaveBeenCalled(); } else { expect(deletePickedItemsSpy).not.toHaveBeenCalled(); diff --git a/imxweb/projects/att/src/lib/pick-category/pick-category-sidesheet/pick-category-sidesheet.component.ts b/imxweb/projects/att/src/lib/pick-category/pick-category-sidesheet/pick-category-sidesheet.component.ts index fe345c3de..5b57ac0f7 100644 --- a/imxweb/projects/att/src/lib/pick-category/pick-category-sidesheet/pick-category-sidesheet.component.ts +++ b/imxweb/projects/att/src/lib/pick-category/pick-category-sidesheet/pick-category-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,61 +24,55 @@ * */ -import { Component, Inject, OnInit, ViewChild } from '@angular/core'; -import { EuiSidesheetService, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { Component, Inject, OnInit } from '@angular/core'; +import { EUI_SIDESHEET_DATA, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { PortalPickcategory, PortalPickcategoryItems } from 'imx-api-qer'; -import { CollectionLoadParameters, DisplayColumns, TypedEntity } from 'imx-qbm-dbts'; +import { PortalPickcategory, PortalPickcategoryItems } from '@imx-modules/imx-api-qer'; +import { CollectionLoadParameters, DisplayColumns, EntitySchema, TypedEntity, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; +import { UntypedFormGroup } from '@angular/forms'; import { BaseCdr, + calculateSidesheetWidth, ClassloggerService, ConfirmationService, - DataSourceToolbarSettings, - DataSourceWrapper, - DataTableComponent, + DataViewInitParameters, + DataViewSource, SnackBarService, } from 'qbm'; import { PickCategorySelectIdentitiesComponent } from '../pick-category-select-identities/pick-category-select-identities.component'; import { PickCategoryService } from '../pick-category.service'; -import { UntypedFormGroup } from '@angular/forms'; @Component({ selector: 'imx-pick-category-sidesheet', templateUrl: './pick-category-sidesheet.component.html', - styleUrls: ['./pick-category-sidesheet.component.scss'] + styleUrls: ['./pick-category-sidesheet.component.scss'], + providers: [DataViewSource], }) export class PickCategorySidesheetComponent implements OnInit { - - public readonly dstWrapper: DataSourceWrapper; public readonly form = new UntypedFormGroup({}); - public dstSettings: DataSourceToolbarSettings; public selectedPickedItems: PortalPickcategoryItems[] = []; public displayNameCdr: any; - - @ViewChild(DataTableComponent) private table: DataTableComponent; + public entitySchema: EntitySchema; + public DisplayColumns = DisplayColumns; private uidPickCategory: string; constructor( - @Inject(EUI_SIDESHEET_DATA) public data: { - pickCategory: PortalPickcategory + @Inject(EUI_SIDESHEET_DATA) + public data: { + pickCategory: PortalPickcategory; }, private readonly sidesheet: EuiSidesheetService, private readonly snackBar: SnackBarService, private readonly confirmationService: ConfirmationService, private readonly pickCategoryService: PickCategoryService, private readonly translate: TranslateService, - private readonly logger: ClassloggerService + private readonly logger: ClassloggerService, + public dataSource: DataViewSource, ) { - const entitySchema = this.pickCategoryService.pickcategoryItemsSchema; - - this.dstWrapper = new DataSourceWrapper( - state => this.pickCategoryService.getPickCategoryItems(this.uidPickCategory, state), - [entitySchema.Columns[DisplayColumns.DISPLAY_PROPERTYNAME]], - entitySchema - ); + this.entitySchema = this.pickCategoryService.pickcategoryItemsSchema; } public async ngOnInit(): Promise { @@ -88,50 +82,55 @@ export class PickCategorySidesheetComponent implements OnInit { await this.getData(); } - public async getData(newState?: CollectionLoadParameters): Promise { - this.pickCategoryService.handleOpenLoader(); - try { - this.dstSettings = await this.dstWrapper.getDstSettings(newState); - } finally { - this.pickCategoryService.handleCloseLoader(); - } + public async getData(): Promise { + const dataViewInitParameters: DataViewInitParameters = { + execute: (params: CollectionLoadParameters): Promise> => + this.pickCategoryService.getPickCategoryItems(this.uidPickCategory, params), + schema: this.entitySchema, + columnsToDisplay: [this.entitySchema.Columns[DisplayColumns.DISPLAY_PROPERTYNAME]], + selectionChange: (selection: Array) => this.onSelectionChanged(selection), + }; + this.dataSource.init(dataViewInitParameters); } public selectedItemsCanBeDeleted(): boolean { - return this.selectedPickedItems != null && - this.selectedPickedItems.length > 0 && - this.data.pickCategory.IsManual.value; + return this.selectedPickedItems != null && this.selectedPickedItems.length > 0 && this.data.pickCategory.IsManual.value; } - public onSelectionChanged(items: PortalPickcategoryItems[]): void { + public onSelectionChanged(items: TypedEntity[]): void { this.logger.trace(this, 'selection changed', items); - this.selectedPickedItems = items; + this.selectedPickedItems = items as PortalPickcategoryItems[]; } public async assignPickedItems(): Promise { - const selection = await this.sidesheet.open(PickCategorySelectIdentitiesComponent, { - title: await this.translate.get('#LDS#Heading Assign Identities').toPromise(), - subTitle: this.data.pickCategory.GetEntity().GetDisplay(), - padding: '0px', - width: '700px', - disableClose: false, - testId: 'pick-category-select-identities', - data: this.dstSettings.dataSource.Data - }).afterClosed().toPromise(); + const selection = await this.sidesheet + .open(PickCategorySelectIdentitiesComponent, { + title: await this.translate.get('#LDS#Heading Assign Identities').toPromise(), + subTitle: this.data.pickCategory.GetEntity().GetDisplay(), + padding: '0px', + width: calculateSidesheetWidth(700, 0.4), + disableClose: false, + testId: 'pick-category-select-identities', + data: this.dataSource?.data, + }) + .afterClosed() + .toPromise(); if (selection && (await this.pickCategoryService.createPickedItems(selection, this.uidPickCategory)) > 0) { - await this.getData(); + this.dataSource.updateState(); } } public async removePickedItems(): Promise { - if (await this.confirmationService.confirm({ - Title: '#LDS#Heading Remove Identities', - Message: '#LDS#Are you sure you want to remove the selected identities?' - })) { - if (await this.pickCategoryService.deletePickedItems(this.uidPickCategory, this.selectedPickedItems) > 0) { - await this.getData(); - this.table?.clearSelection(); + if ( + await this.confirmationService.confirm({ + Title: '#LDS#Heading Remove Identities', + Message: '#LDS#Are you sure you want to remove the selected identities?', + }) + ) { + if ((await this.pickCategoryService.deletePickedItems(this.uidPickCategory, this.selectedPickedItems)) > 0) { + this.dataSource.updateState(); + this.dataSource.selection.clear(); } } } @@ -149,5 +148,4 @@ export class PickCategorySidesheetComponent implements OnInit { } } } - } diff --git a/imxweb/projects/att/src/lib/pick-category/pick-category.component.html b/imxweb/projects/att/src/lib/pick-category/pick-category.component.html index 02f0b87b9..f7efadae5 100644 --- a/imxweb/projects/att/src/lib/pick-category/pick-category.component.html +++ b/imxweb/projects/att/src/lib/pick-category/pick-category.component.html @@ -1,47 +1,43 @@
    -
    -

    - {{ '#LDS#Heading Sample Data' | translate }} +
    +

    + {{ '#LDS#Heading Sampling Data' | translate }} -

    +

    +
    -
    - - - - - - - +
    + + + {{ entitySchema?.Columns?.DisplayName?.Display }} + + {{ item.GetEntity().GetColumn('DisplayName').GetDisplayValue() }} + + + + {{ entitySchema?.Columns?.IsManual?.Display }} + + {{ item.GetEntity().GetColumn('IsManual').GetDisplayValue() }} + + + +
    -
    - -
    - - diff --git a/imxweb/projects/att/src/lib/pick-category/pick-category.component.scss b/imxweb/projects/att/src/lib/pick-category/pick-category.component.scss index f1608e184..7c9b371f9 100644 --- a/imxweb/projects/att/src/lib/pick-category/pick-category.component.scss +++ b/imxweb/projects/att/src/lib/pick-category/pick-category.component.scss @@ -1,43 +1,17 @@ -@import '../../../../../shared/scss/common-table.scss'; +@import 'base/mixins'; @import 'pick-category-common.scss'; :host { - @include imx-flex-column-container(); - overflow: hidden; - height: inherit; - display: flex; - flex-direction: column; - max-width: 100%; + @include flex-column-container($overflow: hidden, $height: inherit, $max-width: 100%); } .imx-pickcategory-page { background-color: inherit; - @include imx-flex-column-container(); - overflow: hidden; - display: flex; - flex-direction: column; + @include flex-column-container($overflow: hidden); flex: 1 1 auto; - div.imx-table-container { - @include imx-flex-column-container(); - overflow: hidden; - height: inherit; - flex: 1 1 auto; - display: flex; - flex-direction: column; - - .imx-pickcategory-table { - flex-grow: 1; - overflow: auto; - } - } - - .imx-button-bar { - @include imx-button-bar(); - } - - .mat-card { - @include imx-flex-fill-control-hidden-overflow(); + .mat-mdc-card { + @include flex-column-container-fill(); } } @@ -45,11 +19,6 @@ .imx-pickcategory-page { .heading-wrapper { display: block; - - .alert-wrapper { - margin: 0 0 20px 0; - width: 100%; - } } } } diff --git a/imxweb/projects/att/src/lib/pick-category/pick-category.component.spec.ts b/imxweb/projects/att/src/lib/pick-category/pick-category.component.spec.ts index 0648abdec..2c2224191 100644 --- a/imxweb/projects/att/src/lib/pick-category/pick-category.component.spec.ts +++ b/imxweb/projects/att/src/lib/pick-category/pick-category.component.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,9 +27,9 @@ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { EuiSidesheetService } from '@elemental-ui/core'; -import { IClientProperty } from 'imx-qbm-dbts'; +import { IClientProperty } from '@imx-modules/imx-qbm-dbts'; -import { ClassloggerService, ConfirmationService, HelpContextualService, UserMessageService } from 'qbm'; +import { ClassloggerService, ConfirmationService, HelpContextualService, SqlWizardApiService, UserMessageService } from 'qbm'; import { Subject } from 'rxjs'; import { PickCategoryComponent } from './pick-category.component'; @@ -39,71 +39,70 @@ describe('PickCategoryComponent', () => { let component: PickCategoryComponent; let fixture: ComponentFixture; - const sidesheetServiceStub = { - open: jasmine.createSpy('open') + open: jasmine.createSpy('open'), }; let confirm = true; const mockConfirmationService = { - confirm: jasmine.createSpy('confirm') - .and.callFake(() => Promise.resolve(confirm)) - } + confirm: jasmine.createSpy('confirm').and.callFake(() => Promise.resolve(confirm)), + }; const deletePickCategoriesSpy = jasmine.createSpy('deletePickCategories').and.returnValue(Promise.resolve({})); const mockHelpContextualService = { - setHelpContextId: jasmine.createSpy('setHelpContextId') - .and.callFake(() => Promise.resolve()) - } + setHelpContextId: jasmine.createSpy('setHelpContextId').and.callFake(() => Promise.resolve()), + }; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ - PickCategoryComponent - ], - schemas: [ - CUSTOM_ELEMENTS_SCHEMA - ], + declarations: [PickCategoryComponent], + schemas: [CUSTOM_ELEMENTS_SCHEMA], providers: [ { provide: PickCategoryService, useValue: { - pickcategorySchema: { Columns: { __Display: { ColumnName: '__Display' } as IClientProperty } }, + pickcategorySchema: { + Columns: { + __Display: { ColumnName: '__Display' } as IClientProperty, + IsManual: { ColumnName: 'IsManual' } as IClientProperty, + DisplayName: { ColumnName: 'DisplayName' } as IClientProperty, + }, + }, getPickCategories: jasmine.createSpy('getPickCategories').and.returnValue({}), deletePickCategories: deletePickCategoriesSpy, createPickCategory: jasmine.createSpy('createPickCategory').and.returnValue({}), handleOpenLoader: jasmine.createSpy('handleOpenLoader').and.callThrough(), handleCloseLoader: jasmine.createSpy('handleCloseLoader').and.callThrough(), - } + }, }, { provide: EuiSidesheetService, - useValue: sidesheetServiceStub + useValue: sidesheetServiceStub, }, { provide: ConfirmationService, - useValue: mockConfirmationService + useValue: mockConfirmationService, }, { provide: UserMessageService, useValue: { - subject: new Subject() - } + subject: new Subject(), + }, }, { provide: ClassloggerService, useValue: { debug: jasmine.createSpy('debug').and.callThrough(), - trace: jasmine.createSpy('trace').and.callThrough() - } + trace: jasmine.createSpy('trace').and.callThrough(), + }, }, { provide: HelpContextualService, - useValue: mockHelpContextualService - } - ] - }) - .compileComponents(); + useValue: mockHelpContextualService, + }, + { provide: SqlWizardApiService, useValue: {} }, + ], + }).compileComponents(); }); beforeEach(() => { @@ -118,15 +117,12 @@ describe('PickCategoryComponent', () => { }); describe('delete() tests', () => { - for (const testcase of [ - { confirm: true }, - { confirm: false } - ]) { + for (const testcase of [{ confirm: true }, { confirm: false }]) { it('should make a call to delete the PickCategories, if the user confirm the dialog', async () => { confirm = testcase.confirm; await component.delete(); - if(testcase.confirm) { + if (testcase.confirm) { expect(deletePickCategoriesSpy).toHaveBeenCalled(); } else { expect(deletePickCategoriesSpy).not.toHaveBeenCalled(); diff --git a/imxweb/projects/att/src/lib/pick-category/pick-category.component.ts b/imxweb/projects/att/src/lib/pick-category/pick-category.component.ts index 0c14cb8a3..1450d3512 100644 --- a/imxweb/projects/att/src/lib/pick-category/pick-category.component.ts +++ b/imxweb/projects/att/src/lib/pick-category/pick-category.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,23 +24,24 @@ * */ -import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { Component, OnInit, ViewChild } from '@angular/core'; import { EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { PortalPickcategory } from 'imx-api-qer'; -import { CollectionLoadParameters, DisplayColumns, TypedEntity, ValType } from 'imx-qbm-dbts'; +import { PortalPickcategory } from '@imx-modules/imx-api-qer'; +import { CollectionLoadParameters, EntitySchema, TypedEntity, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { + calculateSidesheetWidth, ClassloggerService, - DataSourceToolbarSettings, - UserMessageService, - DataTableComponent, - DataSourceWrapper, ConfirmationService, + DataTableComponent, + DataViewInitParameters, + DataViewSource, + HELP_CONTEXTUAL, HelpContextualComponent, HelpContextualService, - HELP_CONTEXTUAL + UserMessageService, } from 'qbm'; import { PickCategoryCreateComponent } from './pick-category-create/pick-category-create.component'; import { PickCategorySidesheetComponent } from './pick-category-sidesheet/pick-category-sidesheet.component'; @@ -49,13 +50,12 @@ import { PickCategoryService } from './pick-category.service'; @Component({ selector: 'imx-pick-category', templateUrl: './pick-category.component.html', - styleUrls: ['./pick-category.component.scss'] + styleUrls: ['./pick-category.component.scss'], + providers: [DataViewSource], }) -export class PickCategoryComponent implements OnInit, OnDestroy { - - public readonly dstWrapper: DataSourceWrapper; - public dstSettings: DataSourceToolbarSettings; +export class PickCategoryComponent implements OnInit { public selectedPickCategoryItems: PortalPickcategory[] = []; + public entitySchema: EntitySchema; @ViewChild(DataTableComponent) private table: DataTableComponent; @@ -66,84 +66,82 @@ export class PickCategoryComponent implements OnInit, OnDestroy { private readonly translate: TranslateService, private readonly messageService: UserMessageService, private readonly logger: ClassloggerService, - private readonly helpContextualService: HelpContextualService + private readonly helpContextualService: HelpContextualService, + public dataSource: DataViewSource, ) { - - const entitySchema = this.pickCategoryService.pickcategorySchema; - - this.dstWrapper = new DataSourceWrapper( - state => this.pickCategoryService.getPickCategories(state), - [ - entitySchema.Columns[DisplayColumns.DISPLAY_PROPERTYNAME], - entitySchema.Columns.IsManual - ], - entitySchema - ); + this.entitySchema = this.pickCategoryService.pickcategorySchema; } public async ngOnInit(): Promise { await this.getData(); } - public ngOnDestroy(): void { - this.table?.clearSelection(); - } - public onSelectionChanged(items: PortalPickcategory[]): void { this.logger.trace(this, 'selection changed', items); this.selectedPickCategoryItems = items; } public selectedItemsCanBeDeleted(): boolean { - return this.selectedPickCategoryItems != null && + return ( + this.selectedPickCategoryItems != null && this.selectedPickCategoryItems.length > 0 && - this.selectedPickCategoryItems.every(item => item.IsManual.value); + this.selectedPickCategoryItems.every((item) => item.IsManual.value) + ); } - public async getData(newState?: CollectionLoadParameters): Promise { - this.pickCategoryService.handleOpenLoader(); - try { - this.dstSettings = await this.dstWrapper.getDstSettings(newState); - } finally { - this.pickCategoryService.handleCloseLoader(); - } + public async getData(): Promise { + const dataViewInitParameters: DataViewInitParameters = { + execute: (params: CollectionLoadParameters, signal: AbortSignal): Promise> => + this.pickCategoryService.getPickCategories(params, signal), + schema: this.entitySchema, + columnsToDisplay: [this.entitySchema.Columns.DisplayName, this.entitySchema.Columns.IsManual], + highlightEntity: (entity: PortalPickcategory) => { + this.viewDetails(entity); + }, + selectionChange: (selection: Array) => this.onSelectionChanged(selection), + }; + this.dataSource.init(dataViewInitParameters); } public async viewDetails(pickCategory: PortalPickcategory): Promise { if (pickCategory) { - this.helpContextualService.setHelpContextId(HELP_CONTEXTUAL.AttestationPreselectionEdit) - const result = await this.sideSheet.open(PickCategorySidesheetComponent, { - title: await this.translate.get('#LDS#Heading Edit Sample').toPromise(), - subTitle: pickCategory.GetEntity().GetDisplay(), - panelClass: 'imx-sidesheet', - padding: '0', - width: '600px', - testId: 'pickCategory-details-sidesheet', - data: { - pickCategory - }, - headerComponent: HelpContextualComponent - }).afterClosed().toPromise(); + this.helpContextualService.setHelpContextId(HELP_CONTEXTUAL.AttestationPreselectionEdit); + const result = await this.sideSheet + .open(PickCategorySidesheetComponent, { + title: await this.translate.get('#LDS#Heading Edit Sample').toPromise(), + subTitle: pickCategory.GetEntity().GetDisplay(), + panelClass: 'imx-sidesheet', + padding: '0', + width: calculateSidesheetWidth(), + testId: 'pickCategory-details-sidesheet', + data: { + pickCategory, + }, + headerComponent: HelpContextualComponent, + }) + .afterClosed() + .toPromise(); if (result) { - this.getData(); + this.dataSource.updateState(); } - } - else { + } else { this.messageService.subject.next({ - text: '#LDS#You cannot edit the sample. The sample does not exist (anymore). Please reload the page.' + text: '#LDS#You cannot edit the sample. The sample does not exist (anymore). Please reload the page.', }); } } public async delete(): Promise { - if (await this.confirmationService.confirm({ - Title: '#LDS#Heading Delete Sample', - Message: '#LDS#Are you sure you want to delete the sample?' - })) { - if (await this.pickCategoryService.deletePickCategories(this.selectedPickCategoryItems) > 0) { - await this.getData(); - this.table?.clearSelection(); + if ( + await this.confirmationService.confirm({ + Title: '#LDS#Heading Delete Samples', + Message: '#LDS#Are you sure you want to delete the selected samples?', + }) + ) { + if ((await this.pickCategoryService.deletePickCategories(this.selectedPickCategoryItems)) > 0) { + this.dataSource.selection.clear(); + this.dataSource.updateState(); } } } @@ -153,29 +151,30 @@ export class PickCategoryComponent implements OnInit, OnDestroy { this.logger.trace(this, 'new pick category created', newPickCategory); if (newPickCategory) { - - this.helpContextualService.setHelpContextId(HELP_CONTEXTUAL.AttestationPreselectionCreate) - const result = await this.sideSheet.open(PickCategoryCreateComponent, { - title: await this.translate.get('#LDS#Heading Create Sample').toPromise(), - panelClass: 'imx-sidesheet', - padding: '0', - width: '700px', - disableClose: true, - testId: 'pickCategory-create-sidesheet', - data: { - pickCategory: newPickCategory - }, - headerComponent: HelpContextualComponent - }).afterClosed().toPromise(); + this.helpContextualService.setHelpContextId(HELP_CONTEXTUAL.AttestationPreselectionCreate); + const result = await this.sideSheet + .open(PickCategoryCreateComponent, { + title: await this.translate.get('#LDS#Heading Create Sample').toPromise(), + panelClass: 'imx-sidesheet', + padding: '0', + width: calculateSidesheetWidth(700, 0.4), + disableClose: true, + testId: 'pickCategory-create-sidesheet', + data: { + pickCategory: newPickCategory, + }, + headerComponent: HelpContextualComponent, + }) + .afterClosed() + .toPromise(); if (result?.create) { await this.pickCategoryService.saveNewPickCategoryAndItems(result.pickCategory, result.pickedItems); - this.getData(); + this.dataSource.updateState(); } - } - else { + } else { this.messageService.subject.next({ - text: '#LDS#The sample could not be created. Please reload the page and try again.' + text: '#LDS#The sample could not be created. Please reload the page and try again.', }); } } diff --git a/imxweb/projects/att/src/lib/pick-category/pick-category.module.ts b/imxweb/projects/att/src/lib/pick-category/pick-category.module.ts index 2ce7c3bc2..ce30959b3 100644 --- a/imxweb/projects/att/src/lib/pick-category/pick-category.module.ts +++ b/imxweb/projects/att/src/lib/pick-category/pick-category.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,32 +24,27 @@ * */ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatDialogModule } from '@angular/material/dialog'; import { MatSnackBarModule } from '@angular/material/snack-bar'; import { MatStepperModule } from '@angular/material/stepper'; -import { TranslateModule } from '@ngx-translate/core'; import { EuiCoreModule } from '@elemental-ui/core'; +import { TranslateModule } from '@ngx-translate/core'; -import { DataSourceToolbarModule, DataTableModule, CdrModule, SelectedElementsModule, HelpContextualModule } from 'qbm'; +import { CdrModule, DataSourceToolbarModule, DataTableModule, DataViewModule, HelpContextualModule, SelectedElementsModule } from 'qbm'; -import { PickCategoryComponent } from './pick-category.component'; -import { PickCategorySidesheetComponent } from './pick-category-sidesheet/pick-category-sidesheet.component'; -import { PickCategorySelectIdentitiesComponent } from './pick-category-select-identities/pick-category-select-identities.component'; +import { MatTableModule } from '@angular/material/table'; import { PickCategoryCreateComponent } from './pick-category-create/pick-category-create.component'; - +import { PickCategorySelectIdentitiesComponent } from './pick-category-select-identities/pick-category-select-identities.component'; +import { PickCategorySidesheetComponent } from './pick-category-sidesheet/pick-category-sidesheet.component'; +import { PickCategoryComponent } from './pick-category.component'; @NgModule({ - declarations: [ - PickCategoryComponent, - PickCategorySidesheetComponent, - PickCategorySelectIdentitiesComponent, - PickCategoryCreateComponent, - ], + declarations: [PickCategoryComponent, PickCategorySidesheetComponent, PickCategorySelectIdentitiesComponent, PickCategoryCreateComponent], imports: [ CommonModule, CdrModule, @@ -65,6 +60,8 @@ import { PickCategoryCreateComponent } from './pick-category-create/pick-categor TranslateModule, SelectedElementsModule, HelpContextualModule, - ] + MatTableModule, + DataViewModule, + ], }) -export class PickCategoryModule { } +export class PickCategoryModule {} diff --git a/imxweb/projects/att/src/lib/pick-category/pick-category.service.spec.ts b/imxweb/projects/att/src/lib/pick-category/pick-category.service.spec.ts index 86c5dcc5a..9d26fcc88 100644 --- a/imxweb/projects/att/src/lib/pick-category/pick-category.service.spec.ts +++ b/imxweb/projects/att/src/lib/pick-category/pick-category.service.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,8 +27,8 @@ import { TestBed, waitForAsync } from '@angular/core/testing'; import { EuiLoadingService } from '@elemental-ui/core'; -import { PortalPickcategory, PortalPickcategoryItems } from 'imx-api-qer'; -import { IEntity } from 'imx-qbm-dbts'; +import { PortalPickcategory, PortalPickcategoryItems } from '@imx-modules/imx-api-qer'; +import { IEntity } from '@imx-modules/imx-qbm-dbts'; import { ClassloggerService, SnackBarService } from 'qbm'; import { QerApiService } from 'qer'; @@ -41,7 +41,7 @@ describe('PickCategoryService', () => { const euiLoadingServiceStub = { hide: jasmine.createSpy('hide'), - show: jasmine.createSpy('show') + show: jasmine.createSpy('show'), }; beforeEach(() => { @@ -51,18 +51,18 @@ describe('PickCategoryService', () => { provide: ClassloggerService, useValue: { debug: jasmine.createSpy('debug').and.callThrough(), - trace: jasmine.createSpy('trace').and.callThrough() - } + trace: jasmine.createSpy('trace').and.callThrough(), + }, }, { provide: EuiLoadingService, - useValue: euiLoadingServiceStub + useValue: euiLoadingServiceStub, }, { provide: SnackBarService, useValue: { - open: jasmine.createSpy('open') - } + open: jasmine.createSpy('open'), + }, }, { provide: QerApiService, @@ -73,30 +73,32 @@ describe('PickCategoryService', () => { Post: jasmine.createSpy('Post').and.returnValue(Promise.resolve({ Data: [{}] })), GetSchema: () => ({ Columns: [] }), createEntity: jasmine.createSpy('createEntity').and.returnValue({ - GetEntity: () => ({ - Commit: commitSpy - }) as unknown as IEntity, - ObjectKeyDelegated: { Value: '' } - } as unknown as PortalPickcategory) + GetEntity: () => + ({ + Commit: commitSpy, + }) as unknown as IEntity, + ObjectKeyDelegated: { Value: '' }, + } as unknown as PortalPickcategory), }, PortalPickcategoryItems: { Get: jasmine.createSpy('Get').and.stub(), - Post: jasmine.createSpy('Post').and.returnValue(Promise.resolve({ Data: [{}]})), + Post: jasmine.createSpy('Post').and.returnValue(Promise.resolve({ Data: [{}] })), GetSchema: () => ({ Columns: [] }), createEntity: jasmine.createSpy('createEntity').and.returnValue({ - GetEntity: () => ({ - Commit: commitSpy - }) as unknown as IEntity, - ObjectKeyDelegated: { Value: '' } - } as unknown as PortalPickcategoryItems) - } + GetEntity: () => + ({ + Commit: commitSpy, + }) as unknown as IEntity, + ObjectKeyDelegated: { Value: '' }, + } as unknown as PortalPickcategoryItems), + }, }, client: { - portal_hyperview_get: jasmine.createSpy('portal_hyperview_get').and.callFake(() => Promise.resolve([1, 2, 3])) + portal_hyperview_get: jasmine.createSpy('portal_hyperview_get').and.callFake(() => Promise.resolve([1, 2, 3])), }, - } - } - ] + }, + }, + ], }); service = TestBed.inject(PickCategoryService); }); diff --git a/imxweb/projects/att/src/lib/pick-category/pick-category.service.ts b/imxweb/projects/att/src/lib/pick-category/pick-category.service.ts index 11c83673d..d881d6fc8 100644 --- a/imxweb/projects/att/src/lib/pick-category/pick-category.service.ts +++ b/imxweb/projects/att/src/lib/pick-category/pick-category.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,35 +25,25 @@ */ import { ErrorHandler, Injectable } from '@angular/core'; -import { OverlayRef } from '@angular/cdk/overlay'; import { EuiLoadingService } from '@elemental-ui/core'; -import { - CollectionLoadParameters, - EntityCollectionData, - EntitySchema, - ExtendedTypedEntityCollection, -} from 'imx-qbm-dbts'; -import { PortalPersonAll, PortalPickcategory, PortalPickcategoryItems } from 'imx-api-qer'; +import { PortalPersonAll, PortalPickcategory, PortalPickcategoryItems } from '@imx-modules/imx-api-qer'; +import { CollectionLoadParameters, EntityCollectionData, EntitySchema, ExtendedTypedEntityCollection } from '@imx-modules/imx-qbm-dbts'; -import { QerApiService } from 'qer'; import { ClassloggerService, SnackBarService } from 'qbm'; - +import { QerApiService } from 'qer'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class PickCategoryService { - - private busyIndicator: OverlayRef; - constructor( private readonly qerClient: QerApiService, private readonly busyService: EuiLoadingService, private readonly snackbar: SnackBarService, private readonly logger: ClassloggerService, private readonly errorHandler: ErrorHandler, - ) { } + ) {} public get pickcategorySchema(): EntitySchema { return this.qerClient.typedClient.PortalPickcategory.GetSchema(); @@ -63,9 +53,11 @@ export class PickCategoryService { return this.qerClient.typedClient.PortalPickcategoryItems.GetSchema(); } - public async getPickCategories(navigationState: CollectionLoadParameters): - Promise> { - return this.qerClient.typedClient.PortalPickcategory.Get(navigationState); + public async getPickCategories( + navigationState: CollectionLoadParameters, + signal?: AbortSignal, + ): Promise> { + return this.qerClient.typedClient.PortalPickcategory.Get(navigationState, { signal }); } /** @@ -84,10 +76,13 @@ export class PickCategoryService { try { deletedObjects = (await this.bulkDeletePickCategories(pickCategories)).length; if (deletedObjects > 0) { - this.snackbar.open({ - key: '#LDS#{0} samples have been successfully deleted.', - parameters: [deletedObjects] - }, '#LDS#Close'); + this.snackbar.open( + { + key: '#LDS#{0} samples have been successfully deleted.', + parameters: [deletedObjects], + }, + '#LDS#Close', + ); } } finally { this.handleCloseLoader(); @@ -99,18 +94,23 @@ export class PickCategoryService { return this.qerClient.typedClient.PortalPickcategory.createEntity(); } - public async getPickCategoryItems(uidQERPickCategory: string, navigationState: CollectionLoadParameters): - Promise> { + public async getPickCategoryItems( + uidQERPickCategory: string, + navigationState: CollectionLoadParameters, + ): Promise> { return this.qerClient.typedClient.PortalPickcategoryItems.Get(uidQERPickCategory, navigationState); } - public async deletePickCategoryItems(uidPickCategory: string, pickCategoriesItems: PortalPickcategoryItems[]) - : Promise { + public async deletePickCategoryItems( + uidPickCategory: string, + pickCategoriesItems: PortalPickcategoryItems[], + ): Promise { return this.handlePromiseLoader( Promise.all( - pickCategoriesItems.map(pickedItem => - this.qerClient.client.portal_pickcategory_items_delete(uidPickCategory, pickedItem.GetEntity().GetKeys().join(','))) - ) + pickCategoriesItems.map((pickedItem) => + this.qerClient.client.portal_pickcategory_items_delete(uidPickCategory, pickedItem.GetEntity().GetKeys().join(',')), + ), + ), ); } @@ -119,7 +119,7 @@ export class PickCategoryService { try { await this.qerClient.client.portal_pickcategory_items_post(uidPickCategory, { Reload: true, - Objects: pickCategoriesItems.map(item => item.EntityWriteDataSingle) + Objects: pickCategoriesItems.map((item) => item.EntityWriteDataSingle), }); assignedObjectsCounter++; this.logger.trace(this, `${pickCategoriesItems.length} pick category items assigned`); @@ -137,10 +137,13 @@ export class PickCategoryService { try { deletedObjects = (await this.deletePickCategoryItems(uidPickCategory, selectedPickedItems)).length; if (deletedObjects > 0) { - this.snackbar.open({ - key: '#LDS#{0} identities have been successfully removed.', - parameters: [deletedObjects] - }, '#LDS#Close'); + this.snackbar.open( + { + key: '#LDS#{0} identities have been successfully removed.', + parameters: [deletedObjects], + }, + '#LDS#Close', + ); } } finally { this.handleCloseLoader(); @@ -149,43 +152,41 @@ export class PickCategoryService { } public async createPickedItems(selection: any, uidPickCategory: string, showResultInSnackbar: boolean = true): Promise { - - const newAssignedObjects = (await this.handlePromiseLoader( - Promise.all( - selection.map(async (selectedItem: { XObjectKey: { value: string; }; }) => { - const pickedItem = this.qerClient.typedClient.PortalPickcategoryItems.createEntity(); - pickedItem.UID_QERPickCategory.value = uidPickCategory; - pickedItem.ObjectKeyItem.value = selectedItem.XObjectKey.value; - await pickedItem.GetEntity().Commit(true); - }) + const newAssignedObjects = ( + await this.handlePromiseLoader( + Promise.all( + selection.map(async (selectedItem: { XObjectKey: { value: string } }) => { + const pickedItem = this.qerClient.typedClient.PortalPickcategoryItems.createEntity(); + pickedItem.UID_QERPickCategory.value = uidPickCategory; + pickedItem.ObjectKeyItem.value = selectedItem.XObjectKey.value; + await pickedItem.GetEntity().Commit(true); + }), + ), ) - )).length; + ).length; if (newAssignedObjects > 0 && showResultInSnackbar) { - this.snackbar.open({ - key: '#LDS#{0} identities have been successfully assigned.', - parameters: [newAssignedObjects] - }, '#LDS#Close'); + this.snackbar.open( + { + key: '#LDS#{0} identities have been successfully assigned.', + parameters: [newAssignedObjects], + }, + '#LDS#Close', + ); } return newAssignedObjects; } public handleOpenLoader(): void { - if (!this.busyIndicator) { - setTimeout(() => this.busyIndicator = this.busyService.show()); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); } } public handleCloseLoader(): void { - if (this.busyIndicator) { - setTimeout(() => { - this.busyService.hide(this.busyIndicator); - this.busyIndicator = undefined; - }); - } + this.busyService.hide(); } public async saveNewPickCategoryAndItems(pickCategory: PortalPickcategory, pickedItems: PortalPersonAll[]): Promise { - this.handleOpenLoader(); try { const uidPickCategory = await this.postPickCategory(pickCategory); @@ -194,23 +195,26 @@ export class PickCategoryService { if (pickedItems && pickedItems.length > 0) { await this.createPickedItems(pickedItems, uidPickCategory, false); } - } finally { this.handleCloseLoader(); } - this.snackbar.open({ - key: '#LDS#The sample has been successfully created.', - parameters: [pickCategory.GetEntity().GetDisplay()] - }, '#LDS#Close'); - + this.snackbar.open( + { + key: '#LDS#The sample has been successfully created.', + parameters: [pickCategory.GetEntity().GetDisplay()], + }, + '#LDS#Close', + ); } private async bulkDeletePickCategories(pickCategories: PortalPickcategory[]): Promise { return this.handlePromiseLoader( Promise.all( - pickCategories.map(pickCategory => this.qerClient.client.portal_pickcategory_delete(pickCategory.GetEntity().GetKeys().join(','))) - ) + pickCategories.map((pickCategory) => + this.qerClient.client.portal_pickcategory_delete(pickCategory.GetEntity().GetKeys().join(',')), + ), + ), ); } diff --git a/imxweb/projects/att/src/lib/policies/attestation-cases/attestation-cases-component-parameter.interface.ts b/imxweb/projects/att/src/lib/policies/attestation-cases/attestation-cases-component-parameter.interface.ts index 3e25d60e6..ebb8af828 100644 --- a/imxweb/projects/att/src/lib/policies/attestation-cases/attestation-cases-component-parameter.interface.ts +++ b/imxweb/projects/att/src/lib/policies/attestation-cases/attestation-cases-component-parameter.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,14 +24,14 @@ * */ -import { PolicyFilterElement} from 'imx-api-att'; +import { PolicyFilterElement } from '@imx-modules/imx-api-att'; export interface AttestationCasesComponentParameter { - subtitle?: string; - uidPickCategory: string; - uidobject: string; - filter: PolicyFilterElement[]; - concat: string; - canCreateRuns: boolean; - uidpolicy?: string; + subtitle?: string; + uidPickCategory: string; + uidobject: string; + filter: PolicyFilterElement[]; + concat: string; + canCreateRuns: boolean; + uidpolicy?: string; } diff --git a/imxweb/projects/att/src/lib/policies/attestation-cases/attestation-cases-tree-database.service.ts b/imxweb/projects/att/src/lib/policies/attestation-cases/attestation-cases-tree-database.service.ts deleted file mode 100644 index 2948f3332..000000000 --- a/imxweb/projects/att/src/lib/policies/attestation-cases/attestation-cases-tree-database.service.ts +++ /dev/null @@ -1,122 +0,0 @@ -/* - * ONE IDENTITY LLC. PROPRIETARY INFORMATION - * - * This software is confidential. One Identity, LLC. or one of its affiliates or - * subsidiaries, has supplied this software to you under terms of a - * license agreement, nondisclosure agreement or both. - * - * You may not copy, disclose, or use this software except in accordance with - * those terms. - * - * - * Copyright 2023 One Identity LLC. - * ALL RIGHTS RESERVED. - * - * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR - * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, OR - * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE - * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE - * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING - * THIS SOFTWARE OR ITS DERIVATIVES. - * - */ - -import { CollectionLoadParameters, EntityData, EntitySchema, HierarchyData, IEntity, TypedEntityBuilder, ValType } from 'imx-qbm-dbts'; -import { BusyService, SettingsService, TreeDatabase, TreeNodeResultParameter } from 'qbm'; -import { PolicyService } from '../policy.service'; -import { AttestationCasesComponentParameter } from './attestation-cases-component-parameter.interface'; -import { PortalAttestationFilterMatchingobjects } from 'imx-api-att'; - -export class AttestationCasesTreeDatabaseService extends TreeDatabase { - private entitySchema: EntitySchema; - private builder: TypedEntityBuilder; - - constructor( - private readonly policyService: PolicyService, - private readonly settingsService: SettingsService, - private readonly data: AttestationCasesComponentParameter, - public busyService: BusyService, - ) { - super(); - this.canSearch = false; - this.builder = new TypedEntityBuilder(PortalAttestationFilterMatchingobjects); - } - - public async getData( - showLoading: boolean, - parameter: CollectionLoadParameters = { ParentKey: '' /* first level */ } - ): Promise { - if (this.data == null) { - return { entities: [], canLoadMore: false, totalCount: 0 }; - } - - const isBusy = showLoading ? this.busyService.beginBusy() : undefined; - - try { - const navigationState = { - ...parameter, - ...{ - PageSize: parameter.PageSize ?? this.settingsService.DefaultPageSize, - StartIndex: parameter.StartIndex ?? 0, - }, - }; - - const data = await this.policyService.getObjectsForFilterUntyped( - this.data.uidobject, - this.data.uidPickCategory, - { Elements: this.data.filter, ConcatenationType: this.data.concat }, - navigationState - ); - - if (data) { - const nodeEntities = await Promise.all( - data.Entities.map(async (elem): Promise => { - return (await this.buildEntityWithHasChildren(elem, data.Hierarchy))?.GetEntity(); - }) - ); - this.dataChanged.emit(nodeEntities.filter((elem) => elem != null)); - return { - entities: nodeEntities.filter((elem) => elem != null), - canLoadMore: navigationState.StartIndex + navigationState.PageSize < data.TotalCount, - totalCount: data.TotalCount, - }; - } - return { entities: [], canLoadMore: false, totalCount: 0 }; - } finally { - isBusy?.endBusy(); - } - } - - public async prepare(entitySchema: EntitySchema, withReload: boolean): Promise { - this.entitySchema = entitySchema; - if (withReload) { - this.reloadData(); - } - } - - /** adds a hasChildren column to the entity */ - private async buildEntityWithHasChildren(entityData: EntityData, data: HierarchyData): Promise { - if (!this.entitySchema) { - return undefined; - } - const entity = this.builder.buildReadWriteEntity({ entitySchema: this.entitySchema, entityData }); - entity.GetEntity().AddColumns([ - { - Type: ValType.Bool, - IsMultiValued: true, - ColumnName: 'HasChildren', - MinLen: 0, - Display: '', - }, - ]); - await entity - .GetEntity() - .GetColumn('HasChildren') - .PutValue(data ? data.EntitiesWithHierarchy.some((elem) => entityData.Keys.some((key) => key === elem)) : false); - - return entity; - } -} diff --git a/imxweb/projects/att/src/lib/policies/attestation-cases/attestation-cases.component.html b/imxweb/projects/att/src/lib/policies/attestation-cases/attestation-cases.component.html index 818858588..21a92379a 100644 --- a/imxweb/projects/att/src/lib/policies/attestation-cases/attestation-cases.component.html +++ b/imxweb/projects/att/src/lib/policies/attestation-cases/attestation-cases.component.html @@ -1,23 +1,43 @@ -
    +
    - +
    {{ '#LDS#Here you can select the objects to be attested.' | translate }}
    -
    {{ '#LDS#Note: After you have started an attestation, it may take some time until the associated attestation cases are created.' | translate }}
    +
    + {{ + '#LDS#Note: After you have started an attestation, it may take some time until the associated attestation cases are created.' + | translate + }} +
    - -
    {{ '#LDS#This policy affects many objects. Running this policy may take some time and generate notifications to many approvers.' | translate }}
    + +
    + {{ + '#LDS#This policy affects many objects. Running this policy may take some time and generate notifications to many approvers.' + | translate + }} +
    -
    +
    -

    {{ '#LDS#A sample is assigned to this attestation policy. You can start the attestation only for all objects in the sample.' | translate }}

    -

    {{ '#LDS#Note: After you have started an attestation, it may take some time until the associated attestation cases are created.' | translate }}

    +

    + {{ + '#LDS#A sample is assigned to this attestation policy. You can start the attestation only for all objects in the sample.' + | translate + }} +

    +

    + {{ + '#LDS#Note: After you have started an attestation, it may take some time until the associated attestation cases are created.' + | translate + }} +

    - - - - - + + + + {{ entitySchemaPolicy?.Columns?.[DisplayColumns.DISPLAY_PROPERTYNAME]?.Display }} + + +
    {{ item.GetEntity().GetDisplay() }}
    +
    + {{ item.GetEntity().GetDisplayLong() }} +
    + +
    + + {{ entitySchemaPolicy?.Columns?.DisplayName?.Display }} + + + + +
    +
    @@ -65,7 +84,7 @@ {{ '#LDS#Start attestation for all' | translate }}
    - - - - - diff --git a/imxweb/projects/att/src/lib/policies/attestation-cases/attestation-cases.component.scss b/imxweb/projects/att/src/lib/policies/attestation-cases/attestation-cases.component.scss index e49457660..651b5fcde 100644 --- a/imxweb/projects/att/src/lib/policies/attestation-cases/attestation-cases.component.scss +++ b/imxweb/projects/att/src/lib/policies/attestation-cases/attestation-cases.component.scss @@ -1,10 +1,9 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/mixins'; :host { + @include flex-column-container($height: 100%); overflow-y: auto; - display: flex; - flex-direction: column; - height: 100%; background-color: $color-gray-2; imx-data-table { @@ -12,141 +11,43 @@ } } -.eui-sidesheet-content { - display: flex; - flex-direction: column; - flex: 1 1 auto; -} - h2 { margin-bottom: 10px; } -.imx-sidesheet-content { - flex: 1 1 auto; - overflow: auto; -} - .imx-cases-card { - display: flex; - flex-direction: column; + @include flex-column-container(); margin: 0px; overflow-y: auto; flex: 1 1 auto; } -:host ::ng-deep imx-data-table .mat-row .mat-cell { - padding: 0 10px; - max-width: 100px; - div[imxTitle] { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } -} - -.imx-dialog-actions { - display: flex; - justify-content: flex-end; - margin-bottom: 0; - padding: 16px 32px; - border-top: 1px solid rgba(0, 0, 0, 0.12); - background-color: $color-gray-0; - - button { - margin-left: 10px; - } - - .justify-start { - margin-right: auto; - } -} .eui-sidesheet-actions { padding: 16px; - .justify-start { - margin-right: auto; - } - - button:not(:last-of-type):not(.justify-start) { - margin-right: 10px; - } } imx-data-table { flex-grow: 2; } -:host ::ng-deep imx-data-table .mat-header-row .mat-header-cell { - font-weight: 600; - font-size: 14px; - padding: 0 10px; -} - -:host ::ng-deep imx-data-table .mat-row .mat-cell { - padding: 0 10px; - max-width: 100px; - div[imxTitle] { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - .button-row { - border-top-style: solid; - border-top-width: 0px; - display: flex; - flex-direction: row; - justify-content: flex-end; - } - div[subtitle] { - font-size: smaller; - color: $color-gray-40; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - } -} - -.mat-card-title { - font-size: large; -} - .eui-sidesheet-actions { - .mat-stroked-button { + .mat-mdc-outlined-button { align-self: flex-start; } } -.mat-toolbar { - background-color: inherit; -} - .imx-mat-card-header-line { display: grid; grid-template-columns: 1fr 3fr; padding: 30px 30px 0 30px; } -.mat-toolbar > button { - margin-left: 5px; -} - -.mat-toolbar { - color: $color-gray-100; -} - .imx-toolbar-spacer { flex: 1 1 auto; } .heading-wrapper { - display: flex; - flex-direction: column; - - .helper-alert { - margin: 0px 0px 30px 0px; - align-self: flex-end; - width: 100%; - } + @include flex-column-container(); .imx-helper-title { font-size: larger; @@ -155,18 +56,12 @@ imx-data-table { } .imx-sample-data-info { + @include flex-column-container(); text-align: center; margin: 20px 0; flex: 1 1 auto; - display: flex; - flex-direction: column; justify-content: center; - .eui-icon { - font-size: 100px; - color: $color-gray-10; - } - p { margin: 0; font-size: 18px; @@ -183,23 +78,21 @@ imx-data-table { } } +.hidden { + display: none !important; + height: 0px; +} + +.imx-attestation-tree-container { + @include flex-column-container(); + flex: 1 1 auto; +} + .eui-dark-theme { :host { background-color: $color-gray-80; - .imx-dialog-actions { - background-color: $color-gray-70; - } - - .mat-toolbar { - color: $color-gray-10; - } - .imx-sample-data-info { - .eui-icon { - color: $color-gray-20; - } - p { color: $color-gray-10; } @@ -210,13 +103,5 @@ imx-data-table { .eui-contrast-theme { :host { background-color: $color-gray-100; - - .imx-dialog-actions { - background-color: $color-gray-90; - } - - .mat-toolbar { - color: $color-gray-0; - } } } diff --git a/imxweb/projects/att/src/lib/policies/attestation-cases/attestation-cases.component.ts b/imxweb/projects/att/src/lib/policies/attestation-cases/attestation-cases.component.ts index f4f617698..1e5e9983a 100644 --- a/imxweb/projects/att/src/lib/policies/attestation-cases/attestation-cases.component.ts +++ b/imxweb/projects/att/src/lib/policies/attestation-cases/attestation-cases.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,47 +24,42 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; import { Component, Inject, OnInit } from '@angular/core'; import { EUI_SIDESHEET_DATA, EuiLoadingService, EuiSidesheetRef } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { PortalAttestationFilterMatchingobjects } from 'imx-api-att'; -import { CollectionLoadParameters, DisplayColumns, EntitySchema, ValType } from 'imx-qbm-dbts'; +import { PortalAttestationFilterMatchingobjects } from '@imx-modules/imx-api-att'; +import { CollectionLoadParameters, DisplayColumns, EntitySchema, TypedEntityCollectionData, ValType } from '@imx-modules/imx-qbm-dbts'; import { - BusyService, ClassloggerService, ClientPropertyForTableColumns, ConfirmationService, - DataSourceToolbarSettings, + DataViewInitParameters, + DataViewSource, LdsReplacePipe, - SettingsService, SnackBarService, } from 'qbm'; import { PolicyService } from '../policy.service'; import { AttestationCasesComponentParameter } from './attestation-cases-component-parameter.interface'; -import { AttestationCasesTreeDatabaseService } from './attestation-cases-tree-database.service'; @Component({ templateUrl: './attestation-cases.component.html', styleUrls: ['./attestation-cases.component.scss'], + providers: [DataViewSource], }) export class AttestationCasesComponent implements OnInit { - public dstSettings: DataSourceToolbarSettings; public readonly entitySchemaPolicy: EntitySchema; public DisplayColumns = DisplayColumns; public isAdmin: boolean; public deactivatedChecked = false; - public selectedItems: PortalAttestationFilterMatchingobjects[] = []; - public treeDatabase: AttestationCasesTreeDatabaseService; + public selectedItems: (PortalAttestationFilterMatchingobjects | undefined)[] = []; public entitySchema = PortalAttestationFilterMatchingobjects.GetEntitySchema(); - private navigationState: CollectionLoadParameters; private displayedColumns: ClientPropertyForTableColumns[]; private threshold = -1; public hierarchical: boolean; - private busyService = new BusyService(); + public isLoading = false; constructor( public readonly sidesheetRef: EuiSidesheetRef, @@ -73,12 +68,11 @@ export class AttestationCasesComponent implements OnInit { private readonly busyServiceEui: EuiLoadingService, private readonly snackbar: SnackBarService, private readonly confirmationService: ConfirmationService, - private readonly settingsService: SettingsService, private readonly translate: TranslateService, private readonly ldsReplace: LdsReplacePipe, - private readonly logger: ClassloggerService + private readonly logger: ClassloggerService, + public dataSource: DataViewSource, ) { - this.navigationState = { PageSize: settingsService.DefaultPageSize, StartIndex: 0, ParentKey: '' }; this.entitySchemaPolicy = policyService.AttestationMatchingObjectsSchema; this.displayedColumns = [this.entitySchemaPolicy.Columns[DisplayColumns.DISPLAY_PROPERTYNAME]]; @@ -92,124 +86,95 @@ export class AttestationCasesComponent implements OnInit { } public get showWarning() { - return this.threshold > 0 && this.threshold < (this.dstSettings?.dataSource?.totalCount ?? 0); + return this.threshold > 0 && this.threshold < (this.dataSource.collectionData().totalCount ?? 0); } public async ngOnInit(): Promise { - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.busyServiceEui.show())); + this.showBusyIndicator(); try { this.threshold = await this.policyService.getCasesThreshold(); - this.hierarchical = - ( - await this.policyService.getObjectsForFilterUntyped( - this.data.uidobject, - this.data.uidPickCategory, - { Elements: this.data.filter, ConcatenationType: this.data.concat }, - { PageSize: -1 } - ) - ).Hierarchy != null; - - this.treeDatabase = new AttestationCasesTreeDatabaseService(this.policyService, this.settingsService, this.data, this.busyService); - await this.treeDatabase.prepare(this.entitySchema, false); } finally { - setTimeout(async () => { - this.busyServiceEui.hide(overlayRef); - }); + this.busyServiceEui.hide(); } - return this.hierarchical ? this.navigateTree() : this.navigate(); + return this.navigate(); } public get hasSampleData(): boolean { return this.data.uidPickCategory != null && this.data.uidPickCategory !== ''; } - public async onNavigationStateChanged(newState: CollectionLoadParameters): Promise { - this.navigationState = newState; - await this.navigate(); - } - public onSelectionChanged(items: PortalAttestationFilterMatchingobjects[]): void { this.selectedItems = items; } - public async createRun(data: PortalAttestationFilterMatchingobjects[]): Promise { - const count = data.length > 0 ? data.length : this.dstSettings?.dataSource.totalCount; + public async createRun(data: (PortalAttestationFilterMatchingobjects | undefined)[]): Promise { + const count = data.length > 0 ? data.length : this.dataSource.collectionData().totalCount || 0; if (count <= this.threshold || (await this.confirmCreation())) { - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.busyServiceEui.show())); + this.showBusyIndicator(); try { await this.policyService.createAttestationRun( - this.data.uidpolicy, - data.map((elem) => elem.Key.value) + this.data.uidpolicy || '', + data.map((elem) => elem?.Key.value ?? ''), ); this.logger.trace( this, 'attestation run created for', this.data.uidpolicy, - data.map((elem) => elem.Key.value) + data.map((elem) => elem?.Key.value ?? ''), ); this.snackbar.open( { key: '#LDS#The attestation has been started successfully. It may take some time until the associated attestation cases are created.', }, - '#LDS#Close' + '#LDS#Close', ); } finally { - setTimeout(async () => { - this.busyServiceEui.hide(overlayRef); - this.sidesheetRef.close(true); - }); + this.busyServiceEui.hide(); + this.sidesheetRef.close(true); } } } - private async navigateTree(): Promise { - await this.treeDatabase.prepare(this.entitySchema, true); - } - private async navigate(): Promise { - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.busyServiceEui.show())); - - try { - const dataSource = await this.policyService.getObjectsForFilter( - this.data.uidobject, - this.data.uidPickCategory, - { Elements: this.data.filter, ConcatenationType: this.data.concat }, - this.navigationState - ); - - this.dstSettings = { - displayedColumns: this.displayedColumns, - dataSource, - entitySchema: this.entitySchemaPolicy, - navigationState: this.navigationState, - }; - - this.logger.debug(this, 'matching objects table navigated to', this.navigationState); - } finally { - setTimeout(() => this.busyServiceEui.hide(overlayRef)); - } + const dataViewInitParameters: DataViewInitParameters = { + execute: (params: CollectionLoadParameters): Promise> => + this.policyService.getObjectsForFilter( + this.data.uidobject, + this.data.uidPickCategory, + { Elements: this.data.filter, ConcatenationType: this.data.concat }, + params, + ), + schema: this.entitySchemaPolicy, + columnsToDisplay: this.displayedColumns, + selectionChange: (selection: PortalAttestationFilterMatchingobjects[]) => this.onSelectionChanged(selection), + localSource: true, + }; + this.dataSource.init(dataViewInitParameters); } private async confirmCreation(): Promise { const message = this.ldsReplace.transform( await this.translate .get( - '#LDS#You have selected more than {0} objects. Attestation of the selected objects may take some time and generate notifications to many approvers. Are you sure you want to start the attestation for the selected objects?' + '#LDS#You have selected more than {0} objects. Attestation of the selected objects may take some time and generate notifications to many approvers. Are you sure you want to start the attestation for the selected objects?', ) .toPromise(), - this.threshold + this.threshold, ); return this.confirmationService.confirm({ Title: await this.translate.get('#LDS#Heading Many Objects Affected').toPromise(), Message: message, }); } + + private showBusyIndicator(): void { + if (this.busyServiceEui.overlayRefs.length === 0) { + this.busyServiceEui.show(); + } + } } diff --git a/imxweb/projects/att/src/lib/policies/confirm-deactivation/confirm-deactivation.component.html b/imxweb/projects/att/src/lib/policies/confirm-deactivation/confirm-deactivation.component.html index 2fb3804b1..b4d94e981 100644 --- a/imxweb/projects/att/src/lib/policies/confirm-deactivation/confirm-deactivation.component.html +++ b/imxweb/projects/att/src/lib/policies/confirm-deactivation/confirm-deactivation.component.html @@ -1,13 +1,13 @@ -

    {{'#LDS#Heading Deactivate Attestation Policy' | translate}}

    - -

    {{'#LDS#Do you want to deactivate this attestation policy?' | translate}}

    -

    {{'#LDS#This will cause automatic cancelation of pending attestation cases when saving your changes.' | translate}} -

    -
    - - - - \ No newline at end of file +

    {{ '#LDS#Heading Deactivate Attestation Policy' | translate }}

    + +

    {{ '#LDS#Do you want to deactivate this attestation policy?' | translate }}

    +

    {{ '#LDS#This will cause automatic cancelation of pending attestation cases when saving your changes.' | translate }}

    +
    + + + + diff --git a/imxweb/projects/att/src/lib/policies/confirm-deactivation/confirm-deactivation.component.scss b/imxweb/projects/att/src/lib/policies/confirm-deactivation/confirm-deactivation.component.scss index 766288fdb..1e489f874 100644 --- a/imxweb/projects/att/src/lib/policies/confirm-deactivation/confirm-deactivation.component.scss +++ b/imxweb/projects/att/src/lib/policies/confirm-deactivation/confirm-deactivation.component.scss @@ -1,9 +1,8 @@ - .imx-confirm-content { - overflow: auto; - white-space: pre-line; - word-wrap: break-word; - max-height: 400px; - max-width: 600px; - margin-bottom: 40px; - } + overflow: auto; + white-space: pre-line; + word-wrap: break-word; + max-height: 400px; + max-width: 600px; + margin-bottom: 40px; +} diff --git a/imxweb/projects/att/src/lib/policies/confirm-deactivation/confirm-deactivation.component.ts b/imxweb/projects/att/src/lib/policies/confirm-deactivation/confirm-deactivation.component.ts index 029f44f65..0fc742e2a 100644 --- a/imxweb/projects/att/src/lib/policies/confirm-deactivation/confirm-deactivation.component.ts +++ b/imxweb/projects/att/src/lib/policies/confirm-deactivation/confirm-deactivation.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,7 +29,6 @@ import { Component } from '@angular/core'; @Component({ selector: 'imx-confirm-deactivation', templateUrl: './confirm-deactivation.component.html', - styleUrls: ['./confirm-deactivation.component.scss'] + styleUrls: ['./confirm-deactivation.component.scss'], }) - export class ConfirmDeactivationComponent {} diff --git a/imxweb/projects/att/src/lib/policies/edit-master-data/edit-master-data.component.html b/imxweb/projects/att/src/lib/policies/edit-master-data/edit-master-data.component.html index 4d90e7b35..e613912bd 100644 --- a/imxweb/projects/att/src/lib/policies/edit-master-data/edit-master-data.component.html +++ b/imxweb/projects/att/src/lib/policies/edit-master-data/edit-master-data.component.html @@ -1,78 +1,129 @@ -
    - +
    + -
    +
    -

    - {{'#LDS#Heading Attestation Policy Settings' | translate}} - -

    - - - #LDS#The sample assigned to the original attestation policy has been removed for the copy. Samples can be assigned to only one attestation policy at a time. - #LDS#However, you can assign a different sample to this attestation policy. - - - - +

    + {{ '#LDS#Heading Attestation Policy Settings' | translate }} + +

    + + + {{ LdsKeySampleRemoved }} + #LDS#However, you can assign a different sample to this attestation policy. + + + + - + - + + + + - + + + - - - + + - + - - + - + - + + + + + -
    - +
    @@ -86,10 +137,16 @@

    [disabled]="!policy.policy?.IsOob?.value || hasAttestations" (click)="delete()" > - + {{ '#LDS#Delete' | translate }} -

    @@ -97,7 +154,7 @@

    -
    +
    diff --git a/imxweb/projects/att/src/lib/policies/edit-master-data/edit-master-data.component.scss b/imxweb/projects/att/src/lib/policies/edit-master-data/edit-master-data.component.scss index ed572bc3a..94f7ed2be 100644 --- a/imxweb/projects/att/src/lib/policies/edit-master-data/edit-master-data.component.scss +++ b/imxweb/projects/att/src/lib/policies/edit-master-data/edit-master-data.component.scss @@ -4,25 +4,6 @@ overflow: hidden; height: 100%; - .tab-group-wrapper { - display: flex; - overflow: auto; - flex: 1; - - .mat-tab-group { - width: 100%; - } - - ::ng-deep .mat-tab-body-content { - display: flex; - flex-direction: column; - } - - ::ng-deep .mat-tab-body-wrapper { - flex: 1; - } - } - .imx-policy-edit-form { display: flex; flex-direction: column; @@ -31,24 +12,20 @@ overflow-x: hidden; } - h2 { + h3 { font-size: x-large; font-weight: 600; margin-bottom: 10px; } - ::ng-deep textarea.mat-input-element { - max-height: 300px; - } - .imx-master-data-cdrs { display: flex; flex-direction: column; margin: 2px 10px 2px 2px; overflow-x: hidden; overflow-y: auto; - h2{ - display:flex; + h3 { + display: flex; align-items: center; } } @@ -59,33 +36,6 @@ overflow: hidden; } - .imx-checkbox-property-list { - display: flex; - flex-direction: column; - } - - .eui-sidesheet-content { - display: flex; - padding-bottom: 10px; - } - - .eui-sidesheet-actions { - padding: 16px; - margin: 10px 0 0 0; - .justify-start { - margin-right: auto; - - eui-icon { - font-size: 14px; - margin-right: 4px; - } - } - - button:not(:last-of-type):not(.justify-start) { - margin-right: 10px; - } - } - .imx-confirm-content { overflow: auto; white-space: pre-line; @@ -93,18 +43,6 @@ max-height: 400px; max-width: 600px; } - - .imx-helper-alert { - width: 100%; - - ::ng-deep .eui-alert { - margin-bottom: 20px; - } - - span { - display: block; - } - } } @media only screen and (max-width: 1024px) { @@ -120,7 +58,7 @@ grid-column-end: 3; } - imx-policy-editor{ + imx-policy-editor { margin-right: 5px; margin-top: 20px; } diff --git a/imxweb/projects/att/src/lib/policies/edit-master-data/edit-master-data.component.ts b/imxweb/projects/att/src/lib/policies/edit-master-data/edit-master-data.component.ts index 2da7efa3e..049693336 100644 --- a/imxweb/projects/att/src/lib/policies/edit-master-data/edit-master-data.component.ts +++ b/imxweb/projects/att/src/lib/policies/edit-master-data/edit-master-data.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,23 +25,32 @@ */ import { Component, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { AbstractControl, UntypedFormArray, UntypedFormGroup } from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; -import { UntypedFormGroup, UntypedFormArray, UntypedFormControl } from '@angular/forms'; -import { EuiLoadingService, EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; -import { OverlayRef } from '@angular/cdk/overlay'; +import { EUI_SIDESHEET_DATA, EuiLoadingService, EuiSidesheetRef } from '@elemental-ui/core'; import { BehaviorSubject, Subscription } from 'rxjs'; -import { ColumnDependentReference, BaseCdr, SnackBarService, ClassloggerService, LdsReplacePipe } from 'qbm'; +import { PolicyFilter } from '@imx-modules/imx-api-att'; +import { EntitySchema } from '@imx-modules/imx-qbm-dbts'; +import { TranslateService } from '@ngx-translate/core'; +import { + BaseCdr, + BusyService, + ClassloggerService, + ColumnDependentReference, + ConfirmationService, + HELP_CONTEXTUAL, + HelpContextualValues, + LdsReplacePipe, + SnackBarService, +} from 'qbm'; import { UserModelService } from 'qer'; +import { ConfirmDeactivationComponent } from '../confirm-deactivation/confirm-deactivation.component'; +import { FilterElementColumnService } from '../editors/filter-element-column.service'; import { FilterModel } from '../policy-editor/filter-model'; +import { PolicyEditorComponent } from '../policy-editor/policy-editor.component'; import { Policy } from '../policy.interface'; import { PolicyService } from '../policy.service'; -import { FilterElementColumnService } from '../editors/filter-element-column.service'; -import { ConfirmDeactivationComponent } from '../confirm-deactivation/confirm-deactivation.component'; -import { ConfirmationService, HELP_CONTEXTUAL, HelpContextualValues } from 'qbm'; -import { EntitySchema } from 'imx-qbm-dbts'; -import { TranslateService } from '@ngx-translate/core'; -import { PolicyEditorComponent } from '../policy-editor/policy-editor.component'; @Component({ templateUrl: './edit-master-data.component.html', @@ -50,16 +59,16 @@ import { PolicyEditorComponent } from '../policy-editor/policy-editor.component' export class EditMasterDataComponent implements OnInit, OnDestroy { public readonly formGroup: UntypedFormGroup; public readonly schema: EntitySchema; - public objectProperties: { [key: string]: { cdr: ColumnDependentReference; formControl?: UntypedFormControl } } = {}; + public objectProperties: { [key: string]: { cdr: ColumnDependentReference; formControl?: AbstractControl } } = {}; public readonly formArray: UntypedFormArray; public reload = false; public filterModel: FilterModel; public hasAttestations: boolean; public contextId: HelpContextualValues; + public busyService: BusyService = new BusyService(); @ViewChild('filterControl', { static: true }) policyEditor: PolicyEditorComponent; - private isPoliyEditorEnabled = true; private valueChangedSubscription: Subscription; private closeSubscription: Subscription; private threshold = -1; @@ -67,7 +76,7 @@ export class EditMasterDataComponent implements OnInit, OnDestroy { constructor( @Inject(EUI_SIDESHEET_DATA) public readonly policy: Policy, public readonly sidesheetRef: EuiSidesheetRef, - private readonly busyService: EuiLoadingService, + private readonly euiBusyService: EuiLoadingService, private readonly snackBar: SnackBarService, private readonly dialog: MatDialog, private readonly policyService: PolicyService, @@ -76,7 +85,7 @@ export class EditMasterDataComponent implements OnInit, OnDestroy { private readonly userService: UserModelService, private readonly confirmationService: ConfirmationService, private readonly translate: TranslateService, - private readonly ldsReplace: LdsReplacePipe + private readonly ldsReplace: LdsReplacePipe, ) { this.schema = policyService.AttestationPolicyEditSchema; this.initOrRefreshCdrDictionary(); @@ -91,7 +100,7 @@ export class EditMasterDataComponent implements OnInit, OnDestroy { } }); - this.filterModel = new FilterModel(this.columnService, new BehaviorSubject(true), new BehaviorSubject(undefined)); + this.filterModel = new FilterModel(this.columnService, new BehaviorSubject('')); this.filterModel.uidAttestationObject = this.policy.policy.UID_AttestationObject.value; this.filterModel.policyFilterData = this.policy.filterData; } @@ -105,6 +114,15 @@ export class EditMasterDataComponent implements OnInit, OnDestroy { } } + public async onFilterChanged(filter: PolicyFilter) { + const isBusy = this.busyService.beginBusy(); + try { + await this.policy.policy.setExtendedData([filter]); + } finally { + isBusy.endBusy(); + } + } + public get objectType(): string { return this.policy.policy.GetEntity().TypeName; } @@ -114,39 +132,38 @@ export class EditMasterDataComponent implements OnInit, OnDestroy { } public async ngOnInit(): Promise { - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.busyService.show())); + if (this.euiBusyService.overlayRefs.length === 0) { + this.euiBusyService.show(); + } try { this.hasAttestations = (await this.policyService.getRunCountForPolicy(this.policy.policy.GetEntity().GetKeys()[0])) > 0; this.threshold = await this.policyService.getCasesThreshold(); } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.euiBusyService.hide(); } if (this.policy.isNew) { this.formGroup.markAsDirty(); } - this.logger.trace('call isEnabledSubject with', this.policy.policy.UID_QERPickCategory.value == null); - this.filterModel.isEnabledSubject.next( - this.policy.policy.UID_QERPickCategory.value == null || this.policy.policy.UID_QERPickCategory.value === '' - ); - this.contextId = this.policy.isNew ? HELP_CONTEXTUAL.AttestationPoliciesCreate : HELP_CONTEXTUAL.AttestationPoliciesEdit + this.contextId = this.policy.isNew ? HELP_CONTEXTUAL.AttestationPoliciesCreate : HELP_CONTEXTUAL.AttestationPoliciesEdit; } - public addControl(evt: UntypedFormControl, columnName: string): void { + public addControl(evt: AbstractControl, columnName?: string): void { setTimeout(() => { - this.formGroup.removeControl(columnName); - this.objectProperties[columnName].formControl = evt; - this.formGroup.addControl(columnName, evt); - this.logger.debug(this, 'new Control added to form group'); - - if (columnName === 'IsInActive') { - if (this.valueChangedSubscription) { - this.valueChangedSubscription.unsubscribe(); + if (columnName) { + this.formGroup.removeControl(columnName); + this.objectProperties[columnName].formControl = evt; + this.formGroup.addControl(columnName, evt); + this.logger.debug(this, 'new Control added to form group'); + + if (columnName === 'IsInActive') { + if (this.valueChangedSubscription) { + this.valueChangedSubscription.unsubscribe(); + } + this.valueChangedSubscription = evt.valueChanges.subscribe((value) => { + this.confirmDeactivation(value); + }); } - this.valueChangedSubscription = evt.valueChanges.subscribe((value) => { - this.confirmDeactivation(value); - }); } }); } @@ -167,67 +184,28 @@ export class EditMasterDataComponent implements OnInit, OnDestroy { this.logger.debug(this, 'Attestator cdr updated'); } - public async updateReadOnlySchedule(){ + public async updateReadOnlySchedule() { this.objectProperties.UID_DialogSchedule.cdr = new BaseCdr(this.policy.policy.UID_DialogSchedule.Column); this.logger.debug(this, 'UID_DialogSchedule cdr updated'); } - public async updatePickCategory(): Promise { - const showConfirmation = - this.isPoliyEditorEnabled && - this.filterModel.policyFilterData?.Filter.Elements.length && - this.policy.policy.UID_QERPickCategory.value?.length > 0; - - this.logger.debug(this, 'Checked for existing filters if sample data was changed from null to value', showConfirmation); - - if (showConfirmation) { - const confirmed = await this.confirmationService.confirm({ - Title: '#LDS#Heading Use Sample Data', - Message: '#LDS#Do you want to use the selected sample data instead of the specified conditions?', - }); - if (confirmed) { - this.policy.filterData = { - IsReadOnly: this.policy.filterData.IsReadOnly, - Filter: { Elements: [] }, - InfoDisplay: [], - }; - this.logger.debug(this, 'filter removed due to sample data seleted'); - } else { - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.busyService.show())); - try { - await this.policy.policy.UID_QERPickCategory.Column.PutValue(undefined); - } finally { - setTimeout(() => this.busyService.hide(overlayRef)); - } - this.objectProperties.UID_QERPickCategory.cdr = new BaseCdr(this.policy.policy.UID_QERPickCategory.Column); - this.logger.debug(this, 'Sample data is removed'); - } - } - - this.isPoliyEditorEnabled = this.policy.policy.UID_QERPickCategory.value == null || this.policy.policy.UID_QERPickCategory.value === ''; - this.filterModel.isEnabledSubject.next(this.isPoliyEditorEnabled); - this.logger.debug(this, 'Visibility of the policy editor:', this.isPoliyEditorEnabled ? 'visible' : 'hidden'); - } - public async submit(): Promise { if (!(await this.confirmCreation())) { return; } - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.busyService.show())); + if (this.euiBusyService.overlayRefs.length === 0) { + this.euiBusyService.show(); + } try { - const filter = this.filterModel.policyFilterData.Filter; + const filter = this.filterModel.policyFilterData?.Filter; - this.policy.policy.extendedData = [ - this.policy.policy.UID_QERPickCategory.value == null || this.policy.policy.UID_QERPickCategory.value === '' ? filter : null, - ]; + this.policy.policy.extendedData = !!filter ? [filter] : []; await this.policy.policy.GetEntity().Commit(false); this.logger.debug(this, 'data submitted'); this.sidesheetRef.close(true); } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.euiBusyService.hide(); } this.snackBar.open( @@ -235,7 +213,7 @@ export class EditMasterDataComponent implements OnInit, OnDestroy { key: '#LDS#The attestation policy "{0}" has been successfully saved.', parameters: [this.policy.policy.GetEntity().GetDisplay()], }, - '#LDS#Close' + '#LDS#Close', ); this.reload = true; } @@ -255,7 +233,7 @@ export class EditMasterDataComponent implements OnInit, OnDestroy { key: '#LDS#The attestation policy "{0}" has been successfully deleted.', parameters: [this.policy.policy.GetEntity().GetDisplay()], }, - '#LDS#Close' + '#LDS#Close', ); this.sidesheetRef.close(true); } @@ -268,10 +246,10 @@ export class EditMasterDataComponent implements OnInit, OnDestroy { const message = this.ldsReplace.transform( await this.translate .get( - '#LDS#This attestation policy affects more than {0} objects. Running this attestation policy may take some time and generate notifications to many approvers. Are you sure you want to save the attestation policy?' + '#LDS#This attestation policy affects more than {0} objects. Running this attestation policy may take some time and generate notifications to many approvers. Are you sure you want to save the attestation policy?', ) .toPromise(), - this.threshold + this.threshold, ); return this.confirmationService.confirm({ Title: '#LDS#Heading Many Objects Affected', @@ -342,4 +320,7 @@ export class EditMasterDataComponent implements OnInit, OnDestroy { } } } + + public LdsKeySampleRemoved = + '#LDS#The sample assigned to the original attestation policy has been removed for the copy. Samples can be assigned to only one attestation policy at a time.'; } diff --git a/imxweb/projects/att/src/lib/policies/editors/edit-generic.component.html b/imxweb/projects/att/src/lib/policies/editors/edit-generic.component.html index 669564403..bcd347d73 100644 --- a/imxweb/projects/att/src/lib/policies/editors/edit-generic.component.html +++ b/imxweb/projects/att/src/lib/policies/editors/edit-generic.component.html @@ -1,2 +1 @@ - - \ No newline at end of file + diff --git a/imxweb/projects/att/src/lib/policies/editors/edit-generic.component.ts b/imxweb/projects/att/src/lib/policies/editors/edit-generic.component.ts index 8e32b99c8..e1254b1da 100644 --- a/imxweb/projects/att/src/lib/policies/editors/edit-generic.component.ts +++ b/imxweb/projects/att/src/lib/policies/editors/edit-generic.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,10 +24,10 @@ * */ -import { Component, Input, Output, EventEmitter, OnChanges } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core'; -import { MultiValue, ValueStruct } from 'imx-qbm-dbts'; -import { ColumnDependentReference, BaseCdr, ClassloggerService, MetadataService } from 'qbm'; +import { MultiValue, ValueStruct } from '@imx-modules/imx-qbm-dbts'; +import { BaseCdr, ClassloggerService, ColumnDependentReference, MetadataService } from 'qbm'; import { FilterChangedArgument } from './filter-changed-argument.interface'; import { FilterElementModel } from './filter-element-model'; @@ -56,7 +56,7 @@ export class EditGenericComponent implements OnChanges { await this.metaData.updateNonExisting([this.filterElementModel.getTableName()]); this.cdr = new BaseCdr( this.filterElementModel.columnForFilter, - this.metaData.tables[this.filterElementModel.getTableName()].Columns[this.filterElementModel.getColumnName()].Display, + this.metaData.tables[this.filterElementModel.getTableName()]?.Columns?.[this.filterElementModel.getColumnName()].Display, ); } } diff --git a/imxweb/projects/att/src/lib/policies/editors/edit-name.component.html b/imxweb/projects/att/src/lib/policies/editors/edit-name.component.html index caaf5587c..41e60457d 100644 --- a/imxweb/projects/att/src/lib/policies/editors/edit-name.component.html +++ b/imxweb/projects/att/src/lib/policies/editors/edit-name.component.html @@ -1,2 +1 @@ - - \ No newline at end of file + diff --git a/imxweb/projects/att/src/lib/policies/editors/edit-name.component.ts b/imxweb/projects/att/src/lib/policies/editors/edit-name.component.ts index baf0ba0f8..cfeedc382 100644 --- a/imxweb/projects/att/src/lib/policies/editors/edit-name.component.ts +++ b/imxweb/projects/att/src/lib/policies/editors/edit-name.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -34,10 +34,9 @@ import { FilterElementModel } from './filter-element-model'; @Component({ templateUrl: './edit-name.component.html', - selector: 'imx-edit-name' + selector: 'imx-edit-name', }) export class EditNameComponent implements OnChanges, OnDestroy { - @Input() public filterElementModel: FilterElementModel; @Input() public identifier: string; @Input() public testId = ''; @@ -62,10 +61,10 @@ export class EditNameComponent implements OnChanges, OnDestroy { if (this.valueChangedSubscription) { this.valueChangedSubscription.unsubscribe(); } - this.valueChangedSubscription = control.valueChanges.subscribe(value => { + this.valueChangedSubscription = control.valueChanges.subscribe((value) => { this.valueChanged.emit({ ParameterValue: value, - displays: [value] + displays: [value], }); }); } diff --git a/imxweb/projects/att/src/lib/policies/editors/edit-origin.component.html b/imxweb/projects/att/src/lib/policies/editors/edit-origin.component.html index 8331c3b81..ebcc0afe7 100644 --- a/imxweb/projects/att/src/lib/policies/editors/edit-origin.component.html +++ b/imxweb/projects/att/src/lib/policies/editors/edit-origin.component.html @@ -1,9 +1,14 @@ - {{'#LDS#Origins' | translate}} - -
    - - {{candidate?.Display}} - -
    -
    \ No newline at end of file + {{ '#LDS#Origins' | translate }} + +
    + + + {{ candidate?.Display }} + +
    + diff --git a/imxweb/projects/att/src/lib/policies/editors/edit-origin.component.scss b/imxweb/projects/att/src/lib/policies/editors/edit-origin.component.scss index bed561c13..f5c5b5477 100644 --- a/imxweb/projects/att/src/lib/policies/editors/edit-origin.component.scss +++ b/imxweb/projects/att/src/lib/policies/editors/edit-origin.component.scss @@ -1,4 +1,4 @@ .imx-multi-value-container { - display: flex; - flex-flow: column; - } \ No newline at end of file + display: flex; + flex-flow: column; +} diff --git a/imxweb/projects/att/src/lib/policies/editors/edit-origin.component.spec.ts b/imxweb/projects/att/src/lib/policies/editors/edit-origin.component.spec.ts index 15aaa9ad3..853898e1b 100644 --- a/imxweb/projects/att/src/lib/policies/editors/edit-origin.component.spec.ts +++ b/imxweb/projects/att/src/lib/policies/editors/edit-origin.component.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { ParmOpt } from 'imx-api-att'; +import { ParmOpt } from '@imx-modules/imx-api-att'; import { ClassloggerService, clearStylesFromDOM } from 'qbm'; import { EditOriginComponent } from './edit-origin.component'; import { FilterElementColumnService } from './filter-element-column.service'; @@ -32,77 +32,76 @@ import { FilterElementModel } from './filter-element-model'; import { MockBuilder, MockedComponentFixture, MockRender } from 'ng-mocks'; import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core'; - function buildFilterModel(config: ParmOpt[], value: string): FilterElementModel { - const ret = new FilterElementModel([{ Uid: '1', Options: config }], - ['1 Display'], { ParameterValue: value, AttestationSubType: '1' }, - '', - ({ buildColumn: jasmine.createSpy('buildColumn') } as unknown) as FilterElementColumnService - ); - - return ret; + const ret = new FilterElementModel( + [{ Uid: '1', Options: config }], + ['1 Display'], + { ParameterValue: value, AttestationSubType: '1' }, + '', + { buildColumn: jasmine.createSpy('buildColumn') } as unknown as FilterElementColumnService, + ); + + return ret; } describe('EditOriginComponent', () => { - let component: EditOriginComponent; - let fixture: MockedComponentFixture; - - beforeEach(() => { - return MockBuilder(EditOriginComponent) - .mock(ClassloggerService) - .beforeCompileComponents(testbed => { - testbed.configureTestingModule({ - schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA] - }) - }) - }) - - - beforeEach(() => { - fixture = MockRender(EditOriginComponent) - component = fixture.point.componentInstance; - fixture.detectChanges(); - }); - - afterAll(() => { - clearStylesFromDOM(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('inits controls', () => { - const options = [ - { Display: 'One', Uid: '1' }, - { Display: 'Two', Uid: '2' }, - { Display: 'Three', Uid: '3' }, - ]; - component.filterElementModel = buildFilterModel(options, '"1","2"'); - component.ngOnInit(); - - expect(component.control.controls.length).toEqual(3); - expect(component.control.controls[0].value).toBeTruthy(); - expect(component.control.controls[1].value).toBeTruthy(); - expect(component.control.controls[2].value).toBeFalsy(); - }); - - it('emits changes', () => { - const options = [ - { Display: 'One', Uid: '1' }, - { Display: 'Two', Uid: '2' }, - { Display: 'Three', Uid: '3' }, - ]; - - component.filterElementModel = buildFilterModel(options, '\'2\',\'3\'') - component.ngOnInit(); - - const spy = spyOn(component.valueChanged, 'emit'); - - component.control.controls[0].setValue(false); - component.control.controls[2].setValue(true); - - expect(spy.calls.mostRecent().args).toEqual([{ ParameterValue: '\'2\',\'3\'', displays: ['\'Two\'', '\'Three\''] }]); - - }); + let component: EditOriginComponent; + let fixture: MockedComponentFixture; + + beforeEach(() => { + return MockBuilder(EditOriginComponent) + .mock(ClassloggerService) + .beforeCompileComponents((testbed) => { + testbed.configureTestingModule({ + schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA], + }); + }); + }); + + beforeEach(() => { + fixture = MockRender(EditOriginComponent); + component = fixture.point.componentInstance; + fixture.detectChanges(); + }); + + afterAll(() => { + clearStylesFromDOM(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('inits controls', () => { + const options = [ + { Display: 'One', Uid: '1' }, + { Display: 'Two', Uid: '2' }, + { Display: 'Three', Uid: '3' }, + ]; + component.filterElementModel = buildFilterModel(options, '"1","2"'); + component.ngOnInit(); + + expect(component.control.controls.length).toEqual(3); + expect(component.control.controls[0].value).toBeTruthy(); + expect(component.control.controls[1].value).toBeTruthy(); + expect(component.control.controls[2].value).toBeFalsy(); + }); + + it('emits changes', () => { + const options = [ + { Display: 'One', Uid: '1' }, + { Display: 'Two', Uid: '2' }, + { Display: 'Three', Uid: '3' }, + ]; + + component.filterElementModel = buildFilterModel(options, "'2','3'"); + component.ngOnInit(); + + const spy = spyOn(component.valueChanged, 'emit'); + + component.control.controls[0].setValue(false); + component.control.controls[2].setValue(true); + + expect(spy.calls.mostRecent().args).toEqual([{ ParameterValue: "'2','3'", displays: ["'Two'", "'Three'"] }]); + }); }); diff --git a/imxweb/projects/att/src/lib/policies/editors/edit-origin.component.ts b/imxweb/projects/att/src/lib/policies/editors/edit-origin.component.ts index ff8f558a3..70d7b9ee9 100644 --- a/imxweb/projects/att/src/lib/policies/editors/edit-origin.component.ts +++ b/imxweb/projects/att/src/lib/policies/editors/edit-origin.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,10 +25,10 @@ */ import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; -import { UntypedFormArray, UntypedFormControl } from '@angular/forms'; +import { FormArray, FormGroup, UntypedFormControl } from '@angular/forms'; import { Subscription } from 'rxjs'; -import { ParmOpt } from 'imx-api-att'; +import { ParmOpt } from '@imx-modules/imx-api-att'; import { ClassloggerService } from 'qbm'; import { FilterChangedArgument } from './filter-changed-argument.interface'; import { FilterElementModel } from './filter-element-model'; @@ -36,12 +36,12 @@ import { FilterElementModel } from './filter-element-model'; @Component({ templateUrl: './edit-origin.component.html', selector: 'imx-edit-origin', - styleUrls: ['./edit-origin.component.scss'] + styleUrls: ['./edit-origin.component.scss'], }) export class EditOriginComponent implements OnInit, OnDestroy { - public candidates: ParmOpt[]; - public readonly control = new UntypedFormArray([]); + public readonly control = new FormArray([]); + public form: FormGroup<{ candidates: FormArray }> = new FormGroup({ candidates: this.control }); @Input() public filterElementModel: FilterElementModel; @Input() public identifier: string; @@ -52,18 +52,17 @@ export class EditOriginComponent implements OnInit, OnDestroy { private selectedParameter: string[]; private valueChangedSubscription: Subscription; - constructor(private readonly logger: ClassloggerService) { } + constructor(private readonly logger: ClassloggerService) {} public ngOnInit(): void { - if (this.filterElementModel == null) { return; } - this.candidates = this.filterElementModel.getParameterData(this.filterElementModel?.attestationSubType).Options; + this.candidates = this.filterElementModel.getParameterData(this.filterElementModel?.attestationSubType).Options || []; this.selectedParameter = this.splitStringAndRemoveQuotes(this.filterElementModel?.parameterValue, ','); - this.candidates.forEach(elem => { + this.candidates.forEach((elem) => { this.control.push(new UntypedFormControl(this.isSelected(elem))); this.logger.trace(this, 'control added for candidate', elem); }); @@ -71,8 +70,8 @@ export class EditOriginComponent implements OnInit, OnDestroy { this.valueChangedSubscription = this.control.valueChanges.subscribe(() => this.valueChanged.emit({ ParameterValue: this.buildNewParameterValue(), - displays: this.buildDisplay() - }) + displays: this.buildDisplay(), + }), ); } @@ -84,7 +83,7 @@ export class EditOriginComponent implements OnInit, OnDestroy { private buildNewParameterValue(): string { const elements = this.control.value; - const returnValue = []; + const returnValue: string[] = []; elements.forEach((element: any, index: number) => { if (element) { returnValue.push(`'${this.candidates[index].Uid}'`); @@ -97,7 +96,7 @@ export class EditOriginComponent implements OnInit, OnDestroy { private buildDisplay(): string[] { const elements = this.control.value; - const returnValue = []; + const returnValue: string[] = []; elements.forEach((element: any, index: number) => { if (element) { returnValue.push(`'${this.candidates[index].Display}'`); @@ -108,12 +107,11 @@ export class EditOriginComponent implements OnInit, OnDestroy { } private isSelected(option: ParmOpt): boolean { - return this.selectedParameter.includes(option.Uid); + return this.selectedParameter.includes(option.Uid || ''); } private splitStringAndRemoveQuotes(listString: string, separator: string): string[] { const splitted = listString.split(separator); - return splitted.map(str => str.substring(1, str.length - 1)); + return splitted.map((str) => str.substring(1, str.length - 1)); } - } diff --git a/imxweb/projects/att/src/lib/policies/editors/edit-threshold.component.html b/imxweb/projects/att/src/lib/policies/editors/edit-threshold.component.html index 8d18fb61e..920383960 100644 --- a/imxweb/projects/att/src/lib/policies/editors/edit-threshold.component.html +++ b/imxweb/projects/att/src/lib/policies/editors/edit-threshold.component.html @@ -1,16 +1,30 @@ -

    {{'#LDS#Heading Risk Index' | translate}}

    +

    {{ '#LDS#Heading Risk Index' | translate }}

    - - {{'#LDS#Lower limit' | translate}} - - - {{'#LDS#Specify a value between 0 and 1 for the lower limit.' |translate}} - - - {{'#LDS#Upper limit' | translate}} - - - {{'#LDS#Specify a value between 0 and 1 for the upper limit.' |translate}} - + + {{ '#LDS#Lower limit' | translate }} + + {{ '#LDS#Specify a value between 0 and 1 for the lower limit.' | translate }} + + + {{ '#LDS#Upper limit' | translate }} + + {{ '#LDS#Specify a value between 0 and 1 for the upper limit.' | translate }} +
    diff --git a/imxweb/projects/att/src/lib/policies/editors/edit-threshold.component.scss b/imxweb/projects/att/src/lib/policies/editors/edit-threshold.component.scss index a47cfc603..82b25916d 100644 --- a/imxweb/projects/att/src/lib/policies/editors/edit-threshold.component.scss +++ b/imxweb/projects/att/src/lib/policies/editors/edit-threshold.component.scss @@ -1,12 +1,12 @@ h4 { - font-size: smaller; - font-weight: 600; + font-size: smaller; + font-weight: 600; } div { - display: grid; - grid-template-columns: 1fr 1fr; - grid-gap: 5px; + display: grid; + grid-template-columns: 1fr 1fr; + grid-gap: 5px; } @media only screen and (max-width: 1024px) { @@ -14,4 +14,4 @@ div { display: flex; flex-direction: column; } -} \ No newline at end of file +} diff --git a/imxweb/projects/att/src/lib/policies/editors/edit-threshold.component.ts b/imxweb/projects/att/src/lib/policies/editors/edit-threshold.component.ts index 562d41769..a6a78d3db 100644 --- a/imxweb/projects/att/src/lib/policies/editors/edit-threshold.component.ts +++ b/imxweb/projects/att/src/lib/policies/editors/edit-threshold.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -35,10 +35,9 @@ import { FilterElementModel } from './filter-element-model'; @Component({ templateUrl: './edit-threshold.component.html', selector: 'imx-edit-threshold', - styleUrls: ['./edit-threshold.component.scss'] + styleUrls: ['./edit-threshold.component.scss'], }) export class EditThresholdComponent implements OnInit, OnDestroy { - public readonly riskIndexForm: UntypedFormGroup; public readonly lowerControl: UntypedFormControl; public readonly upperControl: UntypedFormControl; @@ -56,7 +55,7 @@ export class EditThresholdComponent implements OnInit, OnDestroy { this.upperControl = new UntypedFormControl(undefined, { updateOn: 'blur', validators: [Validators.min(0), Validators.max(1)] }); this.riskIndexForm = new UntypedFormGroup({ lowerBounds: this.lowerControl, - upperBounds: this.upperControl + upperBounds: this.upperControl, }); } @@ -68,9 +67,12 @@ export class EditThresholdComponent implements OnInit, OnDestroy { this.valueChanged.emit({ ParameterValue: this.lowerControl.value?.toLocaleString('en-us'), ParameterValue2: this.upperControl.value?.toLocaleString('en-us'), - displays: [(this.lowerControl.value * 100).toLocaleString(this.translateService.currentLang) - + ' - ' + (this.upperControl.value * 100).toLocaleString(this.translateService.currentLang)] - }) + displays: [ + (this.lowerControl.value * 100).toLocaleString(this.translateService.currentLang) + + ' - ' + + (this.upperControl.value * 100).toLocaleString(this.translateService.currentLang), + ], + }), ); } diff --git a/imxweb/projects/att/src/lib/policies/editors/edit-uint.component.html b/imxweb/projects/att/src/lib/policies/editors/edit-uint.component.html index e5bc7f5d1..4999f524b 100644 --- a/imxweb/projects/att/src/lib/policies/editors/edit-uint.component.html +++ b/imxweb/projects/att/src/lib/policies/editors/edit-uint.component.html @@ -1,6 +1,5 @@ - {{'#LDS#Number of days' | translate}} - - - {{'#LDS#The number of days must be positive.' | translate}} + {{ '#LDS#Number of days' | translate }} + + {{ '#LDS#The number of days must be positive.' | translate }} diff --git a/imxweb/projects/att/src/lib/policies/editors/edit-uint.component.ts b/imxweb/projects/att/src/lib/policies/editors/edit-uint.component.ts index 9ca000d2f..772739e53 100644 --- a/imxweb/projects/att/src/lib/policies/editors/edit-uint.component.ts +++ b/imxweb/projects/att/src/lib/policies/editors/edit-uint.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -34,10 +34,9 @@ import { FilterElementModel } from './filter-element-model'; @Component({ templateUrl: './edit-uint.component.html', - selector: 'imx-edit-uint' + selector: 'imx-edit-uint', }) export class EditUintComponent implements OnInit, OnDestroy { - public control: UntypedFormControl; @Input() public filterElementModel: FilterElementModel; @Input() public identifier: string; @@ -59,8 +58,8 @@ export class EditUintComponent implements OnInit, OnDestroy { this.valueChangedSubscription = this.control.valueChanges.subscribe(() => this.valueChanged.emit({ ParameterValue: this.control.value, - displays: [this.control.value.toLocaleString(this.translateService.currentLang)] - }) + displays: [this.control.value.toLocaleString(this.translateService.currentLang)], + }), ); } diff --git a/imxweb/projects/att/src/lib/policies/editors/filter-changed-argument.interface.ts b/imxweb/projects/att/src/lib/policies/editors/filter-changed-argument.interface.ts index 4bed5acc9..9f3b82828 100644 --- a/imxweb/projects/att/src/lib/policies/editors/filter-changed-argument.interface.ts +++ b/imxweb/projects/att/src/lib/policies/editors/filter-changed-argument.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,9 +24,9 @@ * */ -import { PolicyFilterElement } from 'imx-api-att'; +import { PolicyFilterElement } from '@imx-modules/imx-api-att'; -export interface FilterChangedArgument extends PolicyFilterElement { - displays?: string[]; - setName?: boolean; +export interface FilterChangedArgument extends PolicyFilterElement { + displays?: string[]; + setName?: boolean; } diff --git a/imxweb/projects/att/src/lib/policies/editors/filter-editor.component.html b/imxweb/projects/att/src/lib/policies/editors/filter-editor.component.html index f28c46e2a..4834d6d2e 100644 --- a/imxweb/projects/att/src/lib/policies/editors/filter-editor.component.html +++ b/imxweb/projects/att/src/lib/policies/editors/filter-editor.component.html @@ -1,23 +1,43 @@ - + - + - + - + - - \ No newline at end of file + + diff --git a/imxweb/projects/att/src/lib/policies/editors/filter-editor.component.scss b/imxweb/projects/att/src/lib/policies/editors/filter-editor.component.scss index e110d11b2..248a268c5 100644 --- a/imxweb/projects/att/src/lib/policies/editors/filter-editor.component.scss +++ b/imxweb/projects/att/src/lib/policies/editors/filter-editor.component.scss @@ -1 +1 @@ -// add styles if necessary \ No newline at end of file +// add styles if necessary diff --git a/imxweb/projects/att/src/lib/policies/editors/filter-editor.component.ts b/imxweb/projects/att/src/lib/policies/editors/filter-editor.component.ts index 4b28ed2be..3bf8156e9 100644 --- a/imxweb/projects/att/src/lib/policies/editors/filter-editor.component.ts +++ b/imxweb/projects/att/src/lib/policies/editors/filter-editor.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { EventEmitter, forwardRef, Component, Input, Output } from '@angular/core'; +import { Component, EventEmitter, forwardRef, Input, Output } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { ClassloggerService } from 'qbm'; @@ -39,9 +39,9 @@ import { FilterElementModel } from './filter-element-model'; provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => FilterEditorComponent), multi: true, - } + }, ], - styleUrls: ['./filter-editor.component.scss'] + styleUrls: ['./filter-editor.component.scss'], }) export class FilterEditorComponent implements ControlValueAccessor { public onChange: (event: FilterElementModel) => void; @@ -52,7 +52,7 @@ export class FilterEditorComponent implements ControlValueAccessor { @Output() public filterChanged = new EventEmitter(); - constructor(private readonly logger: ClassloggerService) { } + constructor(private readonly logger: ClassloggerService) {} public writeValue(filter: FilterElementModel): void { this.filterElementModel = filter; @@ -69,14 +69,13 @@ export class FilterEditorComponent implements ControlValueAccessor { } public invokeFilterChangedElement(arg: FilterChangedArgument): void { - this.filterElementModel.parameterValue = arg.ParameterValue; - this.filterElementModel.parameterValue2 = arg.ParameterValue2; + this.filterElementModel.parameterValue = arg.ParameterValue || ''; + this.filterElementModel.parameterValue2 = arg.ParameterValue2 || ''; this.writeValue(this.filterElementModel); this.onTouch(this.filterElementModel); this.onChange(this.filterElementModel); this.filterChanged.emit(arg); - } } diff --git a/imxweb/projects/att/src/lib/policies/editors/filter-element-column.service.ts b/imxweb/projects/att/src/lib/policies/editors/filter-element-column.service.ts index e2b2f3af9..8e56cec1d 100644 --- a/imxweb/projects/att/src/lib/policies/editors/filter-element-column.service.ts +++ b/imxweb/projects/att/src/lib/policies/editors/filter-element-column.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,20 +27,21 @@ import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; -import { ParmData } from 'imx-api-att'; -import { EntityColumnData, IEntityColumn, MultiValue, ValType } from 'imx-qbm-dbts'; +import { ParmData } from '@imx-modules/imx-api-att'; +import { EntityColumnData, FkProviderItem, IEntityColumn, MultiValue, ValType } from '@imx-modules/imx-qbm-dbts'; import { EntityService } from 'qbm'; import { PolicyService } from '../policy.service'; import { FilterElementModel } from './filter-element-model'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class FilterElementColumnService { constructor( public readonly policyService: PolicyService, private readonly translateService: TranslateService, - private readonly entityService: EntityService) { } + private readonly entityService: EntityService, + ) {} public buildColumn( parmdata: ParmData, @@ -48,52 +49,56 @@ export class FilterElementColumnService { value: string, caption: string, displays: string[], - withfk: boolean): IEntityColumn { - + withfk: boolean, + ): IEntityColumn | undefined { if (parmdata == null) { return undefined; } const propertyName = parmdata.RequiredParameter; - return this.entityService.createLocalEntityColumn({ - Type: ValType.String, - IsMultiValued: withfk, - ColumnName: parmdata.ColumnName || propertyName, - MinLen: withfk ? 1 : 0, - Display: caption ? this.translateService.instant(caption) : '', - FkRelation: withfk ? { - IsMemberRelation: false, - ParentTableName: parmdata.TableName, - ParentColumnName: parmdata.ColumnName - } : undefined - }, withfk ? [{ - columnName: parmdata.ColumnName, - fkTableName: parmdata.TableName, - parameterNames: [ - 'OrderBy', - 'StartIndex', - 'PageSize', - 'filter', - 'search', - ], - load: async (__, parameters?) => { - return this.policyService.getFilterCandidates(parameters, uidParameter); + let fkProviderItem: FkProviderItem[] = []; + if (withfk) { + fkProviderItem.push({ + columnName: parmdata.ColumnName || '', + fkTableName: parmdata.TableName || '', + parameterNames: ['OrderBy', 'StartIndex', 'PageSize', 'filter', 'search'], + load: async (__, parameters?) => { + return this.policyService.getFilterCandidates(parameters || {}, uidParameter); + }, + getDataModel: async () => ({}), + getFilterTree: async () => ({}), + }); + } + return this.entityService.createLocalEntityColumn( + { + Type: ValType.String, + IsMultiValued: withfk, + ColumnName: parmdata.ColumnName || propertyName, + MinLen: withfk ? 1 : 0, + Display: caption ? this.translateService.instant(caption) : '', + FkRelation: withfk + ? { + IsMemberRelation: false, + ParentTableName: parmdata.TableName, + ParentColumnName: parmdata.ColumnName, + } + : undefined, }, - getDataModel: async () => ({}), - getFilterTree: async ()=>({}) - }] : [], this.getValue(withfk, value, displays)); + fkProviderItem, + this.getValue(withfk, value, displays), + ); } private getValue(withfk: boolean, value: string, displays: string[]): EntityColumnData { if (withfk) { return { Value: FilterElementModel.buildMultiValueSeparatedList(value), - DisplayValue: displays ? new MultiValue(displays).GetStringValue() : '' + DisplayValue: displays ? new MultiValue(displays).GetStringValue() : '', }; } return { Value: value, - DisplayValue: displays ? new MultiValue(displays).GetStringValue() : '' + DisplayValue: displays ? new MultiValue(displays).GetStringValue() : '', }; } } diff --git a/imxweb/projects/att/src/lib/policies/editors/filter-element-model.ts b/imxweb/projects/att/src/lib/policies/editors/filter-element-model.ts index 3c949ed82..7b0bc7b1c 100644 --- a/imxweb/projects/att/src/lib/policies/editors/filter-element-model.ts +++ b/imxweb/projects/att/src/lib/policies/editors/filter-element-model.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,38 +26,40 @@ import { BehaviorSubject } from 'rxjs'; -import { ParmData, PolicyFilterElement } from 'imx-api-att'; -import { IEntityColumn, MultiValueProperty } from 'imx-qbm-dbts'; +import { ParmData, PolicyFilterElement } from '@imx-modules/imx-api-att'; +import { IEntityColumn, MultiValueProperty } from '@imx-modules/imx-qbm-dbts'; import { SelectecObjectsInfo } from '../selected-objects/selected-objects-info.interface'; import { FilterElementColumnService } from './filter-element-column.service'; export class FilterElementModel { public columnForFilter: IEntityColumn; - public selectedObjectsSubject: BehaviorSubject = new BehaviorSubject(undefined); + public selectedObjectsSubject: BehaviorSubject = new BehaviorSubject( + undefined, + ); public get parameterName(): string { - return this.filterElement.ParameterName; + return this.filterElement.ParameterName || ''; } public set parameterName(value: string) { this.filterElement.ParameterName = value; } public get attestationSubType(): string { - return this.filterElement.AttestationSubType; + return this.filterElement.AttestationSubType || ''; } public set attestationSubType(value: string) { this.filterElement.AttestationSubType = value; } public get parameterValue(): string { - return this.filterElement.ParameterValue; + return this.filterElement.ParameterValue || ''; } public set parameterValue(value: string) { this.filterElement.ParameterValue = value; } public get parameterValue2(): string { - return this.filterElement.ParameterValue2; + return this.filterElement.ParameterValue2 || ''; } public set parameterValue2(value: string) { this.filterElement.ParameterValue2 = value; @@ -68,16 +70,16 @@ export class FilterElementModel { public displays: string[], public readonly filterElement: PolicyFilterElement, private readonly uidAttestationObject: string, - private readonly columnFactory: FilterElementColumnService + private readonly columnFactory: FilterElementColumnService, ) { this.setColumnForFilter(); } public updateColumn(filter: PolicyFilterElement, displays: string[]): void { - this.attestationSubType = filter.AttestationSubType; - this.parameterName = filter.ParameterName; - this.parameterValue = filter.ParameterValue; - this.parameterValue2 = filter.ParameterValue2; + this.attestationSubType = filter.AttestationSubType || ''; + this.parameterName = filter.ParameterName || ''; + this.parameterValue = filter.ParameterValue || ''; + this.parameterValue2 = filter.ParameterValue2 || ''; this.displays = displays; this.setColumnForFilter(); this.recalculateMatching(); @@ -103,7 +105,7 @@ export class FilterElementModel { } public getParameterData(parameterType: string): ParmData { - return FilterElementModel.getParameterData(this.parameterConfig, parameterType); + return FilterElementModel.getParameterData(this.parameterConfig, parameterType) || {}; } public hasFk(): boolean { @@ -114,11 +116,11 @@ export class FilterElementModel { } public getColumnName(): string { - return this.getParameterData(this.attestationSubType)?.ColumnName; + return this.getParameterData(this.attestationSubType)?.ColumnName || ''; } public getTableName(): string { - return this.getParameterData(this.attestationSubType)?.TableName; + return this.getParameterData(this.attestationSubType)?.TableName || ''; } public filterErrors(): { [key: string]: boolean } | null { @@ -148,14 +150,17 @@ export class FilterElementModel { } private setColumnForFilter(): void { - this.columnForFilter = this.columnFactory.buildColumn( + const column = this.columnFactory.buildColumn( this.getParameterData(this.attestationSubType), this.attestationSubType, this.parameterValue, this.getColumnDisplay(), this.displays, - this.hasFk() + this.hasFk(), ); + if (column) { + this.columnForFilter = column; + } } private getColumnDisplay(): string { @@ -173,10 +178,10 @@ export class FilterElementModel { return parameterName === this.getParameterData(parameterType)?.RequiredParameter; } - public static getParameterData(parameters: ParmData[], parameterType: string): ParmData { + public static getParameterData(parameters: ParmData[], parameterType: string): ParmData | undefined { const arr = parameters.filter((p) => p.Uid === parameterType); if (arr.length !== 1) { - return null; + return undefined; } return arr[0]; @@ -210,7 +215,7 @@ export class FilterElementModel { if (str === '') { return ''; } - const seperated = this.replaceAll(this.replaceAll(str, '\'', ''), ',', MultiValueProperty.DefaultSeparator); + const seperated = this.replaceAll(this.replaceAll(str, "'", ''), ',', MultiValueProperty.DefaultSeparator); return seperated; } diff --git a/imxweb/projects/att/src/lib/policies/policy-details/policy-details.component.html b/imxweb/projects/att/src/lib/policies/policy-details/policy-details.component.html index e5cfbe2b0..cb00834bd 100644 --- a/imxweb/projects/att/src/lib/policies/policy-details/policy-details.component.html +++ b/imxweb/projects/att/src/lib/policies/policy-details/policy-details.component.html @@ -1,17 +1,18 @@ -
    - +
    - -
    {{'#LDS#Total number of attestation cases of all attestation runs' | translate}}:
    -
    {{policy?.CountCases.value}}
    -
    {{'#LDS#Total number of pending attestation cases of all attestation runs' | translate}}:
    -
    {{policy?.CountOpenCases.value}}
    + +
    {{ '#LDS#Total number of attestation cases of all attestation runs' | translate }}:
    +
    {{ policy?.CountCases?.value }}
    +
    + {{ '#LDS#Total number of pending attestation cases of all attestation runs' | translate }}: +
    +
    {{ policy?.CountOpenCases?.value }}
    - -
    {{'#LDS#Status' |translate}}:
    -
    {{status | translate}}
    + +
    {{ '#LDS#Status' | translate }}:
    +
    {{ status | translate }}
    diff --git a/imxweb/projects/att/src/lib/policies/policy-details/policy-details.component.scss b/imxweb/projects/att/src/lib/policies/policy-details/policy-details.component.scss index d3d0712af..910fd26c5 100644 --- a/imxweb/projects/att/src/lib/policies/policy-details/policy-details.component.scss +++ b/imxweb/projects/att/src/lib/policies/policy-details/policy-details.component.scss @@ -1,38 +1,19 @@ -:host { - display: flex; - flex-direction: column; - overflow: hidden; -} +@import 'base/mixins'; -.imx-title-card { - margin: 20px; +:host { + @include flex-column-container($overflow: hidden); } .eui-sidesheet-content { - display: flex; - flex-direction: column; - height: 100%; - padding: 0px; + @include flex-column-container($height: 100%); + padding: 0; } -.eui-sidesheet-actions{ - display: flex; - flex-direction: row; - margin: 0px; - } - .imx-runs-grid { - display: flex; - flex-direction: column; - overflow: hidden; - flex: 1 1 auto; - - ::ng-deep .imx-table-card{ - margin: 0px 20px; - } + @include flex-column-container-fill; } -.mat-card-content { +.mat-mdc-card-content { display: flex; } @@ -41,7 +22,3 @@ grid-template-columns: auto auto; gap: 0 10px; } - -.mat-card { - margin-bottom: 30px; -} \ No newline at end of file diff --git a/imxweb/projects/att/src/lib/policies/policy-details/policy-details.component.ts b/imxweb/projects/att/src/lib/policies/policy-details/policy-details.component.ts index c98d0cb4c..a95ed818d 100644 --- a/imxweb/projects/att/src/lib/policies/policy-details/policy-details.component.ts +++ b/imxweb/projects/att/src/lib/policies/policy-details/policy-details.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,31 +27,29 @@ import { Component, Inject } from '@angular/core'; import { EUI_SIDESHEET_DATA } from '@elemental-ui/core'; -import { PortalAttestationPolicy } from 'imx-api-att'; +import { PortalAttestationPolicy } from '@imx-modules/imx-api-att'; @Component({ selector: 'imx-policy-details', templateUrl: './policy-details.component.html', - styleUrls: ['./policy-details.component.scss'] + styleUrls: ['./policy-details.component.scss'], }) export class PolicyDetailsComponent { - public uidAttestationPolicy: string; public status: string; public get policy(): PortalAttestationPolicy { return this.data.policy; - } + } - constructor( - @Inject(EUI_SIDESHEET_DATA) public readonly data: { policy: PortalAttestationPolicy }, - ) { + constructor(@Inject(EUI_SIDESHEET_DATA) public readonly data: { policy: PortalAttestationPolicy }) { this.uidAttestationPolicy = data.policy.GetEntity().GetKeys()[0]; - this.status = data.policy.IsProcessing.value ? '#LDS#Processing' + this.status = data.policy.IsProcessing.value + ? '#LDS#Processing' : data.policy.CountCases.value === 0 - ? '#LDS#Not run yet' : - data.policy.CountOpenCases.value === 0 ? '#LDS#Completed' - : '#LDS#Pending'; + ? '#LDS#Not run yet' + : data.policy.CountOpenCases.value === 0 + ? '#LDS#Completed' + : '#LDS#Pending'; } - } diff --git a/imxweb/projects/att/src/lib/policies/policy-editor/filter-model.ts b/imxweb/projects/att/src/lib/policies/policy-editor/filter-model.ts index 6928e1265..727153ef9 100644 --- a/imxweb/projects/att/src/lib/policies/policy-editor/filter-model.ts +++ b/imxweb/projects/att/src/lib/policies/policy-editor/filter-model.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,41 +26,25 @@ import { BehaviorSubject } from 'rxjs'; -import { ParmData, PolicyFilterData, PolicyFilterElement } from 'imx-api-att'; -import { SelectecObjectsInfo } from '../selected-objects/selected-objects-info.interface'; +import { ParmData, PolicyFilterData, PolicyFilterElement } from '@imx-modules/imx-api-att'; import { FilterElementColumnService } from '../editors/filter-element-column.service'; import { FilterElementModel } from '../editors/filter-element-model'; +import { SelectecObjectsInfo } from '../selected-objects/selected-objects-info.interface'; export class FilterModel { - public totalSelectedObjectsSubject: BehaviorSubject = new BehaviorSubject(undefined); - public policyFilterData: PolicyFilterData; + public totalSelectedObjectsSubject: BehaviorSubject = new BehaviorSubject< + SelectecObjectsInfo | undefined + >(undefined); + public policyFilterData: PolicyFilterData | undefined; public parameterConfig: ParmData[] = []; public uidAttestationObject: string; constructor( public readonly columnFactory: FilterElementColumnService, - public readonly isEnabledSubject: BehaviorSubject, - public readonly attestationObjectSubject: BehaviorSubject) { - isEnabledSubject.subscribe(elem => { - if (!elem && this.policyFilterData && this.policyFilterData.Filter.Elements.length > 0) { - this.policyFilterData = { - IsReadOnly: this.policyFilterData ? this.policyFilterData.IsReadOnly : true, - Filter: { Elements: [], ConcatenationType: 'OR' }, - InfoDisplay: [] - }; - } - if (elem && this.policyFilterData == null) { - this.policyFilterData = { - IsReadOnly: this.policyFilterData ? this.policyFilterData.IsReadOnly : true, - Filter: { Elements: [], ConcatenationType: 'OR' }, - InfoDisplay: [] - }; - } - this.totalSelectedObjectsSubject.next(undefined); - }); - attestationObjectSubject.subscribe(elem => { + public readonly attestationObjectSubject: BehaviorSubject, + ) { + attestationObjectSubject.subscribe((elem) => { this.uidAttestationObject = elem; - }); } @@ -74,45 +58,50 @@ export class FilterModel { public addCondition(): FilterElementModel { const newCondition = this.buildPolicyModel({}, []); - this.policyFilterData.Filter.Elements.push(newCondition.filterElement); - this.policyFilterData.InfoDisplay.push(['']); + this.policyFilterData?.Filter?.Elements?.push(newCondition.filterElement); + this.policyFilterData?.InfoDisplay?.push(['']); return newCondition; } public deleteCondition(index: number): void { - this.policyFilterData.Filter.Elements.splice(index, 1); - this.policyFilterData.InfoDisplay.splice(index, 1); + this.policyFilterData?.Filter?.Elements?.splice(index, 1); + this.policyFilterData?.InfoDisplay?.splice(index, 1); } public filterHasChanged(filterElements: FilterElementModel[], type: string): void { - this.totalSelectedObjectsSubject.next(this.filtersAreValid(filterElements) ? - { - policyFilter: { - Elements: filterElements.map(filter => filter.filterElement), - ConcatenationType: type - }, - uidPickCategory: '', - uidAttestationObject: this.uidAttestationObject - } - : undefined); + this.totalSelectedObjectsSubject.next( + this.filtersAreValid(filterElements) + ? { + policyFilter: { + Elements: filterElements.map((filter) => filter.filterElement), + ConcatenationType: type, + }, + uidPickCategory: '', + uidAttestationObject: this.uidAttestationObject, + } + : undefined, + ); } public updateConcatination(concat: string): void { - this.policyFilterData.Filter.ConcatenationType = concat; + if (this.policyFilterData?.Filter) { + this.policyFilterData.Filter.ConcatenationType = concat; + } } public buildPolicyModel(filterElement: PolicyFilterElement, displays?: string[]): FilterElementModel { - const model = new FilterElementModel(this.parameterConfig, - displays, + const model = new FilterElementModel( + this.parameterConfig, + displays || [], filterElement, this.uidAttestationObject, - this.columnFactory + this.columnFactory, ); return model; } private filtersAreValid(filterElements: FilterElementModel[]): boolean { - return filterElements.filter(elem => elem.filterErrors() == null).length === filterElements.length; + return filterElements.filter((elem) => elem.filterErrors() == null).length === filterElements.length; } } diff --git a/imxweb/projects/att/src/lib/policies/policy-editor/policy-editor.component.html b/imxweb/projects/att/src/lib/policies/policy-editor/policy-editor.component.html index 593e1a95f..a7e4d288f 100644 --- a/imxweb/projects/att/src/lib/policies/policy-editor/policy-editor.component.html +++ b/imxweb/projects/att/src/lib/policies/policy-editor/policy-editor.component.html @@ -1,5 +1,5 @@
    -

    {{ '#LDS#Heading Objects to be Attested by this Attestation Policy' | translate }}

    +

    {{ '#LDS#Heading Objects to be Attested by this Attestation Policy' | translate }}

    {{ '#LDS#Heading Objects to be Attested by this At >
    - -
    {{ '#LDS#This policy affects many objects. Running this policy may take some time and generate notifications to many approvers.' | translate }}
    + +
    + {{ + '#LDS#This policy affects many objects. Running this policy may take some time and generate notifications to many approvers.' + | translate + }} +
    +
    + +
    + {{ mismatch | translate }} +
    - + - {{ '#LDS#How many conditions must be met?' | translate }} - + {{ + '#LDS#How many conditions must be met?' | translate + }} + {{ '#LDS#All conditions have to be met.' | translate }} @@ -28,20 +44,21 @@

    {{ '#LDS#Heading Objects to be Attested by this At - + {{ '#LDS#Add at least one condition.' | translate }} - + + + +
    {{ filterElementModel?.getParameterData(filterElementModel?.attestationSubType || '')?.Display }}
    + + +
    + + {{ '#LDS#New condition' | translate }} + + - - {{'#LDS#Condition type' | translate}} - - - {{elem.Display}} - - - + + {{ '#LDS#Condition type' | translate }} + + + {{ elem.Display }} + + + - - - - {{'#LDS#Please select a condition type.' | translate}} - + + + + + {{ '#LDS#Please select a condition type.' | translate }} + - \ No newline at end of file + diff --git a/imxweb/projects/att/src/lib/policies/policy-filter-element/policy-filter-element.component.scss b/imxweb/projects/att/src/lib/policies/policy-filter-element/policy-filter-element.component.scss index b5051339c..c40b22667 100644 --- a/imxweb/projects/att/src/lib/policies/policy-filter-element/policy-filter-element.component.scss +++ b/imxweb/projects/att/src/lib/policies/policy-filter-element/policy-filter-element.component.scss @@ -1,52 +1,13 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; - - -:host ::ng-deep .mat-expansion-panel-body { - display: flex; - flex-direction: column; - background-color: $color-gray-2; - padding-top: 10px; -} - -.imx-panel-title-with-number, -.imx-title { - display: flex; - flex-direction: row; - align-items: center; -} - -.imx-panel-title-with-number > :first-child { - flex: 1; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; -} +.imx-panel-title-with-number{ + max-width: calc(100% - 52px); + &> :first-child { + flex: 1; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + } .imx-small-error { font-size: small; } - -.mat-expansion-panel-header-title { - align-items: center; - margin-right: 0; -} - -.eui-icon { - color: $color-gray-50; -} - -.eui-dark-theme { - :host { - ::ng-deep .mat-expansion-panel-body{ - background-color: $color-gray-80; - } - } -} - -.eui-contrast-theme { - :host { - ::ng-deep .mat-expansion-panel-body{ - background-color: $color-gray-100; - } - } -} diff --git a/imxweb/projects/att/src/lib/policies/policy-filter-element/policy-filter-element.component.ts b/imxweb/projects/att/src/lib/policies/policy-filter-element/policy-filter-element.component.ts index 218f35a51..e5df5d414 100644 --- a/imxweb/projects/att/src/lib/policies/policy-filter-element/policy-filter-element.component.ts +++ b/imxweb/projects/att/src/lib/policies/policy-filter-element/policy-filter-element.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,9 +24,8 @@ * */ -import { ChangeDetectorRef, EventEmitter, ViewChild } from '@angular/core'; -import { Component, Input, Output } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; +import { ChangeDetectorRef, Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; +import { FormGroup, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { MatExpansionPanel } from '@angular/material/expansion'; import { MatSelectChange } from '@angular/material/select'; @@ -36,11 +35,10 @@ import { FilterElementModel } from '../editors/filter-element-model'; @Component({ selector: 'imx-policy-filter-element', templateUrl: './policy-filter-element.component.html', - styleUrls: ['./policy-filter-element.component.scss'] + styleUrls: ['./policy-filter-element.component.scss'], }) export class PolicyFilterElementComponent { - - @Input() public formGroup: UntypedFormGroup; + @Input() public formGroup: FormGroup<{ filterParameter: UntypedFormControl; type: UntypedFormControl }>; @Input() public idForTest: string; @Output() public deleteFilter = new EventEmitter(); @@ -48,18 +46,14 @@ export class PolicyFilterElementComponent { @Output() public parameterChanged = new EventEmitter(); @ViewChild(MatExpansionPanel) private panel: MatExpansionPanel; - public get filterElementModel(): FilterElementModel { - return this.formGroup?.get(this.filterParameter).value; + public get filterElementModel(): FilterElementModel | undefined { + return this.formGroup.controls.filterParameter.value; } - private readonly filterParameter = 'filterParameter'; - - constructor(private readonly cd: ChangeDetectorRef) { } + constructor(private readonly cd: ChangeDetectorRef) {} public open(): void { - if (this.panel - && (this.filterElementModel.attestationSubType == null || this.filterElementModel.attestationSubType === '') - ) { + if (this.panel && (this.filterElementModel?.attestationSubType == null || this.filterElementModel?.attestationSubType === '')) { this.panel.open(); this.cd.detectChanges(); } @@ -69,12 +63,21 @@ export class PolicyFilterElementComponent { if (this.filterElementModel == null) { return; } - this.filterElementModel.updateColumn({ - ParameterName: this.filterElementModel.getParameterData(arg.value).RequiredParameter, - AttestationSubType: arg.value, - ParameterValue: FilterElementModel.getDefaultValue(this.filterElementModel.getParameterData(arg.value).RequiredParameter, false), - ParameterValue2: FilterElementModel.getDefaultValue(this.filterElementModel.getParameterData(arg.value).RequiredParameter, true), - }, []); + this.filterElementModel.updateColumn( + { + ParameterName: this.filterElementModel.getParameterData(arg.value).RequiredParameter, + AttestationSubType: arg.value, + ParameterValue: FilterElementModel.getDefaultValue( + this.filterElementModel.getParameterData(arg.value).RequiredParameter || '', + false, + ), + ParameterValue2: FilterElementModel.getDefaultValue( + this.filterElementModel.getParameterData(arg.value).RequiredParameter || '', + true, + ), + }, + [], + ); this.formGroup.updateValueAndValidity(); this.conditionTypeChanged.emit(this.filterElementModel); } @@ -83,8 +86,7 @@ export class PolicyFilterElementComponent { if (this.filterElementModel == null) { return; } - this.filterElementModel.updateColumn(this.filterElementModel.filterElement, arg.displays); + this.filterElementModel.updateColumn(this.filterElementModel.filterElement, arg.displays || []); this.parameterChanged.emit(this.filterElementModel); } - } diff --git a/imxweb/projects/att/src/lib/policies/policy-list/attestation-policy.ts b/imxweb/projects/att/src/lib/policies/policy-list/attestation-policy.ts index 195603a5f..a6239f2c3 100644 --- a/imxweb/projects/att/src/lib/policies/policy-list/attestation-policy.ts +++ b/imxweb/projects/att/src/lib/policies/policy-list/attestation-policy.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { PortalAttestationPolicy } from 'imx-api-att'; +import { PortalAttestationPolicy } from '@imx-modules/imx-api-att'; export class AttestationPolicy extends PortalAttestationPolicy { public hasAttestations = false; diff --git a/imxweb/projects/att/src/lib/policies/policy-list/policy-list.component.html b/imxweb/projects/att/src/lib/policies/policy-list/policy-list.component.html index dbb3da9bc..90393c55c 100644 --- a/imxweb/projects/att/src/lib/policies/policy-list/policy-list.component.html +++ b/imxweb/projects/att/src/lib/policies/policy-list/policy-list.component.html @@ -1,91 +1,90 @@ -

    - {{ '#LDS#Heading Attestation Policies' | translate }} - -

    - -
    - - +
    +

    + {{ '#LDS#Heading Attestation Policies' | translate }} + +

    + +
    + + + + + {{ entitySchemaPolicy?.Columns?.[DisplayColumns.DISPLAY_PROPERTYNAME]?.Display }} + + +
    {{ item.GetEntity().GetDisplay() }}
    +
    +
    {{ item.Areas.Column.GetDisplayValue() }}
    +
    + +
    + + {{ entitySchemaPolicy?.Columns?.NextRun?.Display }} + + {{ item.GetEntity().GetColumn('NextRun').GetValue() | shortDate }} + + + + + +
    + +
    - - - -
    {{ data.GetEntity().GetDisplay() }}
    -
    -
    {{ data.Areas.Column.GetDisplayValue() }}
    -
    -
    -
    - - - -
    + + +
    +
    - - -
    - - -
    - - - - -
    - - - - -
    + + + + + +
    +
    +
    -
    - - - diff --git a/imxweb/projects/att/src/lib/policies/policy-list/policy-list.component.scss b/imxweb/projects/att/src/lib/policies/policy-list/policy-list.component.scss index 1d6c6e991..129919c62 100644 --- a/imxweb/projects/att/src/lib/policies/policy-list/policy-list.component.scss +++ b/imxweb/projects/att/src/lib/policies/policy-list/policy-list.component.scss @@ -1,30 +1,11 @@ @import '@elemental-ui/core/src/styles/_eui_palette.scss'; -@import '../../../../../../shared/scss/common-table.scss'; + :host { display: flex; flex-direction: column; overflow: hidden; height: inherit; - ::ng-deep imx-data-table .mat-header-row .mat-header-cell { - font-weight: 600; - font-size: 14px; - padding: 0 10px; - } - - ::ng-deep imx-data-table .mat-row .mat-cell { - padding: 0 10px; - } - - ::ng-deep eui-sidesheet .eui-sidesheet__content { - display: flex; - flex-direction: column; - } - - imx-data-table { - flex-grow: 2; - } - div[subtitle-long] { font-size: smaller; color: $color-gray-40; @@ -36,14 +17,7 @@ .imx-separate-menu-item { border-top: 1px solid $color-gray-20; } - - .imx-table-container { - display: flex; - flex-direction: column; - overflow: hidden; - height: inherit; - flex: 1 1 auto; - } + .imx-policy-footer { display: flex; @@ -54,49 +28,9 @@ min-height: 50px; } - .mat-raised-button { - align-self: flex-end; - } - - .mat-toolbar { - background-color: inherit; - margin-left: 25px; - margin-right: 0; - margin-bottom: 40px; - padding-right: 0; - } - - .mat-toolbar > button { - margin-left: 5px; - } - - .mat-toolbar > mat-button { - color: $color-gray-100; - } - .imx-toolbar-spacer { flex: 1 1 auto; } - - .mat-slide-toggle { - font-size: 16px; - } - - .button-row { - @include imx-button-bar(); - } - - .imx-button-column { - @include imx-button-column-right(); - } - - .imx-content-card { - flex: 1 1 auto; - display: flex; - flex-direction: column; - margin: 3px; - overflow: hidden; - } } .imx-menu-with-spinner { @@ -104,18 +38,10 @@ flex-direction: row; align-items: center; margin-right: 16px; - - .mat-progress-spinner { - overflow: visible; - } } .eui-dark-theme { :host { - .mat-toolbar > mat-button { - color: $color-gray-10; - } - div[subtitle-long] { color: $color-gray-60; } @@ -124,9 +50,6 @@ .eui-contrast-theme { :host { - .mat-toolbar > mat-button { - color: $color-gray-0; - } div[subtitle-long] { color: $color-gray-80; } diff --git a/imxweb/projects/att/src/lib/policies/policy-list/policy-list.component.ts b/imxweb/projects/att/src/lib/policies/policy-list/policy-list.component.ts index 98fa200f5..ac2ba6a4b 100644 --- a/imxweb/projects/att/src/lib/policies/policy-list/policy-list.component.ts +++ b/imxweb/projects/att/src/lib/policies/policy-list/policy-list.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,63 +25,61 @@ */ import { ChangeDetectorRef, Component, OnInit, ViewChild } from '@angular/core'; -import { OverlayRef } from '@angular/cdk/overlay'; +import { MatButton } from '@angular/material/button'; import { EuiDownloadOptions, EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { MatButton } from '@angular/material/button'; +import { PolicyFilterData, PortalAttestationPolicy, PortalAttestationPolicyEdit } from '@imx-modules/imx-api-att'; +import { ViewConfigData } from '@imx-modules/imx-api-qer'; +import { + CollectionLoadParameters, + CompareOperator, + DataModel, + DisplayColumns, + EntitySchema, + ExtendedTypedEntityCollection, + FilterType, + TypedEntityCollectionData, + ValType, +} from '@imx-modules/imx-qbm-dbts'; import { + BusyService, ClassloggerService, + ClientPropertyForTableColumns, ConfirmationService, DataSourceToolbarFilter, - DataSourceToolbarGroupData, - DataSourceToolbarSettings, + DataSourceToolbarViewConfig, DataTableGroupedData, - ClientPropertyForTableColumns, + DataViewInitParameters, + DataViewSource, SettingsService, SnackBarService, SystemInfoService, - createGroupData, - DataSourceToolbarViewConfig, - BusyService, + calculateSidesheetWidth, } from 'qbm'; -import { - DisplayColumns, - ValType, - ExtendedTypedEntityCollection, - EntitySchema, - FilterType, - CompareOperator, - DataModel, -} from 'imx-qbm-dbts'; -import { PolicyFilterData, PortalAttestationPolicy, PortalAttestationPolicyEdit } from 'imx-api-att'; import { UserModelService, ViewConfigService } from 'qer'; -import { PolicyService } from '../policy.service'; -import { EditMasterDataComponent } from '../edit-master-data/edit-master-data.component'; import { AttestationCasesComponentParameter } from '../attestation-cases/attestation-cases-component-parameter.interface'; import { AttestationCasesComponent } from '../attestation-cases/attestation-cases.component'; -import { PolicyLoadParameters } from './policy-load-parameters.interface'; -import { AttestationPolicy } from './attestation-policy'; +import { EditMasterDataComponent } from '../edit-master-data/edit-master-data.component'; import { PolicyDetailsComponent } from '../policy-details/policy-details.component'; import { PolicyCopyData } from '../policy.interface'; -import { ViewConfigData } from 'imx-api-qer'; +import { PolicyService } from '../policy.service'; +import { AttestationPolicy } from './attestation-policy'; @Component({ templateUrl: './policy-list.component.html', styleUrls: ['./policy-list.component.scss'], + providers: [DataViewSource], }) export class PolicyListComponent implements OnInit { @ViewChild('deleteButton') public deleteButton: MatButton; - public dstSettings: DataSourceToolbarSettings; - public navigationState: PolicyLoadParameters; public readonly entitySchemaPolicy: EntitySchema; public readonly DisplayColumns = DisplayColumns; public groupedData: { [key: string]: DataTableGroupedData } = {}; public isComplienceFrameworkEnabled = false; public busyService = new BusyService(); public menuLoading = false; - private groupData: DataSourceToolbarGroupData; private filterOptions: DataSourceToolbarFilter[] = []; private prefilterOwner = false; @@ -101,10 +99,10 @@ export class PolicyListComponent implements OnInit { private readonly userService: UserModelService, private readonly systemInfoService: SystemInfoService, private readonly settingsService: SettingsService, - private readonly changeDetector : ChangeDetectorRef, + private readonly changeDetector: ChangeDetectorRef, private readonly logger: ClassloggerService, + public dataSource: DataViewSource, ) { - this.navigationState = { PageSize: this.settingsService.DefaultPageSize, StartIndex: 0 }; this.entitySchemaPolicy = policyService.AttestationPolicySchema; this.displayedColumns = [ this.entitySchemaPolicy.Columns[DisplayColumns.DISPLAY_PROPERTYNAME], @@ -124,8 +122,8 @@ export class PolicyListComponent implements OnInit { let prep: string[]; try { this.dataModel = await this.policyService.getDataModel(); - features = (await this.userService.getFeatures()).Features; - prep = (await this.systemInfoService.get()).PreProps; + features = (await this.userService.getFeatures()).Features || []; + prep = (await this.systemInfoService.get()).PreProps || []; this.prefilterOwner = !this.policyService.canSeeAllAttestations(prep, features); this.isComplienceFrameworkEnabled = await this.policyService.isComplienceFrameworkEnabled(); @@ -139,49 +137,13 @@ export class PolicyListComponent implements OnInit { public async updateConfig(config: ViewConfigData): Promise { await this.viewConfigService.putViewConfig(config); this.viewConfig = await this.viewConfigService.getDSTExtensionChanges(this.viewConfigPath); - this.dstSettings.viewConfig = this.viewConfig; + this.dataSource.viewConfig.set(this.viewConfig); } public async deleteConfigById(id: string): Promise { await this.viewConfigService.deleteViewConfig(id); this.viewConfig = await this.viewConfigService.getDSTExtensionChanges(this.viewConfigPath); - this.dstSettings.viewConfig = this.viewConfig; - } - - public async onNavigationStateChanged(newState: PolicyLoadParameters): Promise { - this.navigationState = newState; - this.logger.trace(this, 'navigation state change to ', this.navigationState); - await this.navigate(); - } - - public async onSearch(keywords: string): Promise { - this.navigationState = { - ...this.navigationState, - ...{ - StartIndex: 0, - search: keywords, - }, - }; - this.logger.trace(this, 'navigation state change to ', this.navigationState); - return this.navigate(); - } - - public async onGroupingChange(groupKey: string): Promise { - const isBusy = this.busyService.beginBusy(); - - try { - const groupedData = this.groupedData[groupKey]; - groupedData.data = await this.policyService.getPolicies(groupedData.navigationState); - groupedData.settings = { - displayedColumns: this.dstSettings.displayedColumns, - dataModel: this.dstSettings.dataModel, - dataSource: groupedData.data, - entitySchema: this.dstSettings.entitySchema, - navigationState: groupedData.navigationState, - }; - } finally { - isBusy.endBusy(); - } + this.dataSource.viewConfig.set(this.viewConfig); } public async menuOpened(policy: AttestationPolicy): Promise { @@ -196,76 +158,66 @@ export class PolicyListComponent implements OnInit { } } - public async editPolicy(policy: PortalAttestationPolicy): Promise { - let data: ExtendedTypedEntityCollection; - - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.elementalBusyService.show())); - try { - data = await this.policyService.getPolicyEditInteractive(policy.GetEntity().GetKeys()[0]); - } finally { - setTimeout(() => this.elementalBusyService.hide(overlayRef)); - if (data && data.Data.length > 0) { - await this.showPolicy( - data.Data[0], - data.extendedData[0], - await this.translator.get('#LDS#Heading Edit Attestation Policy').toPromise(), - false - ); - } + public async editPolicy(policy: AttestationPolicy): Promise { + this.elementalBusyService.show(); + let data: ExtendedTypedEntityCollection = await this.policyService.getPolicyEditInteractive( + policy.GetEntity().GetKeys()[0], + ); + this.elementalBusyService.hide(); + if (data && data.Data.length > 0) { + await this.showPolicy( + data.Data[0], + data.extendedData?.[0], + await this.translator.get('#LDS#Heading Edit Attestation Policy').toPromise(), + false, + ); } } public async newPolicy(): Promise { - let policy: PolicyCopyData; - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.elementalBusyService.show())); - try { - policy = await this.policyService.buildNewEntity(); - this.logger.trace(this, 'new policy created', policy); - } finally { - setTimeout(() => this.elementalBusyService.hide(overlayRef)); - if (policy) { - await this.showPolicy( - policy.data, - { - IsReadOnly: false, - Filter: { Elements: [], ConcatenationType: 'OR' }, - InfoDisplay: [], - }, - await this.translator.get('#LDS#Heading Create Attestation Policy').toPromise(), - true - ); - } + if (this.elementalBusyService.overlayRefs.length === 0) { + // Its possible we enter this function from another that has used the busy service, check if there is an overlay before showing + this.elementalBusyService.show(); + } + let policy: PolicyCopyData = await this.policyService.buildNewEntity(); + this.logger.trace(this, 'new policy created', policy); + this.elementalBusyService.hide(); + if (policy) { + await this.showPolicy( + policy.data, + { + IsReadOnly: false, + Filter: { Elements: [], ConcatenationType: 'OR' }, + InfoDisplay: [], + }, + await this.translator.get('#LDS#Heading Create Attestation Policy').toPromise(), + true, + ); } } public async copy(policy: PortalAttestationPolicy): Promise { let newPolicy: PolicyCopyData; let filter: PolicyFilterData; - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.elementalBusyService.show())); - try { - const data = await this.policyService.getPolicyEditInteractive(policy.GetEntity().GetKeys()[0]); + this.elementalBusyService.show(); + const data = await this.policyService.getPolicyEditInteractive(policy.GetEntity().GetKeys()[0]); - if (data == null || data.Data.length === 0) { - return this.newPolicy(); - } + if (!data || data.Data.length === 0) { + return this.newPolicy(); + } - newPolicy = await this.policyService.buildNewEntity(data.Data[0], data.extendedData[0]?.Filter); - filter = data.extendedData[0]; - this.logger.trace(this, 'copy for policy (old, new)', data, newPolicy); - } finally { - setTimeout(() => this.elementalBusyService.hide(overlayRef)); - if (newPolicy) { - await this.showPolicy( - newPolicy.data, - filter, - await this.translator.get('#LDS#Heading Copy Attestation Policy').toPromise(), - true, - newPolicy.pickCategorySkipped - ); - } + newPolicy = await this.policyService.buildNewEntity(data.Data[0], data.extendedData?.[0]?.Filter); + filter = data.extendedData?.[0] || { IsReadOnly: true }; + this.logger.trace(this, 'copy for policy (old, new)', data, newPolicy); + this.elementalBusyService.hide(); + if (newPolicy) { + await this.showPolicy( + newPolicy.data, + filter, + await this.translator.instant('#LDS#Heading Copy Attestation Policy'), + true, + newPolicy.pickCategorySkipped, + ); } } @@ -289,7 +241,7 @@ export class PolicyListComponent implements OnInit { key: '#LDS#The attestation policy "{0}" has been successfully deleted.', parameters: [policy.GetEntity().GetDisplay()], }; - this.navigate(); + this.dataSource.updateState(); this.snackbar.open(message, '#LDS#Close'); } } @@ -300,8 +252,7 @@ export class PolicyListComponent implements OnInit { public async run(policy: PortalAttestationPolicy): Promise { let data: AttestationCasesComponentParameter; - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.elementalBusyService.show())); + this.elementalBusyService.show(); try { const policyEdit = await this.policyService.getPolicyEditInteractive(policy.GetEntity().GetKeys()[0]); this.logger.trace(this, 'interactive policy loaded', policyEdit); @@ -321,7 +272,7 @@ export class PolicyListComponent implements OnInit { subtitle: policy.GetEntity().GetDisplay(), }; } finally { - setTimeout(() => this.elementalBusyService.hide(overlayRef)); + this.elementalBusyService.hide(); } if (data) { @@ -330,7 +281,7 @@ export class PolicyListComponent implements OnInit { title: await this.translator.get('#LDS#Heading Start Attestation').toPromise(), subTitle: policy.GetEntity().GetDisplay(), padding: '0px', - width: 'max(600px, 60%)', + width: calculateSidesheetWidth(), data, testId: 'policy-list-start-attestation-run-sidesheet', }) @@ -338,15 +289,14 @@ export class PolicyListComponent implements OnInit { .toPromise(); if (result) { - this.navigate(); + this.dataSource.updateState(); } } } public async showDetails(policy: PortalAttestationPolicy): Promise { - let singlePolicy: PortalAttestationPolicy; - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.elementalBusyService.show())); + let singlePolicy: PortalAttestationPolicy | undefined; + this.elementalBusyService.show(); try { const policies = await this.policyService.getPolicies({ filter: [ @@ -360,14 +310,14 @@ export class PolicyListComponent implements OnInit { }); singlePolicy = policies.Data.length > 0 ? policies.Data[0] : undefined; } finally { - setTimeout(() => this.elementalBusyService.hide(overlayRef)); + this.elementalBusyService.hide(); } if (singlePolicy) { this.sideSheet.open(PolicyDetailsComponent, { title: await this.translator.get('#LDS#Heading View Attestation Runs').toPromise(), subTitle: singlePolicy.GetEntity().GetDisplay(), padding: '0px', - width: 'max(600px, 60%)', + width: calculateSidesheetWidth(), data: { policy: singlePolicy }, testId: 'policy-list-view-details-sidesheet', }); @@ -375,27 +325,21 @@ export class PolicyListComponent implements OnInit { } private async navigate(): Promise { - const isBusy = this.busyService.beginBusy(); - - try { - const policies = await this.policyService.getPolicies(this.navigationState); - const exportMethod = this.policyService.exportPolicies(this.navigationState); - this.logger.trace(this, 'interactive policy loaded', policies); - - this.dstSettings = { - displayedColumns: this.displayedColumns, - dataSource: policies, - filters: this.filterOptions, - groupData: this.groupData, - entitySchema: this.entitySchemaPolicy, - navigationState: this.navigationState, - dataModel: this.dataModel, - viewConfig: this.viewConfig, - exportMethod, - }; - } finally { - isBusy.endBusy(); - } + const dataViewInitParameters: DataViewInitParameters = { + execute: (params: CollectionLoadParameters, signal: AbortSignal): Promise> => + this.policyService.getPolicies(params, signal), + schema: this.entitySchemaPolicy, + columnsToDisplay: this.displayedColumns, + dataModel: this.dataModel, + groupExecute: (column: string, params: CollectionLoadParameters, signal: AbortSignal) => + this.policyService.getGroupInfo({ ...params, by: column }), + exportFunction: this.policyService.exportPolicies(this.dataSource.state()), + viewConfig: this.viewConfig, + highlightEntity: (policy: AttestationPolicy) => { + this.editPolicy(policy); + }, + }; + this.dataSource.init(dataViewInitParameters); } private async showPolicy( @@ -403,16 +347,16 @@ export class PolicyListComponent implements OnInit { filterData: PolicyFilterData, display: string, isNew: boolean, - showSampleDataWarning: boolean = false + showSampleDataWarning: boolean = false, ): Promise { const sidesheetRef = this.sideSheet.open(EditMasterDataComponent, { title: display, subTitle: isNew ? '' : policy.GetEntity().GetDisplay(), padding: '0px', - width: 'max(600px, 80%)', + width: calculateSidesheetWidth(1000), disableClose: true, data: { policy, filterData, isNew, isComplienceFrameworkEnabled: this.isComplienceFrameworkEnabled, showSampleDataWarning }, - testId: 'policy-list-show-policy-sidesheet' + testId: 'policy-list-show-policy-sidesheet', }); const shouldReload = await sidesheetRef.afterClosed().toPromise(); @@ -424,29 +368,30 @@ export class PolicyListComponent implements OnInit { private async initFilterAndGrouping(): Promise { this.viewConfig = await this.viewConfigService.getInitialDSTExtension(this.dataModel, this.viewConfigPath); const defaultSet = this.viewConfigService.isDefaultConfigSet(); - this.filterOptions = this.dataModel.Filters; + this.filterOptions = this.dataModel.Filters || []; // set initial value for OnlyActivePolicies const indexActive = this.filterOptions.findIndex((elem) => elem.Name === 'OnlyActivePolicies'); if (indexActive > -1 && !defaultSet) { this.filterOptions[indexActive].InitialValue = '1'; - this.navigationState.OnlyActivePolicies = '1'; + this.filterOptions.map((filter) => { + if (filter.Name === 'OnlyActivePolicies') { + filter.CurrentValue = '1'; + } + }); + this.dataSource.state.update((state) => ({ ...state, OnlyActivePolicies: '1' })); + this.dataSource.predefinedFilters.set(this.filterOptions); } // remove filter myPolicies, if you are an owner only and not an attestation admin if (this.prefilterOwner && !defaultSet) { - this.navigationState.mypolicies = '1'; - const index = this.filterOptions.findIndex((elem) => elem.Name === 'mypolicies'); - if (index > -1) { - this.filterOptions.splice(index, 1); - } + this.filterOptions.map((filter) => { + if (filter.Name === 'mypolicies') { + filter.CurrentValue = '1'; + } + }); + this.dataSource.state.update((state) => ({ ...state, mypolicies: '1' })); + this.dataSource.predefinedFilters.set(this.filterOptions); } - - this.groupData = createGroupData(this.dataModel, (parameters) => - this.policyService.getGroupInfo({ - ...{ PageSize: this.navigationState.PageSize, StartIndex: 0 }, - ...parameters, - }) - ); } } diff --git a/imxweb/projects/att/src/lib/policies/policy-list/policy-load-parameters.interface.ts b/imxweb/projects/att/src/lib/policies/policy-list/policy-load-parameters.interface.ts index 790797bff..8748e5a20 100644 --- a/imxweb/projects/att/src/lib/policies/policy-list/policy-load-parameters.interface.ts +++ b/imxweb/projects/att/src/lib/policies/policy-list/policy-load-parameters.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { CollectionLoadParameters } from 'imx-qbm-dbts'; +import { CollectionLoadParameters } from '@imx-modules/imx-qbm-dbts'; export interface PolicyLoadParameters extends CollectionLoadParameters { OnlyActivePolicies?: string; diff --git a/imxweb/projects/att/src/lib/policies/policy.interface.ts b/imxweb/projects/att/src/lib/policies/policy.interface.ts index 75965de61..610b95b4b 100644 --- a/imxweb/projects/att/src/lib/policies/policy.interface.ts +++ b/imxweb/projects/att/src/lib/policies/policy.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { PolicyFilterData, PortalAttestationPolicyEdit } from 'imx-api-att'; +import { PolicyFilterData, PortalAttestationPolicyEdit } from '@imx-modules/imx-api-att'; export interface Policy { policy: PortalAttestationPolicyEdit; filterData: PolicyFilterData; diff --git a/imxweb/projects/att/src/lib/policies/policy.module.ts b/imxweb/projects/att/src/lib/policies/policy.module.ts index 877695653..e9ccfbcb9 100644 --- a/imxweb/projects/att/src/lib/policies/policy.module.ts +++ b/imxweb/projects/att/src/lib/policies/policy.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,95 +24,100 @@ * */ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MatInputModule } from '@angular/material/input'; import { MatButtonModule } from '@angular/material/button'; -import { MatRadioModule } from '@angular/material/radio'; +import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatDialogModule } from '@angular/material/dialog'; import { MatExpansionModule } from '@angular/material/expansion'; -import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatInputModule } from '@angular/material/input'; import { MatMenuModule } from '@angular/material/menu'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { MatRadioModule } from '@angular/material/radio'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; +import { MatTooltipModule } from '@angular/material/tooltip'; import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; import { TranslateModule } from '@ngx-translate/core'; -import { MatTooltipModule } from '@angular/material/tooltip'; -import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; -import { LdsReplaceModule, DataSourceToolbarModule, DataTableModule, CdrModule, ClassloggerModule, HelpContextualModule, DataTreeWrapperModule } from 'qbm'; -import { UserModule, StatisticsModule } from 'qer'; -import { EditNameComponent } from './editors/edit-name.component'; +import { + CdrModule, + ClassloggerModule, + DataSourceToolbarModule, + DataTableModule, + DataTreeWrapperModule, + DataViewModule, + DateModule, + HelpContextualModule, + LdsReplaceModule, +} from 'qbm'; +import { StatisticsModule, UserModule } from 'qer'; +import { AttestationRunsModule } from '../runs/attestation-runs.module'; +import { AttestationCasesComponent } from './attestation-cases/attestation-cases.component'; +import { ConfirmDeactivationComponent } from './confirm-deactivation/confirm-deactivation.component'; +import { EditMasterDataComponent } from './edit-master-data/edit-master-data.component'; import { EditGenericComponent } from './editors/edit-generic.component'; +import { EditNameComponent } from './editors/edit-name.component'; import { EditOriginComponent } from './editors/edit-origin.component'; import { EditThresholdComponent } from './editors/edit-threshold.component'; import { EditUintComponent } from './editors/edit-uint.component'; -import { EditMasterDataComponent } from './edit-master-data/edit-master-data.component'; -import { PolicyService } from './policy.service'; -import { AttestationCasesComponent } from './attestation-cases/attestation-cases.component'; import { FilterEditorComponent } from './editors/filter-editor.component'; -import { ConfirmDeactivationComponent } from './confirm-deactivation/confirm-deactivation.component'; -import { PolicyFilterElementComponent } from './policy-filter-element/policy-filter-element.component'; +import { PolicyDetailsComponent } from './policy-details/policy-details.component'; import { PolicyEditorComponent } from './policy-editor/policy-editor.component'; +import { PolicyFilterElementComponent } from './policy-filter-element/policy-filter-element.component'; import { PolicyListComponent } from './policy-list/policy-list.component'; +import { PolicyService } from './policy.service'; import { SelectedObjectsComponent } from './selected-objects/selected-objects.component'; -import { PolicyDetailsComponent } from './policy-details/policy-details.component'; -import { AttestationRunsModule } from '../runs/attestation-runs.module'; - @NgModule({ - imports: [ - CdrModule, - CommonModule, - DataSourceToolbarModule, - DataTableModule, - EuiCoreModule, - EuiMaterialModule, - FormsModule, - LdsReplaceModule, - MatExpansionModule, - MatTooltipModule, - MatInputModule, - MatButtonModule, - MatDialogModule, - MatRadioModule, - MatProgressSpinnerModule, - MatCheckboxModule, - MatSlideToggleModule, - MatMenuModule, - ReactiveFormsModule, - TranslateModule, - UserModule, - ClassloggerModule, - AttestationRunsModule, - LdsReplaceModule, - StatisticsModule, - HelpContextualModule, - DataTreeWrapperModule, - ], - declarations: [ - EditGenericComponent, - EditNameComponent, - EditOriginComponent, - EditMasterDataComponent, - EditThresholdComponent, - EditUintComponent, - PolicyEditorComponent, - PolicyListComponent, - SelectedObjectsComponent, - AttestationCasesComponent, - FilterEditorComponent, - ConfirmDeactivationComponent, - PolicyFilterElementComponent, - PolicyDetailsComponent - ], - providers: [ - PolicyService - ], - exports: [ - PolicyListComponent, - EditMasterDataComponent, - AttestationCasesComponent - ] + imports: [ + CdrModule, + CommonModule, + DataSourceToolbarModule, + DataTableModule, + EuiCoreModule, + EuiMaterialModule, + FormsModule, + LdsReplaceModule, + MatExpansionModule, + MatTooltipModule, + MatInputModule, + MatButtonModule, + MatDialogModule, + MatRadioModule, + MatProgressSpinnerModule, + MatCheckboxModule, + MatSlideToggleModule, + MatMenuModule, + ReactiveFormsModule, + TranslateModule, + UserModule, + ClassloggerModule, + AttestationRunsModule, + LdsReplaceModule, + StatisticsModule, + HelpContextualModule, + DataTreeWrapperModule, + DataViewModule, + DateModule, + ], + declarations: [ + EditGenericComponent, + EditNameComponent, + EditOriginComponent, + EditMasterDataComponent, + EditThresholdComponent, + EditUintComponent, + PolicyEditorComponent, + PolicyListComponent, + SelectedObjectsComponent, + AttestationCasesComponent, + FilterEditorComponent, + ConfirmDeactivationComponent, + PolicyFilterElementComponent, + PolicyDetailsComponent, + ], + providers: [PolicyService], + exports: [PolicyListComponent, EditMasterDataComponent, AttestationCasesComponent], }) -export class PolicyModule { } +export class PolicyModule {} diff --git a/imxweb/projects/att/src/lib/policies/policy.service.ts b/imxweb/projects/att/src/lib/policies/policy.service.ts index e6a3e8118..94a6f9720 100644 --- a/imxweb/projects/att/src/lib/policies/policy.service.ts +++ b/imxweb/projects/att/src/lib/policies/policy.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,14 +29,14 @@ import { EuiDownloadOptions } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; import { - V2ApiClientMethodFactory, ParmData, PolicyFilter, PolicyStartInput, PortalAttestationFilterMatchingobjects, PortalAttestationPolicyEdit, PortalAttestationPolicyEditInteractive, -} from 'imx-api-att'; + V2ApiClientMethodFactory, +} from '@imx-modules/imx-api-att'; import { CollectionLoadParameters, CompareOperator, @@ -50,7 +50,7 @@ import { MethodDefinition, MethodDescriptor, TypedEntityBuilder, -} from 'imx-qbm-dbts'; +} from '@imx-modules/imx-qbm-dbts'; import { AppConfigService, ClassloggerService, DataSourceToolbarExportMethod, ElementalUiConfigService } from 'qbm'; import { ApiService } from '../api.service'; import { AttestationPolicy } from './policy-list/attestation-policy'; @@ -68,7 +68,7 @@ export class PolicyService { private readonly elementalUiConfigService: ElementalUiConfigService, private readonly translator: TranslateService, private readonly config: AppConfigService, - private readonly logger: ClassloggerService + private readonly logger: ClassloggerService, ) {} public get AttestationMatchingObjectsSchema(): EntitySchema { @@ -83,8 +83,11 @@ export class PolicyService { return this.api.typedClient.PortalAttestationPolicyEditInteractive.GetSchema(); } - public async getPolicies(parameters: PolicyLoadParameters): Promise> { - const collection = await this.api.typedClient.PortalAttestationPolicy.Get(parameters); + public async getPolicies( + parameters: PolicyLoadParameters, + signal?: AbortSignal, + ): Promise> { + const collection = await this.api.typedClient.PortalAttestationPolicy.Get(parameters, { signal }); return { tableName: collection.tableName, totalCount: collection.totalCount, @@ -121,7 +124,7 @@ export class PolicyService { } public async getParmData(uidAttestationObject: string): Promise { - return uidAttestationObject ? (await this.api.client.portal_attestation_filter_model_get(uidAttestationObject)).ParmData : []; + return uidAttestationObject ? (await this.api.client.portal_attestation_filter_model_get(uidAttestationObject)).ParmData || [] : []; } public async getFilterCandidates(parameters: CollectionLoadParameters, uidAttestationParm: string): Promise { @@ -136,13 +139,13 @@ export class PolicyService { uidAttestatation: string, uidPickCategory: string, policyfilter: PolicyFilter, - parameters: CollectionLoadParameters + parameters: CollectionLoadParameters, ): Promise> { const data = await this.getObjectsForFilterUntyped(uidAttestatation, uidPickCategory, policyfilter, parameters); return new TypedEntityBuilder(PortalAttestationFilterMatchingobjects).buildReadWriteEntities( data, - PortalAttestationFilterMatchingobjects.GetEntitySchema() + PortalAttestationFilterMatchingobjects.GetEntitySchema(), ); } @@ -150,7 +153,7 @@ export class PolicyService { uidAttestatation: string, uidPickCategory: string, policyfilter: PolicyFilter, - parameters: CollectionLoadParameters + parameters: CollectionLoadParameters, ): Promise { return this.api.client.portal_attestation_filter_matchingobjects_get(uidAttestatation, { uidpickcategory: uidPickCategory, @@ -240,7 +243,7 @@ export class PolicyService { private async copyPropertiesFrom( entity: PortalAttestationPolicyEdit, reference: PortalAttestationPolicyEdit, - filter: PolicyFilter + filter?: PolicyFilter, ): Promise { let uidPickCategorySkipped = false; for (const key in this.api.typedClient.PortalAttestationPolicyEditInteractive.GetSchema().Columns) { diff --git a/imxweb/projects/att/src/lib/policies/selected-objects/selected-objects-info.interface.ts b/imxweb/projects/att/src/lib/policies/selected-objects/selected-objects-info.interface.ts index edd99bf5e..169967b75 100644 --- a/imxweb/projects/att/src/lib/policies/selected-objects/selected-objects-info.interface.ts +++ b/imxweb/projects/att/src/lib/policies/selected-objects/selected-objects-info.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,10 +24,10 @@ * */ -import { PolicyFilter } from 'imx-api-att'; +import { PolicyFilter } from '@imx-modules/imx-api-att'; export interface SelectecObjectsInfo { - policyFilter: PolicyFilter; - uidAttestationObject: string; - uidPickCategory: string; + policyFilter: PolicyFilter; + uidAttestationObject: string; + uidPickCategory: string; } diff --git a/imxweb/projects/att/src/lib/policies/selected-objects/selected-objects.component.html b/imxweb/projects/att/src/lib/policies/selected-objects/selected-objects.component.html index 2bb9cadf4..cb554dfa7 100644 --- a/imxweb/projects/att/src/lib/policies/selected-objects/selected-objects.component.html +++ b/imxweb/projects/att/src/lib/policies/selected-objects/selected-objects.component.html @@ -1,11 +1,16 @@ - - \ No newline at end of file +
    + (0) +
    diff --git a/imxweb/projects/att/src/lib/policies/selected-objects/selected-objects.component.scss b/imxweb/projects/att/src/lib/policies/selected-objects/selected-objects.component.scss index e6c7e2987..7676d9d02 100644 --- a/imxweb/projects/att/src/lib/policies/selected-objects/selected-objects.component.scss +++ b/imxweb/projects/att/src/lib/policies/selected-objects/selected-objects.component.scss @@ -3,7 +3,7 @@ flex-direction: row; align-items: center; - .mat-button { + .mat-mdc-button { padding-left: 5px; padding-right: 5px; min-width: auto; @@ -13,15 +13,4 @@ .imx-larger-font { font-size: large; } - - .like-button { - padding-left: 5px; - padding-right: 5px; - min-width: auto; - margin-left: 5px; - } } - - - - \ No newline at end of file diff --git a/imxweb/projects/att/src/lib/policies/selected-objects/selected-objects.component.ts b/imxweb/projects/att/src/lib/policies/selected-objects/selected-objects.component.ts index a3507a608..31b2db934 100644 --- a/imxweb/projects/att/src/lib/policies/selected-objects/selected-objects.component.ts +++ b/imxweb/projects/att/src/lib/policies/selected-objects/selected-objects.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,20 +28,19 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { EuiSidesheetService } from '@elemental-ui/core'; import { BehaviorSubject } from 'rxjs'; -import { PolicyFilter } from 'imx-api-att'; -import { ClassloggerService } from 'qbm'; -import { SelectecObjectsInfo } from './selected-objects-info.interface'; -import { PolicyService } from '../policy.service'; +import { PolicyFilter } from '@imx-modules/imx-api-att'; +import { calculateSidesheetWidth, ClassloggerService } from 'qbm'; import { AttestationCasesComponentParameter } from '../attestation-cases/attestation-cases-component-parameter.interface'; import { AttestationCasesComponent } from '../attestation-cases/attestation-cases.component'; +import { PolicyService } from '../policy.service'; +import { SelectecObjectsInfo } from './selected-objects-info.interface'; @Component({ templateUrl: './selected-objects.component.html', selector: 'imx-selected-objects', - styleUrls: ['./selected-objects.component.scss'] + styleUrls: ['./selected-objects.component.scss'], }) export class SelectedObjectsComponent implements OnInit, OnDestroy { - public filter: PolicyFilter; public uidAttestationObject: string; public uidPickCategory: string; @@ -51,12 +50,13 @@ export class SelectedObjectsComponent implements OnInit, OnDestroy { @Input() public popupSubtitle = ''; @Input() public isTotal: boolean; @Input() public testId = ''; - @Input() public filterSubject: BehaviorSubject; + @Input() public filterSubject: BehaviorSubject; constructor( private readonly sideSheet: EuiSidesheetService, private readonly policyService: PolicyService, - private readonly logger: ClassloggerService) { } + private readonly logger: ClassloggerService, + ) {} public ngOnInit(): void { if (this.filterSubject) { @@ -88,30 +88,29 @@ export class SelectedObjectsComponent implements OnInit, OnDestroy { subtitle: this.popupSubtitle, uidPickCategory: this.uidPickCategory, uidobject: this.uidAttestationObject, - filter: this.filter.Elements, - concat: this.filter ? this.filter.ConcatenationType : 'OR', - canCreateRuns: false + filter: this.filter.Elements || [], + concat: this.filter?.ConcatenationType ? this.filter.ConcatenationType : 'OR', + canCreateRuns: false, }; this.sideSheet.open(AttestationCasesComponent, { title: this.popupTitle, subTitle: this.popupSubtitle, padding: '0px', - width: 'max(600px, 60%)', + width: calculateSidesheetWidth(), testId: 'selected-objects-showmatching-sidesheet', data, }); } private async getMatchCount(filter: PolicyFilter): Promise { - if (filter.Elements.length === 0) { return -1; } - if (filter.Elements.findIndex(elem => elem.AttestationSubType == null || - elem.AttestationSubType === '') >= 0) { + if (filter.Elements?.length === 0) { + return -1; + } + if ((filter.Elements?.findIndex((elem) => elem.AttestationSubType == null || elem.AttestationSubType === '') || -1) >= 0) { return -1; } - return (await this.policyService.getObjectsForFilter(this.uidAttestationObject, - this.uidPickCategory, - filter, { PageSize: -1 })) + return (await this.policyService.getObjectsForFilter(this.uidAttestationObject, this.uidPickCategory, filter, { PageSize: -1 })) .totalCount; } } diff --git a/imxweb/projects/att/src/lib/policy-group/edit-policy-group-sidesheet/edit-policy-group-sidesheet.component.html b/imxweb/projects/att/src/lib/policy-group/edit-policy-group-sidesheet/edit-policy-group-sidesheet.component.html index fb1401fdb..d80ad1cf2 100644 --- a/imxweb/projects/att/src/lib/policy-group/edit-policy-group-sidesheet/edit-policy-group-sidesheet.component.html +++ b/imxweb/projects/att/src/lib/policy-group/edit-policy-group-sidesheet/edit-policy-group-sidesheet.component.html @@ -1,26 +1,32 @@
    -
    +
    -
    - +
    + -
    diff --git a/imxweb/projects/att/src/lib/policy-group/edit-policy-group-sidesheet/edit-policy-group-sidesheet.component.scss b/imxweb/projects/att/src/lib/policy-group/edit-policy-group-sidesheet/edit-policy-group-sidesheet.component.scss index 507ee4096..19a788d6c 100644 --- a/imxweb/projects/att/src/lib/policy-group/edit-policy-group-sidesheet/edit-policy-group-sidesheet.component.scss +++ b/imxweb/projects/att/src/lib/policy-group/edit-policy-group-sidesheet/edit-policy-group-sidesheet.component.scss @@ -4,17 +4,6 @@ h2 { margin-bottom: 10px; } -.imx-action { - display: flex; - justify-content: flex-end; +.eui-sidesheet-actions__padding { padding: 16px 32px; - - .justify-start { - margin-right: auto; - - eui-icon { - font-size: 14px; - margin-right: 4px; - } - } } diff --git a/imxweb/projects/att/src/lib/policy-group/edit-policy-group-sidesheet/edit-policy-group-sidesheet.component.ts b/imxweb/projects/att/src/lib/policy-group/edit-policy-group-sidesheet/edit-policy-group-sidesheet.component.ts index e0df9ed30..5f0bb4674 100644 --- a/imxweb/projects/att/src/lib/policy-group/edit-policy-group-sidesheet/edit-policy-group-sidesheet.component.ts +++ b/imxweb/projects/att/src/lib/policy-group/edit-policy-group-sidesheet/edit-policy-group-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,22 +25,21 @@ */ import { Component, Inject, OnInit } from '@angular/core'; -import { UntypedFormArray, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; -import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; -import { EntitySchema } from 'imx-qbm-dbts'; +import { AbstractControl, UntypedFormArray, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; +import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; +import { EntitySchema } from '@imx-modules/imx-qbm-dbts'; import { BaseCdr, ClassloggerService, ColumnDependentReference, ConfirmationService, SnackBarService } from 'qbm'; import { Subscription } from 'rxjs'; +import { PolicyGroup } from '../policy-group.interface'; import { PolicyGroupService } from '../policy-group.service'; -import {PolicyGroup} from '../policy-group.interface'; @Component({ templateUrl: './edit-policy-group-sidesheet.component.html', - styleUrls: ['./edit-policy-group-sidesheet.component.scss'] + styleUrls: ['./edit-policy-group-sidesheet.component.scss'], }) export class EditPolicyGroupSidesheetComponent implements OnInit { - public readonly formGroup = new UntypedFormGroup({}); public readonly schema: EntitySchema; - public objectProperties:ColumnDependentReference[]=[] + public objectProperties: ColumnDependentReference[] = []; public formArray: UntypedFormArray; public reload = false; public hasAttestations: boolean; @@ -54,16 +53,15 @@ export class EditPolicyGroupSidesheetComponent implements OnInit { private readonly snackBar: SnackBarService, private readonly policyGroupService: PolicyGroupService, private readonly logger: ClassloggerService, - private readonly confirmationService: ConfirmationService + private readonly confirmationService: ConfirmationService, ) { this.initOrRefreshCdrDictionary(); this.formGroup = new UntypedFormGroup({ - formArray: new UntypedFormArray([]) + formArray: new UntypedFormArray([]), }); this.formArray = this.formGroup.get('formArray') as UntypedFormArray; this.closeSubscription = this.sidesheetRef.closeClicked().subscribe(async () => { - if (!this.formGroup.dirty - || await confirmationService.confirmLeaveWithUnsavedChanges()) { + if (!this.formGroup.dirty || (await confirmationService.confirmLeaveWithUnsavedChanges())) { this.sidesheetRef.close(this.reload); } }); @@ -100,7 +98,7 @@ export class EditPolicyGroupSidesheetComponent implements OnInit { this.policygroup.policyGroup.UID_AERoleOwner.Column, this.policygroup.policyGroup.UID_DialogSchedule.Column, this.policygroup.policyGroup.UID_PersonOwner.Column, - this.policygroup.policyGroup.UID_QERPickCategory.Column + this.policygroup.policyGroup.UID_QERPickCategory.Column, ]; for (const column of columns) { @@ -115,16 +113,16 @@ export class EditPolicyGroupSidesheetComponent implements OnInit { } } - public addControl(columnName: string, evt: UntypedFormControl): void { - setTimeout(() => - this.formGroup.addControl(columnName, evt) - ); + public addControl(columnName: string, evt: AbstractControl): void { + setTimeout(() => this.formGroup.addControl(columnName, evt)); } public async saveChanges(): Promise { if (this.formGroup.valid) { this.policyGroupService.handleOpenLoader(); - let confirmMessage = !this.policygroup.isNew ? '#LDS#The policy collection has been successfully saved.' :'#LDS#The policy collection has been successfully created.'; + let confirmMessage = !this.policygroup.isNew + ? '#LDS#The policy collection has been successfully saved.' + : '#LDS#The policy collection has been successfully created.'; try { this.policygroup.policyGroup.GetEntity().Commit(false); this.sidesheetRef.close(true); @@ -136,10 +134,12 @@ export class EditPolicyGroupSidesheetComponent implements OnInit { } public async delete(): Promise { - if (await this.confirmationService.confirm({ - Title: '#LDS#Heading Delete Policy Collection', - Message: '#LDS#Are you sure you want to delete the policy collection?' - })) { + if ( + await this.confirmationService.confirm({ + Title: '#LDS#Heading Delete Policy Collection', + Message: '#LDS#Are you sure you want to delete the policy collection?', + }) + ) { this.policyGroupService.handleOpenLoader(); try { const key = this.policygroup.policyGroup.GetEntity().GetKeys()[0]; @@ -151,5 +151,4 @@ export class EditPolicyGroupSidesheetComponent implements OnInit { this.sidesheetRef.close(); } } - } diff --git a/imxweb/projects/att/src/lib/policy-group/policy-group-list/policy-group-list.component.html b/imxweb/projects/att/src/lib/policy-group/policy-group-list/policy-group-list.component.html index b0961cf6a..e7c659a21 100644 --- a/imxweb/projects/att/src/lib/policy-group/policy-group-list/policy-group-list.component.html +++ b/imxweb/projects/att/src/lib/policy-group/policy-group-list/policy-group-list.component.html @@ -1,47 +1,52 @@ -

    - {{ '#LDS#Heading Policy Collections' | translate }} - -

    +
    +

    + {{ '#LDS#Heading Policy Collections' | translate }} + +

    + +
    - - - -
    - - - - - - -
    - -
    -
    -
    -
    - -
    + + + + + {{ entitySchemaPolicy?.Columns?.Ident_AttestationPolicyGroup?.Display }} + + +
    {{ item.Ident_AttestationPolicyGroup.Column.GetDisplayValue() }}
    + +
    + + + {{ entitySchemaPolicy?.Columns?.UID_PersonOwner?.Display }} + + +
    {{ item.UID_PersonOwner.Column.GetDisplayValue() }}
    + +
    + + + {{ entitySchemaPolicy?.Columns?.UID_QERPickCategory?.Display }} + + +
    {{ item.UID_QERPickCategory.Column.GetDisplayValue() }}
    + +
    + + + + + + +
    +
    -
    - - diff --git a/imxweb/projects/att/src/lib/policy-group/policy-group-list/policy-group-list.component.scss b/imxweb/projects/att/src/lib/policy-group/policy-group-list/policy-group-list.component.scss index 97f7f28d2..92cf4f37b 100644 --- a/imxweb/projects/att/src/lib/policy-group/policy-group-list/policy-group-list.component.scss +++ b/imxweb/projects/att/src/lib/policy-group/policy-group-list/policy-group-list.component.scss @@ -1,35 +1,5 @@ -@import '../../../../../../shared/scss/common-table.scss'; +@import 'base/mixins'; :host { - display: flex; - flex-direction: column; - overflow: hidden; + @include flex-column-container($overflow: hidden); height: inherit; - - .button-row { - @include imx-button-bar(); - } - - .imx-button-column { - @include imx-button-column-right(); - } - - .imx-margin-right { - margin-right: 10px; - } - - .imx-content-card { - flex: 1 1 auto; - display: flex; - flex-direction: column; - margin: 3px; - overflow: hidden; - } - - .imx-table-container { - display: flex; - flex-direction: column; - overflow: hidden; - height: inherit; - flex: 1 1 auto; - } } diff --git a/imxweb/projects/att/src/lib/policy-group/policy-group-list/policy-group-list.component.ts b/imxweb/projects/att/src/lib/policy-group/policy-group-list/policy-group-list.component.ts index 5bf6ae3cb..b0754aba4 100644 --- a/imxweb/projects/att/src/lib/policy-group/policy-group-list/policy-group-list.component.ts +++ b/imxweb/projects/att/src/lib/policy-group/policy-group-list/policy-group-list.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,63 +25,59 @@ */ import { Component } from '@angular/core'; -import{PolicyGroupService} from '../policy-group.service'; +import { EuiSidesheetService } from '@elemental-ui/core'; +import { PolicyFilterData, PortalAttestationPolicygroups } from '@imx-modules/imx-api-att'; +import { + CollectionLoadParameters, + DisplayColumns, + EntitySchema, + ExtendedTypedEntityCollection, + TypedEntityCollectionData, + ValType, +} from '@imx-modules/imx-qbm-dbts'; +import { TranslateService } from '@ngx-translate/core'; import { ClassloggerService, + ClientPropertyForTableColumns, ConfirmationService, - DataSourceToolbarFilter, - DataSourceToolbarGroupData, - DataSourceToolbarSettings, DataTableGroupedData, - ClientPropertyForTableColumns, - SettingsService, - SnackBarService, + DataViewInitParameters, + DataViewSource, + HELP_CONTEXTUAL, HelpContextualComponent, HelpContextualService, - HELP_CONTEXTUAL + SettingsService, + SnackBarService, + calculateSidesheetWidth, } from 'qbm'; -import { - CollectionLoadParameters, - DisplayColumns, - ValType, - ExtendedTypedEntityCollection, - EntitySchema, - DataModel -} from 'imx-qbm-dbts'; -import { PolicyFilterData, PortalAttestationPolicygroups } from 'imx-api-att'; -import { EuiSidesheetService } from '@elemental-ui/core'; import { EditPolicyGroupSidesheetComponent } from '../edit-policy-group-sidesheet/edit-policy-group-sidesheet.component'; -import { TranslateService } from '@ngx-translate/core'; +import { PolicyGroupService } from '../policy-group.service'; @Component({ selector: 'imx-policy-group-list', templateUrl: './policy-group-list.component.html', - styleUrls: ['./policy-group-list.component.scss'] + styleUrls: ['./policy-group-list.component.scss'], + providers: [DataViewSource], }) - export class PolicyGroupListComponent { - - - public dstSettings: DataSourceToolbarSettings; public readonly entitySchemaPolicy: EntitySchema; public readonly DisplayColumns = DisplayColumns; public groupedData: { [key: string]: DataTableGroupedData } = {}; public navigationState: CollectionLoadParameters; - public isComplienceFrameworkEnabled = false; private groupData: DataSourceToolbarGroupData; - - private filterOptions: DataSourceToolbarFilter[] = []; + public isComplienceFrameworkEnabled = false; private readonly displayedColumns: ClientPropertyForTableColumns[]; - private dataModel: DataModel; - constructor( private readonly policyGroupService:PolicyGroupService , + constructor( + private readonly policyGroupService: PolicyGroupService, private readonly logger: ClassloggerService, private readonly settingsService: SettingsService, private readonly confirmationService: ConfirmationService, private readonly snackbar: SnackBarService, private readonly sideSheet: EuiSidesheetService, private readonly translator: TranslateService, - private readonly helpContextualService: HelpContextualService - ) { + private readonly helpContextualService: HelpContextualService, + public dataSource: DataViewSource, + ) { this.navigationState = { PageSize: this.settingsService.DefaultPageSize, StartIndex: 0 }; this.entitySchemaPolicy = policyGroupService.AttestationPolicyGroupSchema; this.displayedColumns = [ @@ -92,75 +88,36 @@ export class PolicyGroupListComponent { ColumnName: 'actions', Type: ValType.String, afterAdditionals: true, - untranslatedDisplay: '#LDS#Actions' - } + untranslatedDisplay: '#LDS#Actions', + }, ]; - } + } public async ngOnInit(): Promise { await this.navigate(); } private async navigate(): Promise { - this.policyGroupService.handleOpenLoader(); - try { - const policies = await this.policyGroupService.get(this.navigationState); - this.logger.trace(this, 'interactive policy loaded', policies); - - this.dstSettings = { - displayedColumns: this.displayedColumns, - dataSource: policies, - filters: this.filterOptions, - groupData: this.groupData, - entitySchema: this.entitySchemaPolicy, - navigationState: this.navigationState, - dataModel: this.dataModel - }; - } finally { - this.policyGroupService.handleCloseLoader(); - } - } - public async onSearch(keywords: string): Promise { - this.navigationState = { - ...this.navigationState, - ...{ - StartIndex: 0, - search: keywords, - } + const dataViewInitParameters: DataViewInitParameters = { + execute: (params: CollectionLoadParameters): Promise> => + this.policyGroupService.get(params), + schema: this.entitySchemaPolicy, + columnsToDisplay: this.displayedColumns, + highlightEntity: (policyGroup: PortalAttestationPolicygroups) => { + this.editPolicy(policyGroup); + }, }; - this.logger.trace(this, 'navigation state change to ', this.navigationState); - return this.navigate(); - } - - public async onNavigationStateChanged(newState: CollectionLoadParameters): Promise { - this.navigationState = newState; - this.logger.trace(this, 'navigation state change to ', this.navigationState); - await this.navigate(); - } - - public async onGroupingChange(groupKey: string): Promise { - this.policyGroupService.handleOpenLoader(); - try { - const groupedData = this.groupedData[groupKey]; - groupedData.data = await this.policyGroupService.get(groupedData.navigationState); - groupedData.settings = { - displayedColumns: this.dstSettings.displayedColumns, - dataModel: this.dstSettings.dataModel, - dataSource: groupedData.data, - entitySchema: this.dstSettings.entitySchema, - navigationState: groupedData.navigationState - }; - } finally { - this.policyGroupService.handleCloseLoader(); - } + this.dataSource.init(dataViewInitParameters); } public async delete(policyGroup: PortalAttestationPolicygroups, event: any): Promise { event.stopPropagation(); - if (await this.confirmationService.confirm({ - Title: '#LDS#Heading Delete Policy Collection', - Message: '#LDS#Are you sure you want to delete the policy collection?' - })) { + if ( + await this.confirmationService.confirm({ + Title: '#LDS#Heading Delete Policy Collection', + Message: '#LDS#Are you sure you want to delete the policy collection?', + }) + ) { this.policyGroupService.handleOpenLoader(); try { const key = policyGroup.GetEntity().GetKeys()[0]; @@ -172,26 +129,27 @@ export class PolicyGroupListComponent { const message = { key: '#LDS#The policy collection has been successfully deleted.', }; - this.navigate(); + this.dataSource.updateState(); this.snackbar.open(message, '#LDS#Close'); } } - public async editPolicy(policyGroup: PortalAttestationPolicygroups){ - let data: ExtendedTypedEntityCollection; + public async editPolicy(policyGroup: PortalAttestationPolicygroups) { + let data: ExtendedTypedEntityCollection; this.policyGroupService.handleOpenLoader(); try { data = await this.policyGroupService.getPolicyGroupEdit(policyGroup.GetEntity().GetKeys()[0]); - } finally { - this.policyGroupService.handleCloseLoader(); if (data && data.Data.length > 0) { await this.showPolicy( data.Data[0], - data.extendedData? data.extendedData[0]: undefined, + data.extendedData ? data.extendedData[0] : undefined, await this.translator.get('#LDS#Heading Edit Policy Collection').toPromise(), false, - policyGroup.GetEntity().GetDisplay()); + policyGroup.GetEntity().GetDisplay(), + ); } + } finally { + this.policyGroupService.handleCloseLoader(); } } @@ -200,39 +158,48 @@ export class PolicyGroupListComponent { filterData: PolicyFilterData, display: string, isNew: boolean, - subtitle: string + subtitle: string, ): Promise { - this.helpContextualService.setHelpContextId(isNew? HELP_CONTEXTUAL.AttestationPolicyCollectionsCreate : HELP_CONTEXTUAL.AttestationPolicyCollectionsEdit); + this.helpContextualService.setHelpContextId( + isNew ? HELP_CONTEXTUAL.AttestationPolicyCollectionsCreate : HELP_CONTEXTUAL.AttestationPolicyCollectionsEdit, + ); const sidesheetRef = this.sideSheet.open(EditPolicyGroupSidesheetComponent, { title: display, subTitle: subtitle, padding: '0px', - width: 'max(600px, 60%)', + width: calculateSidesheetWidth(), disableClose: true, data: { policyGroup, filterData, isNew, isComplienceFrameworkEnabled: this.isComplienceFrameworkEnabled }, testId: 'policy-group-list-show-policy-sidesheet', - headerComponent: HelpContextualComponent + headerComponent: HelpContextualComponent, }); const shouldReload = await sidesheetRef.afterClosed().toPromise(); - if (shouldReload) { this.navigate(); } + if (shouldReload) { + this.dataSource.updateState(); + } } public async newPolicyGroup(): Promise { - let policyGroup: PortalAttestationPolicygroups; + let policyGroup: PortalAttestationPolicygroups | undefined; this.policyGroupService.handleOpenLoader(); try { - policyGroup = await this.policyGroupService.buildNewEntity(); + policyGroup = await this.policyGroupService.buildNewEntity(); this.logger.trace(this, 'new policy group created', policyGroup); } finally { this.policyGroupService.handleCloseLoader(); if (policyGroup) { - await this.showPolicy(policyGroup, { - IsReadOnly: false, - Filter: { Elements: [], ConcatenationType: 'OR' }, - InfoDisplay: [] - }, - await this.translator.get('#LDS#Heading Create Policy Collection').toPromise(), true,""); + await this.showPolicy( + policyGroup, + { + IsReadOnly: false, + Filter: { Elements: [], ConcatenationType: 'OR' }, + InfoDisplay: [], + }, + await this.translator.get('#LDS#Heading Create Policy Collection').toPromise(), + true, + '', + ); } } } diff --git a/imxweb/projects/att/src/lib/policy-group/policy-group.interface.ts b/imxweb/projects/att/src/lib/policy-group/policy-group.interface.ts index 4aa01ee5e..1f99d02c3 100644 --- a/imxweb/projects/att/src/lib/policy-group/policy-group.interface.ts +++ b/imxweb/projects/att/src/lib/policy-group/policy-group.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,10 +24,10 @@ * */ -import { PolicyFilterData, PortalAttestationPolicygroups } from 'imx-api-att'; +import { PolicyFilterData, PortalAttestationPolicygroups } from '@imx-modules/imx-api-att'; export interface PolicyGroup { policyGroup: PortalAttestationPolicygroups; filterData: PolicyFilterData; - isNew?: boolean; + isNew: boolean; isComplienceFrameworkEnabled: boolean; } diff --git a/imxweb/projects/att/src/lib/policy-group/policy-group.module.ts b/imxweb/projects/att/src/lib/policy-group/policy-group.module.ts index 926b6fca1..eafd3bf6e 100644 --- a/imxweb/projects/att/src/lib/policy-group/policy-group.module.ts +++ b/imxweb/projects/att/src/lib/policy-group/policy-group.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,27 +24,34 @@ * */ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { LdsReplaceModule, DataSourceToolbarModule, DataTableModule, CdrModule, ClassloggerModule, HelpContextualModule } from 'qbm'; -import { PolicyGroupListComponent } from './policy-group-list/policy-group-list.component'; +import { NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MatInputModule } from '@angular/material/input'; import { MatButtonModule } from '@angular/material/button'; -import { MatRadioModule } from '@angular/material/radio'; +import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatDialogModule } from '@angular/material/dialog'; import { MatExpansionModule } from '@angular/material/expansion'; -import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatInputModule } from '@angular/material/input'; import { MatMenuModule } from '@angular/material/menu'; +import { MatRadioModule } from '@angular/material/radio'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; +import { MatTooltipModule } from '@angular/material/tooltip'; import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; import { TranslateModule } from '@ngx-translate/core'; -import { MatTooltipModule } from '@angular/material/tooltip'; +import { + CdrModule, + ClassloggerModule, + DataSourceToolbarModule, + DataTableModule, + DataViewModule, + HelpContextualModule, + LdsReplaceModule, +} from 'qbm'; +import { PolicyGroupListComponent } from './policy-group-list/policy-group-list.component'; import { UserModule } from 'qer'; -import { PolicyGroupService } from './policy-group.service'; import { EditPolicyGroupSidesheetComponent } from './edit-policy-group-sidesheet/edit-policy-group-sidesheet.component'; - +import { PolicyGroupService } from './policy-group.service'; @NgModule({ imports: [ @@ -70,17 +77,10 @@ import { EditPolicyGroupSidesheetComponent } from './edit-policy-group-sidesheet UserModule, ClassloggerModule, HelpContextualModule, + DataViewModule, ], - declarations: [ - PolicyGroupListComponent, - EditPolicyGroupSidesheetComponent, - ], - providers: [ - PolicyGroupService - ], - exports: [ - PolicyGroupListComponent, - EditPolicyGroupSidesheetComponent, - ] + declarations: [PolicyGroupListComponent, EditPolicyGroupSidesheetComponent], + providers: [PolicyGroupService], + exports: [PolicyGroupListComponent, EditPolicyGroupSidesheetComponent], }) -export class PolicyGroupModule { } +export class PolicyGroupModule {} diff --git a/imxweb/projects/att/src/lib/policy-group/policy-group.service.ts b/imxweb/projects/att/src/lib/policy-group/policy-group.service.ts index 9251d81cd..ad2c2ef80 100644 --- a/imxweb/projects/att/src/lib/policy-group/policy-group.service.ts +++ b/imxweb/projects/att/src/lib/policy-group/policy-group.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,112 +25,88 @@ */ import { Injectable } from '@angular/core'; -import { - CollectionLoadParameters, - CompareOperator, - DataModel, - EntityCollectionData, - EntitySchema, - ExtendedEntityCollectionData, - ExtendedTypedEntityCollection, - FilterType, - GroupInfo, - MethodDefinition, -} from 'imx-qbm-dbts'; -import { - PortalAttestationPolicygroups, - PolicyFilter -} from 'imx-api-att'; -import { ApiService } from '../api.service'; import { EuiLoadingService } from '@elemental-ui/core'; -import { OverlayRef } from '@angular/cdk/overlay'; +import { PolicyFilter, PortalAttestationPolicygroups } from '@imx-modules/imx-api-att'; +import { CollectionLoadParameters, EntityCollectionData, EntitySchema, ExtendedTypedEntityCollection } from '@imx-modules/imx-qbm-dbts'; import { TranslateService } from '@ngx-translate/core'; import { AppConfigService, ClassloggerService } from 'qbm'; +import { ApiService } from '../api.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class PolicyGroupService { - private busyIndicator: OverlayRef; constructor( private api: ApiService, - private busyService: EuiLoadingService, + private busyService: EuiLoadingService, private readonly translator: TranslateService, private readonly config: AppConfigService, - private readonly logger: ClassloggerService - ) { } + private readonly logger: ClassloggerService, + ) {} public get AttestationPolicyGroupSchema(): EntitySchema { return this.api.typedClient.PortalAttestationPolicygroups.GetSchema(); } - public async get(parameters: CollectionLoadParameters): Promise> { return this.api.typedClient.PortalAttestationPolicygroups.Get(parameters); } - - public async deleteAttestationPolicyGroup(uidAttestationpolicygroup: string): Promise { + public async deleteAttestationPolicyGroup(uidAttestationpolicygroup: string): Promise { return this.api.client.portal_attestation_policygroups_delete(uidAttestationpolicygroup); } - public async getPolicyGroupEdit(uid: string): - Promise> { + public async getPolicyGroupEdit(uid: string): Promise> { return this.api.typedClient.PortalAttestationPolicygroupsInteractive.Get_byid(uid); } - public async buildNewEntity(reference?: PortalAttestationPolicygroups, filter?: PolicyFilter): - Promise { - const entities = await this.api.typedClient.PortalAttestationPolicygroupsInteractive.Get(); - if (reference == null) { + public async buildNewEntity(reference?: PortalAttestationPolicygroups, filter?: PolicyFilter): Promise { + const entities = await this.api.typedClient.PortalAttestationPolicygroupsInteractive.Get(); + if (reference == null) { + return entities.Data[0]; + } + await this.copyPropertiesFrom(entities.Data[0], reference, filter); return entities.Data[0]; } - await this.copyPropertiesFrom(entities.Data[0], reference, filter); - return entities.Data[0]; -} - -public async copyPropertiesFrom( - entity: PortalAttestationPolicygroups, - reference: PortalAttestationPolicygroups, filter: PolicyFilter): Promise { - for (const key in this.api.typedClient.PortalAttestationPolicyEditInteractive.GetSchema().Columns) { - if (!key.startsWith('__') && entity[key].GetMetadata().CanEdit()) { - await entity[key].Column.PutValueStruct({ - DataValue: reference[key].value, - DisplayValue: reference[key].Column.GetDisplayValue() - }); + public async copyPropertiesFrom( + entity: PortalAttestationPolicygroups, + reference: PortalAttestationPolicygroups, + filter?: PolicyFilter, + ): Promise { + for (const key in this.api.typedClient.PortalAttestationPolicyEditInteractive.GetSchema().Columns) { + if (!key.startsWith('__') && entity[key].GetMetadata().CanEdit()) { + await entity[key].Column.PutValueStruct({ + DataValue: reference[key].value, + DisplayValue: reference[key].Column.GetDisplayValue(), + }); + } } - } - entity.Ident_AttestationPolicyGroup.value = - `${reference.Ident_AttestationPolicyGroup.value} (${(await this.translator.get('#LDS#New').toPromise())})`; + entity.Ident_AttestationPolicyGroup.value = `${reference.Ident_AttestationPolicyGroup.value} (${await this.translator + .get('#LDS#New') + .toPromise()})`; - this.logger.trace(this, 'properties copied from policy', reference, filter); -} + this.logger.trace(this, 'properties copied from policy', reference, filter); + } -public async getPolicyGroups(parameters: CollectionLoadParameters): -Promise> { -const collection = await this.api.typedClient.PortalAttestationPolicygroups.Get(parameters); -return { - tableName: collection.tableName, - totalCount: collection.totalCount, - Data: collection.Data.map((element, index) => - new PortalAttestationPolicygroups(element.GetEntity()) - ) -}; -} + public async getPolicyGroups( + parameters: CollectionLoadParameters, + ): Promise> { + const collection = await this.api.typedClient.PortalAttestationPolicygroups.Get(parameters); + return { + tableName: collection.tableName, + totalCount: collection.totalCount, + Data: collection.Data.map((element, index) => new PortalAttestationPolicygroups(element.GetEntity())), + }; + } public handleOpenLoader(): void { - if (!this.busyIndicator) { - setTimeout(() => this.busyIndicator = this.busyService.show()); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); } } public handleCloseLoader(): void { - if (this.busyIndicator) { - setTimeout(() => { - this.busyService.hide(this.busyIndicator); - this.busyIndicator = undefined; - }); - } + this.busyService.hide(); } } diff --git a/imxweb/projects/att/src/lib/runs/attestation-runs.module.ts b/imxweb/projects/att/src/lib/runs/attestation-runs.module.ts index a547712fe..e0a230a10 100644 --- a/imxweb/projects/att/src/lib/runs/attestation-runs.module.ts +++ b/imxweb/projects/att/src/lib/runs/attestation-runs.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,30 +24,39 @@ * */ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatDialogModule } from '@angular/material/dialog'; import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatSidenavModule } from '@angular/material/sidenav'; import { RouterModule } from '@angular/router'; -import { EuiMaterialModule, EuiCoreModule } from '@elemental-ui/core'; +import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; import { TranslateModule } from '@ngx-translate/core'; -import { DataSourceToolbarModule, DataTableModule, CdrModule, LdsReplaceModule, EntityColumnEditorComponent, SelectedElementsModule, HelpContextualModule, TempBillboardModule } from 'qbm'; +import { + CdrModule, + DataSourceToolbarModule, + DataTableModule, + DataViewModule, + DateModule, + HelpContextualModule, + LdsReplaceModule, + SelectedElementsModule, +} from 'qbm'; -import { RunsComponent } from './runs.component'; -import { RunSidesheetComponent } from './run-sidesheet.component'; +import { AttestationHistoryModule } from '../attestation-history/attestation-history.module'; +import { AttestationWrapperComponent } from './attestation/attestation-wrapper/attestation-wrapper.component'; +import { AttestationComponent } from './attestation/attestation.component'; +import { CaseChartComponent } from './case-chart/case-chart.component'; import { PendingApproversComponent } from './pending-approvers.component'; -import { SendReminderMailComponent } from './send-reminder-mail.component'; import { ProgressComponent } from './progress/progress.component'; import { RunExtendComponent } from './run-extend/run-extend.component'; -import { AttestationComponent } from './attestation/attestation.component'; -import { AttestationHistoryModule } from '../attestation-history/attestation-history.module'; +import { RunSidesheetComponent } from './run-sidesheet.component'; import { RunsGridComponent } from './runs-grid/runs-grid.component'; -import { CaseChartComponent } from './case-chart/case-chart.component'; -import { AttestationWrapperComponent } from './attestation/attestation-wrapper/attestation-wrapper.component'; +import { RunsComponent } from './runs.component'; +import { SendReminderMailComponent } from './send-reminder-mail.component'; @NgModule({ declarations: [ @@ -81,7 +90,8 @@ import { AttestationWrapperComponent } from './attestation/attestation-wrapper/a AttestationHistoryModule, SelectedElementsModule, HelpContextualModule, - TempBillboardModule, + DataViewModule, + DateModule, ], exports: [RunsComponent, RunsGridComponent, AttestationWrapperComponent], }) diff --git a/imxweb/projects/att/src/lib/runs/attestation/attestation-wrapper/attestation-wrapper.component.html b/imxweb/projects/att/src/lib/runs/attestation/attestation-wrapper/attestation-wrapper.component.html index 149ff8775..31fd1cf41 100644 --- a/imxweb/projects/att/src/lib/runs/attestation/attestation-wrapper/attestation-wrapper.component.html +++ b/imxweb/projects/att/src/lib/runs/attestation/attestation-wrapper/attestation-wrapper.component.html @@ -1,2 +1 @@ - - \ No newline at end of file + diff --git a/imxweb/projects/att/src/lib/runs/attestation/attestation-wrapper/attestation-wrapper.component.scss b/imxweb/projects/att/src/lib/runs/attestation/attestation-wrapper/attestation-wrapper.component.scss index af8548a66..e6e1c9030 100644 --- a/imxweb/projects/att/src/lib/runs/attestation/attestation-wrapper/attestation-wrapper.component.scss +++ b/imxweb/projects/att/src/lib/runs/attestation/attestation-wrapper/attestation-wrapper.component.scss @@ -1,5 +1,7 @@ -:host{ +:host { flex: 1 1 auto; display: flex; flex-direction: column; -} \ No newline at end of file + height: 100%; + overflow: hidden; +} diff --git a/imxweb/projects/att/src/lib/runs/attestation/attestation-wrapper/attestation-wrapper.component.ts b/imxweb/projects/att/src/lib/runs/attestation/attestation-wrapper/attestation-wrapper.component.ts index dc3b48939..82ff5a596 100644 --- a/imxweb/projects/att/src/lib/runs/attestation/attestation-wrapper/attestation-wrapper.component.ts +++ b/imxweb/projects/att/src/lib/runs/attestation/attestation-wrapper/attestation-wrapper.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -33,18 +33,22 @@ import { IdentityAttestationService } from '../../../identity-attestation.servic @Component({ templateUrl: './attestation-wrapper.component.html', - styleUrls: ['./attestation-wrapper.component.scss'] + styleUrls: ['./attestation-wrapper.component.scss'], }) -export class AttestationWrapperComponent implements OnInit, OnDestroy{ - public referrer: { objecttable: string; objectuid: string; }; +export class AttestationWrapperComponent implements OnInit, OnDestroy { + public referrer: { objecttable: string; objectuid: string }; public readonly pendingAttestations: HelperAlertContent = { loading: false }; private readonly subscriptions: Subscription[] = []; constructor( private readonly attestations: IdentityAttestationService, - dataProvider: DynamicTabDataProviderDirective){ - this.referrer = dataProvider.data; - } + dataProvider: DynamicTabDataProviderDirective, + ) { + this.referrer = { + objecttable: dataProvider.data.objecttable, + objectuid: dataProvider.data.objectuid, + }; + } public ngOnInit(): void { if (this.attestations) { @@ -52,28 +56,24 @@ export class AttestationWrapperComponent implements OnInit, OnDestroy{ this.updatePendingAttestations(); } - } public ngOnDestroy(): void { - this.subscriptions.forEach(subscription => subscription.unsubscribe()); + this.subscriptions.forEach((subscription) => subscription.unsubscribe()); } private async updatePendingAttestations(): Promise { - this.pendingAttestations.loading = true; const statistics: ObjectAttestationStatistics = await this.attestations.getNumberOfPendingForUser(this.referrer); if (!statistics?.total) { - this.pendingAttestations.infoItems = [ - { description: '#LDS#There are currently no attestation cases for this object.' } - ]; + this.pendingAttestations.infoItems = [{ description: '#LDS#There are currently no attestation cases for this object.' }]; } else { this.pendingAttestations.infoItems = [ { description: '#LDS#Here you can get an overview of all attestations cases for this object.' }, { description: '#LDS#Pending attestation cases: {0}', value: statistics.pendingTotal }, - { description: '#LDS#Pending attestation cases you can approve or deny: {0}', value: statistics.pendingForUser } + { description: '#LDS#Pending attestation cases you can approve or deny: {0}', value: statistics.pendingForUser }, ]; } diff --git a/imxweb/projects/att/src/lib/runs/attestation/attestation.component.html b/imxweb/projects/att/src/lib/runs/attestation/attestation.component.html index ec8d6670d..a8d71a9a7 100644 --- a/imxweb/projects/att/src/lib/runs/attestation/attestation.component.html +++ b/imxweb/projects/att/src/lib/runs/attestation/attestation.component.html @@ -1,29 +1,52 @@ -
    +
    - + -
    +
    - {{ infoItem.description | translate | ldsReplace : infoItem.value }} + {{ infoItem.description | translate | ldsReplace: infoItem.value }}
    - + +
    - -
    -
    - -
    diff --git a/imxweb/projects/att/src/lib/runs/attestation/attestation.component.scss b/imxweb/projects/att/src/lib/runs/attestation/attestation.component.scss index 9480989d1..3985f0357 100644 --- a/imxweb/projects/att/src/lib/runs/attestation/attestation.component.scss +++ b/imxweb/projects/att/src/lib/runs/attestation/attestation.component.scss @@ -1,86 +1,6 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; - :host { display: flex; flex-direction: column; height: 100%; -} - -.imx-content { - height: 100%; - display: flex; - flex-direction: column; - flex: 1 1 auto; - overflow-y: auto; - - .imx-heading-wrapper { - display: flex; - flex-direction: column; - - .imx-helper-alert { - margin: 10px 0 20px auto; - width: 100%; - - .imx-helper-alert-infotext { - display: flex; - flex-direction: column; - } - } - } - - .imx-attestation-history-container { - overflow: hidden; - display: flex; - flex-direction: column; - height:100%; - - > :first-child { - height: 100%; - } - } -} - -imx-selected-elements { - margin: 10px 0 0 10px; -} - -.eui-sidesheet-actions { - display: flex; - justify-content: flex-end; - align-items: center; - padding: 0px 20px 20px; - margin: 16px -20px -20px; - - > * { - margin-top: 20px; - - .mat-icon { - margin-right: 5px; - font-size: 24px; - } - } - - button:not(:last-of-type) { - margin-right: 16px; - } - - button:disabled:hover { - cursor: not-allowed; - } -} - -.eui-dark-theme { - :host { - .imx-button-bar { - background-color: $color-gray-70; - } - } -} - -.eui-contrast-theme { - :host { - .imx-button-bar { - background-color: $color-gray-90; - } - } + overflow: hidden; } diff --git a/imxweb/projects/att/src/lib/runs/attestation/attestation.component.ts b/imxweb/projects/att/src/lib/runs/attestation/attestation.component.ts index 1915c3579..c5ab6694d 100644 --- a/imxweb/projects/att/src/lib/runs/attestation/attestation.component.ts +++ b/imxweb/projects/att/src/lib/runs/attestation/attestation.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,13 +27,18 @@ import { Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core'; import { Subscription } from 'rxjs'; +import { FilterData, TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { AuthenticationService, HELPER_ALERT_KEY_PREFIX, StorageService } from 'qbm'; -import { AttestationHistoryCase } from '../../attestation-history/attestation-history-case'; -import { AttestationHistoryActionService } from '../../attestation-history/attestation-history-action.service'; -import { FilterData } from 'imx-qbm-dbts'; import { HelperAlertContent } from 'qer'; +import { AttestationHistoryActionService } from '../../attestation-history/attestation-history-action.service'; +import { AttestationHistoryCase } from '../../attestation-history/attestation-history-case'; const helperAlertKey = `${HELPER_ALERT_KEY_PREFIX}_attestation`; +export interface AttestationParameters { + objecttable: string; + objectuid: string; + filter?: FilterData[]; +} @Component({ selector: 'imx-attestation', @@ -53,7 +58,7 @@ export class AttestationComponent implements OnDestroy { return !this.storageService.isHelperAlertDismissed(helperAlertKey); } - @Input() public parameters: { objecttable: string; objectuid: string; filter?: FilterData[] }; + @Input() public parameters: AttestationParameters; @Input() public pendingAttestations: HelperAlertContent; public readonly itemStatus = { @@ -71,17 +76,17 @@ export class AttestationComponent implements OnDestroy { constructor( public readonly attestationAction: AttestationHistoryActionService, private readonly storageService: StorageService, - authentication: AuthenticationService + authentication: AuthenticationService, ) { - this.subscriptions.push(authentication.onSessionResponse?.subscribe((sessionState) => (this.userUid = sessionState?.UserUid))); + this.subscriptions.push(authentication.onSessionResponse?.subscribe((sessionState) => (this.userUid = sessionState?.UserUid || ''))); } public ngOnDestroy(): void { this.subscriptions.forEach((s) => s.unsubscribe()); } - public onSelectionChanged(items: AttestationHistoryCase[]): void { - this.selectedCases = items; + public onSelectionChanged(items: TypedEntity[]): void { + this.selectedCases = items as AttestationHistoryCase[]; } public onHelperDismissed(): void { diff --git a/imxweb/projects/att/src/lib/runs/case-chart/case-chart.component.html b/imxweb/projects/att/src/lib/runs/case-chart/case-chart.component.html index b42f1c605..0299a4177 100644 --- a/imxweb/projects/att/src/lib/runs/case-chart/case-chart.component.html +++ b/imxweb/projects/att/src/lib/runs/case-chart/case-chart.component.html @@ -1,3 +1,3 @@ -
    - +
    +
    diff --git a/imxweb/projects/att/src/lib/runs/case-chart/case-chart.component.scss b/imxweb/projects/att/src/lib/runs/case-chart/case-chart.component.scss index 9ae16e19e..b6e2cfe32 100644 --- a/imxweb/projects/att/src/lib/runs/case-chart/case-chart.component.scss +++ b/imxweb/projects/att/src/lib/runs/case-chart/case-chart.component.scss @@ -5,9 +5,3 @@ flex: 1 1 auto; overflow: hidden; } - - .chart-wrapper { - display: flex; - flex-direction: column; - flex: 1 1 auto; - } \ No newline at end of file diff --git a/imxweb/projects/att/src/lib/runs/case-chart/case-chart.component.ts b/imxweb/projects/att/src/lib/runs/case-chart/case-chart.component.ts index 00e28348e..c402bc684 100644 --- a/imxweb/projects/att/src/lib/runs/case-chart/case-chart.component.ts +++ b/imxweb/projects/att/src/lib/runs/case-chart/case-chart.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,10 +25,10 @@ */ import { Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { PortalAttestationRun } from '@imx-modules/imx-api-att'; import { TranslateService } from '@ngx-translate/core'; import { bar, Chart, ChartOptions } from 'billboard.js'; -import { PortalAttestationRun } from 'imx-api-att'; -import { Subscription, fromEvent } from 'rxjs'; +import { fromEvent, Subscription } from 'rxjs'; @Component({ selector: 'imx-case-chart', @@ -38,7 +38,7 @@ import { Subscription, fromEvent } from 'rxjs'; export class CaseChartComponent implements OnInit, OnDestroy { @Input() public run: PortalAttestationRun; - public chartData: ChartOptions; + public chartData: ChartOptions | undefined; private chart: Chart; private subscriptions$: Subscription[] = []; @@ -60,7 +60,7 @@ export class CaseChartComponent implements OnInit, OnDestroy { width: this.chartWrapper?.nativeElement.offsetWidth, }); } - }) + }), ); } @@ -76,7 +76,7 @@ export class CaseChartComponent implements OnInit, OnDestroy { }); } - private buildSingleValueChart(): ChartOptions { + private buildSingleValueChart(): ChartOptions | undefined { if ( this.run.PendingCases.value === 0 && this.run.GrantedCases.value === 0 && diff --git a/imxweb/projects/att/src/lib/runs/helpers.ts b/imxweb/projects/att/src/lib/runs/helpers.ts index f6e10bd29..e7f56f115 100644 --- a/imxweb/projects/att/src/lib/runs/helpers.ts +++ b/imxweb/projects/att/src/lib/runs/helpers.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/att/src/lib/runs/pending-approvers.component.html b/imxweb/projects/att/src/lib/runs/pending-approvers.component.html index 764a63eec..445c1485f 100644 --- a/imxweb/projects/att/src/lib/runs/pending-approvers.component.html +++ b/imxweb/projects/att/src/lib/runs/pending-approvers.component.html @@ -1,33 +1,53 @@
    - - #LDS#Here you can get an overview of attestors who still have to approve attestation cases in the selected attestation run. Additionally, you can select attestors and send them reminders. - + + {{ LdsKeyAttestorOverview }} + - - - -
    - - - - - - - - -
    -
    + + + + + {{ entitySchema?.Columns?.UID_PersonHead?.Display }} + + +
    {{ item.UID_PersonHead.Column.GetDisplayValue() }}
    + +
    + + + {{ '#LDS#Pending' | translate }} + + +
    {{ item.PendingCases.Column.GetDisplayValue() }}
    + +
    + + + {{ '#LDS#Closed' | translate }} + + +
    {{ item.ClosedCases.Column.GetDisplayValue() }}
    + +
    +
    +
    -
    diff --git a/imxweb/projects/att/src/lib/runs/pending-approvers.component.scss b/imxweb/projects/att/src/lib/runs/pending-approvers.component.scss index 38d167d8d..50b697580 100644 --- a/imxweb/projects/att/src/lib/runs/pending-approvers.component.scss +++ b/imxweb/projects/att/src/lib/runs/pending-approvers.component.scss @@ -1,63 +1,15 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; +@import 'base/mixins'; :host { - display: flex; - flex-direction: column; - height: 100%; + @include flex-column-container($overflow: hidden, $height: 100%); } .attestationRun-pendingapprovers-content { - height: 100%; - display: flex; - flex-direction: column; - flex: 1 1 auto; + @include flex-column-container-fill; overflow-y: auto; } -.mat-card { +.mat-mdc-card { margin: 20px; overflow-y: auto; } - -.eui-sidesheet-actions { - display: flex; - justify-content: flex-end; - overflow-y: inherit; - padding: 0px 20px 20px 8px; - margin: 0; - > * { - margin-left: 10px; - margin-top: 20px; - } -} - -.helper-alert { - display: flex; - padding: 30px 20px 0; - flex-direction: column; - span { - display: block; - } -} - -.imx-pending-approvers-container{ - display: flex; - flex-direction: column; - height:100%; -} - -.eui-dark-theme { - :host { - .imx-button-bar { - background-color: $color-gray-70; - } - } -} - -.eui-contrast-theme { - :host { - .imx-button-bar { - background-color: $color-gray-90; - } - } -} diff --git a/imxweb/projects/att/src/lib/runs/pending-approvers.component.ts b/imxweb/projects/att/src/lib/runs/pending-approvers.component.ts index 37d2c7ff2..a49e600f2 100644 --- a/imxweb/projects/att/src/lib/runs/pending-approvers.component.ts +++ b/imxweb/projects/att/src/lib/runs/pending-approvers.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,44 +27,48 @@ import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; import { ApiService } from '../api.service'; -import { PortalAttestationRunApprovers } from 'imx-api-att'; -import { TypedEntityCollectionData } from 'imx-qbm-dbts'; +import { PortalAttestationRunApprovers } from '@imx-modules/imx-api-att'; +import { EntitySchema, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; -import { DataSourceToolbarSettings } from 'qbm'; +import { DataViewInitParameters, DataViewSource } from 'qbm'; @Component({ selector: 'imx-attestation-run-approvers', templateUrl: './pending-approvers.component.html', - styleUrls: ['./pending-approvers.component.scss'] + styleUrls: ['./pending-approvers.component.scss'], + providers: [DataViewSource], }) export class PendingApproversComponent implements OnChanges { - public dstSettings: DataSourceToolbarSettings; - public selected: PortalAttestationRunApprovers[] = []; - + public entitySchema: EntitySchema; public showHelper = true; @Input() public dataSource: TypedEntityCollectionData; @Output() public readonly sendReminder = new EventEmitter(); - constructor(private readonly attApiService: ApiService) { + constructor( + private readonly attApiService: ApiService, + public dataViewSource: DataViewSource, + ) { + this.entitySchema = this.attApiService.typedClient.PortalAttestationRunApprovers.GetSchema(); } public async ngOnChanges(changes: SimpleChanges): Promise { if (changes.dataSource && this.dataSource) { - const entitySchema = this.attApiService.typedClient.PortalAttestationRunApprovers.GetSchema(); - - this.dstSettings = { - displayedColumns: [ - entitySchema.Columns.UID_PersonHead, - entitySchema.Columns.PendingCases, - entitySchema.Columns.ClosedCases + console.log(this.dataSource); + const dataViewInitParameters: DataViewInitParameters = { + execute: () => Promise.resolve(this.dataSource), + schema: this.entitySchema, + columnsToDisplay: [ + this.entitySchema.Columns.UID_PersonHead, + this.entitySchema.Columns.PendingCases, + this.entitySchema.Columns.ClosedCases, ], - dataSource: this.dataSource, - entitySchema, - navigationState: {} + localSource: true, + selectionChange: (selection: PortalAttestationRunApprovers[]) => this.onSelectionChanged(selection), }; + this.dataViewSource.init(dataViewInitParameters); } } @@ -75,4 +79,7 @@ export class PendingApproversComponent implements OnChanges { public onHelperDismissed(): void { this.showHelper = false; } + + public LdsKeyAttestorOverview = + '#LDS#Here you can get an overview of attestors who still have to approve attestation cases in the selected attestation run. Additionally, you can select attestors and send them reminders.'; } diff --git a/imxweb/projects/att/src/lib/runs/progress/progress.component.html b/imxweb/projects/att/src/lib/runs/progress/progress.component.html index d31a0ec43..32c6c2dd8 100644 --- a/imxweb/projects/att/src/lib/runs/progress/progress.component.html +++ b/imxweb/projects/att/src/lib/runs/progress/progress.component.html @@ -1,11 +1,11 @@
    - - - - {{ percentage }} % +
    + +
    {{percentage}}
    +
    + -
    \ No newline at end of file +
    diff --git a/imxweb/projects/att/src/lib/runs/progress/progress.component.scss b/imxweb/projects/att/src/lib/runs/progress/progress.component.scss deleted file mode 100644 index b268e28cb..000000000 --- a/imxweb/projects/att/src/lib/runs/progress/progress.component.scss +++ /dev/null @@ -1,10 +0,0 @@ -.imx-progress-bar-container { - display: flex; - align-items: center; - max-width: 200px; - - > span { - margin-left: 5px; - min-width: 30px; - } -} \ No newline at end of file diff --git a/imxweb/projects/att/src/lib/runs/progress/progress.component.ts b/imxweb/projects/att/src/lib/runs/progress/progress.component.ts index 9191db3a3..73493d8c7 100644 --- a/imxweb/projects/att/src/lib/runs/progress/progress.component.ts +++ b/imxweb/projects/att/src/lib/runs/progress/progress.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,13 +26,12 @@ import { Component, Input } from '@angular/core'; -import { PortalAttestationRun } from 'imx-api-att'; +import { PortalAttestationRun } from '@imx-modules/imx-api-att'; import { percentage } from '../helpers'; @Component({ selector: 'imx-progress', - templateUrl: './progress.component.html', - styleUrls: ['./progress.component.scss'] + templateUrl: './progress.component.html' }) export class ProgressComponent { public get progress(): number { diff --git a/imxweb/projects/att/src/lib/runs/run-extend/run-extend.component.html b/imxweb/projects/att/src/lib/runs/run-extend/run-extend.component.html index 116f1b1dd..c747046fd 100644 --- a/imxweb/projects/att/src/lib/runs/run-extend/run-extend.component.html +++ b/imxweb/projects/att/src/lib/runs/run-extend/run-extend.component.html @@ -1,29 +1,36 @@
    - - - #LDS#Here you can extend the attestation run and enter a reason for extending the run. - + + #LDS#Here you can extend the attestation run and enter a reason for extending the run.
    {{ '#LDS#New due date' | translate }} - + - {{ ( date?.value ? '#LDS#Specify a date that lies in the future.' : '#LDS#This field is mandatory.' ) | translate }} + {{ (date?.value ? '#LDS#Specify a date that lies in the future.' : '#LDS#This field is mandatory.') | translate }} - - +
    -
    - -
    \ No newline at end of file +
    diff --git a/imxweb/projects/att/src/lib/runs/run-extend/run-extend.component.scss b/imxweb/projects/att/src/lib/runs/run-extend/run-extend.component.scss index 89db4b066..a8d0b89c4 100644 --- a/imxweb/projects/att/src/lib/runs/run-extend/run-extend.component.scss +++ b/imxweb/projects/att/src/lib/runs/run-extend/run-extend.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; :host { display: flex; @@ -19,27 +19,3 @@ display: flex; flex-direction: column; } - -.mat-form-field { - width: 100%; -} - -.imx-button-bar { - display: flex; - justify-content: flex-end; - border-top: $color-gray-20 1px solid; - padding: 0px 20px 20px 8px; - > * { - margin-left: 10px; - margin-top: 20px; - } -} - -.helper-alert { - display: flex; - padding-bottom: 20px; - flex-direction: column; - span { - display: block; - } -} diff --git a/imxweb/projects/att/src/lib/runs/run-extend/run-extend.component.ts b/imxweb/projects/att/src/lib/runs/run-extend/run-extend.component.ts index 6884d61f9..3b9c1620a 100644 --- a/imxweb/projects/att/src/lib/runs/run-extend/run-extend.component.ts +++ b/imxweb/projects/att/src/lib/runs/run-extend/run-extend.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,8 +25,8 @@ */ import { Component, Inject, OnDestroy } from '@angular/core'; -import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; -import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { AbstractControl, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; +import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; import { Subscription } from 'rxjs'; import { ColumnDependentReference } from 'qbm'; @@ -34,7 +34,7 @@ import { ColumnDependentReference } from 'qbm'; @Component({ selector: 'imx-run-extend', templateUrl: './run-extend.component.html', - styleUrls: ['./run-extend.component.scss'] + styleUrls: ['./run-extend.component.scss'], }) export class RunExtendComponent implements OnDestroy { public showHelper = true; @@ -47,28 +47,26 @@ export class RunExtendComponent implements OnDestroy { private readonly subscriptions: Subscription[] = []; constructor( - @Inject(EUI_SIDESHEET_DATA) readonly data: { ProlongateUntil: Date, reason: ColumnDependentReference }, - public readonly sideSheetRef: EuiSidesheetRef + @Inject(EUI_SIDESHEET_DATA) readonly data: { ProlongateUntil: Date; reason: ColumnDependentReference }, + public readonly sideSheetRef: EuiSidesheetRef, ) { this.tomorrow = new Date(); - this.tomorrow.setDate((new Date()).getDate() + 1); + this.tomorrow.setDate(new Date().getDate() + 1); this.date = new UntypedFormControl(data.ProlongateUntil, { updateOn: 'blur' }); - this.date.valueChanges.subscribe(value => data.ProlongateUntil = value); + this.date.valueChanges.subscribe((value) => (data.ProlongateUntil = value)); this.addControl('date', this.date); this.reason = data.reason; } public ngOnDestroy(): void { - this.subscriptions.forEach(s => s.unsubscribe()); + this.subscriptions.forEach((s) => s.unsubscribe()); } public onHelperDismissed(): void { this.showHelper = false; } - public addControl(name: string, control: UntypedFormControl): void { - setTimeout(() => - this.form.addControl(name, control) - ); + public addControl(name: string, control: AbstractControl): void { + setTimeout(() => this.form.addControl(name, control)); } } diff --git a/imxweb/projects/att/src/lib/runs/run-sidesheet.component.html b/imxweb/projects/att/src/lib/runs/run-sidesheet.component.html index 521d03719..090e03988 100644 --- a/imxweb/projects/att/src/lib/runs/run-sidesheet.component.html +++ b/imxweb/projects/att/src/lib/runs/run-sidesheet.component.html @@ -1,155 +1,192 @@ - + -
    - - - - - {{ '#LDS#General' | translate }} - - {{ '#LDS#Completed' | translate}} - - - {{'#LDS#In progress' | translate}} - - - +
    + + + + + + {{ '#LDS#General' | translate }} + + {{ '#LDS#Completed' | translate }} + + + {{ '#LDS#In progress' | translate }} + + + - - - -
    - {{ '#LDS#Progress' | translate }} - -
    -
    + + + + +
    + {{ '#LDS#Progress' | translate }} + +
    + + + + + + {{ '#LDS#Attestation details' | translate }} + + +
    +
    +
    + {{ '#LDS#Cases' | translate }} + {{ run.ClosedCases.value + run.PendingCases.value }} +
    + + +
    + + + +
    + + {{ run.Speed.Column.GetMetadata().GetDisplay() }} + + + + {{ '#LDS#{0} attestations/day' | translate | ldsReplace: run.Speed.Column.GetDisplayValue() }} +
    +
    + +
    +
    - - - - {{ '#LDS#Attestation details' | translate }} - - -
    -
    -
    - {{ '#LDS#Cases' | translate }} - {{ run.ClosedCases.value + run.PendingCases.value }} + + + + {{ '#LDS#Attestation prediction' | translate }} + + + +
    + {{ '#LDS#Estimated progress on due date' | translate }} + + + + +
    + {{ '#LDS#No prediction possible because the attestation run has no due date.' | translate }} +
    + +
    {{ '#LDS#No prediction possible because the attestation run is already overdue.' | translate }}
    +
    +
    - - -
    - - - +
    {{ run.Speed.Column.GetMetadata().GetDisplay() }} - + - {{ '#LDS#{0} attestations/day' | translate | ldsReplace:run.Speed.Column.GetDisplayValue() - }} + {{ '#LDS#{0} attestations/day' | translate | ldsReplace: run.Speed.Column.GetDisplayValue() }}
    -
    - -
    - - - - - - {{ '#LDS#Attestation prediction' | translate }} - - - -
    - {{ '#LDS#Estimated progress on due date' | translate }} - - - - - -
    - {{ '#LDS#No prediction possible because the attestation run has no due date.' | translate }} -
    - -
    {{ '#LDS#No prediction possible because the attestation run is already overdue.' | translate }} -
    -
    -
    -
    - -
    - - {{ run.Speed.Column.GetMetadata().GetDisplay() }} - - - - {{ '#LDS#{0} attestations/day' | translate | ldsReplace:run.Speed.Column.GetDisplayValue() }} -
    - -
    - {{ '#LDS#Estimated completion of the attestation run' | translate }} - - {{ run.ForecastRemainingDays.value ? - ('#LDS#{0} days' | translate | - ldsReplace:(run.ForecastRemainingDays.value)) :( '#LDS#N/A' | translate) }} - -
    - - -

    - {{ categoryExplanation.message | translate | ldsReplace:categoryExplanation.limit }}

    - -
    - -

    - {{ '#LDS#The prediction is available as soon as at least {0}% of the attestation cases of this attestation run have been closed.' | translate |ldsReplace:threshold }} -

    -
    - -

    - {{ '#LDS#The prediction is not available because the attestation run has already been completed.' | translate }} -

    -
    -
    - - -
    - - - -
    + +
    + {{ '#LDS#Estimated completion of the attestation run' | translate }} + + {{ + run.ForecastRemainingDays.value + ? ('#LDS#{0} days' | translate | ldsReplace: run.ForecastRemainingDays.value) + : ('#LDS#N/A' | translate) + }} + +
    + + +

    + {{ categoryExplanation.message | translate | ldsReplace: categoryExplanation.limit }} +

    + + +

    + {{ + '#LDS#The prediction is available as soon as at least {0}% of the attestation cases of this attestation run have been closed.' + | translate + | ldsReplace: threshold + }} +

    +
    + +

    + {{ '#LDS#The prediction is not available because the attestation run has already been completed.' | translate }} +

    +
    + + + +
    +
    + + +
    - - + + - -
    - - -
    + + + diff --git a/imxweb/projects/att/src/lib/runs/run-sidesheet.component.scss b/imxweb/projects/att/src/lib/runs/run-sidesheet.component.scss index 90bea5638..cff54f121 100644 --- a/imxweb/projects/att/src/lib/runs/run-sidesheet.component.scss +++ b/imxweb/projects/att/src/lib/runs/run-sidesheet.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; :host { display: flex; @@ -7,26 +7,6 @@ height: 100%; } -.mat-tab-group { - flex: 1 1 auto; - overflow-y: auto; -} - -:host ::ng-deep .mat-tab-body-wrapper { - flex: 1 1 auto; - - .mat-tab-body { - display: flex; - flex-direction: column; - flex: 1 1 auto; - } -} - -:host ::ng-deep .mat-tab-body-content { - display: flex; - flex-direction: column; -} - .imx-progress-content { overflow-y: auto; flex: 1 1 auto; @@ -49,21 +29,19 @@ .imx-property-display { margin-bottom: 20px; - >span { - display: block; + > span { + display: flex; + align-items: center; } - >span:first-child { + > span:first-child { font-size: 12px; color: $color-gray-40; padding-bottom: 5px; } - .help-icon { - color: $color-blue-60; - font-size: 16px; + .imx-icon-info { margin-left: 5px; - margin-top: -3px; } } @@ -72,29 +50,6 @@ } } -.helper-alert { - display: flex; - padding: 20px 20px 0 20px; - flex-direction: column; - - span { - display: block; - } -} - -.eui-sidesheet-actions { - display: flex; - justify-content: flex-end; - padding: 0px 20px 20px 8px; - flex-wrap: wrap; - margin: 0 -20px -20px; - - > * { - margin-left: 10px; - margin-top: 20px; - } -} - .imx-readonly-view { margin-bottom: 20px; @@ -123,17 +78,12 @@ padding-bottom: 5px; } } - } -eui-badge { - margin-left: 10px; -} .imx-run-info-category-content { display: flex; flex-direction: row; - } .seperator { @@ -141,35 +91,3 @@ eui-badge { margin-bottom: 10px; margin-top: -10px; } - -.imx-tab-content-body { - display: flex; - flex-direction: column; - flex: 1; - padding: 20px; - overflow: auto; -} - -.eui-dark-theme { - :host { - ::ng-deep .mat-tab-header { - background-color: $color-gray-80; - } - - .imx-button-bar { - background-color: $color-gray-70; - } - } -} - -.eui-contrast-theme { - :host { - ::ng-deep .mat-tab-header { - background-color: $color-gray-90; - } - - .imx-button-bar { - background-color: $color-gray-80; - } - } -} diff --git a/imxweb/projects/att/src/lib/runs/run-sidesheet.component.ts b/imxweb/projects/att/src/lib/runs/run-sidesheet.component.ts index 7d0e1cae7..0cda93de4 100644 --- a/imxweb/projects/att/src/lib/runs/run-sidesheet.component.ts +++ b/imxweb/projects/att/src/lib/runs/run-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,29 +24,28 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; import { Component, Inject } from '@angular/core'; import { MatTabChangeEvent } from '@angular/material/tabs'; -import { EuiLoadingService, EuiSidesheetRef, EuiSidesheetService, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { EUI_SIDESHEET_DATA, EuiDownloadOptions, EuiLoadingService, EuiSidesheetRef, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { EuiDownloadOptions } from '@elemental-ui/core'; import { Subscription } from 'rxjs'; -import { CompareOperator, FilterType, TypedEntityCollectionData } from 'imx-qbm-dbts'; -import { PortalAttestationRun, PortalAttestationRunApprovers, RunStatisticsConfig } from 'imx-api-att'; -import { SnackBarService } from 'qbm'; +import { PortalAttestationRun, PortalAttestationRunApprovers, RunStatisticsConfig } from '@imx-modules/imx-api-att'; +import { CompareOperator, FilterType, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; +import { calculateSidesheetWidth, SnackBarService } from 'qbm'; -import { RunsService } from './runs.service'; -import { percentage } from './helpers'; -import { RunExtendComponent } from './run-extend/run-extend.component'; +import { HelperAlertContent } from 'qer'; import { AttestationActionService } from '../attestation-action/attestation-action.service'; -import { AttestationHistoryActionService } from '../attestation-history/attestation-history-action.service'; import { AttestationCaseLoadParameters } from '../attestation-history/attestation-case-load-parameters.interface'; -import { HelperAlertContent } from 'qer'; +import { AttestationHistoryActionService } from '../attestation-history/attestation-history-action.service'; +import { AttestationParameters } from './attestation/attestation.component'; +import { percentage } from './helpers'; +import { RunExtendComponent } from './run-extend/run-extend.component'; +import { RunsService } from './runs.service'; @Component({ templateUrl: './run-sidesheet.component.html', - styleUrls: ['./run-sidesheet.component.scss'] + styleUrls: ['./run-sidesheet.component.scss'], }) export class RunSidesheetComponent { public readonly run: PortalAttestationRun; @@ -57,6 +56,10 @@ export class RunSidesheetComponent { public approvers: TypedEntityCollectionData; public readonly attestationParameters: AttestationCaseLoadParameters; + public readonly attestationFilterParam: AttestationParameters = { + objecttable: '', + objectuid: '', + }; public readonly pendingAttestations: HelperAlertContent = { loading: false }; private readonly subscriptions: Subscription[] = []; @@ -73,21 +76,23 @@ export class RunSidesheetComponent { if (this.attestationRunConfig) { if (this.run.ForecastProgress.value <= this.attestationRunConfig.LimitBad) { return { - message: '#LDS#This attestation run is categorized bad because the progress on the due date is estimated to be no more than {0}%.', - limit: percentage(this.attestationRunConfig.LimitBad) + message: + '#LDS#This attestation run is categorized bad because the progress on the due date is estimated to be no more than {0}%.', + limit: percentage(this.attestationRunConfig.LimitBad), }; } if (this.run.ForecastProgress.value <= this.attestationRunConfig.LimitGood) { return { - message: '#LDS#This attestation run is categorized as mediocre because the progress on the due date is estimated to be no more than {0}%.', - limit: percentage(this.attestationRunConfig.LimitGood) + message: + '#LDS#This attestation run is categorized as mediocre because the progress on the due date is estimated to be no more than {0}%.', + limit: percentage(this.attestationRunConfig.LimitGood), }; } return { message: '#LDS#This attestation run is categorized as good because the progress on the due date is estimated to be more than {0}%.', - limit: percentage(this.attestationRunConfig.LimitGood) + limit: percentage(this.attestationRunConfig.LimitGood), }; } @@ -95,12 +100,13 @@ export class RunSidesheetComponent { } constructor( - @Inject(EUI_SIDESHEET_DATA) public readonly data: { - run: PortalAttestationRun, - attestationRunConfig: RunStatisticsConfig, - canSeeAttestationPolicies: boolean, - threshold: number, - completed: boolean + @Inject(EUI_SIDESHEET_DATA) + public readonly data: { + run: PortalAttestationRun; + attestationRunConfig: RunStatisticsConfig; + canSeeAttestationPolicies: boolean; + threshold: number; + completed: boolean; }, public readonly runsService: RunsService, private readonly snackBarService: SnackBarService, @@ -109,66 +115,67 @@ export class RunSidesheetComponent { private readonly busyService: EuiLoadingService, private readonly sideSheetRef: EuiSidesheetRef, private readonly action: AttestationActionService, - attestationAction: AttestationHistoryActionService + attestationAction: AttestationHistoryActionService, ) { this.run = this.data.run; this.attestationRunConfig = this.data.attestationRunConfig; this.threshold = data.threshold; - this.subscriptions.push( - attestationAction.applied?.subscribe(() => this.updatePendingAttestations()) - ); + this.subscriptions.push(attestationAction.applied?.subscribe(() => this.updatePendingAttestations())); this.reportDownload = runsService.getReportDownloadOptions(this.run); this.attestationParameters = { - filter: [{ - CompareOp: CompareOperator.Equal, - Type: FilterType.Compare, - ColumnName: 'UID_AttestationRun', - Value1: this.run.GetEntity().GetKeys()[0] - },{ - CompareOp: CompareOperator.Equal, - Type: FilterType.Compare, - ColumnName: 'UID_AttestationPolicy', - Value1: this.run.UID_AttestationPolicy.value - }] + filter: [ + { + CompareOp: CompareOperator.Equal, + Type: FilterType.Compare, + ColumnName: 'UID_AttestationRun', + Value1: this.run.GetEntity().GetKeys()[0], + }, + { + CompareOp: CompareOperator.Equal, + Type: FilterType.Compare, + ColumnName: 'UID_AttestationPolicy', + Value1: this.run.UID_AttestationPolicy.value, + }, + ], }; + this.attestationFilterParam.filter = this.attestationParameters?.filter; } public async extendAttestationRun(): Promise { const data = { ProlongateUntil: this.run.DueDate.value, - reason: this.action.createCdrReason({ display: '#LDS#Reason', mandatory: true }) + reason: this.action.createCdrReason({ display: '#LDS#Reason', mandatory: true }), }; - const result = await this.sideSheet.open( - RunExtendComponent, - { + const result = await this.sideSheet + .open(RunExtendComponent, { title: await this.translate.get('#LDS#Heading Extend Attestation Run').toPromise(), subTitle: this.run.GetEntity().GetDisplay(), padding: '0px', - width: '600px', + width: calculateSidesheetWidth(600, 0.4), testId: 'attestationruns-extendrun-sidesheet', - data - } - ).afterClosed().toPromise(); + data, + }) + .afterClosed() + .toPromise(); if (result) { - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + this.showBusyIndicator(); - let success: boolean; + let success = false; try { await this.runsService.extendRun(this.run, { ProlongateUntil: data.ProlongateUntil, - Reason: data.reason.column.GetValue() + Reason: data.reason.column.GetValue(), }); success = true; } finally { if (success) { this.snackBarService.open({ key: '#LDS#The attestation run has been successfully extended.' }); } - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); this.sideSheetRef.close(true); } } @@ -176,22 +183,20 @@ export class RunSidesheetComponent { public async onTabChange(event: MatTabChangeEvent): Promise { if (event.index === 1) { - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + this.showBusyIndicator(); try { this.approvers = await this.runsService.getApprovers(this.run); } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } } if (event.index === 2) { - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + this.showBusyIndicator(); try { this.updatePendingAttestations(); } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } } } @@ -206,22 +211,26 @@ export class RunSidesheetComponent { const total = this.run.ClosedCases.value + this.run.PendingCases.value; if (total === 0) { - this.pendingAttestations.infoItems = [ - { description: '#LDS#There are currently no attestation cases for this attestation run.' } - ]; + this.pendingAttestations.infoItems = [{ description: '#LDS#There are currently no attestation cases for this attestation run.' }]; } else { const statistics = { pendingTotal: this.run.PendingCases.value, - pendingForUser: await this.runsService.getNumberOfPendingForLoggedInUser(this.attestationParameters) + pendingForUser: await this.runsService.getNumberOfPendingForLoggedInUser(this.attestationParameters), }; this.pendingAttestations.infoItems = [ { description: '#LDS#Here you can get an overview of all attestation cases in this attestation run.' }, { description: '#LDS#Pending attestation cases: {0}', value: statistics.pendingTotal }, - { description: '#LDS#Pending attestation cases you can approve or deny: {0}', value: statistics.pendingForUser } + { description: '#LDS#Pending attestation cases you can approve or deny: {0}', value: statistics.pendingForUser }, ]; } this.pendingAttestations.loading = false; } + + private showBusyIndicator(): void { + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } + } } diff --git a/imxweb/projects/att/src/lib/runs/runs-grid/runs-grid.component.html b/imxweb/projects/att/src/lib/runs/runs-grid/runs-grid.component.html index 0968e95ca..9a6143604 100644 --- a/imxweb/projects/att/src/lib/runs/runs-grid/runs-grid.component.html +++ b/imxweb/projects/att/src/lib/runs/runs-grid/runs-grid.component.html @@ -1,71 +1,107 @@ - - + + - -
    - - - -
    -
    {{ item.UID_AttestationPolicy.Column.GetDisplayValue() }}
    -
    - {{ item.UID_AttestationPolicyGroup.Column.GetDisplayValue() }} -
    + + + {{ entitySchema?.Columns?.UID_AttestationPolicy?.Display }} + + +
    +
    {{ item.UID_AttestationPolicy.Column.GetDisplayValue() }}
    +
    + {{ item.UID_AttestationPolicyGroup.Column.GetDisplayValue() }}
    - - - - - - {{ item.RunCategory.Column.GetDisplayValue() }} - - - - - - - - - - - - - - - -
    - +
    +
    +
    + {{ + '#LDS#Started via policy collection "{0}"' | translate | ldsReplace: item.UID_AttestationPolicyGroup.Column.GetDisplayValue() + }} +
    +
    + + + + + + + {{ item.RunCategory.Column.GetDisplayValue() }} + + + + + + + {{ entitySchema?.Columns?.PolicyProcessed?.Display }} + + + + + {{ entitySchema?.Columns?.PolicyProcessed?.Display }} + + + +
    {{ item.PolicyProcessed.Column.GetValue() | shortDate }}
    + +
    + + + {{ entitySchema?.Columns?.DueDate?.Display }} + + +
    {{ item.DueDate.Column.GetValue() | shortDate }}
    + +
    + + + {{ '#LDS#Pending' | translate }} + + +
    {{ item.PendingCases.Column.GetDisplayValue() }}
    + +
    + + + {{ '#LDS#Closed' | translate }} + + +
    {{ item.ClosedCases.Column.GetDisplayValue() }}
    + +
    + + + {{ '#LDS#Progress' | translate }} + + + + + + + -
    -
    diff --git a/imxweb/projects/att/src/lib/runs/runs-grid/runs-grid.component.scss b/imxweb/projects/att/src/lib/runs/runs-grid/runs-grid.component.scss index 22e4bf075..ae7f3ca57 100644 --- a/imxweb/projects/att/src/lib/runs/runs-grid/runs-grid.component.scss +++ b/imxweb/projects/att/src/lib/runs/runs-grid/runs-grid.component.scss @@ -1,10 +1,7 @@ -@import '../../../../../../shared/scss/common-table.scss'; +@import 'base/mixins'; :host { - flex: 1 1 auto; - overflow: hidden; - display: flex; - flex-direction: column; + @include flex-column-container-fill(); ::ng-deep .mat-column-RunCategory, ::ng-deep .mat-column-ClosedCases, @@ -20,19 +17,5 @@ } .imx-table-card { - @include imx-flex-fill-control-hidden-overflow(); - -} - -.imx-table-container { - flex-grow: 1; - overflow: auto; -} - -.button-row { - @include imx-button-bar(); -} - -div[subtitle] { - font-size: smaller; + @include flex-column-container-fill(); } diff --git a/imxweb/projects/att/src/lib/runs/runs-grid/runs-grid.component.ts b/imxweb/projects/att/src/lib/runs/runs-grid/runs-grid.component.ts index 064067643..6dc5aaebc 100644 --- a/imxweb/projects/att/src/lib/runs/runs-grid/runs-grid.component.ts +++ b/imxweb/projects/att/src/lib/runs/runs-grid/runs-grid.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,43 +24,55 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; import { Component, Input, OnInit } from '@angular/core'; import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { CollectionLoadParameters, CompareOperator, DataModel, EntityCollectionData, EntitySchema, FilterData, FilterType, MethodDefinition, MethodDescriptor } from 'imx-qbm-dbts'; -import { PortalAttestationRun, RunStatisticsConfig, V2ApiClientMethodFactory } from 'imx-api-att'; -import { DataSourceToolbarExportMethod, DataSourceToolbarFilter, DataSourceToolbarGroupData, DataSourceToolbarSettings, DataSourceToolbarViewConfig, DataTableGroupedData, SettingsService } from 'qbm'; +import { PortalAttestationRun, RunStatisticsConfig, V2ApiClientMethodFactory } from '@imx-modules/imx-api-att'; +import { + CollectionLoadParameters, + CompareOperator, + DataModel, + EntityCollectionData, + EntitySchema, + FilterType, + MethodDefinition, + MethodDescriptor, + TypedEntityCollectionData, +} from '@imx-modules/imx-qbm-dbts'; +import { + calculateSidesheetWidth, + DataSourceToolbarExportMethod, + DataSourceToolbarFilter, + DataSourceToolbarViewConfig, + DataViewInitParameters, + DataViewSource, + SettingsService, +} from 'qbm'; + +import { ViewConfigData } from '@imx-modules/imx-api-qer'; +import { ViewConfigService } from 'qer'; +import { PermissionsService } from '../../admin/permissions.service'; import { ApiService } from '../../api.service'; -import { createGroupData } from '../../datamodel/datamodel-helper'; import { RunSidesheetComponent } from '../run-sidesheet.component'; import { RunsService } from '../runs.service'; -import { PermissionsService } from '../../admin/permissions.service'; -import { ViewConfigData } from 'imx-api-qer'; -import { ViewConfigService } from 'qer'; @Component({ selector: 'imx-runs-grid', templateUrl: './runs-grid.component.html', - styleUrls: ['./runs-grid.component.scss'] + styleUrls: ['./runs-grid.component.scss'], + providers: [DataViewSource], }) export class RunsGridComponent implements OnInit { - - /** - * Settings needed by the DataSourceToolbarComponent - */ - public dstSettings: DataSourceToolbarSettings; public readonly categoryBadgeColor = { Bad: 'red', Mediocre: 'orange', - Good: 'white' + Good: 'white', }; public entitySchema: EntitySchema; @Input() public uidAttestationPolicy; - public groupedData: { [key: string]: DataTableGroupedData } = {}; public attestationRunConfig: RunStatisticsConfig | undefined; public canSeeAttestationPolicies: boolean; @@ -70,14 +82,6 @@ export class RunsGridComponent implements OnInit { private runs: PortalAttestationRun[]; private filterOptions: DataSourceToolbarFilter[] | undefined = []; - private groupData: DataSourceToolbarGroupData; - /** - * Page size, start index, search and filtering options etc. - */ - private navigationState: CollectionLoadParameters; - private readonly orderBy = 'PolicyProcessed desc'; - - private filter: { filter: FilterData[] }; private dataModel: DataModel; private viewConfig: DataSourceToolbarViewConfig; private viewConfigPath = 'attestation/run'; @@ -90,47 +94,24 @@ export class RunsGridComponent implements OnInit { private readonly attService: ApiService, private readonly settingsService: SettingsService, private readonly permissions: PermissionsService, - private translate: TranslateService + private translate: TranslateService, + public dataSource: DataViewSource, ) { - this.entitySchema = this.attService.typedClient.PortalAttestationRun.GetSchema(); - + this.entitySchema = this.attService.typedClient.PortalAttestationRun.GetSchema(); } public async ngOnInit(): Promise { - this.filter = { - filter: this.uidAttestationPolicy == null ? [] : [{ - CompareOp: CompareOperator.Equal, - Type: FilterType.Compare, - ColumnName: 'UID_AttestationPolicy', - Value1: this.uidAttestationPolicy - }] - }; - this.navigationState = { ...{ PageSize: this.settingsService.DefaultPageSize, StartIndex: 0 }, ...this.filter }; const config = await this.attService.client.portal_attestation_config_get(); this.attestationRunConfig = config.AttestationRunConfig; this.progressCalcThreshold = config.ProgressCalculationThreshold; this.canSeeAttestationPolicies = await this.permissions.canSeeAttestationPolicies(); - let busyIndicator: OverlayRef; - setTimeout(() => busyIndicator = this.busyService.show()); + this.showBusyIndicator(); try { this.dataModel = await this.runsService.getDataModel(); this.viewConfig = await this.viewConfigService.getInitialDSTExtension(this.dataModel, this.viewConfigPath); - // We will check the configs for default state only on init - if (!this.viewConfigService.isDefaultConfigSet()) { - // If we have no default settings, we have a default - this.navigationState.OrderBy = this.orderBy; - } - this.filterOptions = this.dataModel.Filters; - this.groupData = createGroupData( - this.dataModel, - parameters => this.runsService.getGroupInfo({ ...{ PageSize: this.navigationState.PageSize, StartIndex: 0 }, ...parameters }), - this.uidAttestationPolicy == null ? [] : ['UID_AttestationPolicy'] - ); } finally { - setTimeout(() => { - this.busyService.hide(busyIndicator); - }); + this.busyService.hide(); } await this.getData(); @@ -139,51 +120,59 @@ export class RunsGridComponent implements OnInit { public async updateConfig(config: ViewConfigData): Promise { await this.viewConfigService.putViewConfig(config); this.viewConfig = await this.viewConfigService.getDSTExtensionChanges(this.viewConfigPath); - this.dstSettings.viewConfig = this.viewConfig; + this.dataSource.viewConfig.set(this.viewConfig); } public async deleteConfigById(id: string): Promise { await this.viewConfigService.deleteViewConfig(id); this.viewConfig = await this.viewConfigService.getDSTExtensionChanges(this.viewConfigPath); - this.dstSettings.viewConfig = this.viewConfig; + this.dataSource.viewConfig.set(this.viewConfig); } - public async getData(newState?: CollectionLoadParameters): Promise { - if (newState) { - const filter = this.filter.filter.concat(newState.filter ?? []); - this.navigationState = { ...newState, filter }; - } - - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); - - try { - const data = await this.attService.typedClient.PortalAttestationRun.Get(this.navigationState); - this.runs = data.Data; - this.dstSettings = { - displayedColumns: [ - this.entitySchema.Columns.UID_AttestationPolicy, - this.entitySchema.Columns.RunCategory, - this.entitySchema.Columns.PolicyProcessed, - this.entitySchema.Columns.DueDate, - this.entitySchema.Columns.PendingCases, - this.entitySchema.Columns.ClosedCases, - this.entitySchema.Columns.Progress - ], - dataSource: data, - entitySchema:this.entitySchema, - navigationState: this.navigationState, - filters: this.filterOptions, - dataModel: this.dataModel, - groupData: this.groupData, - viewConfig: this.viewConfig, - exportMethod: this.getExportMethod(), - }; - - this.hasPendingAttestations = this.runs?.some(run => run.PendingCases.value > 0); - } finally { - setTimeout(() => this.busyService.hide(overlayRef)); - } + public async getData(): Promise { + const displayedColumns = [ + this.entitySchema.Columns.UID_AttestationPolicy, + this.entitySchema.Columns.RunCategory, + this.entitySchema.Columns.PolicyProcessed, + this.entitySchema.Columns.DueDate, + this.entitySchema.Columns.PendingCases, + this.entitySchema.Columns.ClosedCases, + this.entitySchema.Columns.Progress, + ]; + const dataViewInitParameters: DataViewInitParameters = { + execute: (params: CollectionLoadParameters, signal: AbortSignal): Promise> => + this.attService.typedClient.PortalAttestationRun.Get( + { + ...params, + filter: [ + ...(params.filter || []), + ...(this.uidAttestationPolicy == null + ? [] + : [ + { + CompareOp: CompareOperator.Equal, + Type: FilterType.Compare, + ColumnName: 'UID_AttestationPolicy', + Value1: this.uidAttestationPolicy, + }, + ]), + ], + }, + { signal }, + ), + schema: this.entitySchema, + columnsToDisplay: displayedColumns, + dataModel: this.dataModel, + groupExecute: (column: string, params: CollectionLoadParameters, signal: AbortSignal) => { + return this.runsService.getGroupInfo({ ...params, by: column }); + }, + exportFunction: this.getExportMethod(), + viewConfig: this.viewConfig, + highlightEntity: (identity: PortalAttestationRun) => { + this.onRunChanged(identity); + }, + }; + this.dataSource.init(dataViewInitParameters); } public getExportMethod(): DataSourceToolbarExportMethod { @@ -192,58 +181,34 @@ export class RunsGridComponent implements OnInit { getMethod: (withProperties: string, PageSize?: number) => { let method: MethodDescriptor; if (PageSize) { - method = factory.portal_attestation_run_get({...this.navigationState, withProperties, PageSize, StartIndex: 0}); + method = factory.portal_attestation_run_get({ ...this.dataSource.state(), withProperties, PageSize, StartIndex: 0 }); } else { - method = factory.portal_attestation_run_get({...this.navigationState, withProperties}); + method = factory.portal_attestation_run_get({ ...this.dataSource.state(), withProperties }); } return new MethodDefinition(method); - } - } - } - - public async onSearch(keywords: string): Promise { - return this.getData({ - PageSize: this.navigationState.PageSize, - StartIndex: 0, - search: keywords - }); + }, + }; } public async onRunChanged(run: PortalAttestationRun): Promise { - await this.sideSheet.open(RunSidesheetComponent, { - title: await this.translate.get('#LDS#Heading View Attestation Run Details').toPromise(), - subTitle: run.GetEntity().GetDisplay(), - padding: '0px', - width: 'max(768px, 60%)', - testId: 'runs-grid-view-attestation-run-details', - data: { - run: await this.runsService.getSingleRun(run.GetEntity().GetKeys()[0]), - attestationRunConfig: this.attestationRunConfig, - canSeeAttestationPolicies: this.canSeeAttestationPolicies, - threshold: this.progressCalcThreshold, - completed: this.isCompleted(run) - } - }).afterClosed().toPromise(); - await this.getData(); - } - - public async onGroupingChange(groupKey: string): Promise { - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.busyService.show())); - - try { - const groupedData = this.groupedData[groupKey]; - groupedData.data = await this.attService.typedClient.PortalAttestationRun.Get(groupedData.navigationState); - groupedData.settings = { - displayedColumns: this.dstSettings.displayedColumns, - dataModel: this.dstSettings.dataModel, - dataSource: groupedData.data, - entitySchema: this.dstSettings.entitySchema, - navigationState: groupedData.navigationState - }; - } finally { - setTimeout(() => this.busyService.hide(overlayRef)); - } + await this.sideSheet + .open(RunSidesheetComponent, { + title: await this.translate.get('#LDS#Heading View Attestation Run Details').toPromise(), + subTitle: run.GetEntity().GetDisplay(), + padding: '0px', + width: calculateSidesheetWidth(1000), + testId: 'runs-grid-view-attestation-run-details', + data: { + run: await this.runsService.getSingleRun(run.GetEntity().GetKeys()[0]), + attestationRunConfig: this.attestationRunConfig, + canSeeAttestationPolicies: this.canSeeAttestationPolicies, + threshold: this.progressCalcThreshold, + completed: this.isCompleted(run), + }, + }) + .afterClosed() + .toPromise(); + await this.dataSource.updateState(); } public async sendReminderEmail(): Promise { @@ -251,6 +216,12 @@ export class RunsGridComponent implements OnInit { } public isCompleted(run: PortalAttestationRun): boolean { - return (run.ClosedCases.value + run.PendingCases.value) > 0 && run.PendingCases.value === 0; + return run.ClosedCases.value + run.PendingCases.value > 0 && run.PendingCases.value === 0; + } + + private showBusyIndicator(): void { + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } } } diff --git a/imxweb/projects/att/src/lib/runs/runs-load-parameters.interface.ts b/imxweb/projects/att/src/lib/runs/runs-load-parameters.interface.ts index c368f28e8..721a60ff1 100644 --- a/imxweb/projects/att/src/lib/runs/runs-load-parameters.interface.ts +++ b/imxweb/projects/att/src/lib/runs/runs-load-parameters.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { CollectionLoadParameters, FilterData } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, FilterData } from '@imx-modules/imx-qbm-dbts'; export interface RunsLoadParameters extends CollectionLoadParameters { groupFilter?: FilterData[]; diff --git a/imxweb/projects/att/src/lib/runs/runs.component.html b/imxweb/projects/att/src/lib/runs/runs.component.html index 8fe59cc4d..fb1134d36 100644 --- a/imxweb/projects/att/src/lib/runs/runs.component.html +++ b/imxweb/projects/att/src/lib/runs/runs.component.html @@ -1,11 +1,18 @@ -
    -

    +
    +

    #LDS#Heading Attestation Runs -

    +

    + + + +
    - +
    - diff --git a/imxweb/projects/att/src/lib/runs/runs.component.scss b/imxweb/projects/att/src/lib/runs/runs.component.scss index f327036fa..f6551ebf8 100644 --- a/imxweb/projects/att/src/lib/runs/runs.component.scss +++ b/imxweb/projects/att/src/lib/runs/runs.component.scss @@ -8,17 +8,6 @@ .heading-wrapper { display: flex; align-items: flex-start; - - .helper-alert { - display: flex; - margin: 0 0 20px auto; - align-self: flex-end; - width: 50%; - - span { - display: block; - } - } } .imx-runs-grid { @@ -26,8 +15,4 @@ flex-direction: column; overflow: hidden; flex: 1 1 auto; - - ::ng-deep .eui-sidesheet-actions{ - background-color: transparent; - } } diff --git a/imxweb/projects/att/src/lib/runs/runs.component.ts b/imxweb/projects/att/src/lib/runs/runs.component.ts index e61f2f6fb..a82cdaa6f 100644 --- a/imxweb/projects/att/src/lib/runs/runs.component.ts +++ b/imxweb/projects/att/src/lib/runs/runs.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,15 +24,22 @@ * */ -import { Component } from '@angular/core'; - +import { AfterViewInit, Component, signal, ViewChild, WritableSignal } from '@angular/core'; +import { PortalAttestationRun } from '@imx-modules/imx-api-att'; +import { DataViewSource } from 'qbm'; +import { RunsGridComponent } from './runs-grid/runs-grid.component'; @Component({ templateUrl: './runs.component.html', - styleUrls: ['./runs.component.scss'] + styleUrls: ['./runs.component.scss'], }) -export class RunsComponent { +export class RunsComponent implements AfterViewInit { + public dataSource: WritableSignal | undefined> = signal(undefined); public canSeeAttestationPolicies: boolean; + @ViewChild('runsGridComponent', { static: false }) public runsGridComponent: RunsGridComponent; + ngAfterViewInit(): void { + this.dataSource.set(this.runsGridComponent?.dataSource); + } // HelpChapterID = 2A288F2C-345B-4A0D-BD88-0C488289C495 } diff --git a/imxweb/projects/att/src/lib/runs/runs.service.ts b/imxweb/projects/att/src/lib/runs/runs.service.ts index 4269f71b8..36336344d 100644 --- a/imxweb/projects/att/src/lib/runs/runs.service.ts +++ b/imxweb/projects/att/src/lib/runs/runs.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,10 +24,8 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; import { Injectable } from '@angular/core'; -import { EuiDownloadOptions } from '@elemental-ui/core'; -import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; +import { EuiDownloadOptions, EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; import { @@ -37,7 +35,7 @@ import { PortalAttestationRun, PortalAttestationRunApprovers, V2ApiClientMethodFactory, -} from 'imx-api-att'; +} from '@imx-modules/imx-api-att'; import { CollectionLoadParameters, @@ -49,9 +47,9 @@ import { GroupInfoData, MethodDefinition, TypedEntityCollectionData, -} from 'imx-qbm-dbts'; +} from '@imx-modules/imx-qbm-dbts'; -import { AppConfigService, ElementalUiConfigService, SnackBarService } from 'qbm'; +import { AppConfigService, calculateSidesheetWidth, ElementalUiConfigService, SnackBarService } from 'qbm'; import { ApiService } from '../api.service'; import { AttestationCaseLoadParameters } from '../attestation-history/attestation-case-load-parameters.interface'; import { AttestationCasesService } from '../decision/attestation-cases.service'; @@ -72,7 +70,7 @@ export class RunsService { private readonly translate: TranslateService, private readonly busyService: EuiLoadingService, private readonly config: AppConfigService, - private readonly attestationApprove: AttestationCasesService + private readonly attestationApprove: AttestationCasesService, ) {} public async getDataModel(): Promise { @@ -83,7 +81,7 @@ export class RunsService { const { withProperties, groupFilter, search, OrderBy, ...params } = parameters; return this.attService.client.portal_attestation_run_group_get({ ...params, - filter: parameters.groupFilter, + filter: [...(parameters.groupFilter || []), ...(parameters.filter || [])], withcount: true, }); } @@ -108,23 +106,24 @@ export class RunsService { subTitle: runs.length === 1 ? runs[0].GetEntity().GetDisplay() : '', padding: '0px', testId: 'attestationruns-sendReminder-sidesheet', - width: '600px', + width: calculateSidesheetWidth(600, 0.4), data, }) .afterClosed() .toPromise(); if (result) { - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.busyService.show())); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } - let success: boolean; + let success = false; try { if (approvers == null) { approvers = []; for (const run of runs) { - (await this.getApprovers(run)).Data.forEach((approver) => approvers.push(approver)); + (await this.getApprovers(run)).Data.forEach((approver) => approvers?.push(approver)); } } @@ -140,7 +139,7 @@ export class RunsService { this.snackBar.open({ key: '#LDS#The reminder mails have been sent.' }); } - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } } } @@ -162,7 +161,7 @@ export class RunsService { public getCasesForRun( uidRun: string, - parameter: CollectionLoadParameters + parameter: CollectionLoadParameters, ): Promise> { return this.attService.typedClient.PortalAttestationCase.Get({ ...parameter, diff --git a/imxweb/projects/att/src/lib/runs/send-reminder-mail.component.html b/imxweb/projects/att/src/lib/runs/send-reminder-mail.component.html index 1de9545d1..597a7d4e6 100644 --- a/imxweb/projects/att/src/lib/runs/send-reminder-mail.component.html +++ b/imxweb/projects/att/src/lib/runs/send-reminder-mail.component.html @@ -1,17 +1,21 @@
    - + #LDS#Here you can optionally add a custom message to the reminder. #LDS#Each attestor will receive a reminder. {{ '#LDS#Message' | translate }} - +
    -
    - -
    \ No newline at end of file +
    diff --git a/imxweb/projects/att/src/lib/runs/send-reminder-mail.component.scss b/imxweb/projects/att/src/lib/runs/send-reminder-mail.component.scss index c516f36bd..fb4877cf2 100644 --- a/imxweb/projects/att/src/lib/runs/send-reminder-mail.component.scss +++ b/imxweb/projects/att/src/lib/runs/send-reminder-mail.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; :host { display: flex; @@ -12,33 +12,5 @@ flex-direction: column; flex: 1 1 auto; overflow-y: auto; -} - -.mat-form-field { - width: 100%; padding: 20px; } - -textarea.mat-input-element.cdk-textarea-autosize { - box-sizing: content-box; -} - -.helper-alert { - display: flex; - padding: 20px 20px 0 20px; - flex-direction: column; - span { - display: block; - } -} - -.imx-button-bar { - display: flex; - justify-content: flex-end; - border-top: $color-gray-20 1px solid; - padding: 0px 20px 20px 8px; - > * { - margin-left: 10px; - margin-top: 20px; - } -} diff --git a/imxweb/projects/att/src/lib/runs/send-reminder-mail.component.ts b/imxweb/projects/att/src/lib/runs/send-reminder-mail.component.ts index ed5cc91f3..ed4d8139b 100644 --- a/imxweb/projects/att/src/lib/runs/send-reminder-mail.component.ts +++ b/imxweb/projects/att/src/lib/runs/send-reminder-mail.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,7 +31,7 @@ import { Subscription } from 'rxjs'; @Component({ templateUrl: './send-reminder-mail.component.html', - styleUrls: ['./send-reminder-mail.component.scss'] + styleUrls: ['./send-reminder-mail.component.scss'], }) export class SendReminderMailComponent implements OnDestroy { public showHelper = true; @@ -40,13 +40,16 @@ export class SendReminderMailComponent implements OnDestroy { private readonly subscriptions: Subscription[] = []; - constructor(@Inject(EUI_SIDESHEET_DATA) data: { message: string }, public readonly sideSheetRef: EuiSidesheetRef) { + constructor( + @Inject(EUI_SIDESHEET_DATA) data: { message: string }, + public readonly sideSheetRef: EuiSidesheetRef, + ) { this.message = new UntypedFormControl(data.message); - this.subscriptions.push(this.message.valueChanges.subscribe(value => data.message = value)); + this.subscriptions.push(this.message.valueChanges.subscribe((value) => (data.message = value))); } public ngOnDestroy(): void { - this.subscriptions.forEach(s => s.unsubscribe()); + this.subscriptions.forEach((s) => s.unsubscribe()); } public onHelperDismissed(): void { diff --git a/imxweb/projects/att/src/public_api.ts b/imxweb/projects/att/src/public_api.ts index 82114b411..939c9e39d 100644 --- a/imxweb/projects/att/src/public_api.ts +++ b/imxweb/projects/att/src/public_api.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,24 +30,24 @@ export { AttestationRunsModule } from './lib/runs/attestation-runs.module'; export { AttestationWrapperComponent } from './lib/runs/attestation/attestation-wrapper/attestation-wrapper.component'; export { RunsComponent } from './lib/runs/runs.component'; export { RunsGridComponent } from './lib/runs/runs-grid/runs-grid.component'; -export { PolicyListComponent} from './lib/policies/policy-list/policy-list.component'; -export { EditMasterDataComponent} from './lib/policies/edit-master-data/edit-master-data.component'; -export { AttestationCasesComponent} from './lib/policies/attestation-cases/attestation-cases.component'; -export { AttestationCasesComponentParameter} from './lib/policies/attestation-cases/attestation-cases-component-parameter.interface'; +export { PolicyListComponent } from './lib/policies/policy-list/policy-list.component'; +export { EditMasterDataComponent } from './lib/policies/edit-master-data/edit-master-data.component'; +export { AttestationCasesComponent } from './lib/policies/attestation-cases/attestation-cases.component'; +export { AttestationCasesComponentParameter } from './lib/policies/attestation-cases/attestation-cases-component-parameter.interface'; export { AttestationHistoryModule } from './lib/attestation-history/attestation-history.module'; export { PickCategoryModule } from './lib/pick-category/pick-category.module'; export { AttestationHistoryComponent } from './lib/attestation-history/attestation-history.component'; export { AttestationHistoryService } from './lib/attestation-history/attestation-history.service'; export { AttestationHistoryCase } from './lib/attestation-history/attestation-history-case'; export { AttestationHistoryActionService } from './lib/attestation-history/attestation-history-action.service'; -export { PolicyService} from './lib/policies/policy.service'; +export { PolicyService } from './lib/policies/policy.service'; export { ApiService } from './lib/api.service'; export { canSeeAttestationPolicies } from './lib/admin/permissions-helper'; export { AttestationFeatureGuardService } from './lib/attestation-feature-guard.service'; export { IdentityAttestationService } from './lib/identity-attestation.service'; export { NewUserModule } from './lib/new-user/new-user.module'; export { ClaimDeviceModule } from './lib/claim-device/claim-device.module'; -export {PolicyGroupModule} from './lib/policy-group/policy-group.module'; -export {PolicyGroupListComponent} from './lib/policy-group/policy-group-list/policy-group-list.component'; -export {EditPolicyGroupSidesheetComponent} from './lib/policy-group/edit-policy-group-sidesheet/edit-policy-group-sidesheet.component'; -export {OpenSidesheetComponent} from './lib/new-user/open-sidesheet.component'; +export { PolicyGroupModule } from './lib/policy-group/policy-group.module'; +export { PolicyGroupListComponent } from './lib/policy-group/policy-group-list/policy-group-list.component'; +export { EditPolicyGroupSidesheetComponent } from './lib/policy-group/edit-policy-group-sidesheet/edit-policy-group-sidesheet.component'; +export { OpenSidesheetComponent } from './lib/new-user/open-sidesheet.component'; diff --git a/imxweb/projects/att/src/test.ts b/imxweb/projects/att/src/test.ts index fba6795cd..c0be3629d 100644 --- a/imxweb/projects/att/src/test.ts +++ b/imxweb/projects/att/src/test.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,26 +26,17 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'core-js/es7/reflect'; -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +// Zone must be imported first. Be careful with prettier import organizing +import 'zone.js'; +import 'zone.js/testing'; + import { getTestBed } from '@angular/core/testing'; -import { - BrowserDynamicTestingModule, - platformBrowserDynamicTesting -} from '@angular/platform-browser-dynamic/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; +import 'core-js/es/reflect'; import { TestHelperModule } from 'qbm'; import { AttDefaultMocks } from './default-mocks.spec'; -declare const require: any; - // First, initialize the Angular testing environment. -getTestBed().initTestEnvironment( - [BrowserDynamicTestingModule, TestHelperModule], - platformBrowserDynamicTesting() -); -// Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); -// And load the modules. -context.keys().map(context); -AttDefaultMocks.registerDefaultMocks(); \ No newline at end of file +getTestBed().initTestEnvironment([BrowserDynamicTestingModule, TestHelperModule], platformBrowserDynamicTesting()); + +AttDefaultMocks.registerDefaultMocks(); diff --git a/imxweb/projects/att/tsconfig.lib.json b/imxweb/projects/att/tsconfig.lib.json index b77b13c01..9786008a7 100644 --- a/imxweb/projects/att/tsconfig.lib.json +++ b/imxweb/projects/att/tsconfig.lib.json @@ -5,11 +5,13 @@ "outDir": "../../out-tsc/lib", "declaration": true, "declarationMap": true, + "sourceMap": true, "inlineSources": true, - "types": [] + "types": [], + "strictNullChecks": true }, - "exclude": [ - "src/test.ts", - "**/*.spec.ts" - ] + "angularCompilerOptions": { + "strictTemplates": true + }, + "exclude": ["src/test.ts", "**/*.spec.ts"] } diff --git a/imxweb/projects/att/tsconfig.lib.prod.json b/imxweb/projects/att/tsconfig.lib.prod.json index 61c7592e7..fb40dbbb0 100644 --- a/imxweb/projects/att/tsconfig.lib.prod.json +++ b/imxweb/projects/att/tsconfig.lib.prod.json @@ -3,8 +3,5 @@ "extends": "./tsconfig.lib.json", "compilerOptions": { "declarationMap": false - }, - "angularCompilerOptions": { - "enableIvy": true } } diff --git a/imxweb/projects/att/tsconfig.spec.json b/imxweb/projects/att/tsconfig.spec.json index 16da33db0..6bd74e1f7 100644 --- a/imxweb/projects/att/tsconfig.spec.json +++ b/imxweb/projects/att/tsconfig.spec.json @@ -1,17 +1,10 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "strictNullChecks": false, "outDir": "../../out-tsc/spec", - "types": [ - "jasmine", - "node" - ] + "types": ["jasmine", "node"] }, - "files": [ - "src/test.ts" - ], - "include": [ - "**/*.spec.ts", - "**/*.d.ts" - ] + "files": ["src/test.ts"], + "include": ["**/*.spec.ts", "**/*.d.ts"] } diff --git a/imxweb/projects/att/tslint.json b/imxweb/projects/att/tslint.json deleted file mode 100644 index 8bab15f53..000000000 --- a/imxweb/projects/att/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "imx", - "camelCase" - ], - "component-selector": [ - true, - "element", - "imx", - "kebab-case" - ] - } -} diff --git a/imxweb/projects/cpl/.compodocrc.json b/imxweb/projects/cpl/.compodocrc.json index a54cb8453..6312ead7f 100644 --- a/imxweb/projects/cpl/.compodocrc.json +++ b/imxweb/projects/cpl/.compodocrc.json @@ -1,6 +1,6 @@ { "name": "IMX Web - CPL Library", - "output": "../../documentation/v92/cpl", + "output": "../../documentation/v93/cpl", "theme": "material", "assetsFolder": "../../compodoc/assets", "customLogo": "../../compodoc/assets/images/oneidentity-logo.png", diff --git a/imxweb/projects/cpl/.eslintrc.json b/imxweb/projects/cpl/.eslintrc.json new file mode 100644 index 000000000..be298cb71 --- /dev/null +++ b/imxweb/projects/cpl/.eslintrc.json @@ -0,0 +1,35 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*", "**/*.spec.ts"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": ["imxweb/projects/cpl/tsconfig.lib.json", "imxweb/projects/cpl/tsconfig.spec.json"], + "createDefaultProgram": true + }, + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "imx", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "imx", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "rules": {} + } + ] +} diff --git a/imxweb/projects/cpl/imx-plugin-config.json b/imxweb/projects/cpl/imx-plugin-config.json index ce7c7f144..b20a3f2eb 100644 --- a/imxweb/projects/cpl/imx-plugin-config.json +++ b/imxweb/projects/cpl/imx-plugin-config.json @@ -1,8 +1,9 @@ { - "qer-app-portal": [ - { - "Container": "cpl", - "Name": "CplConfigModule" - } - ] -} \ No newline at end of file + "qer-app-portal": [ + { + "Container": "cpl", + "Condition": "COMPLIANCE", + "Name": "CplConfigModule" + } + ] +} diff --git a/imxweb/projects/cpl/karma.conf.js b/imxweb/projects/cpl/karma.conf.js index d7a9db062..3e53a3ac8 100644 --- a/imxweb/projects/cpl/karma.conf.js +++ b/imxweb/projects/cpl/karma.conf.js @@ -15,7 +15,7 @@ module.exports = function (config) { require('karma-junit-reporter'), ], client: { - clearContext: false // leave Jasmine Spec Runner output visible in browser + clearContext: false, // leave Jasmine Spec Runner output visible in browser }, coverageIstanbulReporter: { dir: require('path').join(__dirname, 'results'), @@ -23,14 +23,14 @@ module.exports = function (config) { fixWebpackSourcePaths: true, 'report-config': { html: { - subdir: 'coverage-html' + subdir: 'coverage-html', }, }, thresholds: { statements: 0, branches: 0, functions: 0, - lines: 0 + lines: 0, }, }, junitReporter: { @@ -40,13 +40,14 @@ module.exports = function (config) { port: 9876, captureTimeout: 210000, browserDisconnectTolerance: 3, - browserDisconnectTimeout : 210000, - browserNoActivityTimeout : 210000, + browserDisconnectTimeout: 210000, + browserNoActivityTimeout: 210000, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], singleRun: false, - restartOnFileChange: true + failOnEmptyTestSuite: false, + restartOnFileChange: true, }); }; diff --git a/imxweb/projects/cpl/ng-package.dynamic.json b/imxweb/projects/cpl/ng-package.dynamic.json index b14f6657f..3bd1e3cc7 100644 --- a/imxweb/projects/cpl/ng-package.dynamic.json +++ b/imxweb/projects/cpl/ng-package.dynamic.json @@ -16,12 +16,11 @@ "@elemental-ui/core", "@ngx-translate/core", "@ngx-translate/http-loader", - "applicationinsights-js", "billboard.js" ], - "dest": "../../html/cpl", + "dest": "../../html/qer-app-portal/cpl", "lib": { "entryFile": "src/public_api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] } } diff --git a/imxweb/projects/cpl/ng-package.json b/imxweb/projects/cpl/ng-package.json index e19d580ba..62e32f54a 100644 --- a/imxweb/projects/cpl/ng-package.json +++ b/imxweb/projects/cpl/ng-package.json @@ -3,6 +3,6 @@ "dest": "../../dist/cpl", "lib": { "entryFile": "src/public_api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] } } diff --git a/imxweb/projects/cpl/package.json b/imxweb/projects/cpl/package.json index 22d4e9b12..4d1ab29df 100644 --- a/imxweb/projects/cpl/package.json +++ b/imxweb/projects/cpl/package.json @@ -1,6 +1,6 @@ { "name": "cpl", - "version": "9.2.1", + "version": "9.3.0", "private": true, "bundledDependencies": [ "imx-api-cpl" diff --git a/imxweb/projects/cpl/project.json b/imxweb/projects/cpl/project.json new file mode 100644 index 000000000..ba9634722 --- /dev/null +++ b/imxweb/projects/cpl/project.json @@ -0,0 +1,64 @@ +{ + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "name": "cpl", + "sourceRoot": "projects/cpl/src", + "implicitDependencies": ["qer"], + "projectType": "library", + "generators": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "prefix": "imx", + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:ng-packagr", + "options": { + "tsConfig": "projects/cpl/tsconfig.lib.json", + "project": "projects/cpl/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "projects/cpl/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "projects/cpl/tsconfig.lib.json" + }, + "dynamic": { + "project": "projects/cpl/ng-package.dynamic.json" + }, + "remote-dev": { + "tsConfig": "projects/cpl/tsconfig.lib.json" + }, + "remote-qs": { + "tsConfig": "projects/cpl/tsconfig.lib.json" + } + }, + "outputs": ["{workspaceRoot}/dist/cpl"] + }, + "test": { + "executor": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/cpl/src/test.ts", + "tsConfig": "projects/cpl/tsconfig.spec.json", + "karmaConfig": "projects/cpl/karma.conf.js", + "stylePreprocessorOptions": { + "includePaths": [ + "./shared/assets", + "./shared/scss", + "./node_modules", + "./node_modules/@elemental-ui/cadence-icon", + "./node_modules/@elemental-ui/core" + ] + } + } + }, + "lint": { + "executor": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": ["projects/cpl/tsconfig.lib.json", "projects/cpl/tsconfig.spec.json"], + "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + } + } + } +} diff --git a/imxweb/projects/cpl/src/lib/api.service.ts b/imxweb/projects/cpl/src/lib/api.service.ts index 688882b3f..63d7ed28c 100644 --- a/imxweb/projects/cpl/src/lib/api.service.ts +++ b/imxweb/projects/cpl/src/lib/api.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,12 +26,12 @@ import { Injectable } from '@angular/core'; -import { V2Client, TypedClient } from 'imx-api-cpl'; -import { ApiClient } from 'imx-qbm-dbts'; +import { V2Client, TypedClient } from '@imx-modules/imx-api-cpl'; +import { ApiClient } from '@imx-modules/imx-qbm-dbts'; import { AppConfigService, ClassloggerService, ImxTranslationProviderService } from 'qbm'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ApiService { private tc: TypedClient; @@ -51,7 +51,8 @@ export class ApiService { constructor( private readonly config: AppConfigService, private readonly logger: ClassloggerService, - private readonly translationProvider: ImxTranslationProviderService) { + private readonly translationProvider: ImxTranslationProviderService, + ) { try { this.logger.debug(this, 'Initializing CPL API service'); diff --git a/imxweb/projects/cpl/src/lib/cpl-config.module.ts b/imxweb/projects/cpl/src/lib/cpl-config.module.ts index f0202e3a7..da2a76823 100644 --- a/imxweb/projects/cpl/src/lib/cpl-config.module.ts +++ b/imxweb/projects/cpl/src/lib/cpl-config.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -50,33 +50,30 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { ComplianceRulesGuardService } from './guards/compliance-rules-guard.service'; import { RuleViolationsGuardService } from './guards/rule-violations-guard.service'; import { MatCardModule } from '@angular/material/card'; -import { RequestModule} from './request/request.module'; +import { RequestModule } from './request/request.module'; const routes: Routes = [ { path: 'compliance/rules', component: RulesComponent, canActivate: [RouteGuardService, ComplianceRulesGuardService], resolve: [RouteGuardService], - data:{ - contextId: HELP_CONTEXTUAL.ComplianceRules - } + data: { + contextId: HELP_CONTEXTUAL.ComplianceRules, + }, }, { path: 'compliance/rulesviolations/approve', component: RulesViolationsComponent, canActivate: [RouteGuardService, RuleViolationsGuardService], resolve: [RouteGuardService], - data:{ - contextId: HELP_CONTEXTUAL.ComplianceRulesViolationsApprove - } + data: { + contextId: HELP_CONTEXTUAL.ComplianceRulesViolationsApprove, + }, }, ]; @NgModule({ - declarations: [ - DashboardPluginComponent, - CartItemComplianceCheckComponent, - ], + declarations: [DashboardPluginComponent, CartItemComplianceCheckComponent], imports: [ CommonModule, CdrModule, @@ -96,11 +93,14 @@ const routes: Routes = [ TilesModule, TranslateModule, EuiCoreModule, - IdentityRuleViolationsModule + IdentityRuleViolationsModule, ], }) export class CplConfigModule { - constructor(private readonly initializer: InitService, private readonly logger: ClassloggerService) { + constructor( + private readonly initializer: InitService, + private readonly logger: ClassloggerService, + ) { this.logger.info(this, '🔥 CPL loaded'); this.initializer.onInit(routes); this.logger.info(this, '▶︝ CPL initialized'); diff --git a/imxweb/projects/cpl/src/lib/dashboard-plugin/dashboard-plugin.component.html b/imxweb/projects/cpl/src/lib/dashboard-plugin/dashboard-plugin.component.html index 00dbfd4ae..11d748799 100644 --- a/imxweb/projects/cpl/src/lib/dashboard-plugin/dashboard-plugin.component.html +++ b/imxweb/projects/cpl/src/lib/dashboard-plugin/dashboard-plugin.component.html @@ -1,5 +1,9 @@ - - \ No newline at end of file + + diff --git a/imxweb/projects/cpl/src/lib/dashboard-plugin/dashboard-plugin.component.ts b/imxweb/projects/cpl/src/lib/dashboard-plugin/dashboard-plugin.component.ts index 9610327ad..0c3db4182 100644 --- a/imxweb/projects/cpl/src/lib/dashboard-plugin/dashboard-plugin.component.ts +++ b/imxweb/projects/cpl/src/lib/dashboard-plugin/dashboard-plugin.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,23 +31,20 @@ import { DashboardService, PendingItemsType, UserModelService } from 'qer'; import { CplPermissionsService } from '../rules/admin/cpl-permissions.service'; @Component({ - templateUrl: './dashboard-plugin.component.html' + templateUrl: './dashboard-plugin.component.html', }) export class DashboardPluginComponent implements OnInit { - public pendingItems: PendingItemsType; - public isExceptionAdmin = false; constructor( public readonly router: Router, private readonly dashboardService: DashboardService, private readonly permissionService: CplPermissionsService, - private readonly userModelSvc: UserModelService - ) { } + private readonly userModelSvc: UserModelService, + ) {} public async ngOnInit(): Promise { - const busy = this.dashboardService.beginBusy(); try { diff --git a/imxweb/projects/cpl/src/lib/guards/compliance-rules-guard.service.ts b/imxweb/projects/cpl/src/lib/guards/compliance-rules-guard.service.ts index 8a1d054f7..cc8ed34d0 100644 --- a/imxweb/projects/cpl/src/lib/guards/compliance-rules-guard.service.ts +++ b/imxweb/projects/cpl/src/lib/guards/compliance-rules-guard.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { Injectable } from '@angular/core'; -import { CanActivate, Router } from '@angular/router'; +import { Router } from '@angular/router'; import { AppConfigService } from 'qbm'; import { CplPermissionsService } from '../rules/admin/cpl-permissions.service'; @@ -33,17 +33,17 @@ import { CplPermissionsService } from '../rules/admin/cpl-permissions.service'; @Injectable({ providedIn: 'root', }) -export class ComplianceRulesGuardService implements CanActivate { +export class ComplianceRulesGuardService { constructor( private readonly permissionService: CplPermissionsService, private readonly appConfig: AppConfigService, - private readonly router: Router - ) { } + private readonly router: Router, + ) {} public async canActivate(): Promise { const userRuleStatistics = await this.permissionService.isRuleStatistics(); if (!userRuleStatistics) { - this.router.navigate([this.appConfig.Config.routeConfig.start], { queryParams: {} }); + this.router.navigate([this.appConfig.Config.routeConfig?.start], { queryParams: {} }); return false; } return true; diff --git a/imxweb/projects/cpl/src/lib/guards/rule-violations-guard.service.ts b/imxweb/projects/cpl/src/lib/guards/rule-violations-guard.service.ts index fdb529509..d9d8e6aa9 100644 --- a/imxweb/projects/cpl/src/lib/guards/rule-violations-guard.service.ts +++ b/imxweb/projects/cpl/src/lib/guards/rule-violations-guard.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { Injectable } from '@angular/core'; -import { CanActivate, Router } from '@angular/router'; +import { Router } from '@angular/router'; import { AppConfigService } from 'qbm'; import { CplPermissionsService } from '../rules/admin/cpl-permissions.service'; @@ -33,17 +33,17 @@ import { CplPermissionsService } from '../rules/admin/cpl-permissions.service'; @Injectable({ providedIn: 'root', }) -export class RuleViolationsGuardService implements CanActivate { +export class RuleViolationsGuardService { constructor( private readonly permissionService: CplPermissionsService, private readonly appConfig: AppConfigService, - private readonly router: Router - ) { } + private readonly router: Router, + ) {} public async canActivate(): Promise { const isExceptionAdmin = await this.permissionService.isExceptionAdmin(); if (!isExceptionAdmin) { - this.router.navigate([this.appConfig.Config.routeConfig.start], { queryParams: {} }); + this.router.navigate([this.appConfig.Config.routeConfig?.start], { queryParams: {} }); return false; } return isExceptionAdmin; diff --git a/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations-mitigation-control/identity-rule-violations-mitigation-control.component.html b/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations-mitigation-control/identity-rule-violations-mitigation-control.component.html index 54403ed33..61ed39837 100644 --- a/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations-mitigation-control/identity-rule-violations-mitigation-control.component.html +++ b/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations-mitigation-control/identity-rule-violations-mitigation-control.component.html @@ -1,9 +1,13 @@
    - - - + + -
    \ No newline at end of file +
    diff --git a/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations-mitigation-control/identity-rule-violations-mitigation-control.component.scss b/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations-mitigation-control/identity-rule-violations-mitigation-control.component.scss index e110d11b2..248a268c5 100644 --- a/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations-mitigation-control/identity-rule-violations-mitigation-control.component.scss +++ b/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations-mitigation-control/identity-rule-violations-mitigation-control.component.scss @@ -1 +1 @@ -// add styles if necessary \ No newline at end of file +// add styles if necessary diff --git a/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations-mitigation-control/identity-rule-violations-mitigation-control.component.ts b/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations-mitigation-control/identity-rule-violations-mitigation-control.component.ts index 2ed7d93b7..1edae39eb 100644 --- a/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations-mitigation-control/identity-rule-violations-mitigation-control.component.ts +++ b/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations-mitigation-control/identity-rule-violations-mitigation-control.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,28 +24,33 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; import { Component, Inject, OnInit } from '@angular/core'; -import { EuiLoadingService, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; -import { CollectionLoadParameters, EntitySchema, ExtendedTypedEntityCollection, IClientProperty, TypedEntity } from 'imx-qbm-dbts'; +import { EUI_SIDESHEET_DATA, EuiLoadingService } from '@elemental-ui/core'; +import { + CollectionLoadParameters, + EntitySchema, + ExtendedTypedEntityCollection, + IClientProperty, + TypedEntity, +} from '@imx-modules/imx-qbm-dbts'; import { DataSourceToolbarSettings } from 'qbm'; @Component({ selector: 'imx-identity-rule-violations-mitigation-control', templateUrl: './identity-rule-violations-mitigation-control.component.html', - styleUrls: ['./identity-rule-violations-mitigation-control.component.scss'] + styleUrls: ['./identity-rule-violations-mitigation-control.component.scss'], }) export class IdentityRuleViolationsMitigationControlComponent implements OnInit { - public dstSettings: DataSourceToolbarSettings; constructor( private readonly busy: EuiLoadingService, - @Inject(EUI_SIDESHEET_DATA) public data: { - getData: (param: CollectionLoadParameters) => Promise>, - entitySchema: EntitySchema, - displayedColumns: IClientProperty[] - } - ) { } + @Inject(EUI_SIDESHEET_DATA) + public data: { + getData: (param: CollectionLoadParameters) => Promise>; + entitySchema: EntitySchema; + displayedColumns: IClientProperty[]; + }, + ) {} public async ngOnInit(): Promise { this.getData({}); @@ -56,23 +61,20 @@ export class IdentityRuleViolationsMitigationControlComponent implements OnInit } public async getData(param: CollectionLoadParameters): Promise { - let overlay: OverlayRef; - - setTimeout(() => { overlay = this.busy.show(); }); + if (this.busy.overlayRefs.length === 0) { + this.busy.show(); + } try { - const dataSource = await this.data.getData(param); this.dstSettings = { displayedColumns: this.data.displayedColumns, dataSource, entitySchema: this.data.entitySchema, - navigationState: param + navigationState: param, }; } finally { - setTimeout(() => this.busy.hide(overlay)); + this.busy.hide(); } - } - } diff --git a/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations.component.html b/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations.component.html index 4e9aea876..8ba15ad21 100644 --- a/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations.component.html +++ b/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations.component.html @@ -1,10 +1,14 @@ -
    +
    - +
    - +
    {{ item.GetEntity().GetDisplay() }}
    @@ -13,18 +17,27 @@
    - + - + (click)="onShowDetails(dataItem)" + > + {{ '#LDS#View mitigating controls' | translate }} +
    -
    \ No newline at end of file +
    diff --git a/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations.component.scss b/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations.component.scss index 5fd06df4e..431c18088 100644 --- a/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations.component.scss +++ b/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations.component.scss @@ -1,7 +1,6 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; - :host { overflow: hidden; display: flex; @@ -16,40 +15,13 @@ overflow: auto; } -.imx-card-header { - margin-top: 10px; - background-color: $white; - border-top-left-radius: 4px; - border-top-right-radius: 4px; - z-index: 100; - border: 1px solid rgba($iris-blue, 0.6); - - .imx-card-header-bg { - border-top-left-radius: 4px; - border-top-right-radius: 4px; - background-color: $iris-pastel; - padding: 10px 24px; - display: flex; - align-items: center; - justify-content: flex-start; - - .eui-icon { - color: rgba($iris-blue, 0.6); - margin-right: 10px; - } - - & > span { - font-size: 20px; - } - } -} - .imx-content { height: 100%; display: flex; flex-direction: column; flex: 1 1 auto; overflow-y: auto; + padding: 20px; .imx-rule-violation-table-container { overflow: hidden; @@ -58,19 +30,3 @@ flex: 1 1 auto; } } - -.eui-dark-theme { - :host { - .imx-card-header { - background-color: $color-gray-80; - } - } -} - -.eui-contrast-theme { - :host { - .imx-card-header { - background-color: $color-gray-100; - } - } -} diff --git a/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations.component.ts b/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations.component.ts index 9d0fb7764..aeab2a6b2 100644 --- a/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations.component.ts +++ b/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,17 +24,17 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; import { Component, OnInit } from '@angular/core'; import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; +import { PortalPersonRolemembershipsNoncompliance } from '@imx-modules/imx-api-cpl'; import { TranslateService } from '@ngx-translate/core'; -import { PortalPersonRolemembershipsNoncompliance } from 'imx-api-cpl'; -import { CollectionLoadParameters, DisplayColumns, EntitySchema, ValType } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, DisplayColumns, EntitySchema, ValType } from '@imx-modules/imx-qbm-dbts'; import { + calculateSidesheetWidth, + ClientPropertyForTableColumns, DataSourceToolbarSettings, DynamicTabDataProviderDirective, - ClientPropertyForTableColumns, MetadataService, SettingsService, } from 'qbm'; @@ -49,7 +49,7 @@ export class IdentityRuleViolationsComponent implements OnInit { public dstSettings: DataSourceToolbarSettings; public readonly DisplayColumns = DisplayColumns; public displayedColumns: ClientPropertyForTableColumns[]; - public caption: string; + public caption: string | undefined; public entitySchema: EntitySchema; private referrer: { objectuid: string; objecttable: string }; @@ -83,7 +83,7 @@ export class IdentityRuleViolationsComponent implements OnInit { } finally { this.busyService.hide(overlay); } - this.caption = this.metadataService.tables[this.referrer.objecttable].Display; + this.caption = this.metadataService.tables[this.referrer.objecttable]?.Display; return this.getData(); } @@ -113,7 +113,7 @@ export class IdentityRuleViolationsComponent implements OnInit { title: await this.translate.get('#LDS#Heading View Mitigating Controls').toPromise(), subTitle: entity.GetEntity().GetDisplay(), padding: '0px', - width: 'max(600px,60%)', + width: calculateSidesheetWidth(), disableClose: false, testId: 'identity-rule-violation-mitigating-sidesheet', data, @@ -134,8 +134,9 @@ export class IdentityRuleViolationsComponent implements OnInit { } private async getData(): Promise { - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.busyService.show())); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } try { const dataSource = await this.roleMembershipsService.getNonCompliance(this.referrer.objectuid, this.navigationState); @@ -146,7 +147,7 @@ export class IdentityRuleViolationsComponent implements OnInit { navigationState: this.navigationState, }; } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } } } diff --git a/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations.module.ts b/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations.module.ts index c03063640..2187f0518 100644 --- a/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations.module.ts +++ b/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -37,14 +37,6 @@ import { MatCardModule } from '@angular/material/card'; @NgModule({ declarations: [IdentityRuleViolationsComponent, IdentityRuleViolationsMitigationControlComponent], - imports: [ - CommonModule, - EuiCoreModule, - DataSourceToolbarModule, - DataTableModule, - TranslateModule, - MatButtonModule, - MatCardModule - ] + imports: [CommonModule, EuiCoreModule, DataSourceToolbarModule, DataTableModule, TranslateModule, MatButtonModule, MatCardModule], }) -export class IdentityRuleViolationsModule { } +export class IdentityRuleViolationsModule {} diff --git a/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations.service.ts b/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations.service.ts index b2ff2a91d..f0428deb1 100644 --- a/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations.service.ts +++ b/imxweb/projects/cpl/src/lib/identity-rule-violations/identity-rule-violations.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,19 +25,21 @@ */ import { Injectable } from '@angular/core'; -import { ComplianceFeatureConfig, PortalPersonMitigatingcontrols, PortalPersonRolemembershipsNoncompliance, PortalRulesMitigatingcontrols } from 'imx-api-cpl'; -import { CollectionLoadParameters, EntitySchema, ExtendedTypedEntityCollection } from 'imx-qbm-dbts'; +import { + ComplianceFeatureConfig, + PortalPersonMitigatingcontrols, + PortalPersonRolemembershipsNoncompliance, + PortalRulesMitigatingcontrols, +} from '@imx-modules/imx-api-cpl'; +import { CollectionLoadParameters, EntitySchema, ExtendedTypedEntityCollection } from '@imx-modules/imx-qbm-dbts'; import { ApiService } from '../api.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class IdentityRuleViolationService { - - - constructor(private readonly api: ApiService) { - } + constructor(private readonly api: ApiService) {} public get nonComplianceSchema(): EntitySchema { return this.api.typedClient.PortalPersonRolemembershipsNoncompliance.GetSchema(); @@ -51,8 +53,10 @@ export class IdentityRuleViolationService { return this.api.typedClient.PortalRulesMitigatingcontrols.GetSchema(); } - public async getNonCompliance(uidPerson: string, parameter: CollectionLoadParameters) - : Promise> { + public async getNonCompliance( + uidPerson: string, + parameter: CollectionLoadParameters, + ): Promise> { return this.api.typedClient.PortalPersonRolemembershipsNoncompliance.Get(uidPerson, parameter); } @@ -60,13 +64,18 @@ export class IdentityRuleViolationService { return this.api.client.portal_compliance_config_get(); } - public async getPersonMitigatingcontrols(uidComplianceRule: string, uidPerson: string, param: CollectionLoadParameters) - : Promise> { + public async getPersonMitigatingcontrols( + uidComplianceRule: string, + uidPerson: string, + param: CollectionLoadParameters, + ): Promise> { return this.api.typedClient.PortalPersonMitigatingcontrols.Get(uidPerson, uidComplianceRule, param); } - public async getRulesMitigatingcontrols(uidComplianceRule: string, param: CollectionLoadParameters) - : Promise> { + public async getRulesMitigatingcontrols( + uidComplianceRule: string, + param: CollectionLoadParameters, + ): Promise> { return this.api.typedClient.PortalRulesMitigatingcontrols.Get(uidComplianceRule, param); } } diff --git a/imxweb/projects/cpl/src/lib/init.service.ts b/imxweb/projects/cpl/src/lib/init.service.ts index da5edf4e8..cf542e40e 100644 --- a/imxweb/projects/cpl/src/lib/init.service.ts +++ b/imxweb/projects/cpl/src/lib/init.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,20 +25,19 @@ */ import { Injectable } from '@angular/core'; -import { Router, Route } from '@angular/router'; -import { IdentityRoleMembershipsService, NotificationRegistryService, ShoppingCartValidationDetailService } from 'qer'; +import { Route, Router } from '@angular/router'; +import { NotificationRegistryService, ShoppingCartValidationDetailService } from 'qer'; -import { ExtService, MenuItem, MenuService, TabItem} from 'qbm'; +import { ExtService, MenuItem, MenuService, TabItem } from 'qbm'; import { DashboardPluginComponent } from './dashboard-plugin/dashboard-plugin.component'; +import { IdentityRuleViolationsComponent } from './identity-rule-violations/identity-rule-violations.component'; import { CartItemComplianceCheckComponent } from './item-validator/cart-item-compliance-check/cart-item-compliance-check.component'; -import { isRuleStatistics } from './rules/admin/permissions-helper'; import { RequestRuleViolation } from './request/request-rule-violation'; import { RequestRuleViolationDetail } from './request/request-rule-violation-detail'; -import { RoleComplianceViolationsService } from './role-compliance-violations/role-compliance-violations.service'; import { RoleComplianceViolationsComponent } from './role-compliance-violations/role-compliance-violations.component'; -import { ApiService } from './api.service'; -import { IdentityRuleViolationsComponent } from './identity-rule-violations/identity-rule-violations.component'; +import { RoleComplianceViolationsService } from './role-compliance-violations/role-compliance-violations.service'; +import { isRuleStatistics } from './rules/admin/permissions-helper'; @Injectable({ providedIn: 'root' }) export class InitService { @@ -46,11 +45,9 @@ export class InitService { private readonly extService: ExtService, private readonly router: Router, private readonly menuService: MenuService, - private readonly api: ApiService, private readonly cplService: RoleComplianceViolationsService, private readonly notificationService: NotificationRegistryService, private readonly validationDetailService: ShoppingCartValidationDetailService, - private readonly identityRoleMembershipService: IdentityRoleMembershipsService ) { this.setupMenu(); } @@ -73,12 +70,12 @@ export class InitService { this.extService.register('identitySidesheet', { instance: IdentityRuleViolationsComponent, - inputData: - { + inputData: { id: 'NonCompliance', label: '#LDS#Heading Rule Violations', - checkVisibility: async _ => true - }, sortOrder: 20 + checkVisibility: async (_) => true, + }, + sortOrder: 20, } as TabItem); this.validationDetailService.register(CartItemComplianceCheckComponent, 'CartItemComplianceCheck'); @@ -86,9 +83,8 @@ export class InitService { this.notificationService.registerRedirectNotificationHandler({ id: 'OpenNonCompliance', message: '#LDS#There are new rule violations for which you can grant or deny exceptions.', - route: 'compliance/rulesviolations/approve' + route: 'compliance/rulesviolations/approve', }); - } private async checkCompliances(referrer: any): Promise { @@ -115,10 +111,10 @@ export class InitService { private setupMenu(): void { this.menuService.addMenuFactories((preProps: string[], features: string[]) => { if (!preProps.includes('COMPLIANCE') || !isRuleStatistics(features)) { - return null; + return undefined; } - const items = []; + const items: MenuItem[] = []; if (isRuleStatistics(features)) { items.push({ diff --git a/imxweb/projects/cpl/src/lib/item-validator/cart-item-compliance-check/cart-item-compliance-check.component.html b/imxweb/projects/cpl/src/lib/item-validator/cart-item-compliance-check/cart-item-compliance-check.component.html index 3e5932fcc..bcd6f8193 100644 --- a/imxweb/projects/cpl/src/lib/item-validator/cart-item-compliance-check/cart-item-compliance-check.component.html +++ b/imxweb/projects/cpl/src/lib/item-validator/cart-item-compliance-check/cart-item-compliance-check.component.html @@ -1 +1,3 @@ - + diff --git a/imxweb/projects/cpl/src/lib/item-validator/cart-item-compliance-check/cart-item-compliance-check.component.ts b/imxweb/projects/cpl/src/lib/item-validator/cart-item-compliance-check/cart-item-compliance-check.component.ts index b460803e2..8fcbf1e34 100644 --- a/imxweb/projects/cpl/src/lib/item-validator/cart-item-compliance-check/cart-item-compliance-check.component.ts +++ b/imxweb/projects/cpl/src/lib/item-validator/cart-item-compliance-check/cart-item-compliance-check.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,7 +28,8 @@ import { Component } from '@angular/core'; import { EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { ICartItemCheck } from 'imx-api-qer'; +import { ICartItemCheck } from '@imx-modules/imx-api-qer'; +import { calculateSidesheetWidth } from 'qbm'; import { ComplianceViolationDetailsComponent } from '../../request/compliance-violation-details/compliance-violation-details.component'; @Component({ @@ -40,18 +41,17 @@ export class CartItemComplianceCheckComponent { public check: ICartItemCheck; constructor( - private readonly sidesheetService: EuiSidesheetService, - private readonly translateService: TranslateService + private readonly sidesheetService: EuiSidesheetService, + private readonly translateService: TranslateService, ) {} public async onOpenDetails(): Promise { this.sidesheetService.open(ComplianceViolationDetailsComponent, { title: await this.translateService.get('#LDS#Heading View Rule Violation Details').toPromise(), - width: 'max(550px,50%)', + width: calculateSidesheetWidth(800, 0.5), padding: '0px', data: this.check, testId: 'cart-item-compliance-violation-details', }); } } - diff --git a/imxweb/projects/cpl/src/lib/item-validator/item-validator.service.ts b/imxweb/projects/cpl/src/lib/item-validator/item-validator.service.ts index cf4198ebb..89ed2709c 100644 --- a/imxweb/projects/cpl/src/lib/item-validator/item-validator.service.ts +++ b/imxweb/projects/cpl/src/lib/item-validator/item-validator.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,18 +25,18 @@ */ import { Injectable } from '@angular/core'; -import { PortalRules } from 'imx-api-cpl'; -import { EntitySchema, ExtendedTypedEntityCollection } from 'imx-qbm-dbts'; +import { PortalRules } from '@imx-modules/imx-api-cpl'; +import { EntitySchema, ExtendedTypedEntityCollection } from '@imx-modules/imx-qbm-dbts'; import { ApiService } from '../api.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ItemValidatorService { - constructor(private readonly api: ApiService) { } + constructor(private readonly api: ApiService) {} public async getRules(): Promise> { - return await this.api.typedClient.PortalRules.Get({PageSize: 1000}); + return await this.api.typedClient.PortalRules.Get({ PageSize: 1000 }); } public getRulesSchema(): EntitySchema { diff --git a/imxweb/projects/cpl/src/lib/mitigating-control-container/mitigating-control-container.component.html b/imxweb/projects/cpl/src/lib/mitigating-control-container/mitigating-control-container.component.html index bfa1a3680..88fd4194e 100644 --- a/imxweb/projects/cpl/src/lib/mitigating-control-container/mitigating-control-container.component.html +++ b/imxweb/projects/cpl/src/lib/mitigating-control-container/mitigating-control-container.component.html @@ -1,10 +1,10 @@
    -
    +
    -
    +
    -
    +
    @@ -45,8 +45,7 @@ [matTooltip]="'#LDS#Assigns a mitigating control' | translate" data-imx-identifier="mitigating-control-container-button-add" [attr.aria-label]="'#LDS#Assign mitigating control' | translate" - mat-button - class="mat-icon-button" + mat-icon-button (click)="onCreateControl()" > diff --git a/imxweb/projects/cpl/src/lib/mitigating-control-container/mitigating-control-container.component.ts b/imxweb/projects/cpl/src/lib/mitigating-control-container/mitigating-control-container.component.ts index 5e504d199..8c1d75dde 100644 --- a/imxweb/projects/cpl/src/lib/mitigating-control-container/mitigating-control-container.component.ts +++ b/imxweb/projects/cpl/src/lib/mitigating-control-container/mitigating-control-container.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,11 +24,11 @@ * */ -import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core'; import { FormArray } from '@angular/forms'; import { EuiSelectOption } from '@elemental-ui/core'; import { ConfirmationService } from 'qbm'; -import { MitigatingControlData } from '../request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/mitigating-control-data.interface'; +import { ExtendedDeferredOperationsData } from '../request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/extended-deferred-operations-data'; import { RequestMitigatingControls } from '../request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/request-mitigating-controls'; import { PersonMitigatingControls } from '../rules-violations/mitigating-controls-person/person-mitigating-controls'; @@ -38,34 +38,50 @@ import { PersonMitigatingControls } from '../rules-violations/mitigating-control styleUrls: ['../mitigating-controls-common.scss'], }) export class MitigatingControlContainerComponent { - @Input() public mControls: MitigatingControlData[] | PersonMitigatingControls[] = []; + @Input() public mControls: Array = []; @Input() public mitigatingCaption: string; @Input() public formArray: FormArray; @Input() public options: EuiSelectOption[] = []; - @Output() public controlDeleted = new EventEmitter(); + @Output() public controlDeleted = new EventEmitter(); @Output() public controlsRequested = new EventEmitter(); - constructor(private readonly cd: ChangeDetectorRef, private readonly confirmationService: ConfirmationService) {} + constructor( + private readonly cd: ChangeDetectorRef, + private readonly confirmationService: ConfirmationService, + ) {} - public async onSelectionChange(mcontrol: RequestMitigatingControls | PersonMitigatingControls, value: string): Promise { - mcontrol.UID_MitigatingControl.value = value; + public filter = (option: EuiSelectOption, searchInputValue: string): boolean => + option.value.toString().toUpperCase() === searchInputValue.toUpperCase(); + + public async onSelectionChange( + mcontrol: RequestMitigatingControls | ExtendedDeferredOperationsData | PersonMitigatingControls, + option: EuiSelectOption | EuiSelectOption[], + ): Promise { + // Multiple is set to false so there is no array of options + mcontrol.uidMitigatingControl = (option as EuiSelectOption).value; this.formArray.updateValueAndValidity(); this.cd.detectChanges(); return; } - public onOpenChange(isopen: boolean, mControl: RequestMitigatingControls | PersonMitigatingControls): void { + public onOpenChange( + isopen: boolean, + mControl: RequestMitigatingControls | ExtendedDeferredOperationsData | PersonMitigatingControls, + ): void { if (!isopen) { - mControl.formControl.updateValueAndValidity({ onlySelf: true }); + mControl?.formControl.updateValueAndValidity({ onlySelf: true }); } } - public async onDelete(mControl: RequestMitigatingControls | PersonMitigatingControls | undefined, index: number): Promise { - if (mControl.UID_MitigatingControl.value !== '' && !(await this.confirmationService.confirmDelete())) { + public async onDelete( + mControl: RequestMitigatingControls | ExtendedDeferredOperationsData | PersonMitigatingControls | undefined, + index: number, + ): Promise { + if (mControl?.uidMitigatingControl !== '' && !(await this.confirmationService.confirmDelete())) { return; } - if (mControl.GetEntity().GetKeys() != null) { + if (!(mControl instanceof ExtendedDeferredOperationsData) && mControl?.GetEntity().GetKeys() != null) { this.controlDeleted.emit(mControl); } this.mControls.splice(index, 1); diff --git a/imxweb/projects/cpl/src/lib/mitigating-control-container/mitigating-control-container.module.ts b/imxweb/projects/cpl/src/lib/mitigating-control-container/mitigating-control-container.module.ts index a9f28a9e3..f49bcb5e4 100644 --- a/imxweb/projects/cpl/src/lib/mitigating-control-container/mitigating-control-container.module.ts +++ b/imxweb/projects/cpl/src/lib/mitigating-control-container/mitigating-control-container.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -33,7 +33,7 @@ import { CdrModule } from 'qbm'; @NgModule({ declarations: [MitigatingControlContainerComponent], - imports: [CommonModule, EuiCoreModule,EuiMaterialModule, CdrModule, TranslateModule.forChild()], + imports: [CommonModule, EuiCoreModule, EuiMaterialModule, CdrModule, TranslateModule.forChild()], exports: [MitigatingControlContainerComponent], }) export class MitigatingControlContainerModule {} diff --git a/imxweb/projects/cpl/src/lib/mitigating-controls-common.scss b/imxweb/projects/cpl/src/lib/mitigating-controls-common.scss index 0cfe10cae..4de661ee8 100644 --- a/imxweb/projects/cpl/src/lib/mitigating-controls-common.scss +++ b/imxweb/projects/cpl/src/lib/mitigating-controls-common.scss @@ -1,154 +1 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; - -:host { - height: 100%; - display: flex; - flex-direction: column; - justify-content: space-between; - - .eui-sidesheet-actions { - padding: 16px 20px; - display: flex; - justify-content: flex-end; - } - - .imx-helper-alert { - margin-bottom: 15px; - } - - .control-container { - display: flex; - flex-direction: column; - overflow: auto; - } - - .mitigating-control { - margin-bottom: 15px; - display: flex; - justify-content: space-between; - align-items: center; - - .imx-delete-button { - margin-top: 15px; - align-self: flex-start; - } - - .mitigating-control-properties { - display: flex; - flex-direction: column; - width: 100%; - margin-right: 15px; - } - } - - .imx-no-mitigating-controls { - flex: 1 1 auto; - margin: 3px; - display: flex; - align-items: center; - justify-content: center; - } - - .imx-mitigating-control-content { - flex: 1 1 auto; - margin: 3px; - display: flex; - flex-direction: column; - overflow: hidden; - - .content { - overflow: auto; - display: flex; - flex: 1 1 auto; - flex-direction: column; - } - } - - .imx-data-no-results { - text-align: center; - margin: 20px 0; - justify-self: center; - - .eui-icon { - font-size: 100px; - } - - p { - margin: 0; - font-size: 18px; - } - - button { - margin-top: 10px; - } - } - - .button-actions { - display: flex; - justify-content: space-between; - margin-top: 20px; - } -} - -// Theming -:host { - .imx-data-no-results { - .eui-icon { - color: $color-gray-10; - } - - p { - color: $color-gray-50; - } - } - - .imx-no-mitigating-controls { - color: $color-gray-50; - } - - .mitigating-control { - border-bottom: 1px solid $color-gray-50; - } -} - -.eui-dark-theme { - :host { - .imx-data-no-results { - .eui-icon { - color: $color-gray-20; - } - - p { - color: $color-gray-10; - } - } - .imx-no-mitigating-controls { - color: $color-gray-10; - } - - .mitigating-control { - border-bottom: 1px solid $color-gray-10; - } - } -} - -.eui-contrast-theme { - :host { - .imx-data-no-results { - .eui-icon { - color: $color-gray-10; - } - - p { - color: $color-gray-0; - } - } - .imx-no-mitigating-controls { - color: $color-gray-0; - } - - .mitigating-control { - border-bottom: 1px solid $color-gray-0; - } - } -} +@import 'common/mitigating-controls'; diff --git a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/compliance-violation-details.component.html b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/compliance-violation-details.component.html index 54f4e2c2f..c09a0d01d 100644 --- a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/compliance-violation-details.component.html +++ b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/compliance-violation-details.component.html @@ -1,9 +1,11 @@
    - + {{ - (rules.length > 1 ? '#LDS#Here you can get an overview of the rule violations this request may cause.' : '#LDS#Here you can see the rule violation this request may cause.') - | translate + (rules.length > 1 + ? '#LDS#Here you can get an overview of the rule violations this request may cause.' + : '#LDS#Here you can see the rule violation this request may cause.' + ) | translate }} @@ -19,13 +21,27 @@ - - - - @@ -34,13 +50,13 @@
    -
    +

    {{ '#LDS#No data' | translate }}

    + + {{ '#LDS#Assigning this entitlement will cause a rule violation.' | translate }} + diff --git a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/compliance-violation-details.component.scss b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/compliance-violation-details.component.scss index 5dfdd652f..28e6effd5 100644 --- a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/compliance-violation-details.component.scss +++ b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/compliance-violation-details.component.scss @@ -55,21 +55,14 @@ hyphens: auto; } - -.mat-card{ - .mat-divider-horizontal { - position: inherit; - } -} - -.imx-accordion .mat-expansion-panel-header-title{ +.imx-accordion .mat-expansion-panel-header-title { flex-basis: 70%; } .imx-accordion .mat-expansion-panel-header-description { flex-basis: 30%; } -.imx-accordion .mat-expansion-panel-header-title{ +.imx-accordion .mat-expansion-panel-header-title { font-weight: bold; } @@ -79,15 +72,10 @@ color: $black-9; } -.imx-no-data { +.imx-no-results { text-align: center; margin: 20px 0; - .eui-icon { - font-size: 100px; - color: rgba($black-c, 0.55); - } - p { margin: 0; font-size: 18px; diff --git a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/compliance-violation-details.component.ts b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/compliance-violation-details.component.ts index fb6a1726d..fcec1d345 100644 --- a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/compliance-violation-details.component.ts +++ b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/compliance-violation-details.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,16 +25,16 @@ */ import { Component, Inject, Input, OnInit } from '@angular/core'; -import { EuiLoadingService, EuiSidesheetRef, EuiSidesheetService, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { EUI_SIDESHEET_DATA, EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { ComplianceViolation, ContributingEntitlement, PortalRules } from 'imx-api-cpl'; -import { ICartItemCheck } from 'imx-api-qer'; -import { DbObjectKey, EntityData, EntitySchema, IEntityColumn, ValType } from 'imx-qbm-dbts'; -import { BaseReadonlyCdr, ColumnDependentReference, EntityService, MetadataService, SystemInfoService } from 'qbm'; +import { ComplianceViolation, ContributingEntitlement, PortalRules } from '@imx-modules/imx-api-cpl'; +import { ICartItemCheck } from '@imx-modules/imx-api-qer'; +import { DbObjectKey, EntityData, EntitySchema, IEntityColumn, ValType } from '@imx-modules/imx-qbm-dbts'; +import { BaseReadonlyCdr, calculateSidesheetWidth, ColumnDependentReference, EntityService, MetadataService, SystemInfoService } from 'qbm'; import { RequestParameterDataEntity } from 'qer'; -import { ApplicableRule, RuleCdrs } from '../compliance-violation-model'; import { ItemValidatorService } from '../../item-validator/item-validator.service'; +import { ApplicableRule, RuleCdrs } from '../compliance-violation-model'; import { ComplianceViolationService } from './compliance-violation.service'; import { EditMitigatingControlsComponent } from './edit-mitigating-controls/edit-mitigating-controls.component'; @@ -74,7 +74,7 @@ export class ComplianceViolationDetailsComponent implements OnInit { try { this.schema = this.validator.getRulesSchema(); this.isICartItemCheck(this.data) ? await this.loadCartItemViolations(this.data) : await this.loadRequestViolations(this.pwoId); - this.hasRiskIndex = (await this.systemInfoService.get()).PreProps.includes('RISKINDEX'); + this.hasRiskIndex = !!(await this.systemInfoService.get()).PreProps?.includes('RISKINDEX'); this.checkHistoryForViolations(); this.mitigatingControlsPerViolation = await this.complianceApi.getMitigatingControlsPerViolation(); @@ -83,6 +83,15 @@ export class ComplianceViolationDetailsComponent implements OnInit { } } + /** + * Check for org type violations, we disable the mitigation control settings for them + * @param item + * @returns if we can access the EditMitigatingControlsComponent + */ + public canEditMitigationControls(item: ApplicableRule): boolean { + return item.violationDetail.ViolationType ? !['Org'].includes(item.violationDetail.ViolationType) : !!item.violationDetail.UidPerson; + } + public async addMitigationControls(item: ApplicableRule): Promise { this.sidesheets .open(EditMitigatingControlsComponent, { @@ -90,7 +99,7 @@ export class ComplianceViolationDetailsComponent implements OnInit { subTitle: item.violationDetail.DisplayRule, padding: '0px', disableClose: true, - width: 'max(700px, 60%)', + width: calculateSidesheetWidth(1000), testId: 'compliance-violation-details-mitigating-sidesheet', data: { uidPerson: item.violationDetail.UidPerson, @@ -105,15 +114,17 @@ export class ComplianceViolationDetailsComponent implements OnInit { } private getDisplayForSource(item: ContributingEntitlement): string { - return this.metaData.tables[DbObjectKey.FromXml(item.ObjectKeyEntitlement).TableName]?.DisplaySingular ?? ''; + return this.metaData.tables[DbObjectKey.FromXml(item.ObjectKeyEntitlement || '').TableName]?.DisplaySingular ?? ''; } private async loadTableNamesForSources(violations: ComplianceViolation[]): Promise { - const sourecedRules = violations.filter((elem) => elem.Sources?.length > 0); + const sourecedRules = violations.filter((elem) => !!elem.Sources?.length); if (sourecedRules.length > 0) { for (const source of sourecedRules) { - await this.metaData.updateNonExisting(source.Sources.map((item) => DbObjectKey.FromXml(item.ObjectKeyEntitlement).TableName)); + await this.metaData.updateNonExisting( + source.Sources?.map((item) => DbObjectKey.FromXml(item.ObjectKeyEntitlement || '').TableName) || [], + ); } } } @@ -155,7 +166,7 @@ export class ComplianceViolationDetailsComponent implements OnInit { }); } - private getDisplayRuleCdr(rule: PortalRules, detail: ComplianceViolation): ColumnDependentReference { + private getDisplayRuleCdr(rule: PortalRules | undefined, detail: ComplianceViolation): ColumnDependentReference { const column = rule ? rule.GetEntity().GetColumn('DisplayRule') : this.buildColumn('DisplayRule', this.translate.instant('#LDS#Violated compliance rule'), detail.DisplayRule); @@ -163,28 +174,28 @@ export class ComplianceViolationDetailsComponent implements OnInit { return new BaseReadonlyCdr(column); } - private async buildCdrForViolations(rule: PortalRules, detail: ComplianceViolation): Promise { - const tableName = DbObjectKey.FromXml(detail.ObjectKeyElement).TableName; + private async buildCdrForViolations(rule: PortalRules | undefined, detail: ComplianceViolation): Promise { + const tableName = DbObjectKey.FromXml(detail.ObjectKeyElement || '').TableName; await this.metaData.updateNonExisting([tableName]); - const displayTitle = this.metaData.tables[tableName]?.DisplaySingular; + const displayTitle = this.metaData.tables[tableName]?.DisplaySingular || ''; //Build common elments const cdrColumns = [ this.buildColumn('DisplayElement', displayTitle, detail.DisplayElement), rule ? rule.GetEntity().GetColumn('Description') - : this.buildColumn('Description', this.schema.Columns['Description'].Display, detail.Description), + : this.buildColumn('Description', this.schema.Columns['Description'].Display || '', detail.Description), this.buildColumn('DisplayPerson', await this.translate.get('#LDS#Identity').toPromise(), detail.DisplayPerson), rule ? rule.GetEntity().GetColumn('RuleNumber') - : this.buildColumn('RuleNumber', this.schema.Columns['RuleNumber'].Display, detail.RuleNumber), + : this.buildColumn('RuleNumber', this.schema.Columns['RuleNumber'].Display || '', detail.RuleNumber), ]; if (this.hasRiskIndex) { cdrColumns.push( rule ? rule.GetEntity().GetColumn('RiskIndex') - : this.buildColumn('RiskIndex', this.schema.Columns['RiskIndex'].Display, detail.RiskIndex, ValType.Double), + : this.buildColumn('RiskIndex', this.schema.Columns['RiskIndex'].Display || '', detail.RiskIndex, ValType.Double), ); if ( @@ -195,15 +206,20 @@ export class ComplianceViolationDetailsComponent implements OnInit { } else { if (detail.RiskIndex !== detail.RiskIndexReduced) { cdrColumns.push( - this.buildColumn('RiskIndexReduced', this.schema.Columns['RiskIndexReduced'].Display, detail.RiskIndexReduced, ValType.Double), + this.buildColumn( + 'RiskIndexReduced', + this.schema.Columns['RiskIndexReduced'].Display || '', + detail.RiskIndexReduced, + ValType.Double, + ), ); } } } let sources: ColumnDependentReference[] | undefined; - if (detail.Sources?.length > 0) { - sources = detail.Sources.map( + if (!!detail.Sources?.length) { + sources = detail.Sources?.map( (source, index) => new BaseReadonlyCdr(this.buildColumn(`source ${index}`, this.getDisplayForSource(source), source.Display)), ); } diff --git a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/compliance-violation.service.ts b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/compliance-violation.service.ts index 653a931b1..14410caac 100644 --- a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/compliance-violation.service.ts +++ b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/compliance-violation.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,21 +25,20 @@ */ import { Injectable } from '@angular/core'; -import { ComplianceViolation } from 'imx-api-cpl'; +import { ComplianceViolation } from '@imx-modules/imx-api-cpl'; import { ApiService } from '../../api.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ComplianceViolationService { - - constructor(private api: ApiService) { } + constructor(private api: ApiService) {} public async getRequestViolations(pwoId: string): Promise { return await this.api.client.portal_itshop_requests_compliance_get(pwoId); } - public async getMitigatingControlsPerViolation(): Promise{ + public async getMitigatingControlsPerViolation(): Promise { return (await this.api.client.portal_compliance_config_get()).MitigatingControlsPerViolation; } } diff --git a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/edit-mitigating-controls.component.scss b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/edit-mitigating-controls.component.scss index 434ad89af..a234f168c 100644 --- a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/edit-mitigating-controls.component.scss +++ b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/edit-mitigating-controls.component.scss @@ -1,3 +1,3 @@ -.eui-sidesheet-content{ +.eui-sidesheet-content { padding: 0px; -} \ No newline at end of file +} diff --git a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/edit-mitigating-controls.component.ts b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/edit-mitigating-controls.component.ts index 49fa63329..4c52f96a8 100644 --- a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/edit-mitigating-controls.component.ts +++ b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/edit-mitigating-controls.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { Component, Inject} from '@angular/core'; +import { Component, Inject } from '@angular/core'; import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; @Component({ @@ -32,17 +32,15 @@ import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; styleUrls: ['./edit-mitigating-controls.component.scss'], }) export class EditMitigatingControlsComponent { - constructor( public sidesheetRef: EuiSidesheetRef, @Inject(EUI_SIDESHEET_DATA) public data: { uidPerson: string; uidNonCompliance: string; - uidPersonWantsOrg:string; + uidPersonWantsOrg: string; readOnly: boolean; - isMControlPerViolation:boolean; + isMControlPerViolation: boolean; }, ) {} - } diff --git a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/extended-deferred-operations-data.ts b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/extended-deferred-operations-data.ts index 59fd816ba..c28abae46 100644 --- a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/extended-deferred-operations-data.ts +++ b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/extended-deferred-operations-data.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,28 +26,34 @@ import { FormControl } from '@angular/forms'; -import { DeferredOperationMControlData, PortalPersonMitigatingcontrols } from 'imx-api-cpl'; -import { ValType } from 'imx-qbm-dbts'; +import { DeferredOperationMControlData, PortalPersonMitigatingcontrols } from '@imx-modules/imx-api-cpl'; +import { ValType } from '@imx-modules/imx-qbm-dbts'; import { BaseReadonlyCdr, ColumnDependentReference, EntityService } from 'qbm'; import { MitigatingControlData } from './mitigating-control-data.interface'; export class ExtendedDeferredOperationsData implements MitigatingControlData { - public formControl = new FormControl(undefined); + public formControl: FormControl = new FormControl('', { nonNullable: true }); public cdrs: ColumnDependentReference[]; public isDeferredData = true; public editable: boolean = false; - public get uidMitigatingControl() { - return this.deferred.Uid; + public get uidMitigatingControl(): string { + return this.deferred.Uid || ''; + } + public set uidMitigatingControl(value: string) { + this.deferred.Uid = value; } public get displayMitigatingControls() { - return this.deferred.Display; + return this.deferred.Display || ''; } public isInActive = true; public data = undefined; - constructor(private readonly deferred: DeferredOperationMControlData, entiyService: EntityService) { + constructor( + private readonly deferred: DeferredOperationMControlData, + entiyService: EntityService, + ) { const schema = PortalPersonMitigatingcontrols.GetEntitySchema(); this.cdrs = [ new BaseReadonlyCdr( @@ -60,9 +66,9 @@ export class ExtendedDeferredOperationsData implements MitigatingControlData { { Value: deferred.Uid, DisplayValue: deferred.Display, - } + }, ), - '#LDS#Mitigating control' + '#LDS#Mitigating control', ), new BaseReadonlyCdr( entiyService.createLocalEntityColumn( @@ -74,9 +80,9 @@ export class ExtendedDeferredOperationsData implements MitigatingControlData { { Value: true, DisplayValue: '#LDS#Yes', - } + }, ), - '#LDS#Inactive' + '#LDS#Inactive', ), ]; } diff --git a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/mitigating-control-data.interface.ts b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/mitigating-control-data.interface.ts index 571124b7a..c1ec4e207 100644 --- a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/mitigating-control-data.interface.ts +++ b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/mitigating-control-data.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,10 +24,10 @@ * */ -import { FormControl } from "@angular/forms"; +import { FormControl } from '@angular/forms'; -import { PortalPersonMitigatingcontrols } from "imx-api-cpl"; -import { ColumnDependentReference } from "qbm"; +import { PortalPersonMitigatingcontrols } from '@imx-modules/imx-api-cpl'; +import { ColumnDependentReference } from 'qbm'; export interface MitigatingControlData { cdrs: ColumnDependentReference[]; @@ -38,4 +38,4 @@ export interface MitigatingControlData { uidMitigatingControl: string; displayMitigatingControls: string; data: PortalPersonMitigatingcontrols | undefined; -} \ No newline at end of file +} diff --git a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/mitigating-controls-request.component.html b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/mitigating-controls-request.component.html index 6e5325887..2f2401e7e 100644 --- a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/mitigating-controls-request.component.html +++ b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/mitigating-controls-request.component.html @@ -1,5 +1,5 @@
    - + {{ headertext | translate }}
    @@ -15,7 +15,13 @@ - + @@ -30,7 +36,13 @@ ) | translate }}

    -
    @@ -38,7 +50,13 @@
    - @@ -51,7 +69,7 @@ {{ '#LDS#Active mitigating controls' | translate }} - + @@ -59,10 +77,10 @@ {{ '#LDS#Inactive mitigating controls' | translate }} - + - +
    diff --git a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/mitigating-controls-request.component.scss b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/mitigating-controls-request.component.scss index ce3b634d4..14c1cb17b 100644 --- a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/mitigating-controls-request.component.scss +++ b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/mitigating-controls-request.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; :host { .mitigating-control-readonly { diff --git a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/mitigating-controls-request.component.ts b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/mitigating-controls-request.component.ts index dee850112..648514954 100644 --- a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/mitigating-controls-request.component.ts +++ b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/mitigating-controls-request.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,13 +25,13 @@ */ import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; -import { FormControl, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { AbstractControl, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { EuiLoadingService, EuiSelectOption, EuiSidesheetRef } from '@elemental-ui/core'; import { Subscription } from 'rxjs'; import { ConfirmationService, EntityService, SettingsService, SnackBarService } from 'qbm'; +import { PersonMitigatingControls } from './../../../../rules-violations/mitigating-controls-person/person-mitigating-controls'; import { ExtendedDeferredOperationsData } from './extended-deferred-operations-data'; -import { MitigatingControlData } from './mitigating-control-data.interface'; import { MitigatingControlsRequestService } from './mitigating-controls-request.service'; import { RequestMitigatingControlFilterPipe } from './request-mitigating-control-filter.pipe'; import { RequestMitigatingControls } from './request-mitigating-controls'; @@ -55,8 +55,8 @@ export class MitigatingControlsRequestComponent implements OnInit { public formGroup: UntypedFormGroup; public options: EuiSelectOption[] = []; - public mControls: MitigatingControlData[] = []; - public mControlsToDelete: MitigatingControlData[] = []; + public mControls: (RequestMitigatingControls | ExtendedDeferredOperationsData)[] = []; + public mControlsToDelete: RequestMitigatingControls[] = []; public mitigatingCaption: string; public headertext: string; @@ -69,9 +69,9 @@ export class MitigatingControlsRequestComponent implements OnInit { private snackbarService: SnackBarService, private settingService: SettingsService, private confirmationService: ConfirmationService, - private entityService: EntityService + private entityService: EntityService, ) { - this.mitigatingCaption = mControlService.mitigationSchema.Columns.UID_MitigatingControl.Display; + this.mitigatingCaption = mControlService.mitigationSchema.Columns.UID_MitigatingControl.Display || ''; this.formGroup = new UntypedFormGroup({ formArray: formBuilder.array([]) }); } @@ -79,7 +79,7 @@ export class MitigatingControlsRequestComponent implements OnInit { return this.formGroup.get('formArray') as UntypedFormArray; } - public hasItems(filter: 'active' | 'inactive' | 'deferred'):boolean { + public hasItems(filter: 'active' | 'inactive' | 'deferred'): boolean { return RequestMitigatingControlFilterPipe.getItems(this.mControls, filter)?.length > 0; } @@ -117,11 +117,9 @@ export class MitigatingControlsRequestComponent implements OnInit { if (!this.isDirty || (await this.confirmationService.confirmLeaveWithUnsavedChanges())) { this.sidesheetRef.close(); } - }) + }), ); - this.headertext = !this.readOnly - ? '#LDS#Heading Mitigating Controls Can Be Assigned Individually' - : '#LDS#Heading Mitigating Controls'; + this.headertext = !this.readOnly ? '#LDS#Heading Mitigating Controls Can Be Assigned Individually' : '#LDS#Heading Mitigating Controls'; await this.loadMitigatingControls(); } @@ -142,8 +140,10 @@ export class MitigatingControlsRequestComponent implements OnInit { } } - public onControlDeleted( mControl: RequestMitigatingControls){ - this.mControlsToDelete.push(mControl); + public onControlDeleted(mControl: RequestMitigatingControls | PersonMitigatingControls) { + if (!(mControl instanceof PersonMitigatingControls)) { + this.mControlsToDelete.push(mControl); + } } public getMControlId(mControl: RequestMitigatingControls): string { @@ -158,12 +158,11 @@ export class MitigatingControlsRequestComponent implements OnInit { mControl.formControl.setValidators([ Validators.required, - (control: FormControl) => (this.isDuplicate(control) ? { duplicated: true } : null), + (control: AbstractControl) => (this.isDuplicate(control) ? { duplicated: true } : null), ]); this.formGroup.markAsDirty(); } - public async onSave(): Promise { const overlay = this.busyService.show(); try { @@ -189,9 +188,9 @@ export class MitigatingControlsRequestComponent implements OnInit { this.uidPerson, this.uidNonCompliance, this.uidPersonWantsOrg, - ctrl.uidMitigatingControl + ctrl.uidMitigatingControl, ); - } else { + } else if (ctrl.data) { await this.mControlService.postControl(this.uidPerson, this.uidNonCompliance, ctrl.data); } } @@ -221,7 +220,7 @@ export class MitigatingControlsRequestComponent implements OnInit { const data = await this.mControlService.getControls(this.uidPerson, this.uidNonCompliance); this.mControls = data.Data.map((elem) => new RequestMitigatingControls(false, elem)); this.mControls.push( - ...data.extendedData.MitigatingControls.map((elem) => new ExtendedDeferredOperationsData(elem, this.entityService)) + ...(data.extendedData?.MitigatingControls?.map((elem) => new ExtendedDeferredOperationsData(elem, this.entityService)) || []), ); if (!this.readOnly) { @@ -233,7 +232,7 @@ export class MitigatingControlsRequestComponent implements OnInit { } } - private isDuplicate(actrl: FormControl): boolean { + private isDuplicate(actrl: AbstractControl): boolean { const test = this.mControls.findIndex((ctrl) => ctrl.formControl != actrl && ctrl?.uidMitigatingControl === actrl.value) !== -1; return test; } @@ -245,8 +244,9 @@ export class MitigatingControlsRequestComponent implements OnInit { {}, { PageSize: this.settingService.PageSizeForAllElements, - } + }, ); - this.options = optionCandidates.Entities.map((elem) => ({ display: elem.Display, value: elem.Keys[0] })); + this.options = + optionCandidates.Entities?.map((elem): EuiSelectOption => ({ display: elem.Display || '', value: elem.Keys?.[0] })) || []; } } diff --git a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/mitigating-controls-request.service.ts b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/mitigating-controls-request.service.ts index 030326003..2b6ef82b1 100644 --- a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/mitigating-controls-request.service.ts +++ b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/mitigating-controls-request.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,8 +26,8 @@ import { Injectable } from '@angular/core'; -import { DeferredOperationData, PortalPersonMitigatingcontrols } from 'imx-api-cpl'; -import { CollectionLoadParameters, EntityKeysData, EntitySchema, ExtendedTypedEntityCollection } from 'imx-qbm-dbts'; +import { DeferredOperationData, PortalPersonMitigatingcontrols } from '@imx-modules/imx-api-cpl'; +import { CollectionLoadParameters, EntityKeysData, EntitySchema, ExtendedTypedEntityCollection } from '@imx-modules/imx-qbm-dbts'; import { ApiService } from '../../../../api.service'; import { MitigatingControlData } from './mitigating-control-data.interface'; import { RequestMitigatingControls } from './request-mitigating-controls'; @@ -44,7 +44,7 @@ export class MitigatingControlsRequestService { public async getControls( uidPerson: string, - uidNonCompliance: string + uidNonCompliance: string, ): Promise> { return this.apiService.typedClient.PortalPersonMitigatingcontrols.Get(uidPerson, uidNonCompliance); } @@ -68,7 +68,7 @@ export class MitigatingControlsRequestService { uidPerson: string, uidNonCompliance: string, uidPersonWantsOrg: string, - uidMitigatinControl: string + uidMitigatinControl: string, ): Promise { const inter = (await this.apiService.typedClient.PortalPersonMitigatingcontrolsInteractive.Get(uidPerson, uidNonCompliance)).Data[0]; await inter.UID_PersonWantsOrg.Column.PutValue(uidPersonWantsOrg); @@ -76,14 +76,14 @@ export class MitigatingControlsRequestService { await this.apiService.client.portal_person_mitigatingcontrols_interactive_forrequest_get( uidPerson, uidNonCompliance, - inter.InteractiveEntityStateData.EntityId, - { keys: inter.InteractiveEntityStateData.Keys, state: inter.InteractiveEntityStateData.State } + inter.InteractiveEntityStateData.EntityId || '', + { keys: inter.InteractiveEntityStateData.Keys, state: inter.InteractiveEntityStateData.State }, ); } public async deleteControl(mControl: MitigatingControlData): Promise { - await mControl.data.GetEntity().MarkForDeletion(); - await mControl.data.GetEntity().Commit(); + await mControl.data?.GetEntity().MarkForDeletion(); + await mControl.data?.GetEntity().Commit(); } public async getCandidates(uid: string, uidNonCompliance: string, data: EntityKeysData, parameter?: CollectionLoadParameters) { @@ -91,7 +91,7 @@ export class MitigatingControlsRequestService { uid, uidNonCompliance, data, - parameter + parameter, ); } } diff --git a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/request-mitigating-control-filter.pipe.ts b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/request-mitigating-control-filter.pipe.ts index 99761abf9..76fcb21bb 100644 --- a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/request-mitigating-control-filter.pipe.ts +++ b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/request-mitigating-control-filter.pipe.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/request-mitigating-controls.ts b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/request-mitigating-controls.ts index 9b59d2716..f5467b704 100644 --- a/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/request-mitigating-controls.ts +++ b/imxweb/projects/cpl/src/lib/request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/request-mitigating-controls.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,9 +26,9 @@ import { FormControl } from '@angular/forms'; +import { PortalPersonMitigatingcontrols } from '@imx-modules/imx-api-cpl'; +import { IWriteValue } from '@imx-modules/imx-qbm-dbts'; import { BaseCdr, ColumnDependentReference } from 'qbm'; -import { PortalPersonMitigatingcontrols } from 'imx-api-cpl'; -import { IWriteValue } from 'imx-qbm-dbts'; import { MitigatingControlData } from './mitigating-control-data.interface'; /** @@ -41,9 +41,12 @@ export class RequestMitigatingControls extends PortalPersonMitigatingcontrols im */ public cdrs: ColumnDependentReference[]; - public formControl = new FormControl(undefined); + public formControl: FormControl = new FormControl('', { nonNullable: true }); - constructor(public editable: boolean, readonly baseObject: PortalPersonMitigatingcontrols) { + constructor( + public editable: boolean, + readonly baseObject: PortalPersonMitigatingcontrols, + ) { super(baseObject.GetEntity()); this.cdrs = this.initPropertyInfo(); diff --git a/imxweb/projects/cpl/src/lib/request/compliance-violation-model.ts b/imxweb/projects/cpl/src/lib/request/compliance-violation-model.ts index 177fbccc3..9edfd650c 100644 --- a/imxweb/projects/cpl/src/lib/request/compliance-violation-model.ts +++ b/imxweb/projects/cpl/src/lib/request/compliance-violation-model.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { ComplianceViolation, PortalRules } from 'imx-api-cpl'; +import { ComplianceViolation, PortalRules } from '@imx-modules/imx-api-cpl'; import { ColumnDependentReference } from 'qbm'; export interface ApplicableRule { diff --git a/imxweb/projects/cpl/src/lib/request/request-rule-violation-detail.ts b/imxweb/projects/cpl/src/lib/request/request-rule-violation-detail.ts index 7e22b1b21..0605f1678 100644 --- a/imxweb/projects/cpl/src/lib/request/request-rule-violation-detail.ts +++ b/imxweb/projects/cpl/src/lib/request/request-rule-violation-detail.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,12 +24,10 @@ * */ -import { IExtension } from "qbm"; -import { ComplianceViolationDetailsComponent } from "./compliance-violation-details/compliance-violation-details.component"; +import { IExtension } from 'qbm'; +import { ComplianceViolationDetailsComponent } from './compliance-violation-details/compliance-violation-details.component'; export class RequestRuleViolationDetail implements IExtension { public static readonly id = 'cpl.ruleViolationDetail'; public instance = ComplianceViolationDetailsComponent; - - } diff --git a/imxweb/projects/cpl/src/lib/request/request-rule-violation.spec.ts b/imxweb/projects/cpl/src/lib/request/request-rule-violation.spec.ts index 8a83f3e30..c03776350 100644 --- a/imxweb/projects/cpl/src/lib/request/request-rule-violation.spec.ts +++ b/imxweb/projects/cpl/src/lib/request/request-rule-violation.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/cpl/src/lib/request/request-rule-violation.ts b/imxweb/projects/cpl/src/lib/request/request-rule-violation.ts index 781d5990e..14ca4d35b 100644 --- a/imxweb/projects/cpl/src/lib/request/request-rule-violation.ts +++ b/imxweb/projects/cpl/src/lib/request/request-rule-violation.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { EntityData } from 'imx-qbm-dbts'; +import { EntityData } from '@imx-modules/imx-qbm-dbts'; import { DataSourceToolbarSettings, IExtension } from 'qbm'; import { ItshopRequest } from 'qer'; import { Subject } from 'rxjs'; @@ -41,11 +41,11 @@ export class RequestRuleViolation implements IExtension { this.dstSettings = dstSettings; if (this.dstSettings?.extendedData) { - for (let i = 0; i < this.dstSettings.dataSource.Data.length; i++) { - const item = this.dstSettings.dataSource.Data[i] as ItshopRequest; - item.complianceRuleViolation = item.pwoData.WorkflowHistory.Entities.filter((wh: EntityData) => - wh.Columns['UID_ComplianceRule']?.Value?.length > 0 - ).length > 0; + for (let i = 0; i < (this.dstSettings.dataSource?.Data.length || 0); i++) { + const item = this.dstSettings.dataSource?.Data[i] as ItshopRequest; + item.complianceRuleViolation = !!item.pwoData.WorkflowHistory?.Entities?.filter( + (wh: EntityData) => wh.Columns?.['UID_ComplianceRule']?.Value?.length > 0, + ).length; } } diff --git a/imxweb/projects/cpl/src/lib/request/request.module.ts b/imxweb/projects/cpl/src/lib/request/request.module.ts index a5f0fd1df..857af94d4 100644 --- a/imxweb/projects/cpl/src/lib/request/request.module.ts +++ b/imxweb/projects/cpl/src/lib/request/request.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -48,7 +48,7 @@ import { MitigatingControlContainerModule } from '../mitigating-control-containe EditMitigatingControlsComponent, WorkflowViolationDetailsComponent, ComplianceViolationDetailsComponent, - RequestMitigatingControlFilterPipe + RequestMitigatingControlFilterPipe, ], imports: [ CdrModule, @@ -63,9 +63,9 @@ import { MitigatingControlContainerModule } from '../mitigating-control-containe MatExpansionModule, ReactiveFormsModule, TranslateModule, - MitigatingControlContainerModule + MitigatingControlContainerModule, ], - exports: [MitigatingControlsRequestComponent] + exports: [MitigatingControlsRequestComponent], }) export class RequestModule { constructor(logger: ClassloggerService) { diff --git a/imxweb/projects/cpl/src/lib/request/workflow-violation-details/workflow-violation-details.component.html b/imxweb/projects/cpl/src/lib/request/workflow-violation-details/workflow-violation-details.component.html index cf585bc63..459bfec56 100644 --- a/imxweb/projects/cpl/src/lib/request/workflow-violation-details/workflow-violation-details.component.html +++ b/imxweb/projects/cpl/src/lib/request/workflow-violation-details/workflow-violation-details.component.html @@ -7,24 +7,24 @@ - {{ item?.Columns['UID_ComplianceRule']?.DisplayValue }} + {{ item?.Columns?.['UID_ComplianceRule']?.DisplayValue }} - {{ item?.Columns['DateHead']?.DisplayValue }} + {{ item?.Columns?.['DateHead']?.DisplayValue }} - +
    {{ '#LDS#Affected identity' | translate }}

    - {{ item?.Columns['UID_PersonRelated']?.DisplayValue }} + {{ item?.Columns?.['UID_PersonRelated']?.DisplayValue }}

    - +
    {{ '#LDS#Approval procedure' | translate }}

    - {{ item?.Columns['UID_PWODecisionRule']?.DisplayValue }} + {{ item?.Columns?.['UID_PWODecisionRule']?.DisplayValue }}

    diff --git a/imxweb/projects/cpl/src/lib/request/workflow-violation-details/workflow-violation-details.component.scss b/imxweb/projects/cpl/src/lib/request/workflow-violation-details/workflow-violation-details.component.scss index 4abc88420..53d6b3279 100644 --- a/imxweb/projects/cpl/src/lib/request/workflow-violation-details/workflow-violation-details.component.scss +++ b/imxweb/projects/cpl/src/lib/request/workflow-violation-details/workflow-violation-details.component.scss @@ -45,7 +45,6 @@ hyphens: auto; } - .imx-accordion .mat-expansion-panel-header-title, .imx-accordion .mat-expansion-panel-header-description { flex-basis: 0; @@ -55,19 +54,3 @@ justify-content: space-between; align-items: center; } - -.imx-no-data { - text-align: center; - margin: 20px 0; - - .eui-icon { - font-size: 100px; - color: rgba($black-c, 0.55); - } - - p { - margin: 0; - font-size: 18px; - color: $black-9; - } -} diff --git a/imxweb/projects/cpl/src/lib/request/workflow-violation-details/workflow-violation-details.component.ts b/imxweb/projects/cpl/src/lib/request/workflow-violation-details/workflow-violation-details.component.ts index 283d605ad..0b143f074 100644 --- a/imxweb/projects/cpl/src/lib/request/workflow-violation-details/workflow-violation-details.component.ts +++ b/imxweb/projects/cpl/src/lib/request/workflow-violation-details/workflow-violation-details.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,23 +25,22 @@ */ import { Component, Input, OnInit } from '@angular/core'; -import { PwoData } from 'imx-api-qer'; -import { EntityData } from 'imx-qbm-dbts'; +import { PwoData } from '@imx-modules/imx-api-qer'; +import { EntityData } from '@imx-modules/imx-qbm-dbts'; @Component({ selector: 'imx-workflow-violation-details', templateUrl: './workflow-violation-details.component.html', - styleUrls: ['./workflow-violation-details.component.scss'] + styleUrls: ['./workflow-violation-details.component.scss'], }) export class WorkflowViolationDetailsComponent implements OnInit { public violations: EntityData[] = []; @Input() public pwoData: PwoData; - constructor() { } + constructor() {} public ngOnInit(): void { - this.violations = this.pwoData.WorkflowHistory.Entities.filter(item => item.Columns['UID_ComplianceRule'].Value); + this.violations = this.pwoData.WorkflowHistory?.Entities?.filter((item) => item.Columns?.['UID_ComplianceRule'].Value) || []; } - } diff --git a/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violation-typed-entity.ts b/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violation-typed-entity.ts index 5323a7401..931d307da 100644 --- a/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violation-typed-entity.ts +++ b/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violation-typed-entity.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,20 +24,20 @@ * */ -import { TypedEntity, EntitySchema, DisplayColumns, ValType } from 'imx-qbm-dbts'; +import { TypedEntity, EntitySchema, DisplayColumns, ValType } from '@imx-modules/imx-qbm-dbts'; export class RoleComplianceViolationTypedEntity extends TypedEntity { - public static GetEntitySchema(): EntitySchema { - const columns = { - UID_ComplianceRule: { Type: ValType.String, ColumnName: 'UID_ComplianceRule' }, - RuleName: { Type: ValType.String, ColumnName: 'RuleName' }, - DbObjectKey: { Type: ValType.String, ColumnName: 'DbObjectKey' }, - ObjectDisplay: { Type: ValType.String, ColumnName: 'ObjectDisplay' }, - ObjectKeyElement: { Type: ValType.String, ColumnName: 'ObjectKeyElement' } - }; + public static GetEntitySchema(): EntitySchema { + const columns = { + UID_ComplianceRule: { Type: ValType.String, ColumnName: 'UID_ComplianceRule' }, + RuleName: { Type: ValType.String, ColumnName: 'RuleName' }, + DbObjectKey: { Type: ValType.String, ColumnName: 'DbObjectKey' }, + ObjectDisplay: { Type: ValType.String, ColumnName: 'ObjectDisplay' }, + ObjectKeyElement: { Type: ValType.String, ColumnName: 'ObjectKeyElement' }, + }; - columns[DisplayColumns.DISPLAY_PROPERTYNAME] = DisplayColumns.DISPLAY_PROPERTY; + columns[DisplayColumns.DISPLAY_PROPERTYNAME] = DisplayColumns.DISPLAY_PROPERTY; - return { Columns: columns }; - } + return { Columns: columns }; + } } diff --git a/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violations-wrapper.ts b/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violations-wrapper.ts index f2632c641..fa2083964 100644 --- a/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violations-wrapper.ts +++ b/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violations-wrapper.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,22 +26,21 @@ import { Injectable } from '@angular/core'; -import { RoleComplianceViolation } from 'imx-api-cpl'; -import { EntityColumnData, EntityData, TypedEntityBuilder, TypedEntityCollectionData } from 'imx-qbm-dbts'; +import { RoleComplianceViolation } from '@imx-modules/imx-api-cpl'; +import { EntityColumnData, EntityData, TypedEntityBuilder, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { RoleComplianceViolationTypedEntity } from './role-compliance-violation-typed-entity'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class RoleComplianceViolationsWrapperService { - public readonly roleComplianceEntitySchema = RoleComplianceViolationTypedEntity.GetEntitySchema(); private readonly builder = new TypedEntityBuilder(RoleComplianceViolationTypedEntity); public build(data: RoleComplianceViolation[]): TypedEntityCollectionData { const violations = { TotalCount: data.length, - Entities: data.map(elem => this.buildEntityData(elem)) + Entities: data.map((elem) => this.buildEntityData(elem)), }; return this.builder.buildReadWriteEntities(violations, this.roleComplianceEntitySchema); } @@ -57,4 +56,3 @@ export class RoleComplianceViolationsWrapperService { return { Columns: ret }; } } - diff --git a/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violations.component.html b/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violations.component.html index 1f5fed658..e18ff64d6 100644 --- a/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violations.component.html +++ b/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violations.component.html @@ -1,21 +1,31 @@
    - - {{ keyDescription | translate }} - - - - - - + + {{ keyDescription | translate }} + +
    + + + - + data-imx-identifier="violations-table-column-ObjectDisplay" + > - +
    diff --git a/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violations.component.scss b/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violations.component.scss index 26d762986..664091826 100644 --- a/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violations.component.scss +++ b/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violations.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; :host { display: flex; @@ -14,13 +14,9 @@ overflow: hidden; } -.imx-tab-body{ +.imx-tab-body { flex: 1 1 auto; display: flex; flex-direction: column; overflow: auto; } - -.imx-helper-alert { - margin-bottom: 20px; -} diff --git a/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violations.component.ts b/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violations.component.ts index dadc4ea2c..7fed0a7b7 100644 --- a/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violations.component.ts +++ b/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violations.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,7 +26,7 @@ import { Component, OnInit } from '@angular/core'; -import { DisplayColumns, EntitySchema, IClientProperty } from 'imx-qbm-dbts'; +import { DisplayColumns, EntitySchema, IClientProperty } from '@imx-modules/imx-qbm-dbts'; import { DataSourceToolbarSettings, DynamicTabDataProviderDirective, MetadataService } from 'qbm'; import { RoleComplianceViolationsWrapperService } from './role-compliance-violations-wrapper'; import { RoleComplianceViolationsService } from './role-compliance-violations.service'; @@ -56,7 +56,7 @@ export class RoleComplianceViolationsComponent implements OnInit { this.entitySchema = this.entityService.roleComplianceEntitySchema; this.displayedColumns = [this.entitySchema.Columns.RuleName, this.entitySchema.Columns.ObjectDisplay]; - // tslint:disable:max-line-length + /* eslint-disable max-len */ switch ((this.tablename ?? '').toLowerCase()) { case 'aerole': this.keyDescription = @@ -87,7 +87,7 @@ export class RoleComplianceViolationsComponent implements OnInit { '#LDS#Here you can get an overview of all entitlements assigned to this object that may violate a compliance rule.'; break; } - // tslint:enable:max-line-length + /* eslint-enable max-len */ } public async ngOnInit(): Promise { @@ -101,7 +101,7 @@ export class RoleComplianceViolationsComponent implements OnInit { const data = await this.roleComplianceViolationService.getRoleComplianceViolations(this.tablename, this.uidRole); this.dstSettings = { displayedColumns: this.displayedColumns, - dataSource: this.entityService.build(data.Violations), + dataSource: this.entityService.build(data.Violations || []), entitySchema: this.entityService.roleComplianceEntitySchema, navigationState: {}, }; diff --git a/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violations.module.ts b/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violations.module.ts index cb70b5e9c..45481a61b 100644 --- a/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violations.module.ts +++ b/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violations.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -35,14 +35,6 @@ import { RoleComplianceViolationsComponent } from './role-compliance-violations. @NgModule({ declarations: [RoleComplianceViolationsComponent], - imports: [ - CommonModule, - EuiCoreModule, - DataSourceToolbarModule, - DataTableModule, - MatCardModule, - TranslateModule, - LdsReplaceModule - ] + imports: [CommonModule, EuiCoreModule, DataSourceToolbarModule, DataTableModule, MatCardModule, TranslateModule, LdsReplaceModule], }) -export class RoleComplianceViolationsModule { } +export class RoleComplianceViolationsModule {} diff --git a/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violations.service.ts b/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violations.service.ts index 6c835ec59..f7572ff44 100644 --- a/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violations.service.ts +++ b/imxweb/projects/cpl/src/lib/role-compliance-violations/role-compliance-violations.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,36 +28,30 @@ import { OverlayRef } from '@angular/cdk/overlay'; import { Injectable } from '@angular/core'; import { EuiLoadingService } from '@elemental-ui/core'; -import { RoleComplianceViolations } from 'imx-api-cpl'; +import { RoleComplianceViolations } from '@imx-modules/imx-api-cpl'; import { ApiService } from '../api.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class RoleComplianceViolationsService { - - private busyIndicator: OverlayRef; + private busyIndicator: OverlayRef | undefined; constructor( private apiservice: ApiService, - private busyService: EuiLoadingService - ) { } + private busyService: EuiLoadingService, + ) {} public async getRoleComplianceViolations(table: string, uidRole: string): Promise { return this.apiservice.client.portal_roles_config_compliance_get(table, uidRole); } public handleOpenLoader(): void { - if (!this.busyIndicator) { - this.busyIndicator = this.busyService.show(); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); } } public handleCloseLoader(): void { - if (this.busyIndicator) { - setTimeout(() => { - this.busyService.hide(this.busyIndicator); - this.busyIndicator = undefined; - }); - } + this.busyService.hide(); } } diff --git a/imxweb/projects/cpl/src/lib/rules-violations/mitigating-controls-person/mitigating-controls-person.component.html b/imxweb/projects/cpl/src/lib/rules-violations/mitigating-controls-person/mitigating-controls-person.component.html index 38b5497d6..a77e839ef 100644 --- a/imxweb/projects/cpl/src/lib/rules-violations/mitigating-controls-person/mitigating-controls-person.component.html +++ b/imxweb/projects/cpl/src/lib/rules-violations/mitigating-controls-person/mitigating-controls-person.component.html @@ -1,18 +1,30 @@
    - + {{ '#LDS#Heading Mitigating Controls Can Be Assigned Individually' | translate }} -

    {{ '#LDS#Here you can assign mitigating controls to the rule violation. NOTE: If no mitigating controls have been assigned to the violated compliance rule, you cannot assign any mitigating controls to the rule violation either.' | translate }}

    +

    + {{ + '#LDS#Here you can assign mitigating controls to the rule violation. NOTE: If no mitigating controls have been assigned to the violated compliance rule, you cannot assign any mitigating controls to the rule violation either.' + | translate + }} +

    - + -
    +

    {{ @@ -22,15 +34,28 @@ ) | translate }}

    -
    - - diff --git a/imxweb/projects/cpl/src/lib/rules-violations/mitigating-controls-person/mitigating-controls-person.component.scss b/imxweb/projects/cpl/src/lib/rules-violations/mitigating-controls-person/mitigating-controls-person.component.scss index 39959a6ef..7bdfa5572 100644 --- a/imxweb/projects/cpl/src/lib/rules-violations/mitigating-controls-person/mitigating-controls-person.component.scss +++ b/imxweb/projects/cpl/src/lib/rules-violations/mitigating-controls-person/mitigating-controls-person.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; :host { height: 100%; @@ -6,10 +6,6 @@ flex-direction: column; justify-content: space-between; - .imx-helper-alert { - margin-bottom: 15px; - } - .control-container { display: flex; flex-direction: column; @@ -62,15 +58,11 @@ } } - .imx-data-no-results { + .imx-no-results { text-align: center; margin: 20px 0; justify-self: center; - .eui-icon { - font-size: 100px; - } - p { margin: 0; font-size: 18px; @@ -80,21 +72,11 @@ margin-top: 10px; } } - - .button-actions { - display: flex; - justify-content: space-between; - margin-top: 20px; - } } // Theming :host { - .imx-data-no-results { - .eui-icon { - color: $color-gray-10; - } - + .imx-no-results { p { color: $color-gray-50; } @@ -111,11 +93,7 @@ .eui-dark-theme { :host { - .imx-data-no-results { - .eui-icon { - color: $color-gray-20; - } - + .imx-no-results { p { color: $color-gray-10; } @@ -133,10 +111,6 @@ .eui-contrast-theme { :host { .imx-data-no-results { - .eui-icon { - color: $color-gray-10; - } - p { color: $color-gray-0; } diff --git a/imxweb/projects/cpl/src/lib/rules-violations/mitigating-controls-person/mitigating-controls-person.component.ts b/imxweb/projects/cpl/src/lib/rules-violations/mitigating-controls-person/mitigating-controls-person.component.ts index 09cf6b809..728e1173c 100644 --- a/imxweb/projects/cpl/src/lib/rules-violations/mitigating-controls-person/mitigating-controls-person.component.ts +++ b/imxweb/projects/cpl/src/lib/rules-violations/mitigating-controls-person/mitigating-controls-person.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,6 +30,7 @@ import { EuiLoadingService, EuiSelectOption, EuiSidesheetRef } from '@elemental- import { Subscription } from 'rxjs'; import { ConfirmationService, SettingsService, SnackBarService } from 'qbm'; +import { MitigatingControlData } from '../../request/compliance-violation-details/edit-mitigating-controls/mitigating-controls-request/mitigating-control-data.interface'; import { MitigatingControlsPersonService } from './mitigating-controls-person.service'; import { PersonMitigatingControls } from './person-mitigating-controls'; @@ -61,9 +62,9 @@ export class MitigatingControlsPersonComponent implements OnInit { private busyService: EuiLoadingService, private snackbarService: SnackBarService, private settingService: SettingsService, - private confirmationService: ConfirmationService + private confirmationService: ConfirmationService, ) { - this.mitigatingCaption = mControlService.mitigationSchema.Columns.UID_MitigatingControl.Display; + this.mitigatingCaption = mControlService.mitigationSchema.Columns.UID_MitigatingControl.Display || ''; this.formGroup = new UntypedFormGroup({ formArray: formBuilder.array([]) }); } @@ -99,7 +100,7 @@ export class MitigatingControlsPersonComponent implements OnInit { if (!this.isDirty || (await this.confirmationService.confirmLeaveWithUnsavedChanges())) { this.sidesheetRef.close(); } - }) + }), ); await this.loadMitigatingControls(); } @@ -129,17 +130,19 @@ export class MitigatingControlsPersonComponent implements OnInit { const mControl = this.mControlService.createControl(this.uidPerson, this.uidNonCompliance); this.mControls.push(mControl); this.formArray.push(mControl.formControl); - this.cd.detectChanges(); mControl.formControl.setValidators([ Validators.required, (control: FormControl) => (this.isDuplicate(control) ? { duplicated: true } : null), ]); this.formGroup.markAsDirty(); + this.cd.detectChanges(); } - public onDelete(mControl: PersonMitigatingControls): void { - this.mControlsToDelete.push(mControl); + public onDelete(mControl: MitigatingControlData | PersonMitigatingControls): void { + if (mControl instanceof PersonMitigatingControls) { + this.mControlsToDelete.push(mControl); + } } public async onSave(): Promise { @@ -208,8 +211,9 @@ export class MitigatingControlsPersonComponent implements OnInit { {}, { PageSize: this.settingService.PageSizeForAllElements, - } + }, ); - this.options = optionCandidates.Entities.map((elem) => ({ display: elem.Display, value: elem.Keys[0] })); + this.options = + optionCandidates.Entities?.map((elem): EuiSelectOption => ({ display: elem.Display || '', value: elem?.Keys?.[0] || '' })) || []; } } diff --git a/imxweb/projects/cpl/src/lib/rules-violations/mitigating-controls-person/mitigating-controls-person.service.ts b/imxweb/projects/cpl/src/lib/rules-violations/mitigating-controls-person/mitigating-controls-person.service.ts index 4ae0a3fff..47e9c6987 100644 --- a/imxweb/projects/cpl/src/lib/rules-violations/mitigating-controls-person/mitigating-controls-person.service.ts +++ b/imxweb/projects/cpl/src/lib/rules-violations/mitigating-controls-person/mitigating-controls-person.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,8 +26,8 @@ import { Injectable } from '@angular/core'; -import { DeferredOperationData, PortalPersonMitigatingcontrols } from 'imx-api-cpl'; -import { CollectionLoadParameters, EntityKeysData, EntitySchema, ExtendedTypedEntityCollection } from 'imx-qbm-dbts'; +import { DeferredOperationData, PortalPersonMitigatingcontrols } from '@imx-modules/imx-api-cpl'; +import { CollectionLoadParameters, EntityKeysData, EntitySchema, ExtendedTypedEntityCollection } from '@imx-modules/imx-qbm-dbts'; import { ApiService } from '../../api.service'; import { PersonMitigatingControls } from './person-mitigating-controls'; @@ -43,7 +43,7 @@ export class MitigatingControlsPersonService { public async getControls( uidPerson: string, - uidNonCompliance: string + uidNonCompliance: string, ): Promise> { return this.apiService.typedClient.PortalPersonMitigatingcontrols.Get(uidPerson, uidNonCompliance); } @@ -67,7 +67,7 @@ export class MitigatingControlsPersonService { uidPerson: string, uidNonCompliance: string, uidPersonWantsOrg: string, - uidMitigatinControl: string + uidMitigatinControl: string, ): Promise { const inter = (await this.apiService.typedClient.PortalPersonMitigatingcontrolsInteractive.Get(uidPerson, uidNonCompliance)).Data[0]; await inter.UID_PersonWantsOrg.Column.PutValue(uidPersonWantsOrg); @@ -75,14 +75,14 @@ export class MitigatingControlsPersonService { await this.apiService.client.portal_person_mitigatingcontrols_interactive_forrequest_get( uidPerson, uidNonCompliance, - inter.InteractiveEntityStateData.EntityId, - { keys: inter.InteractiveEntityStateData.Keys, state: inter.InteractiveEntityStateData.State } + inter.InteractiveEntityStateData.EntityId || '', + { keys: inter.InteractiveEntityStateData.Keys, state: inter.InteractiveEntityStateData.State || '' }, ); } public async deleteControl(mControl: PersonMitigatingControls): Promise { - await mControl.GetEntity().MarkForDeletion(); - await mControl.GetEntity().Commit(); + await mControl.GetEntity().MarkForDeletion(); + await mControl.GetEntity().Commit(); } public async getCandidates(uid: string, uidNonCompliance: string, data: EntityKeysData, parameter?: CollectionLoadParameters) { @@ -90,7 +90,7 @@ export class MitigatingControlsPersonService { uid, uidNonCompliance, data, - parameter + parameter, ); } } diff --git a/imxweb/projects/cpl/src/lib/rules-violations/mitigating-controls-person/person-mitigating-controls.ts b/imxweb/projects/cpl/src/lib/rules-violations/mitigating-controls-person/person-mitigating-controls.ts index 9c91483a2..5e22d5ed0 100644 --- a/imxweb/projects/cpl/src/lib/rules-violations/mitigating-controls-person/person-mitigating-controls.ts +++ b/imxweb/projects/cpl/src/lib/rules-violations/mitigating-controls-person/person-mitigating-controls.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,10 +24,10 @@ * */ -import { BaseCdr, ColumnDependentReference } from 'qbm'; -import { PortalPersonMitigatingcontrols } from 'imx-api-cpl'; -import { IWriteValue } from 'imx-qbm-dbts'; import { FormControl } from '@angular/forms'; +import { PortalPersonMitigatingcontrols } from '@imx-modules/imx-api-cpl'; +import { IWriteValue } from '@imx-modules/imx-qbm-dbts'; +import { BaseCdr, ColumnDependentReference } from 'qbm'; /** * Class thats extends the {@link PortalPersonMitigatingcontrols} with some additional properties that are needed for @@ -39,14 +39,24 @@ export class PersonMitigatingControls extends PortalPersonMitigatingcontrols { */ public cdrs: ColumnDependentReference[]; - public formControl = new FormControl(undefined); + public formControl: FormControl = new FormControl('', { nonNullable: true }); - constructor(public editable: boolean, readonly baseObject: PortalPersonMitigatingcontrols) { + public readonly isDeferredData = false; + constructor( + public editable: boolean, + readonly baseObject: PortalPersonMitigatingcontrols, + ) { super(baseObject.GetEntity()); this.cdrs = this.initPropertyInfo(); } + public get uidMitigatingControl(): string { + return this.baseObject.UID_MitigatingControl.value; + } + public set uidMitigatingControl(value: string) { + this.baseObject.UID_MitigatingControl.value = value; + } private initPropertyInfo(): ColumnDependentReference[] { const properties: IWriteValue[] = [this.UID_MitigatingControl, this.IsInActive]; diff --git a/imxweb/projects/cpl/src/lib/rules-violations/resolve/resolve.component.html b/imxweb/projects/cpl/src/lib/rules-violations/resolve/resolve.component.html index 75c73f175..4a9c469cc 100644 --- a/imxweb/projects/cpl/src/lib/rules-violations/resolve/resolve.component.html +++ b/imxweb/projects/cpl/src/lib/rules-violations/resolve/resolve.component.html @@ -7,9 +7,11 @@ {{ LdsContributingPermissions | translate }}
    - {{ LdsNoPermissions | translate }} + + {{ LdsNoPermissions | translate }} - + -
    -
    @@ -48,11 +56,22 @@
    - + + -
    +
    -
    @@ -62,7 +81,9 @@ - {{ LdsNoLoseAdditional | translate }} + + {{ LdsNoLoseAdditional | translate }} {{ LdsLoseEntitlements | translate }} @@ -75,9 +96,11 @@ -
    +
    - +
    diff --git a/imxweb/projects/cpl/src/lib/rules-violations/resolve/resolve.component.scss b/imxweb/projects/cpl/src/lib/rules-violations/resolve/resolve.component.scss index fd64f0da0..8ce396709 100644 --- a/imxweb/projects/cpl/src/lib/rules-violations/resolve/resolve.component.scss +++ b/imxweb/projects/cpl/src/lib/rules-violations/resolve/resolve.component.scss @@ -1,9 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; - -eui-alert { - margin-bottom: 1em; - display: block; -} +@import '@elemental-ui/core/src/styles/_palette.scss'; .item { flex-grow: 1; @@ -17,14 +12,6 @@ eui-alert { margin-bottom: 10px; } -.imx-step-button, -.imx-step-button-save { - margin-top: 10px; - > *:not(:last-child) { - margin-right: 10px; - } -} - li.entllose { list-style-type: initial; } diff --git a/imxweb/projects/cpl/src/lib/rules-violations/resolve/resolve.component.ts b/imxweb/projects/cpl/src/lib/rules-violations/resolve/resolve.component.ts index 2a6765857..e7aedeff3 100644 --- a/imxweb/projects/cpl/src/lib/rules-violations/resolve/resolve.component.ts +++ b/imxweb/projects/cpl/src/lib/rules-violations/resolve/resolve.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,156 +24,160 @@ * */ -import { Component, OnInit, Inject } from "@angular/core"; -import { EuiLoadingService, EuiSidesheetRef, EUI_SIDESHEET_DATA } from "@elemental-ui/core"; -import { ContributingEntitlement, UiActionData } from "imx-api-cpl"; -import { BaseCdr, EntityService, MetadataService, SnackBarService } from "qbm"; -import { DbObjectKey, ValType } from "imx-qbm-dbts"; -import { ApiService } from "../../api.service"; -import { StepperSelectionEvent } from "@angular/cdk/stepper"; +import { StepperSelectionEvent } from '@angular/cdk/stepper'; +import { Component, Inject, OnInit } from '@angular/core'; +import { EUI_SIDESHEET_DATA, EuiLoadingService, EuiSidesheetRef } from '@elemental-ui/core'; +import { ContributingEntitlement, UiActionData } from '@imx-modules/imx-api-cpl'; +import { DbObjectKey, ValType } from '@imx-modules/imx-qbm-dbts'; +import { BaseCdr, EntityService, MetadataService, SnackBarService } from 'qbm'; +import { ApiService } from '../../api.service'; -type ExtendedEntitlement = (ContributingEntitlement & { Key?: DbObjectKey, TypeDisplay?: string }); +type ExtendedEntitlement = ContributingEntitlement & { Key?: DbObjectKey; TypeDisplay?: string }; @Component({ - templateUrl: "./resolve.component.html", - styleUrls: ["./resolve.component.scss"] + templateUrl: './resolve.component.html', + styleUrls: ['./resolve.component.scss'], }) export class ResolveComponent implements OnInit { - - constructor(private readonly sidesheetRef: EuiSidesheetRef, - private readonly snackbar: SnackBarService, - private readonly cplApi: ApiService, - private readonly busySvc: EuiLoadingService, - private readonly metadata: MetadataService, - private readonly entityService: EntityService, - @Inject(EUI_SIDESHEET_DATA) private readonly data: { - uidPerson: string, - uidNonCompliance: string - } - ) { - } - - public busy = false; - public completed = false; - public actions: UiActionData[] = []; - public entitlements: ExtendedEntitlement[] = []; - public entitlementsLoseAlso: ExtendedEntitlement[] = []; - public selectedEntitlements: string[] = []; - public uidActions: string[] = []; - private reasonCdr: BaseCdr; - public cdrs: BaseCdr[]; - - public async ngOnInit(): Promise { - - this.reasonCdr = new BaseCdr( - this.entityService.createLocalEntityColumn({ - ColumnName: 'ReasonHead', - Type: ValType.Text, - IsMultiLine: true - }), - '#LDS#Reason for unsubscribing' - ); - this.busy = true; - // load entitlements contributing to the rule violation - this.selectedEntitlements = []; - try { - this.entitlements = await this.cplApi.client.portal_rules_violations_entitlements_get(this.data.uidPerson, this.data.uidNonCompliance); - await this.enhanceWithTypeDisplay(this.entitlements); - } - finally { - this.busy = false; - } - } - - private async enhanceWithTypeDisplay(obj: { ObjectKeyEntitlement?: string, Key?: DbObjectKey, TypeDisplay?: string }[]) { - obj.forEach(element => { - element.Key = DbObjectKey.FromXml(element.ObjectKeyEntitlement); - }); - await this.metadata.updateNonExisting(obj.map(i => i.Key.TableName)); - obj.forEach(element => { - element.TypeDisplay = this.metadata.tables[element.Key.TableName].DisplaySingular; - }); - } - - public async selectedStepChanged(event: StepperSelectionEvent): Promise { - if (this.completed) - return; - if (event.selectedIndex === 1 && event.previouslySelectedIndex === 0) { - await this.LoadActions(); - } - else if (event.selectedIndex === 2 && event.previouslySelectedIndex === 1) { - await this.LoadEntitlementsLoseAlso(); - } - } - - private async LoadActions(): Promise { - - // load actions - this.actions = []; - this.uidActions = []; - this.busy = true; - try { - this.actions = await this.cplApi.client.portal_rules_violations_actions_post(this.data.uidPerson, this.data.uidNonCompliance, { - ObjectKeys: this.selectedEntitlements - }); - this.uidActions = this.actions.filter(a => a.IsActive).map(a => a.Id); - - // do we have any unsubscribe actions? - if (this.actions.filter(a => a.Id.endsWith(".Unsubscribe")).length > 0) { - // allow the user to enter a reason - this.cdrs = [this.reasonCdr]; - } - else { - this.cdrs = []; - } - - } finally { - this.busy = false; - } - } - - private async LoadEntitlementsLoseAlso(): Promise { - // load the entitlements that will also be lost if the selected actions are run - this.entitlementsLoseAlso = []; - this.busy = true; - try { - this.entitlementsLoseAlso = await this.cplApi.client.portal_rules_violations_analyzeloss_post(this.data.uidPerson, this.data.uidNonCompliance, { - ObjectKeys: this.selectedEntitlements, - ActionIds: this.uidActions - }); - await this.enhanceWithTypeDisplay(this.entitlementsLoseAlso); - } finally { - this.busy = false; - } - } - - public async Execute(): Promise { - const b = this.busySvc.show(); - try { - await this.cplApi.client.portal_rules_violations_result_post(this.data.uidPerson, this.data.uidNonCompliance, { - ReasonText: this.reasonCdr.column.GetValue(), - ObjectKeys: this.selectedEntitlements, - ActionIds: this.uidActions - }); - this.completed = true; - - this.sidesheetRef.close(true); - this.snackbar.open({ key: this.LdsChangesQueued }); - } finally { - this.busySvc.hide(b); - } - } - - - public LdsChangesQueued = '#LDS#Your changes have been successfully saved. It may take some time for the changes to take effect.'; - - public LdsLoseEntitlements = '#LDS#The identity will lose the following entitlements when the selected actions take effect. Check the list to avoid unintentional loss of access. To change the selection, go back to the previous page.'; - - public LdsNoPermissions = '#LDS#No entitlements were found. The cause for the violation may has already been resolved. You may close this wizard.'; - - public LdsContributingPermissions = '#LDS#The following entitlements contribute to this rule violation. Select the entitlements to be removed from the identity.'; - - public LdsNoLoseAdditional = '#LDS#The identity will not lose any additional entitlements.'; - - public LdsActionList = '#LDS#The following actions will be performed to remove the selected entitlements from the identity.'; + constructor( + private readonly sidesheetRef: EuiSidesheetRef, + private readonly snackbar: SnackBarService, + private readonly cplApi: ApiService, + private readonly busySvc: EuiLoadingService, + private readonly metadata: MetadataService, + private readonly entityService: EntityService, + @Inject(EUI_SIDESHEET_DATA) + private readonly data: { + uidPerson: string; + uidNonCompliance: string; + }, + ) {} + + public busy = false; + public completed = false; + public actions: UiActionData[] = []; + public entitlements: ExtendedEntitlement[] = []; + public entitlementsLoseAlso: ExtendedEntitlement[] = []; + public selectedEntitlements: string[] = []; + public uidActions: string[] = []; + private reasonCdr: BaseCdr; + public cdrs: BaseCdr[]; + + public async ngOnInit(): Promise { + this.reasonCdr = new BaseCdr( + this.entityService.createLocalEntityColumn({ + ColumnName: 'ReasonHead', + Type: ValType.Text, + IsMultiLine: true, + }), + '#LDS#Reason for unsubscribing', + ); + this.busy = true; + // load entitlements contributing to the rule violation + this.selectedEntitlements = []; + try { + this.entitlements = await this.cplApi.client.portal_rules_violations_entitlements_get( + this.data.uidPerson, + this.data.uidNonCompliance, + ); + await this.enhanceWithTypeDisplay(this.entitlements); + } finally { + this.busy = false; + } + } + + private async enhanceWithTypeDisplay(obj: { ObjectKeyEntitlement?: string; Key?: DbObjectKey; TypeDisplay?: string }[]) { + obj.forEach((element) => { + element.Key = DbObjectKey.FromXml(element.ObjectKeyEntitlement || ''); + }); + await this.metadata.updateNonExisting(obj.map((i) => i.Key?.TableName || '')); + obj.forEach((element) => { + if (element.Key?.TableName) { + element.TypeDisplay = this.metadata.tables[element.Key.TableName]?.DisplaySingular; + } + }); + } + + public async selectedStepChanged(event: StepperSelectionEvent): Promise { + if (this.completed) return; + if (event.selectedIndex === 1 && event.previouslySelectedIndex === 0) { + await this.LoadActions(); + } else if (event.selectedIndex === 2 && event.previouslySelectedIndex === 1) { + await this.LoadEntitlementsLoseAlso(); + } + } + + private async LoadActions(): Promise { + // load actions + this.actions = []; + this.uidActions = []; + this.busy = true; + try { + this.actions = await this.cplApi.client.portal_rules_violations_actions_post(this.data.uidPerson, this.data.uidNonCompliance, { + ObjectKeys: this.selectedEntitlements, + }); + this.uidActions = this.actions.filter((a) => a.IsActive).map((a) => a.Id || ''); + + // do we have any unsubscribe actions? + if (this.actions.filter((a) => a.Id?.endsWith('.Unsubscribe')).length > 0) { + // allow the user to enter a reason + this.cdrs = [this.reasonCdr]; + } else { + this.cdrs = []; + } + } finally { + this.busy = false; + } + } + + private async LoadEntitlementsLoseAlso(): Promise { + // load the entitlements that will also be lost if the selected actions are run + this.entitlementsLoseAlso = []; + this.busy = true; + try { + this.entitlementsLoseAlso = await this.cplApi.client.portal_rules_violations_analyzeloss_post( + this.data.uidPerson, + this.data.uidNonCompliance, + { + ObjectKeys: this.selectedEntitlements, + ActionIds: this.uidActions, + }, + ); + await this.enhanceWithTypeDisplay(this.entitlementsLoseAlso); + } finally { + this.busy = false; + } + } + + public async Execute(): Promise { + const b = this.busySvc.show(); + try { + await this.cplApi.client.portal_rules_violations_result_post(this.data.uidPerson, this.data.uidNonCompliance, { + ReasonText: this.reasonCdr.column.GetValue(), + ObjectKeys: this.selectedEntitlements, + ActionIds: this.uidActions, + }); + this.completed = true; + + this.sidesheetRef.close(true); + this.snackbar.open({ key: this.LdsChangesQueued }); + } finally { + this.busySvc.hide(b); + } + } + + public LdsChangesQueued = '#LDS#Your changes have been successfully saved. It may take some time for the changes to take effect.'; + + public LdsLoseEntitlements = + '#LDS#The identity will lose the following entitlements when the selected actions take effect. Check the list to avoid unintentional loss of access. To change the selection, go back to the previous page.'; + + public LdsNoPermissions = + '#LDS#No entitlements were found. The cause for the violation may has already been resolved. You may close this wizard.'; + + public LdsContributingPermissions = + '#LDS#The following entitlements contribute to this rule violation. Select the entitlements to be removed from the identity.'; + + public LdsNoLoseAdditional = '#LDS#The identity will not lose any additional entitlements.'; + + public LdsActionList = '#LDS#The following actions will be performed to remove the selected entitlements from the identity.'; } diff --git a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-action-parameters.interface.ts b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-action-parameters.interface.ts index 811f9dd4f..9263d65ea 100644 --- a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-action-parameters.interface.ts +++ b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-action-parameters.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-action.component.html b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-action.component.html index e81fab571..134c743e8 100644 --- a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-action.component.html +++ b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-action.component.html @@ -2,18 +2,22 @@

    {{ data.description | translate }}

    - - + - - +
    -
    -
    diff --git a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-action.component.scss b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-action.component.scss index c605dc0b3..e9b37918f 100644 --- a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-action.component.scss +++ b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-action.component.scss @@ -1,31 +1,15 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; - -@mixin imx-flex-column-container { - display: flex; - flex-direction: column; -} +@import '@elemental-ui/core/src/styles/_palette.scss'; +@import 'base/mixins'; :host { .eui-sidesheet-content { - @include imx-flex-column-container(); + @include flex-column-container(); .imx-rules-violations-action-form { - @include imx-flex-column-container(); - overflow: auto; + @include flex-column-container($overflow: auto); > * { - @include imx-flex-column-container(); - overflow: auto; - } - } - - .eui-sidesheet-actions { - .justify-start { - margin-right: auto; - } - - button:not(:last-of-type):not(.justify-start) { - margin-right: 10px; + @include flex-column-container($overflow: auto); } } } diff --git a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-action.component.ts b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-action.component.ts index 460421c2c..a0080c1bc 100644 --- a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-action.component.ts +++ b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-action.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -42,10 +42,9 @@ import { RulesViolationsAction } from './rules-violations-action.interface'; @Component({ selector: 'imx-rules-violations-action', templateUrl: './rules-violations-action.component.html', - styleUrls: ['./rules-violations-action.component.scss'] + styleUrls: ['./rules-violations-action.component.scss'], }) export class RulesViolationsActionComponent { - /** * The form group to which the created form controls will be added. */ @@ -58,6 +57,6 @@ export class RulesViolationsActionComponent { */ constructor( @Inject(EUI_SIDESHEET_DATA) public readonly data: RulesViolationsAction, - public readonly sideSheetRef: EuiSidesheetRef - ) { } + public readonly sideSheetRef: EuiSidesheetRef, + ) {} } diff --git a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-action.interface.ts b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-action.interface.ts index 0c395a953..3be6598cd 100644 --- a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-action.interface.ts +++ b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-action.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-action.service.ts b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-action.service.ts index f2c5e0558..8d1805882 100644 --- a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-action.service.ts +++ b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-action.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,29 +25,28 @@ */ import { Injectable } from '@angular/core'; -import { OverlayRef } from '@angular/cdk/overlay'; import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; import { Subject } from 'rxjs'; -import { NonComplianceDecisionInput } from 'imx-api-cpl'; -import { ValType } from 'imx-qbm-dbts'; -import { SnackBarService, EntityService, ColumnDependentReference, BaseCdr } from 'qbm'; +import { NonComplianceDecisionInput } from '@imx-modules/imx-api-cpl'; +import { ValType } from '@imx-modules/imx-qbm-dbts'; +import { BaseCdr, ColumnDependentReference, EntityService, SnackBarService, calculateSidesheetWidth } from 'qbm'; import { JustificationService, JustificationType, UserModelService } from 'qer'; import { ApiService } from '../../api.service'; +import { ResolveComponent } from '../resolve/resolve.component'; import { RulesViolationsApproval } from '../rules-violations-approval'; -import { RulesViolationsActionComponent } from './rules-violations-action.component'; import { RulesViolationsActionParameters } from './rules-violations-action-parameters.interface'; -import { ResolveComponent } from '../resolve/resolve.component'; +import { RulesViolationsActionComponent } from './rules-violations-action.component'; /** * Service that handles the approve and deny of one or more rules violations. */ @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class RulesViolationsActionService { - public readonly applied = new Subject(); + public readonly applied = new Subject(); constructor( private readonly justification: JustificationService, @@ -56,9 +55,9 @@ export class RulesViolationsActionService { private readonly busyService: EuiLoadingService, private readonly translate: TranslateService, private readonly snackBar: SnackBarService, + private readonly entityService: EntityService, private readonly userService: UserModelService, - private readonly entityService: EntityService - ) { } + ) {} /** * Approve the rules violation @@ -83,31 +82,30 @@ export class RulesViolationsActionService { title: await this.translate.get('#LDS#Heading Resolve Rule Violation').toPromise(), subTitle: ruleViolation.GetEntity().GetDisplay(), padding: '0px', - width: 'max(600px, 60%)', + width: calculateSidesheetWidth(), testId: 'rulesviolations-resolve-sidesheet', data: { uidPerson: ruleViolation.UID_Person.value, - uidNonCompliance: ruleViolation.UID_NonCompliance.value - } + uidNonCompliance: ruleViolation.UID_NonCompliance.value, + }, }); return sidesheetRef.afterClosed().toPromise(); } private async makeDecisions(rulesViolationsApprovals: RulesViolationsApproval[], approve: boolean): Promise { - let justification: ColumnDependentReference; + let justification: ColumnDependentReference | undefined; - let busyIndicator: OverlayRef; - setTimeout(() => busyIndicator = this.busyService.show()); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } try { justification = await this.justification.createCdr( - approve - ? JustificationType.approveRuleViolation - : JustificationType.denyRuleViolation + approve ? JustificationType.approveRuleViolation : JustificationType.denyRuleViolation, ); } finally { - setTimeout(() => this.busyService.hide(busyIndicator)); + this.busyService.hide(); } const actionParameters: RulesViolationsActionParameters = { @@ -129,7 +127,7 @@ export class RulesViolationsActionService { return this.editAction({ title: sidesheetTitle, - data: { rulesViolationsApprovals, actionParameters, approve, }, + data: { rulesViolationsApprovals, actionParameters, approve }, message: approve ? '#LDS#Exceptions have been successfully granted for {0} rule violations.' : '#LDS#Exceptions have been successfully denied for {0} rule violations.', @@ -140,7 +138,7 @@ export class RulesViolationsActionService { ExceptionValidUntil: actionParameters.validUntil?.column?.GetValue(), Decision: approve, }); - } + }, }); } /** @@ -149,37 +147,38 @@ export class RulesViolationsActionService { * @param config the configuration for the sidesheet (title) and the corresponding rules violations data */ private async editAction(config: any): Promise { - const result = await this.sidesheet.open(RulesViolationsActionComponent, { - title: await this.translate.get(config.title).toPromise(), - subTitle: config.data.rulesViolationsApprovals.length == 1 - ? config.data.rulesViolationsApprovals[0].GetEntity().GetDisplay() - : '', - panelClass: 'imx-sidesheet', - padding: '0', - width: '600px', - testId: `rulesvioalations-action-sidesheet-${config.data.approve ? 'approve' : 'deny'}`, - data: config.data - }).afterClosed().toPromise(); + const result = await this.sidesheet + .open(RulesViolationsActionComponent, { + title: await this.translate.get(config.title).toPromise(), + subTitle: config.data.rulesViolationsApprovals.length == 1 ? config.data.rulesViolationsApprovals[0].GetEntity().GetDisplay() : '', + panelClass: 'imx-sidesheet', + padding: '0', + width: calculateSidesheetWidth(600, 0.4), + testId: `rulesvioalations-action-sidesheet-${config.data.approve ? 'approve' : 'deny'}`, + data: config.data, + }) + .afterClosed() + .toPromise(); if (result) { - let busyIndicator: OverlayRef; - setTimeout(() => busyIndicator = this.busyService.show()); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } let success: boolean; try { for (const rulesViolation of config.data.rulesViolationsApprovals) { await config.apply(rulesViolation); } - success = true; + success = true; await this.userService.reloadPendingItems(); } finally { - setTimeout(() => this.busyService.hide(busyIndicator)); + this.busyService.hide(); } if (success) { - this.snackBar.open(config.getMessage ? - config.getMessage() : - { key: config.message, parameters: [config.data.rulesViolationsApprovals.length] } + this.snackBar.open( + config.getMessage ? config.getMessage() : { key: config.message, parameters: [config.data.rulesViolationsApprovals.length] }, ); this.applied.next(); } @@ -197,21 +196,22 @@ export class RulesViolationsActionService { return this.cplClient.client.portal_rules_violations_post( ruleViolationToApprove.UID_Person.value, ruleViolationToApprove.UID_NonCompliance.value, - input); + input, + ); } /** * Creates a Cdr-Editor for the reason. */ - private createCdrReason(metadata: { display?: string, mandatory?: boolean } = {}): ColumnDependentReference { + private createCdrReason(metadata: { display?: string; mandatory?: boolean } = {}): ColumnDependentReference { return new BaseCdr( this.entityService.createLocalEntityColumn({ ColumnName: 'ReasonHead', Type: ValType.Text, IsMultiLine: true, - MinLen: metadata.mandatory ? 1 : 0 + MinLen: metadata.mandatory ? 1 : 0, }), - metadata.display || '#LDS#Reason for your decision' + metadata.display || '#LDS#Reason for your decision', ); } @@ -228,13 +228,12 @@ export class RulesViolationsActionService { Type: schema.Columns.ExceptionValidUntil.Type, IsMultiLine: schema.Columns.ExceptionValidUntil.IsMultiLine, MinLen: schema.Columns.ExceptionValidUntil.MinLen, - Display: schema.Columns.ExceptionValidUntil.Display + Display: schema.Columns.ExceptionValidUntil.Display, }, undefined, - { ValueConstraint: { MinValue: minDateUntil } } + { ValueConstraint: { MinValue: minDateUntil } }, ); return new BaseCdr(validUntilColumn); } - } diff --git a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-multi-action/rules-violations-multi-action.component.html b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-multi-action/rules-violations-multi-action.component.html index 6dbd68161..78e9324c7 100644 --- a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-multi-action/rules-violations-multi-action.component.html +++ b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-multi-action/rules-violations-multi-action.component.html @@ -1,5 +1,5 @@ - + {{ '#LDS#Heading General Settings' | translate }} @@ -7,25 +7,28 @@
    - + [attr.data-imx-identifier]="'rules-violations-multi-action-' + cdr.column.ColumnName + '-' + i" + >
    - - - {{ '#LDS#Selected rules violations' | translate }} + + + {{ '#LDS#Heading Selected Rule Violations' | translate }} - + -
    +
    {{ ruleViolationApproval?.GetEntity()?.GetDisplay() }}
    -
    +
    {{ ruleViolationApproval?.stateBadge?.caption }}
    - \ No newline at end of file + diff --git a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-multi-action/rules-violations-multi-action.component.scss b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-multi-action/rules-violations-multi-action.component.scss index 00103f788..e89975998 100644 --- a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-multi-action/rules-violations-multi-action.component.scss +++ b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-multi-action/rules-violations-multi-action.component.scss @@ -1,48 +1,16 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; - -@mixin imx-flex-column-container { - display: flex; - flex-direction: column; -} +@import '@elemental-ui/core/src/styles/_palette.scss'; +@import 'base/mixins'; mat-card { + @include flex-column-container(); margin-bottom: 20px; - @include imx-flex-column-container(); - - .mat-card-title { - font-size: 20px; - } - - > .mat-selection-list { - overflow: auto; - } } .imx-items-title { font-size: 16px; } -.imx-list { +.imx-list-container { overflow: hidden; margin-bottom: 10px; - - .mat-list { - overflow: auto; - } - - mat-list-item { - font-size: 14px; - height: auto; - margin-bottom: 10px; - - .mat-line { - white-space: normal; - } - - .imx-list-item-subtitle { - font-size: smaller; - font-style: italic; - color: $black_9; - } - } } diff --git a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-multi-action/rules-violations-multi-action.component.ts b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-multi-action/rules-violations-multi-action.component.ts index 952088eff..47cf512e0 100644 --- a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-multi-action/rules-violations-multi-action.component.ts +++ b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-multi-action/rules-violations-multi-action.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -40,10 +40,9 @@ import { ColumnDependentReference } from 'qbm'; @Component({ selector: 'imx-rules-violations-multi-action', templateUrl: './rules-violations-multi-action.component.html', - styleUrls: ['./rules-violations-multi-action.component.scss'] + styleUrls: ['./rules-violations-multi-action.component.scss'], }) export class RulesViolationsMultiActionComponent implements OnInit { - /** * @ignore since this is only an internal component. * @@ -72,7 +71,6 @@ export class RulesViolationsMultiActionComponent implements OnInit { * Sets up the {@link columns} to be displayed/edited during OnInit lifecycle hook. */ public ngOnInit(): void { - if (this.data.actionParameters.validUntil) { this.columns.push(this.data.actionParameters.validUntil); } @@ -94,8 +92,6 @@ export class RulesViolationsMultiActionComponent implements OnInit { */ public onFormControlCreated(name: string, control: AbstractControl): void { // use setTimeout to make addControl asynchronous in order to avoid "NG0100: Expression has changed after it was checked" - setTimeout(() => - this.formGroup.addControl(name, control) - ); + setTimeout(() => this.formGroup.addControl(name, control)); } } diff --git a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-single-action/rules-violations-single-action.component.html b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-single-action/rules-violations-single-action.component.html index b96cbc102..160ad0ccd 100644 --- a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-single-action/rules-violations-single-action.component.html +++ b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-single-action/rules-violations-single-action.component.html @@ -1,10 +1,13 @@ - {{ rulesViolation.GetEntity()?.GetDisplay() }} + {{ rulesViolation.GetEntity()?.GetDisplay() }} {{ rulesViolation?.stateBadge?.caption }} - + [attr.data-imx-identifier]="'rules-violations-single-action-' + cdr.column.ColumnName + '-' + i" + > - \ No newline at end of file + diff --git a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-single-action/rules-violations-single-action.component.scss b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-single-action/rules-violations-single-action.component.scss index e8a19f444..656ed4909 100644 --- a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-single-action/rules-violations-single-action.component.scss +++ b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-single-action/rules-violations-single-action.component.scss @@ -1,17 +1,7 @@ // Create a config with the default typography levels. @use '@angular/material' as mat; // see: https://material.angular.io/guide/typography -@import "@angular/material/theming"; -$config: mat.define-typography-config(); mat-card-content { overflow: hidden; } - -mat-card-title { - font-size: mat.font-size($config, subheading-2); -} - -mat-card-subtitle { - font-size: mat.font-size($config, subheading-1); -} diff --git a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-single-action/rules-violations-single-action.component.ts b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-single-action/rules-violations-single-action.component.ts index eca205636..2a2fd4b84 100644 --- a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-single-action/rules-violations-single-action.component.ts +++ b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-action/rules-violations-single-action/rules-violations-single-action.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -41,10 +41,9 @@ import { RulesViolationsApproval } from '../../rules-violations-approval'; @Component({ selector: 'imx-rules-violations-single-action', templateUrl: './rules-violations-single-action.component.html', - styleUrls: ['./rules-violations-single-action.component.scss'] + styleUrls: ['./rules-violations-single-action.component.scss'], }) export class RulesViolationsSingleActionComponent implements OnInit { - /** * @ignore since this is only an internal component. * @@ -74,7 +73,6 @@ export class RulesViolationsSingleActionComponent implements OnInit { */ public rulesViolation: RulesViolationsApproval; - /** * @ignore since this is only an internal component * @@ -104,8 +102,6 @@ export class RulesViolationsSingleActionComponent implements OnInit { */ public onFormControlCreated(name: string, control: AbstractControl): void { // use setTimeout to make addControl asynchronous in order to avoid "NG0100: Expression has changed after it was checked" - setTimeout(() => - this.formGroup.addControl(name, control) - ); + setTimeout(() => this.formGroup.addControl(name, control)); } } diff --git a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-approval.ts b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-approval.ts index a5d394f22..a12d92516 100644 --- a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-approval.ts +++ b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-approval.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,7 +26,7 @@ import { TranslateService } from '@ngx-translate/core'; -import { PortalRulesViolations } from 'imx-api-cpl'; +import { PortalRulesViolations } from '@imx-modules/imx-api-cpl'; import { BaseCdr } from 'qbm'; /** @@ -55,7 +55,7 @@ export class RulesViolationsApproval extends PortalRulesViolations { constructor( readonly baseObject: PortalRulesViolations, private readonly hasRiskIndex: boolean, - private readonly translate: TranslateService + private readonly translate: TranslateService, ) { super(baseObject.GetEntity()); diff --git a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-details/rules-violations-details.component.html b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-details/rules-violations-details.component.html index d41f8a9a8..5433064bf 100644 --- a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-details/rules-violations-details.component.html +++ b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-details/rules-violations-details.component.html @@ -1,35 +1,48 @@ - + -
    +
    {{ data.selectedRulesViolation?.State?.GetMetadata()?.GetDisplay() }}
    - + {{ data.selectedRulesViolation?.stateBadge?.caption }}
    -
    +
    - - +
    #LDS#Heading Mitigating Controls - + - - + diff --git a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-details/rules-violations-details.component.scss b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-details/rules-violations-details.component.scss index dce5bab68..e964cb92f 100644 --- a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-details/rules-violations-details.component.scss +++ b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-details/rules-violations-details.component.scss @@ -1,49 +1,16 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; :host { - .mat-tab-group, ::ng-deep .mat-tab-body-wrapper { - height: 100%; - } - - ::ng-deep .mat-tab-body-content { - display: flex; - flex-direction: column; - justify-content: space-between; - } - - - .imx-mat-tab-container { - display: flex; - flex-direction: column; - overflow: auto; - } - - .eui-sidesheet-content { - display: flex; - flex-direction: column; - } - - .eui-badge ::ng-deep .eui-badge-content { - font-size: 12px; - line-height: 12px; - margin-bottom: 20px; - } .imx-state-caption { font-size: 12px; padding-bottom: 5px; } - .eui-sidesheet-actions { - button:not(:last-of-type) { - margin-right: 10px; - } - } - .imx-dirty-indicator { margin-left: 5px; } - imx-ext{ + imx-ext { height: 100%; } } @@ -57,24 +24,4 @@ .imx-state-caption { color: $color-gray-40; } - - ::ng-deep .mat-tab-labels { - background-color: $color-gray-0; - } -} - -.eui-dark-theme { - :host { - ::ng-deep .mat-tab-labels { - background-color: $color-gray-70; - } - } -} - -.eui-contrast-theme { - :host { - ::ng-deep .mat-tab-labels { - background-color: $color-gray-100; - } - } } diff --git a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-details/rules-violations-details.component.ts b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-details/rules-violations-details.component.ts index 7f8ceb50a..b7316616f 100644 --- a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-details/rules-violations-details.component.ts +++ b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-details/rules-violations-details.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,8 +28,8 @@ import { Component, Inject, OnDestroy, OnInit, Type, ViewChild } from '@angular/ import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; import { MatTabGroup } from '@angular/material/tabs'; +import { PortalRules } from '@imx-modules/imx-api-cpl'; import { TranslateService } from '@ngx-translate/core'; -import { PortalRules } from 'imx-api-cpl'; import { BaseCdr, ExtService, IExtension } from 'qbm'; import { Subscription } from 'rxjs'; import { RulesViolationsActionService } from '../rules-violations-action/rules-violations-action.service'; @@ -59,7 +59,7 @@ export class RulesViolationsDetailsComponent implements OnInit, OnDestroy { public uidNonCompliance: string; public uidCompliance: string; public ruleInfoCdrList: BaseCdr[] = []; - private subscriptions$: Subscription[] = []; + private subscriptions$: (Subscription | undefined)[] = []; constructor( @Inject(EUI_SIDESHEET_DATA) @@ -71,29 +71,29 @@ export class RulesViolationsDetailsComponent implements OnInit, OnDestroy { public sidesheetRef: EuiSidesheetRef, private readonly actionService: RulesViolationsActionService, private readonly extService: ExtService, - private translateService: TranslateService + private translateService: TranslateService, ) { this.uidPerson = data.selectedRulesViolation.GetEntity().GetColumn('UID_Person').GetValue(); this.uidNonCompliance = data.selectedRulesViolation.GetEntity().GetColumn('UID_NonCompliance').GetValue(); this.cdrList = data.selectedRulesViolation.propertyInfo; if (this.data.complianceRule) this.ruleInfoCdrList.push( - new BaseCdr(this.data.complianceRule.Description.Column, this.translateService.instant('#LDS#Description')), - new BaseCdr(this.data.complianceRule.RuleNumber.Column) + new BaseCdr(this.data.complianceRule.Description.Column, this.translateService.instant('#LDS#Rule description')), + new BaseCdr(this.data.complianceRule.RuleNumber.Column), ); } public ngOnInit(): void { this.subscriptions$.push( - this.sidesheetRef.componentInstance.onOpen().subscribe(() => { + this.sidesheetRef.componentInstance?.onOpen().subscribe(() => { // Recalculates header this.matTabGroup.updatePagination(); - }) + }), ); } /** - * Opens the Approve-Sidesheet for the current selected rules violations and closes the sidesheet afterwards. + * Opens the Approve-Sidesheet for the current selected rule violations and closes the sidesheet afterwards. */ public async approve(): Promise { await this.actionService.approve([this.data.selectedRulesViolation]); @@ -101,7 +101,7 @@ export class RulesViolationsDetailsComponent implements OnInit, OnDestroy { } /** - * Opens the Deny-Sidesheet for the current selected rules violations and closes the sidesheet afterwards. + * Opens the Deny-Sidesheet for the current selected rule violations and closes the sidesheet afterwards. */ public async deny(): Promise { await this.actionService.deny([this.data.selectedRulesViolation]); @@ -118,6 +118,6 @@ export class RulesViolationsDetailsComponent implements OnInit, OnDestroy { } public ngOnDestroy(): void { - this.subscriptions$.map((sub) => sub.unsubscribe()); + this.subscriptions$.map((sub) => sub?.unsubscribe()); } } diff --git a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-load-parameters.interface.ts b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-load-parameters.interface.ts index 8853ba55b..a61d8620a 100644 --- a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-load-parameters.interface.ts +++ b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations-load-parameters.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { CollectionLoadParameters } from 'imx-qbm-dbts'; +import { CollectionLoadParameters } from '@imx-modules/imx-qbm-dbts'; /** * Extends the {@link CollectionLoadParameters} with some addtional parameters for the grouping API. diff --git a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations.component.html b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations.component.html index 04c72193c..fdfbd4bab 100644 --- a/imxweb/projects/cpl/src/lib/rules-violations/rules-violations.component.html +++ b/imxweb/projects/cpl/src/lib/rules-violations/rules-violations.component.html @@ -1,86 +1,114 @@
    -
    -

    +
    +

    {{ '#LDS#Heading Rule Violations' | translate }} -

    +

    +
    - -
    - - - - - - - - + + + + + + {{ entitySchema?.Columns?.UID_Person?.Display }} + + + + + {{ item.UID_Person.Column.GetDisplayValue() }} + + + + + + + {{ entitySchema?.Columns?.UID_NonCompliance?.Display }} + + + + + {{ item.UID_NonCompliance.Column.GetDisplayValue() }} + + + + + + + {{ entitySchema?.Columns?.State?.Display }} + + + + {{ item?.stateBadge?.caption }} - - - - - - - - -
    - - -
    -
    -
    -
    - -
    + + + + + + + {{ entitySchema?.Columns?.RiskIndexCalculated?.Display }} + + + + + {{ item.RiskIndexCalculated.Column.GetDisplayValue() }} + + + + + + + {{ entitySchema?.Columns?.RiskIndexReduced?.Display }} + + + + + {{ item.RiskIndexReduced.Column.GetDisplayValue() }} + + + + + + + {{ entitySchema?.Columns?.CountOpen?.Display }} + + + +
    + + +
    + +
    + +
    -
    - -
    +
    + - +
    diff --git a/imxweb/projects/cpl/src/lib/rules/mitigating-controls-rules/mitigating-controls-rules.component.scss b/imxweb/projects/cpl/src/lib/rules/mitigating-controls-rules/mitigating-controls-rules.component.scss index 979bb992b..4747cbf18 100644 --- a/imxweb/projects/cpl/src/lib/rules/mitigating-controls-rules/mitigating-controls-rules.component.scss +++ b/imxweb/projects/cpl/src/lib/rules/mitigating-controls-rules/mitigating-controls-rules.component.scss @@ -6,15 +6,11 @@ flex-direction: column; justify-content: space-between; - .imx-helper-alert { - margin: 0px 0 20px auto; - width: 100%; - } - .control-container { padding: 20px; display: flex; flex-direction: column; + flex: 1 1 auto; overflow: auto; } @@ -29,23 +25,11 @@ width: 100%; margin-right: 15px; } - - eui-icon { - cursor: pointer; - display: flex; - flex-direction: column; - justify-content: center; - } } - .imx-no-mitigating-controls { + .imx-no-results { text-align: center; - margin: 20px 0; - - .eui-icon { - font-size: 100px; - color: rgba($black-c, 0.55); - } + margin: auto; p { margin: 0; @@ -53,10 +37,4 @@ color: $black-9; } } - - .button-actions { - display: flex; - justify-content: space-between; - margin-top: 20px; - } } diff --git a/imxweb/projects/cpl/src/lib/rules/mitigating-controls-rules/mitigating-controls-rules.component.spec.ts b/imxweb/projects/cpl/src/lib/rules/mitigating-controls-rules/mitigating-controls-rules.component.spec.ts index bfc10034c..178223498 100644 --- a/imxweb/projects/cpl/src/lib/rules/mitigating-controls-rules/mitigating-controls-rules.component.spec.ts +++ b/imxweb/projects/cpl/src/lib/rules/mitigating-controls-rules/mitigating-controls-rules.component.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,14 +28,12 @@ import { ChangeDetectorRef, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { UntypedFormBuilder, UntypedFormControl } from '@angular/forms'; import { EuiLoadingService, EuiSidesheetRef } from '@elemental-ui/core'; import { MockBuilder, MockedComponentFixture, MockRender } from 'ng-mocks'; -import { clearStylesFromDOM, ConfirmationService, SnackBarService, } from 'qbm'; +import { clearStylesFromDOM, ConfirmationService, SnackBarService } from 'qbm'; import { MitigatingControlsRulesComponent } from './mitigating-controls-rules.component'; import { MitigatingControlsRulesService } from './mitigating-controls-rules.service'; import { RulesMitigatingControls } from './rules-mitigating-controls'; - - describe('MitigatingControlsRulesComponent', () => { let component: MitigatingControlsRulesComponent; let fixture: MockedComponentFixture; @@ -45,13 +43,13 @@ describe('MitigatingControlsRulesComponent', () => { GetEntity: () => ({ GetKeys: () => [''], GetColumn: (column: string) => { - return {GetValue: () => ''} - } + return { GetValue: () => '' }; + }, }), UID_ComplianceRule: {}, UID_MitigatingControl: {}, UID_NonCompliance: {}, - } + }; beforeEach(() => { return MockBuilder([MitigatingControlsRulesComponent, UntypedFormBuilder, ChangeDetectorRef]) @@ -59,15 +57,15 @@ describe('MitigatingControlsRulesComponent', () => { createControl: jasmine.createSpy('createControl').and.returnValue(mockedControl), postControl: jasmine.createSpy('postControl').and.resolveTo({}), getControls: jasmine.createSpy('getControls').and.resolveTo({ - Data: [mockedControl] - }) + Data: [mockedControl], + }), }) .mock(EuiLoadingService) .mock(SnackBarService) .mock(ConfirmationService) - .beforeCompileComponents(testBed => { + .beforeCompileComponents((testBed) => { testBed.configureTestingModule({ - schemas: [CUSTOM_ELEMENTS_SCHEMA] + schemas: [CUSTOM_ELEMENTS_SCHEMA], }); }); }); @@ -78,7 +76,7 @@ describe('MitigatingControlsRulesComponent', () => { uidNonCompliance: '', uidCompliance: '', mControls: [], - sidesheetRef: new EuiSidesheetRef(null) + sidesheetRef: new EuiSidesheetRef(null), }); component = fixture.point.componentInstance; fixture.detectChanges(); @@ -86,7 +84,7 @@ describe('MitigatingControlsRulesComponent', () => { afterAll(() => { clearStylesFromDOM(); - }) + }); it('should create', () => { expect(component).toBeTruthy(); @@ -99,7 +97,7 @@ describe('MitigatingControlsRulesComponent', () => { await component.onSave(); expect(fixture.point.injector.get(MitigatingControlsRulesService).postControl).not.toHaveBeenCalled(); - }) + }); it('should create a control, save, and then delete', async () => { component.onCreateControl(); @@ -114,5 +112,5 @@ describe('MitigatingControlsRulesComponent', () => { await component.onDelete(mockedControl as RulesMitigatingControls, 0); expect(component.mControls.length).toBe(0); - }) + }); }); diff --git a/imxweb/projects/cpl/src/lib/rules/mitigating-controls-rules/mitigating-controls-rules.component.ts b/imxweb/projects/cpl/src/lib/rules/mitigating-controls-rules/mitigating-controls-rules.component.ts index c6284f10d..c929ab4d4 100644 --- a/imxweb/projects/cpl/src/lib/rules/mitigating-controls-rules/mitigating-controls-rules.component.ts +++ b/imxweb/projects/cpl/src/lib/rules/mitigating-controls-rules/mitigating-controls-rules.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -43,7 +43,7 @@ export class MitigatingControlsRulesComponent implements OnInit, OnDestroy { @Input() public uidCompliance: string; @Input() public mControls: RulesMitigatingControls[]; @Input() public sidesheetRef: EuiSidesheetRef; - @Input() public canEdit = true; + @Input() public canEdit = true; public subscriptions$: Subscription[] = []; public formGroup: UntypedFormGroup; @@ -53,7 +53,7 @@ export class MitigatingControlsRulesComponent implements OnInit, OnDestroy { private cd: ChangeDetectorRef, private busyService: EuiLoadingService, private snackbarService: SnackBarService, - private confirmationService: ConfirmationService + private confirmationService: ConfirmationService, ) { this.formGroup = new UntypedFormGroup({ formArray: formBuilder.array([]) }); } @@ -68,7 +68,7 @@ export class MitigatingControlsRulesComponent implements OnInit, OnDestroy { if (!this.formGroup.dirty || (await this.confirmationService.confirmLeaveWithUnsavedChanges())) { this.sidesheetRef.close(); } - }) + }), ); } @@ -113,7 +113,8 @@ export class MitigatingControlsRulesComponent implements OnInit, OnDestroy { await this.confirmationService.confirmGeneral({ ShowOk: true, Title: '#LDS#Heading Mitigating Control Assigned More Than Once', - Message: '#LDS#Saving is not possible. At least one mitigating control is assigned more than once. Remove the multiple assigned mitigating control and try again.', + Message: + '#LDS#Saving is not possible. At least one mitigating control is assigned more than once. Remove the multiple assigned mitigating control and try again.', }); return; } diff --git a/imxweb/projects/cpl/src/lib/rules/mitigating-controls-rules/mitigating-controls-rules.service.spec.ts b/imxweb/projects/cpl/src/lib/rules/mitigating-controls-rules/mitigating-controls-rules.service.spec.ts index 3a3021473..7697d56d5 100644 --- a/imxweb/projects/cpl/src/lib/rules/mitigating-controls-rules/mitigating-controls-rules.service.spec.ts +++ b/imxweb/projects/cpl/src/lib/rules/mitigating-controls-rules/mitigating-controls-rules.service.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -36,9 +36,8 @@ describe('MitigatingControlsRulesService', () => { beforeEach(() => { return MockBuilder([MitigatingControlsRulesService]) .mock(ApiService) - .beforeCompileComponents(testBed => { - testBed.configureTestingModule({ - }); + .beforeCompileComponents((testBed) => { + testBed.configureTestingModule({}); }); }); diff --git a/imxweb/projects/cpl/src/lib/rules/mitigating-controls-rules/mitigating-controls-rules.service.ts b/imxweb/projects/cpl/src/lib/rules/mitigating-controls-rules/mitigating-controls-rules.service.ts index a00ac2f5c..59967c1c9 100644 --- a/imxweb/projects/cpl/src/lib/rules/mitigating-controls-rules/mitigating-controls-rules.service.ts +++ b/imxweb/projects/cpl/src/lib/rules/mitigating-controls-rules/mitigating-controls-rules.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,35 +25,32 @@ */ import { Injectable } from '@angular/core'; -import { PortalRulesMitigatingcontrols } from 'imx-api-cpl'; -import { ExtendedTypedEntityCollection } from 'imx-qbm-dbts'; +import { PortalRulesMitigatingcontrols } from '@imx-modules/imx-api-cpl'; +import { ExtendedTypedEntityCollection } from '@imx-modules/imx-qbm-dbts'; import { ApiService } from '../../api.service'; import { RulesMitigatingControls } from './rules-mitigating-controls'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class MitigatingControlsRulesService { - - constructor( - private apiService: ApiService, - ) { } + constructor(private apiService: ApiService) {} public async getControls(uidNonCompliance: string): Promise> { const collection = await this.apiService.typedClient.PortalRulesMitigatingcontrols.Get(uidNonCompliance); return { tableName: collection.tableName, totalCount: collection.totalCount, - Data: collection.Data.map((item: PortalRulesMitigatingcontrols) => new RulesMitigatingControls(item)) - } + Data: collection.Data.map((item: PortalRulesMitigatingcontrols) => new RulesMitigatingControls(item)), + }; } public createControl(uidCompliance: string): RulesMitigatingControls { // TODO: When API can handle permission issues uncomment. PBI: 305793 const newMControl = this.apiService.typedClient.PortalRulesWorkingcopiesMitigatingcontrols.createEntity({ Columns: { - "UID_ComplianceRule": { Value: uidCompliance } - } + UID_ComplianceRule: { Value: uidCompliance }, + }, }); return new RulesMitigatingControls(newMControl as PortalRulesMitigatingcontrols); // const newMControl = this.apiService.typedClient.PortalRulesMitigatingcontrols.createEntity({ diff --git a/imxweb/projects/cpl/src/lib/rules/mitigating-controls-rules/rules-mitigating-controls.ts b/imxweb/projects/cpl/src/lib/rules/mitigating-controls-rules/rules-mitigating-controls.ts index 1ff467db7..e2e1a95e7 100644 --- a/imxweb/projects/cpl/src/lib/rules/mitigating-controls-rules/rules-mitigating-controls.ts +++ b/imxweb/projects/cpl/src/lib/rules/mitigating-controls-rules/rules-mitigating-controls.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,38 +24,29 @@ * */ -import { IWriteValue } from 'imx-qbm-dbts'; +import { IWriteValue } from '@imx-modules/imx-qbm-dbts'; import { BaseCdr, ColumnDependentReference } from 'qbm'; -import { PortalRulesMitigatingcontrols } from 'imx-api-cpl'; +import { PortalRulesMitigatingcontrols } from '@imx-modules/imx-api-cpl'; /** * Class thats extends the {@link PortalRulesMitigatingcontrols} with some additional properties that are needed for * the components in the approval context. */ export class RulesMitigatingControls extends PortalRulesMitigatingcontrols { - /** * The property list depending on the value of the state of a {@link PortalRulesMitigatingcontrols}. */ public readonly propertyInfo: ColumnDependentReference[]; - - constructor( - readonly baseObject: PortalRulesMitigatingcontrols - ) { + constructor(readonly baseObject: PortalRulesMitigatingcontrols) { super(baseObject.GetEntity()); this.propertyInfo = this.initPropertyInfo(); } private initPropertyInfo(): ColumnDependentReference[] { - const properties: IWriteValue[] = - [ - this.UID_MitigatingControl, - ]; + const properties: IWriteValue[] = [this.UID_MitigatingControl]; - return properties - .map(property => new BaseCdr(property.Column)); + return properties.map((property) => new BaseCdr(property.Column)); } - } diff --git a/imxweb/projects/cpl/src/lib/rules/rule-parameter.ts b/imxweb/projects/cpl/src/lib/rules/rule-parameter.ts index eb31886b3..9822f2395 100644 --- a/imxweb/projects/cpl/src/lib/rules/rule-parameter.ts +++ b/imxweb/projects/cpl/src/lib/rules/rule-parameter.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,8 +24,8 @@ * */ -import { CollectionLoadParameters } from 'imx-qbm-dbts'; +import { CollectionLoadParameters } from '@imx-modules/imx-qbm-dbts'; -export interface RuleParameter extends CollectionLoadParameters{ +export interface RuleParameter extends CollectionLoadParameters { active?: string; } diff --git a/imxweb/projects/cpl/src/lib/rules/rules-sidesheet/rules-sidesheet.component.html b/imxweb/projects/cpl/src/lib/rules/rules-sidesheet/rules-sidesheet.component.html index 8ea2f0577..d4343a070 100644 --- a/imxweb/projects/cpl/src/lib/rules/rules-sidesheet/rules-sidesheet.component.html +++ b/imxweb/projects/cpl/src/lib/rules/rules-sidesheet/rules-sidesheet.component.html @@ -1,4 +1,4 @@ - +
    @@ -8,7 +8,12 @@
    -
    @@ -17,8 +22,8 @@ -
    - +
    +
    @@ -39,7 +44,11 @@
    - + +
    diff --git a/imxweb/projects/cpl/src/lib/rules/rules-sidesheet/rules-sidesheet.component.scss b/imxweb/projects/cpl/src/lib/rules/rules-sidesheet/rules-sidesheet.component.scss index 886de866f..4312f362a 100644 --- a/imxweb/projects/cpl/src/lib/rules/rules-sidesheet/rules-sidesheet.component.scss +++ b/imxweb/projects/cpl/src/lib/rules/rules-sidesheet/rules-sidesheet.component.scss @@ -1,48 +1,5 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; - -:host { - .mat-tab-group, ::ng-deep .mat-tab-body-wrapper { - height: 100%; - } - - ::ng-deep .mat-tab-body-content { - display: flex; - flex-direction: column; - justify-content: space-between; - } - - .imx-mat-tab-container { - padding: 20px; - display: flex; - flex-direction: column; - overflow: auto; - } -} +@import 'base/mixins'; .eui-sidesheet-actions { - .justify-start { - margin-right: auto; - } -} - -:host { - ::ng-deep .mat-tab-labels { - background-color: $color-gray-0; - } -} - -.eui-dark-theme { - :host { - ::ng-deep .mat-tab-labels { - background-color: $color-gray-80; - } - } -} - -.eui-contrast-theme { - :host { - ::ng-deep .mat-tab-labels { - background-color: $color-gray-100; - } - } + @include eui-sidesheet-actions__mixin_01($padding: 16px 20px); } diff --git a/imxweb/projects/cpl/src/lib/rules/rules-sidesheet/rules-sidesheet.component.ts b/imxweb/projects/cpl/src/lib/rules/rules-sidesheet/rules-sidesheet.component.ts index cff700ee8..f5d677a34 100644 --- a/imxweb/projects/cpl/src/lib/rules/rules-sidesheet/rules-sidesheet.component.ts +++ b/imxweb/projects/cpl/src/lib/rules/rules-sidesheet/rules-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,12 +25,12 @@ */ import { Component, Inject, OnDestroy } from '@angular/core'; -import { EuiDownloadOptions, EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { EUI_SIDESHEET_DATA, EuiDownloadOptions, EuiSidesheetRef } from '@elemental-ui/core'; import { Subscription } from 'rxjs'; -import { PortalRules } from 'imx-api-cpl'; -import { DisplayColumns } from 'imx-qbm-dbts'; -import { BaseReadonlyCdr, ColumnDependentReference, ElementalUiConfigService } from 'qbm'; +import { PortalRules } from '@imx-modules/imx-api-cpl'; +import { DisplayColumns } from '@imx-modules/imx-qbm-dbts'; +import { BaseReadonlyCdr, ElementalUiConfigService } from 'qbm'; import { RulesMitigatingControls } from '../mitigating-controls-rules/rules-mitigating-controls'; import { RulesService } from '../rules.service'; @@ -41,7 +41,7 @@ import { RulesService } from '../rules.service'; }) export class RulesSidesheetComponent implements OnDestroy { public reportDownload: EuiDownloadOptions; - public cdrList: ColumnDependentReference[]; + public cdrList: BaseReadonlyCdr[]; public uidNonCompliance: string; public uidCompliance: string; @@ -58,7 +58,7 @@ export class RulesSidesheetComponent implements OnDestroy { }, public sidesheetRef: EuiSidesheetRef, private readonly rulesProvider: RulesService, - elementalUiConfigService: ElementalUiConfigService + elementalUiConfigService: ElementalUiConfigService, ) { this.uidNonCompliance = data.selectedRule.GetEntity().GetColumn('UID_NonCompliance').GetValue(); this.uidCompliance = data.selectedRule.GetEntity().GetKeys().join(','); @@ -66,14 +66,14 @@ export class RulesSidesheetComponent implements OnDestroy { new BaseReadonlyCdr(this.data.selectedRule.GetEntity().GetColumn(DisplayColumns.DISPLAY_PROPERTYNAME)), new BaseReadonlyCdr(this.data.selectedRule.Description.Column), new BaseReadonlyCdr(this.data.selectedRule.RuleNumber.Column), - data.hasRiskIndex ? new BaseReadonlyCdr(this.data.selectedRule.RiskIndex.Column) : null, - data.hasRiskIndex && this.data.selectedRule.RiskIndex.value !== this.data.selectedRule.RiskIndexReduced.value - ? new BaseReadonlyCdr(this.data.selectedRule.RiskIndexReduced.Column) - : null, + ...(data.hasRiskIndex ? [new BaseReadonlyCdr(this.data.selectedRule.RiskIndex.Column)] : []), + ...(data.hasRiskIndex && this.data.selectedRule.RiskIndex.value !== this.data.selectedRule.RiskIndexReduced.value + ? [new BaseReadonlyCdr(this.data.selectedRule.RiskIndexReduced.Column)] + : []), new BaseReadonlyCdr(this.data.selectedRule.IsInActive.Column), new BaseReadonlyCdr(this.data.selectedRule.CountOpen.Column), new BaseReadonlyCdr(this.data.selectedRule.CountClosed.Column), - ].filter((elem) => elem != null); + ]; this.reportDownload = { ...elementalUiConfigService.Config.downloadOptions, diff --git a/imxweb/projects/cpl/src/lib/rules/rules-sidesheet/violations-per-rule/violations-per-rule.component.html b/imxweb/projects/cpl/src/lib/rules/rules-sidesheet/violations-per-rule/violations-per-rule.component.html index f944ea132..62a0ec28f 100644 --- a/imxweb/projects/cpl/src/lib/rules/rules-sidesheet/violations-per-rule/violations-per-rule.component.html +++ b/imxweb/projects/cpl/src/lib/rules/rules-sidesheet/violations-per-rule/violations-per-rule.component.html @@ -1,8 +1,8 @@
    - + {{ '#LDS#Here you can get an overview of the violations of the compliance rule.' | translate }} - + ; - public dstSettings: DataSourceToolbarSettings; + public dstSettings: DataSourceToolbarSettings | undefined; public groupedData: { [key: string]: DataTableGroupedData } = {}; public entitySchema: EntitySchema; @@ -52,7 +52,7 @@ export class ViolationsPerRuleComponent implements OnInit { constructor( private readonly rulesViolationsService: RulesViolationsService, private readonly translate: TranslateService, - private readonly sidesheet: EuiSidesheetService + private readonly sidesheet: EuiSidesheetService, ) {} async ngOnInit(): Promise { @@ -74,7 +74,7 @@ export class ViolationsPerRuleComponent implements OnInit { this.entitySchema.Columns.RiskIndexReduced, ], this.entitySchema, - this.dataModelWrapper + this.dataModelWrapper, ); await this.getData(); @@ -90,11 +90,11 @@ export class ViolationsPerRuleComponent implements OnInit { const groupedData = this.groupedData[groupKey]; groupedData.data = await this.rulesViolationsService.getRulesViolationsApprove(groupedData.navigationState); groupedData.settings = { - displayedColumns: this.dstSettings.displayedColumns, - dataModel: this.dstSettings.dataModel, + displayedColumns: this.dstSettings?.displayedColumns, + dataModel: this.dstSettings?.dataModel, dataSource: groupedData.data, - entitySchema: this.dstSettings.entitySchema, - navigationState: groupedData.navigationState, + entitySchema: this.entitySchema, + navigationState: groupedData.navigationState || {}, }; } finally { this.rulesViolationsService.handleCloseLoader(); @@ -105,7 +105,8 @@ export class ViolationsPerRuleComponent implements OnInit { * Opens rules-violations-details sidesheet, where you can manage the selected rule. * @param rule The selected rule from the Rule Violations list */ - public async showRulesViolationsDetail(rule: RulesViolationsApproval) { + public async showRulesViolationsDetail(entity: TypedEntity) { + const rule = entity as RulesViolationsApproval; const complianceRule = await this.rulesViolationsService.getComplianceRuleByUId(rule); const config = await this.rulesViolationsService.featureConfig(); await this.sidesheet @@ -113,7 +114,7 @@ export class ViolationsPerRuleComponent implements OnInit { title: await this.translate.get('#LDS#Heading View Rule Violation Details').toPromise(), subTitle: rule.GetEntity().GetDisplay(), padding: '0px', - width: 'max(1000px, 70%)', + width: calculateSidesheetWidth(1000), testId: 'rules-violations-informations-sidesheet', data: { selectedRulesViolation: rule, diff --git a/imxweb/projects/cpl/src/lib/rules/rules.component.html b/imxweb/projects/cpl/src/lib/rules/rules.component.html index ef2f25312..2adcffd25 100644 --- a/imxweb/projects/cpl/src/lib/rules/rules.component.html +++ b/imxweb/projects/cpl/src/lib/rules/rules.component.html @@ -1,58 +1,71 @@ -

    - {{ '#LDS#Heading Compliance Rules' | translate }} - -

    - -
    - - -
    - - - -
    -
    -
    {{ item.GetEntity().GetDisplay() }}
    -
    {{ item.Description.Column.GetDisplayValue() }}
    -
    - {{ '#LDS#Deactivated' | translate }} -
    -
    -
    - - - - {{ rule.CountOpen.value + rule.CountClosed.value }} - - - - - - - -
    - -
    -
    -
    -
    -
    - -
    +
    +

    + {{ '#LDS#Heading Compliance Rules' | translate }} + +

    + +
    + + + + + {{ ruleSchema?.Columns?.[DisplayColumns.DISPLAY_PROPERTYNAME]?.Display }} + + +
    +
    +
    {{ item.GetEntity().GetDisplay() }}
    +
    {{ item.Description.Column.GetDisplayValue() }}
    +
    + {{ '#LDS#Deactivated' | translate }} +
    + +
    + + + + {{ '#LDS#Rule violations' | translate }} + + + + + {{ rule.CountOpen.value + rule.CountClosed.value }} + + + + + + + {{ ruleSchema?.Columns?.CountOpen?.Display }} + + + + + {{ rule.CountOpen.Column.GetDisplayValue() }} + + + + + + + + +
    + +
    + +
    +
    +
    diff --git a/imxweb/projects/cpl/src/lib/rules/rules.component.scss b/imxweb/projects/cpl/src/lib/rules/rules.component.scss index 0fe557c68..12b0efc17 100644 --- a/imxweb/projects/cpl/src/lib/rules/rules.component.scss +++ b/imxweb/projects/cpl/src/lib/rules/rules.component.scss @@ -1,5 +1,5 @@ @import '@elemental-ui/core/src/styles/_palette.scss'; -@import '../../../../../shared/scss/common-table.scss'; +@import 'base/mixins'; div[subtitle] { font-size: smaller; @@ -13,32 +13,15 @@ div[subtitle] { height: inherit; } -.mat-card { - @include imx-flex-fill-control-hidden-overflow(); - margin: 3px; -} - -.imx-table-container { - display: flex; - flex-direction: column; - overflow: hidden; - flex: 1 1 auto; -} - -.imx-button-column { - @include imx-button-column-right(); -} - -.imx-displayColumn { - display: flex; - flex-direction: row; - - :first-child { +.imx-display-column { + width: 100% !important; + cursor: inherit !important; + &-main { flex: 1 1 auto; + display: block !important; + cursor: inherit !important; } - .imx-badge { - align-self: center; - margin-left: 15px; + align-self: flex-end; } } diff --git a/imxweb/projects/cpl/src/lib/rules/rules.component.ts b/imxweb/projects/cpl/src/lib/rules/rules.component.ts index ee27d6cea..2145c52f1 100644 --- a/imxweb/projects/cpl/src/lib/rules/rules.component.ts +++ b/imxweb/projects/cpl/src/lib/rules/rules.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,22 +28,29 @@ import { Component, OnInit } from '@angular/core'; import { EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { PortalRules } from 'imx-api-cpl'; -import { CplPermissionsService } from './admin/cpl-permissions.service'; -import { ViewConfigData } from 'imx-api-qer'; -import { CollectionLoadParameters, DataModel, DisplayColumns, EntitySchema, ValType } from 'imx-qbm-dbts'; +import { PortalRules } from '@imx-modules/imx-api-cpl'; +import { ViewConfigData } from '@imx-modules/imx-api-qer'; +import { + CollectionLoadParameters, + DataModel, + DisplayColumns, + EntitySchema, + TypedEntityCollectionData, + ValType, +} from '@imx-modules/imx-qbm-dbts'; import { - DataSourceToolbarFilter, - DataSourceToolbarSettings, + BusyService, + calculateSidesheetWidth, ClientPropertyForTableColumns, DataSourceToolbarViewConfig, + DataViewInitParameters, + DataViewSource, SystemInfoService, - BusyService, } from 'qbm'; import { ViewConfigService } from 'qer'; +import { CplPermissionsService } from './admin/cpl-permissions.service'; import { MitigatingControlsRulesService } from './mitigating-controls-rules/mitigating-controls-rules.service'; import { RulesMitigatingControls } from './mitigating-controls-rules/rules-mitigating-controls'; -import { RuleParameter } from './rule-parameter'; import { RulesSidesheetComponent } from './rules-sidesheet/rules-sidesheet.component'; import { RulesService } from './rules.service'; @@ -51,20 +58,16 @@ import { RulesService } from './rules.service'; selector: 'imx-rules', templateUrl: './rules.component.html', styleUrls: ['./rules.component.scss'], + providers: [DataViewSource], }) export class RulesComponent implements OnInit { - public dstSettings: DataSourceToolbarSettings; public readonly DisplayColumns = DisplayColumns; public ruleSchema: EntitySchema; public isMControlPerViolation: boolean; - public busyService = new BusyService(); - + public canRecalculate = false; private displayedColumns: ClientPropertyForTableColumns[] = []; private dataModel: DataModel; - private filterOptions: DataSourceToolbarFilter[] = []; - private navigationState: RuleParameter = {}; - public canRecalculate = false; private viewConfig: DataSourceToolbarViewConfig; private viewConfigPath = 'rules'; @@ -75,7 +78,8 @@ export class RulesComponent implements OnInit { private readonly sideSheetService: EuiSidesheetService, private readonly systemInfoService: SystemInfoService, private readonly translate: TranslateService, - private readonly permissionService: CplPermissionsService + private readonly permissionService: CplPermissionsService, + public dataSource: DataViewSource, ) { this.ruleSchema = rulesProvider.ruleSchema; this.displayedColumns = [ @@ -99,26 +103,15 @@ export class RulesComponent implements OnInit { const isBusy = this.busyService.beginBusy(); try { this.dataModel = await this.rulesProvider.getDataModel(); - this.filterOptions = this.dataModel.Filters; this.viewConfig = await this.viewConfigService.getInitialDSTExtension(this.dataModel, this.viewConfigPath); this.isMControlPerViolation = (await this.rulesProvider.featureConfig()).MitigatingControlsPerViolation; - // We will check the configs for default state only on init - if (!this.viewConfigService.isDefaultConfigSet()) { - // If we have no default settings, we have a default - const indexActive = this.filterOptions.findIndex((elem) => elem.Name === 'active'); - if (indexActive > -1) { - this.filterOptions[indexActive].InitialValue = '1'; - this.navigationState.active = '1'; - } - } - this.canRecalculate = await this.permissionService.isRuleStatistics(); if (!this.canRecalculate) { this.displayedColumns.pop(); } - await this.navigate({}); + await this.getData(); } finally { isBusy.endBusy(); } @@ -127,16 +120,17 @@ export class RulesComponent implements OnInit { public async updateConfig(config: ViewConfigData): Promise { await this.viewConfigService.putViewConfig(config); this.viewConfig = await this.viewConfigService.getDSTExtensionChanges(this.viewConfigPath); - this.dstSettings.viewConfig = this.viewConfig; + this.dataSource.viewConfig.set(this.viewConfig); } public async deleteConfigById(id: string): Promise { await this.viewConfigService.deleteViewConfig(id); this.viewConfig = await this.viewConfigService.getDSTExtensionChanges(this.viewConfigPath); - this.dstSettings.viewConfig = this.viewConfig; + this.dataSource.viewConfig.set(this.viewConfig); } - public async showDetails(selectedRule: PortalRules): Promise { + public async showDetails(entity: PortalRules): Promise { + const selectedRule = entity; this.rulesProvider.handleOpenLoader(); let mControls: RulesMitigatingControls[]; try { @@ -145,14 +139,14 @@ export class RulesComponent implements OnInit { this.rulesProvider.handleCloseLoader(); } - const hasRiskIndex = (await this.systemInfoService.get()).PreProps.includes('RISKINDEX'); + const hasRiskIndex = !!(await this.systemInfoService.get()).PreProps?.includes('RISKINDEX'); await this.sideSheetService .open(RulesSidesheetComponent, { title: await this.translate.get('#LDS#Heading View Compliance Rule Details').toPromise(), subTitle: selectedRule.GetEntity().GetDisplay(), padding: '0px', - width: 'max(1200px, 80%)', + width: calculateSidesheetWidth(1200, 0.7), testId: 'rule-details-sidesheet', data: { selectedRule, @@ -170,32 +164,25 @@ export class RulesComponent implements OnInit { this.rulesProvider.handleOpenLoader(); try { await this.rulesProvider.recalculate(selectedRule.GetEntity().GetKeys().join(',')); - await this.navigate(this.navigationState); + await this.dataSource.updateState(); } finally { this.rulesProvider.handleCloseLoader(); } } - public async navigate(parameter: CollectionLoadParameters): Promise { - this.navigationState = { ...this.navigationState, ...parameter }; - - const isBusy = this.busyService.beginBusy(); - try { - const data = await this.rulesProvider.getRules(this.navigationState); - const exportMethod = this.rulesProvider.exportRules(this.navigationState); - exportMethod.initialColumns = this.displayedColumns.map((col) => col.ColumnName); - this.dstSettings = { - displayedColumns: this.displayedColumns, - dataSource: data, - dataModel: this.dataModel, - entitySchema: this.ruleSchema, - navigationState: this.navigationState, - filters: this.filterOptions, - viewConfig: this.viewConfig, - exportMethod, - }; - } finally { - isBusy.endBusy(); - } + public async getData(): Promise { + const dataViewInitParameters: DataViewInitParameters = { + execute: (params: CollectionLoadParameters, signal: AbortSignal): Promise> => + this.rulesProvider.getRules(params, signal), + schema: this.ruleSchema, + columnsToDisplay: this.displayedColumns, + dataModel: this.dataModel, + exportFunction: this.rulesProvider.exportRules(this.dataSource.state()), + viewConfig: this.viewConfig, + highlightEntity: (identity: PortalRules) => { + this.showDetails(identity); + }, + }; + this.dataSource.init(dataViewInitParameters); } } diff --git a/imxweb/projects/cpl/src/lib/rules/rules.module.ts b/imxweb/projects/cpl/src/lib/rules/rules.module.ts index ba4da18d9..0c83319df 100644 --- a/imxweb/projects/cpl/src/lib/rules/rules.module.ts +++ b/imxweb/projects/cpl/src/lib/rules/rules.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,29 +24,25 @@ * */ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { TranslateModule } from '@ngx-translate/core'; +import { NgModule } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; -import { EuiCoreModule } from '@elemental-ui/core'; import { MatTabsModule } from '@angular/material/tabs'; +import { EuiCoreModule } from '@elemental-ui/core'; +import { TranslateModule } from '@ngx-translate/core'; -import { CdrModule, DataSourceToolbarModule, DataTableModule, HelpContextualModule, ExtModule } from 'qbm'; -import { StatisticsModule, ObjectHyperviewModule } from 'qer'; -import { RulesComponent } from './rules.component'; -import { RulesSidesheetComponent } from './rules-sidesheet/rules-sidesheet.component'; +import { MatTableModule } from '@angular/material/table'; +import { CdrModule, DataSourceToolbarModule, DataTableModule, DataViewModule, ExtModule, HelpContextualModule } from 'qbm'; +import { ObjectHyperviewModule, StatisticsModule } from 'qer'; +import { RulesViolationsModule } from '../rules-violations/rules-violations.module'; import { MitigatingControlsRulesComponent } from './mitigating-controls-rules/mitigating-controls-rules.component'; +import { RulesSidesheetComponent } from './rules-sidesheet/rules-sidesheet.component'; import { ViolationsPerRuleComponent } from './rules-sidesheet/violations-per-rule/violations-per-rule.component'; -import { RulesViolationsModule } from '../rules-violations/rules-violations.module'; +import { RulesComponent } from './rules.component'; @NgModule({ - declarations: [ - RulesComponent, - RulesSidesheetComponent, - MitigatingControlsRulesComponent, - ViolationsPerRuleComponent, - ], + declarations: [RulesComponent, RulesSidesheetComponent, MitigatingControlsRulesComponent, ViolationsPerRuleComponent], imports: [ CommonModule, EuiCoreModule, @@ -61,7 +57,9 @@ import { RulesViolationsModule } from '../rules-violations/rules-violations.modu ObjectHyperviewModule, HelpContextualModule, ExtModule, - RulesViolationsModule + RulesViolationsModule, + DataViewModule, + MatTableModule, ], }) export class RulesModule {} diff --git a/imxweb/projects/cpl/src/lib/rules/rules.service.ts b/imxweb/projects/cpl/src/lib/rules/rules.service.ts index 7422cecd2..4cacc86af 100644 --- a/imxweb/projects/cpl/src/lib/rules/rules.service.ts +++ b/imxweb/projects/cpl/src/lib/rules/rules.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,26 +24,30 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; import { Injectable } from '@angular/core'; import { EuiLoadingService } from '@elemental-ui/core'; -import { ComplianceFeatureConfig, PortalRules, V2ApiClientMethodFactory } from 'imx-api-cpl'; -import { } from 'imx-api-cpl'; -import { CollectionLoadParameters, DataModel, EntityCollectionData, EntitySchema, ExtendedTypedEntityCollection, MethodDefinition, MethodDescriptor } from 'imx-qbm-dbts'; +import { ComplianceFeatureConfig, PortalRules, V2ApiClientMethodFactory } from '@imx-modules/imx-api-cpl'; +import { + CollectionLoadParameters, + DataModel, + EntityCollectionData, + EntitySchema, + ExtendedTypedEntityCollection, + MethodDefinition, + MethodDescriptor, +} from '@imx-modules/imx-qbm-dbts'; import { AppConfigService, DataSourceToolbarExportMethod } from 'qbm'; import { ApiService } from '../api.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class RulesService { - private busyIndicator: OverlayRef; - constructor( private apiservice: ApiService, private busyService: EuiLoadingService, - private appConfig: AppConfigService - ) { } + private appConfig: AppConfigService, + ) {} public get ruleSchema(): EntitySchema { return this.apiservice.typedClient.PortalRules.GetSchema(); @@ -53,9 +57,11 @@ export class RulesService { return this.apiservice.client.portal_compliance_config_get(); } - public async getRules(parameter?: CollectionLoadParameters) - : Promise> { - return this.apiservice.typedClient.PortalRules.Get(parameter); + public async getRules( + parameter?: CollectionLoadParameters, + signal?: AbortSignal, + ): Promise> { + return this.apiservice.typedClient.PortalRules.Get(parameter, { signal }); } public exportRules(parameter: CollectionLoadParameters): DataSourceToolbarExportMethod { @@ -64,13 +70,13 @@ export class RulesService { getMethod: (withProperties: string, PageSize?: number) => { let method: MethodDescriptor; if (PageSize) { - method = factory.portal_rules_get({...parameter, withProperties, PageSize, StartIndex: 0}) + method = factory.portal_rules_get({ ...parameter, withProperties, PageSize, StartIndex: 0 }); } else { - method = factory.portal_rules_get({...parameter, withProperties}) + method = factory.portal_rules_get({ ...parameter, withProperties }); } return new MethodDefinition(method); - } - } + }, + }; } public async getDataModel(): Promise { @@ -87,18 +93,12 @@ export class RulesService { } public handleOpenLoader(): void { - if (!this.busyIndicator) { - this.busyIndicator = this.busyService.show(); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); } } public handleCloseLoader(): void { - if (this.busyIndicator) { - setTimeout(() => { - this.busyService.hide(this.busyIndicator); - this.busyIndicator = undefined; - }); - } + this.busyService.hide(); } - } diff --git a/imxweb/projects/cpl/src/public_api.ts b/imxweb/projects/cpl/src/public_api.ts index 932792d38..7d192d16f 100644 --- a/imxweb/projects/cpl/src/public_api.ts +++ b/imxweb/projects/cpl/src/public_api.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,7 +30,7 @@ export { CplConfigModule } from './lib/cpl-config.module'; export { ApiService } from './lib/api.service'; -export { RulesModule} from './lib/rules/rules.module'; +export { RulesModule } from './lib/rules/rules.module'; export { RequestRuleViolationDetail } from './lib/request/request-rule-violation-detail'; export { RoleComplianceViolationsModule } from './lib/role-compliance-violations/role-compliance-violations.module'; export { RequestRuleViolation } from './lib/request/request-rule-violation'; diff --git a/imxweb/projects/cpl/src/test.ts b/imxweb/projects/cpl/src/test.ts index f7ac7d548..159c87d7d 100644 --- a/imxweb/projects/cpl/src/test.ts +++ b/imxweb/projects/cpl/src/test.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,24 +26,14 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'core-js/es7/reflect'; -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +// Zone must be imported first. Be careful with prettier import organizing +import 'zone.js'; +import 'zone.js/testing'; + import { getTestBed } from '@angular/core/testing'; -import { - BrowserDynamicTestingModule, - platformBrowserDynamicTesting -} from '@angular/platform-browser-dynamic/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; +import 'core-js/es/reflect'; import { TestHelperModule } from 'qbm'; -declare const require: any; - // First, initialize the Angular testing environment. -getTestBed().initTestEnvironment( - [BrowserDynamicTestingModule, TestHelperModule], - platformBrowserDynamicTesting() -); -// Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); -// And load the modules. -context.keys().map(context); +getTestBed().initTestEnvironment([BrowserDynamicTestingModule, TestHelperModule], platformBrowserDynamicTesting()); diff --git a/imxweb/projects/cpl/tsconfig.lib.json b/imxweb/projects/cpl/tsconfig.lib.json index 6bc419f84..9786008a7 100644 --- a/imxweb/projects/cpl/tsconfig.lib.json +++ b/imxweb/projects/cpl/tsconfig.lib.json @@ -7,10 +7,11 @@ "declarationMap": true, "sourceMap": true, "inlineSources": true, - "types": [] + "types": [], + "strictNullChecks": true }, - "exclude": [ - "src/test.ts", - "**/*.spec.ts" - ] + "angularCompilerOptions": { + "strictTemplates": true + }, + "exclude": ["src/test.ts", "**/*.spec.ts"] } diff --git a/imxweb/projects/cpl/tsconfig.lib.prod.json b/imxweb/projects/cpl/tsconfig.lib.prod.json index 61c7592e7..fb40dbbb0 100644 --- a/imxweb/projects/cpl/tsconfig.lib.prod.json +++ b/imxweb/projects/cpl/tsconfig.lib.prod.json @@ -3,8 +3,5 @@ "extends": "./tsconfig.lib.json", "compilerOptions": { "declarationMap": false - }, - "angularCompilerOptions": { - "enableIvy": true } } diff --git a/imxweb/projects/cpl/tsconfig.spec.json b/imxweb/projects/cpl/tsconfig.spec.json index 16da33db0..6bd74e1f7 100644 --- a/imxweb/projects/cpl/tsconfig.spec.json +++ b/imxweb/projects/cpl/tsconfig.spec.json @@ -1,17 +1,10 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "strictNullChecks": false, "outDir": "../../out-tsc/spec", - "types": [ - "jasmine", - "node" - ] + "types": ["jasmine", "node"] }, - "files": [ - "src/test.ts" - ], - "include": [ - "**/*.spec.ts", - "**/*.d.ts" - ] + "files": ["src/test.ts"], + "include": ["**/*.spec.ts", "**/*.d.ts"] } diff --git a/imxweb/projects/cpl/tslint.json b/imxweb/projects/cpl/tslint.json deleted file mode 100644 index 8bab15f53..000000000 --- a/imxweb/projects/cpl/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "imx", - "camelCase" - ], - "component-selector": [ - true, - "element", - "imx", - "kebab-case" - ] - } -} diff --git a/imxweb/projects/custom-app/.eslintrc.json b/imxweb/projects/custom-app/.eslintrc.json new file mode 100644 index 000000000..615eff834 --- /dev/null +++ b/imxweb/projects/custom-app/.eslintrc.json @@ -0,0 +1,35 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*", "**/*.spec.ts"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": ["imxweb/projects/custom-app/tsconfig.app.json", "imxweb/projects/custom-app/tsconfig.spec.json"], + "createDefaultProgram": true + }, + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "imx", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "imx", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "rules": {} + } + ] +} diff --git a/imxweb/projects/custom-app/karma.conf.js b/imxweb/projects/custom-app/karma.conf.js index b454fa9d7..80eeaba08 100644 --- a/imxweb/projects/custom-app/karma.conf.js +++ b/imxweb/projects/custom-app/karma.conf.js @@ -12,10 +12,10 @@ module.exports = function (config) { require('karma-jasmine-html-reporter'), require('karma-coverage-istanbul-reporter'), require('@angular-devkit/build-angular/plugins/karma'), - require('karma-junit-reporter') + require('karma-junit-reporter'), ], client: { - clearContext: false // leave Jasmine Spec Runner output visible in browser + clearContext: false, // leave Jasmine Spec Runner output visible in browser }, coverageIstanbulReporter: { dir: require('path').join(__dirname, 'results'), @@ -26,7 +26,7 @@ module.exports = function (config) { functions: 0, lines: 0, }, - fixWebpackSourcePaths: true + fixWebpackSourcePaths: true, }, junitReporter: { outputDir: require('path').join(__dirname, 'results'), @@ -35,13 +35,14 @@ module.exports = function (config) { port: 9876, captureTimeout: 210000, browserDisconnectTolerance: 3, - browserDisconnectTimeout : 210000, - browserNoActivityTimeout : 210000, + browserDisconnectTimeout: 210000, + browserNoActivityTimeout: 210000, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], singleRun: false, - restartOnFileChange: true + failOnEmptyTestSuite: false, + restartOnFileChange: true, }); }; diff --git a/imxweb/projects/custom-app/package.json b/imxweb/projects/custom-app/package.json index 8e65fa6ec..894562488 100644 --- a/imxweb/projects/custom-app/package.json +++ b/imxweb/projects/custom-app/package.json @@ -1,5 +1,5 @@ { "name": "custom-app", - "version": "9.2.1", + "version": "9.3.0", "private": true -} \ No newline at end of file +} diff --git a/imxweb/projects/custom-app/project.json b/imxweb/projects/custom-app/project.json new file mode 100644 index 000000000..e897cee8a --- /dev/null +++ b/imxweb/projects/custom-app/project.json @@ -0,0 +1,197 @@ +{ + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "name": "custom-app", + "implicitDependencies": ["qer"], + "projectType": "application", + "sourceRoot": "projects/custom-app/src", + "prefix": "imx", + "generators": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:application", + "options": { + "outputPath": { + "base": "dist/custom-app", + "browser": "" + }, + "allowedCommonJsDependencies": [ + "lodash", + "highlight.js", + "file-saver", + "billboard.js", + "moment-timezone", + "core-js/fn/map", + "core-js/fn/set", + "core-js/fn/weak-map", + "core-js/fn/array/from", + "core-js/fn/object/assign", + "core-js/es/array/from", + "core-js/es/object/assign", + "core-js/es/map", + "core-js/es/set", + "core-js/es/weak-map", + "lodash.debounce", + "lodash.clamp", + "moment", + "@elemental-ui/cadence-icon/codepoints" + ], + "index": "projects/custom-app/src/index.html", + "browser": "projects/custom-app/src/main.ts", + "polyfills": ["projects/custom-app/src/polyfills.ts"], + "tsConfig": "projects/custom-app/tsconfig.app.json", + "aot": true, + "assets": [ + "projects/custom-app/src/appconfig.json", + "projects/custom-app/src/assets", + { + "glob": "**/*", + "input": "./html", + "output": "./html" + }, + { + "glob": "**/*", + "input": "./shared/assets/", + "output": "./assets" + }, + { + "glob": "**/*", + "input": "./node_modules/@elemental-ui/core/assets", + "output": "./assets" + } + ], + "styles": ["projects/custom-app/src/styles.scss", "projects/qbm/src/lib/styles/imx-page-title.scss"], + "stylePreprocessorOptions": { + "includePaths": [ + "./shared/assets", + "./shared/scss", + "./node_modules", + "./node_modules/@elemental-ui/cadence-icon", + "./node_modules/@elemental-ui/core" + ] + } + }, + "configurations": { + "production": { + "fileReplacements": [ + { + "replace": "projects/custom-app/src/environments/environment.ts", + "with": "projects/custom-app/src/environments/environment.prod.ts" + } + ], + "optimization": true, + "outputHashing": "all", + "sourceMap": false, + "namedChunks": true, + "extractLicenses": true, + "budgets": [ + { + "type": "initial", + "maximumWarning": "20mb", + "maximumError": "40mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "6kb" + } + ] + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + }, + "remote-dev": { + "fileReplacements": [ + { + "replace": "projects/custom-app/src/environments/environment.ts", + "with": "../imxweb_envs/custom-app/environments/environment.remote-dev.ts" + } + ], + "optimization": false, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + }, + "remote-qs": { + "fileReplacements": [ + { + "replace": "projects/custom-app/src/environments/environment.ts", + "with": "../imxweb_envs/custom-app/environments/environment.remote-qs.ts" + } + ], + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + }, + "es5": { + "tsConfig": "./projects/custom-app/tsconfig-es5.app.json" + } + }, + "outputs": ["{options.outputPath}"] + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "options": { + "browserTarget": "custom-app:build" + }, + "configurations": { + "production": { + "browserTarget": "custom-app:build:production" + }, + "development": { + "browserTarget": "custom-app:build:development" + }, + "remote-dev": { + "browserTarget": "custom-app:build:remote-dev" + }, + "remote-qs": { + "browserTarget": "custom-app:build:remote-qs" + }, + "es5": { + "browserTarget": "custom-app:build:es5" + } + } + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "custom-app:build" + } + }, + "test": { + "executor": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/custom-app/src/test.ts", + "polyfills": "projects/custom-app/src/polyfills.ts", + "tsConfig": "projects/custom-app/tsconfig.spec.json", + "karmaConfig": "projects/custom-app/karma.conf.js", + "assets": ["projects/custom-app/src/appconfig.json", "projects/custom-app/src/assets"], + "styles": ["projects/custom-app/src/styles.scss"], + "stylePreprocessorOptions": { + "includePaths": [ + "./shared/assets", + "./shared/scss", + "./node_modules", + "./node_modules/@elemental-ui/cadence-icon", + "./node_modules/@elemental-ui/core" + ] + } + } + }, + "lint": { + "executor": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": ["projects/custom-app/tsconfig.app.json", "projects/custom-app/tsconfig.spec.json"], + "exclude": ["**/node_modules/**"] + } + } + } +} diff --git a/imxweb/projects/custom-app/readme.md b/imxweb/projects/custom-app/readme.md index d10e72d05..044283a17 100644 --- a/imxweb/projects/custom-app/readme.md +++ b/imxweb/projects/custom-app/readme.md @@ -16,7 +16,7 @@ The [`app.service.ts`](./src/app/app.service.ts) service is responsible for init The [`appconfig.json`](./src/appconfig.json) file sets two default routes: -``` json +```json "routeConfig": { "login": "", "start": "start" diff --git a/imxweb/projects/custom-app/src/app/app-routing.module.ts b/imxweb/projects/custom-app/src/app/app-routing.module.ts index 31f36d7cd..ecabaed49 100644 --- a/imxweb/projects/custom-app/src/app/app-routing.module.ts +++ b/imxweb/projects/custom-app/src/app/app-routing.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { InjectionToken, NgModule } from '@angular/core'; -import { Routes, RouterModule, ActivatedRouteSnapshot } from '@angular/router'; +import { ActivatedRouteSnapshot, RouterModule, Routes } from '@angular/router'; import { AuthenticationGuardService, LoginComponent, RouteGuardService } from 'qbm'; import { StartComponent } from './start.component'; @@ -36,19 +36,19 @@ const routes: Routes = [ path: '', component: LoginComponent, canActivate: [AuthenticationGuardService], - resolve: [RouteGuardService] + resolve: [RouteGuardService], }, { path: 'start', component: StartComponent, canActivate: [RouteGuardService], - resolve: [RouteGuardService] + resolve: [RouteGuardService], }, - { path: '**', redirectTo: 'start' } + { path: '**', redirectTo: 'start' }, ]; @NgModule({ - imports: [RouterModule.forRoot(routes, { useHash: true, relativeLinkResolution: 'legacy' })], + imports: [RouterModule.forRoot(routes, { useHash: true })], exports: [RouterModule], providers: [ { @@ -58,9 +58,8 @@ const routes: Routes = [ if (externalUrl && externalUrl.toLocaleLowerCase() !== 'undefined') { window.open(externalUrl, '_self'); } - } + }, }, ], }) - -export class AppRoutingModule { } +export class AppRoutingModule {} diff --git a/imxweb/projects/custom-app/src/app/app.component.html b/imxweb/projects/custom-app/src/app/app.component.html index ae5e1f2c2..8579568da 100644 --- a/imxweb/projects/custom-app/src/app/app.component.html +++ b/imxweb/projects/custom-app/src/app/app.component.html @@ -1,10 +1,9 @@
    - - - - -
    - - -
    -
    \ No newline at end of file + + + +
    + + +
    +
    diff --git a/imxweb/projects/custom-app/src/app/app.component.scss b/imxweb/projects/custom-app/src/app/app.component.scss index c2f8a644d..71a013c67 100644 --- a/imxweb/projects/custom-app/src/app/app.component.scss +++ b/imxweb/projects/custom-app/src/app/app.component.scss @@ -6,7 +6,7 @@ } .pageContent { - margin: 20px 30px; + margin: 24px; display: flex; flex-direction: column; overflow: hidden; diff --git a/imxweb/projects/custom-app/src/app/app.component.ts b/imxweb/projects/custom-app/src/app/app.component.ts index 879431916..23f3506ba 100644 --- a/imxweb/projects/custom-app/src/app/app.component.ts +++ b/imxweb/projects/custom-app/src/app/app.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { Component, OnDestroy, OnInit } from '@angular/core'; -import { NavigationCancel, NavigationEnd, NavigationError, NavigationStart, Router, RouterEvent } from '@angular/router'; +import { Event, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, Router } from '@angular/router'; import { Subscription } from 'rxjs'; import { AuthenticationService, ISessionState, SplashService } from 'qbm'; @@ -33,7 +33,7 @@ import { AuthenticationService, ISessionState, SplashService } from 'qbm'; @Component({ selector: 'imx-root', styleUrls: ['./app.component.scss'], - templateUrl: './app.component.html' + templateUrl: './app.component.html', }) export class AppComponent implements OnInit, OnDestroy { public isLoggedIn = false; @@ -49,23 +49,22 @@ export class AppComponent implements OnInit, OnDestroy { ) { this.subscriptions.push( this.authentication.onSessionResponse.subscribe(async (sessionState: ISessionState) => { - if (sessionState.hasErrorState) { // Needs to close here when there is an error on sessionState this.splash.close(); - }else { + } else { if (sessionState.IsLoggedOut) { this.showPageContent = false; } } - this.isLoggedIn = sessionState.IsLoggedIn; + this.isLoggedIn = sessionState.IsLoggedIn ?? false; if (this.isLoggedIn) { // Close the splash screen that opened in app service initialisation // Needs to close here when session is already logged in. this.splash.close(); } - }) + }), ); this.setupRouter(); @@ -79,11 +78,11 @@ export class AppComponent implements OnInit, OnDestroy { } public ngOnDestroy(): void { - this.subscriptions.forEach(subscription => subscription.unsubscribe()); + this.subscriptions.forEach((subscription) => subscription.unsubscribe()); } private setupRouter(): void { - this.router.events.subscribe(((event: RouterEvent) => { + this.router.events.subscribe((event: Event) => { if (event instanceof NavigationStart) { this.hideUserMessage = true; if (this.isLoggedIn && event.url === '/') { @@ -104,6 +103,6 @@ export class AppComponent implements OnInit, OnDestroy { if (event instanceof NavigationError) { this.hideUserMessage = false; } - })); + }); } } diff --git a/imxweb/projects/custom-app/src/app/app.module.ts b/imxweb/projects/custom-app/src/app/app.module.ts index 89d693804..ef21eaf61 100644 --- a/imxweb/projects/custom-app/src/app/app.module.ts +++ b/imxweb/projects/custom-app/src/app/app.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,96 +24,81 @@ * */ -import { HttpClientModule } from '@angular/common/http'; -import { BrowserModule } from '@angular/platform-browser'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { NgModule, APP_INITIALIZER, ErrorHandler } from '@angular/core'; -import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; -import { MatCardModule } from '@angular/material/card'; +import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; +import { APP_INITIALIZER, ErrorHandler, NgModule } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; import { MatIconModule } from '@angular/material/icon'; -import { LoggerModule, NgxLoggerLevel } from 'ngx-logger'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { BrowserModule } from '@angular/platform-browser'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; -import { TranslateModule, TranslateLoader, MissingTranslationHandler, TranslateService } from '@ngx-translate/core'; +import { MissingTranslationHandler, TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; +import { LoggerModule, NgxLoggerLevel } from 'ngx-logger'; +import { MatPaginatorIntl } from '@angular/material/paginator'; import { - ImxTranslateLoader, + AuthenticationModule, + CustomThemeModule, + GlobalErrorHandler, ImxMissingTranslationHandler, + ImxTranslateLoader, + LdsReplacePipe, MastHeadModule, - UserMessageModule, - GlobalErrorHandler, Paginator, - LdsReplacePipe, QbmModule, - AuthenticationModule, - MenuModule, - CustomThemeModule + UserMessageModule, } from 'qbm'; +import appConfigJson from '../appconfig.json'; +import { environment } from '../environments/environment'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { AppService } from './app.service'; -import { MatPaginatorIntl } from '@angular/material/paginator'; -import { environment } from '../environments/environment'; -import appConfigJson from '../appconfig.json'; import { StartComponent } from './start.component'; - -@NgModule({ - declarations: [ - AppComponent, - StartComponent - ], - imports: [ - AppRoutingModule, - AuthenticationModule, - BrowserAnimationsModule, - BrowserModule, - EuiCoreModule, - EuiMaterialModule, - MatButtonModule, - MatCardModule, - MatIconModule, - MatProgressSpinnerModule, - HttpClientModule, - LoggerModule.forRoot({ level: NgxLoggerLevel.DEBUG, serverLogLevel: NgxLoggerLevel.OFF }), - MastHeadModule, - MenuModule, - QbmModule, - CustomThemeModule, - TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useClass: ImxTranslateLoader - }, - missingTranslationHandler: { - provide: MissingTranslationHandler, - useClass: ImxMissingTranslationHandler - } - }), - UserMessageModule - ], - providers: [ - { provide: 'environment', useValue: environment }, - { provide: 'appConfigJson', useValue: appConfigJson }, - { - provide: APP_INITIALIZER, - useFactory: AppService.init, - deps: [AppService], - multi: true - }, - { - provide: ErrorHandler, - useClass: GlobalErrorHandler - }, - { - provide: MatPaginatorIntl, - useFactory: Paginator.Create, - deps: [ - TranslateService, - LdsReplacePipe - ] - } - ], - bootstrap: [AppComponent] -}) -export class AppModule { } +@NgModule({ declarations: [AppComponent, StartComponent], + bootstrap: [AppComponent], imports: [AppRoutingModule, + AuthenticationModule, + BrowserAnimationsModule, + BrowserModule, + EuiCoreModule, + EuiMaterialModule, + MatButtonModule, + MatCardModule, + MatIconModule, + MatProgressSpinnerModule, + LoggerModule.forRoot({ level: NgxLoggerLevel.DEBUG, serverLogLevel: NgxLoggerLevel.OFF }), + MastHeadModule, + QbmModule, + CustomThemeModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: ImxTranslateLoader, + }, + missingTranslationHandler: { + provide: MissingTranslationHandler, + useClass: ImxMissingTranslationHandler, + }, + }), + UserMessageModule], providers: [ + { provide: 'environment', useValue: environment }, + { provide: 'appConfigJson', useValue: appConfigJson }, + { + provide: APP_INITIALIZER, + useFactory: AppService.init, + deps: [AppService], + multi: true, + }, + { + provide: ErrorHandler, + useClass: GlobalErrorHandler, + }, + { + provide: MatPaginatorIntl, + useFactory: Paginator.Create, + deps: [TranslateService, LdsReplacePipe], + }, + provideHttpClient(withInterceptorsFromDi()), + ] }) +export class AppModule {} diff --git a/imxweb/projects/custom-app/src/app/app.service.ts b/imxweb/projects/custom-app/src/app/app.service.ts index 3354c9d34..9f76ba4f0 100644 --- a/imxweb/projects/custom-app/src/app/app.service.ts +++ b/imxweb/projects/custom-app/src/app/app.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,21 +28,21 @@ import { Injectable } from '@angular/core'; import { Title } from '@angular/platform-browser'; import { TranslateService } from '@ngx-translate/core'; -import { Globals } from 'imx-qbm-dbts'; +import { TypedClient } from '@imx-modules/imx-api-qbm'; +import { Globals } from '@imx-modules/imx-qbm-dbts'; import { AppConfigService, AuthenticationService, - imx_SessionService, + ISessionState, ImxTranslationProviderService, SplashService, SystemInfoService, - ISessionState + imx_SessionService, } from 'qbm'; import { environment } from '../environments/environment'; -import { TypedClient } from 'imx-api-qbm'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class AppService { constructor( @@ -54,19 +54,22 @@ export class AppService { private readonly title: Title, private readonly authentication: AuthenticationService, private readonly splash: SplashService, - ) { } + ) {} public async init(): Promise { this.showSplash(); await this.config.init(environment.clientUrl); - - this.translateService.addLangs(this.config.Config.Translation.Langs); - const browserCulture = this.translateService.getBrowserCultureLang(); + if (this.config.Config.Translation?.Langs) { + this.translateService.addLangs(this.config.Config.Translation.Langs); + } + const browserCulture = this.translateService.getBrowserCultureLang() as string; this.translateService.setDefaultLang(browserCulture); await this.translateService.use(browserCulture).toPromise(); // If the session defines another culture, update the translation provider - this.authentication.onSessionResponse.subscribe((sessionState: ISessionState) => this.translationProvider.init(sessionState?.culture, sessionState?.cultureFormat)); + this.authentication.onSessionResponse.subscribe((sessionState: ISessionState) => + this.translationProvider.init(sessionState?.culture, sessionState?.cultureFormat), + ); this.translateService.onLangChange.subscribe(() => { this.setTitle(); diff --git a/imxweb/projects/custom-app/src/app/start.component.html b/imxweb/projects/custom-app/src/app/start.component.html index c8aa9c84e..b972f2480 100644 --- a/imxweb/projects/custom-app/src/app/start.component.html +++ b/imxweb/projects/custom-app/src/app/start.component.html @@ -1,8 +1,7 @@ -
    Hello world!
    - + @@ -14,5 +13,4 @@
    - - \ No newline at end of file + diff --git a/imxweb/projects/custom-app/src/app/start.component.ts b/imxweb/projects/custom-app/src/app/start.component.ts index 1349f8d42..86a717218 100644 --- a/imxweb/projects/custom-app/src/app/start.component.ts +++ b/imxweb/projects/custom-app/src/app/start.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,23 +24,21 @@ * */ -import { Component } from "@angular/core"; -import { PortalPersonAll } from "imx-api-qer"; -import { QerApiService } from "qer"; +import { Component } from '@angular/core'; +import { PortalPersonAll } from '@imx-modules/imx-api-qer'; +import { QerApiService } from 'qer'; @Component({ - templateUrl: './start.component.html' + templateUrl: './start.component.html', }) export class StartComponent { - - constructor(private qerApi: QerApiService) { } + constructor(private qerApi: QerApiService) {} persons: PortalPersonAll[] = []; totalCount = 0; busy = false; async loadPersonData() { - try { this.busy = true; @@ -56,5 +54,4 @@ export class StartComponent { this.busy = false; } } - -} \ No newline at end of file +} diff --git a/imxweb/projects/custom-app/src/environments/environment.prod.ts b/imxweb/projects/custom-app/src/environments/environment.prod.ts index 413d69e7b..59f1e80ca 100644 --- a/imxweb/projects/custom-app/src/environments/environment.prod.ts +++ b/imxweb/projects/custom-app/src/environments/environment.prod.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,5 +28,5 @@ export const environment = { production: true, clientUrl: '', appName: 'custom-app', - appVersion: '1.0.0' + appVersion: '1.0.0', }; diff --git a/imxweb/projects/custom-app/src/environments/environment.ts b/imxweb/projects/custom-app/src/environments/environment.ts index 916ec99d4..0582c177b 100644 --- a/imxweb/projects/custom-app/src/environments/environment.ts +++ b/imxweb/projects/custom-app/src/environments/environment.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -32,7 +32,7 @@ export const environment = { production: false, clientUrl: 'http://localhost:8182', appName: 'custom-app', - appVersion: '1.0.0' + appVersion: '1.0.0', }; /* @@ -42,4 +42,4 @@ export const environment = { * This import should be commented out in production mode because it will have a negative impact * on performance if an error is thrown. */ -// import 'zone.js/dist/zone-error'; // Included with Angular CLI. +// import 'zone.js'; // Included with Angular CLI. diff --git a/imxweb/projects/custom-app/src/index.html b/imxweb/projects/custom-app/src/index.html index 4b785ba4e..f08db67fd 100644 --- a/imxweb/projects/custom-app/src/index.html +++ b/imxweb/projects/custom-app/src/index.html @@ -1,15 +1,13 @@ + + + + + + - - - - - - - - - - - + + + diff --git a/imxweb/projects/custom-app/src/main.ts b/imxweb/projects/custom-app/src/main.ts index 5b7a3816d..8cb3e4f09 100644 --- a/imxweb/projects/custom-app/src/main.ts +++ b/imxweb/projects/custom-app/src/main.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -34,5 +34,6 @@ if (environment.production) { enableProdMode(); } -platformBrowserDynamic().bootstrapModule(AppModule) - .catch(err => console.error(err)); +platformBrowserDynamic() + .bootstrapModule(AppModule) + .catch((err) => console.error(err)); diff --git a/imxweb/projects/custom-app/src/polyfills.ts b/imxweb/projects/custom-app/src/polyfills.ts index 31ad3d496..9c1e09bff 100644 --- a/imxweb/projects/custom-app/src/polyfills.ts +++ b/imxweb/projects/custom-app/src/polyfills.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -41,9 +41,8 @@ */ /*************************************************************************************************** -* BROWSER POLYFILLS -*/ - + * BROWSER POLYFILLS + */ /** IE10 and IE11 requires the following for NgClass support on SVG elements */ // import 'classlist.js'; // Run `npm install --save classlist.js`. @@ -51,13 +50,12 @@ /** IE10 and IE11 requires the following for the Reflect API. */ // import 'core-js/es6/reflect'; - /** Evergreen browsers require these. */ // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. -import 'core-js/es7/reflect'; +import 'core-js/es/reflect'; // Used for support array.includes -import 'core-js/es7/array'; +import 'core-js/es/array'; /** * Required to support Web Animations `@angular/platform-browser/animations`. @@ -65,17 +63,13 @@ import 'core-js/es7/array'; */ import 'web-animations-js'; - - /*************************************************************************************************** * Zone JS is required by default for Angular itself. */ -import 'zone.js/dist/zone'; // Included with Angular CLI. - - +import 'zone.js'; // Included with Angular CLI. /*************************************************************************************************** * APPLICATION IMPORTS */ -import 'whatwg-fetch'; import 'url-polyfill'; +import 'whatwg-fetch'; diff --git a/imxweb/projects/custom-app/src/styles.scss b/imxweb/projects/custom-app/src/styles.scss index b2784f9a9..c71ce0b5d 100644 --- a/imxweb/projects/custom-app/src/styles.scss +++ b/imxweb/projects/custom-app/src/styles.scss @@ -1,13 +1,8 @@ /* You can add global styles to this file, and also import other style files */ @use '@angular/material' as mat; - -$material_icons_font_path: "~node_modules/@elemental-ui/core/assets/MaterialIcons"; -$cadence_font_path: "~node_modules/@elemental-ui/core/assets/Cadence"; -$source_sans_pro_font_path: "~node_modules/@elemental-ui/core/assets/Source_Sans_Pro"; - @import '@elemental-ui/core/src/styles/core.scss'; -.imx-dialog-panel-class > .mat-dialog-container { - background-color: mat.get-color-from-palette($asher-gray-palette, 200); +.imx-dialog-panel-class > .mat-mdc-dialog-container { + background-color: mat.m2-get-color-from-palette($asher-gray-palette, 200); } diff --git a/imxweb/projects/o3t/src/test.ts b/imxweb/projects/custom-app/src/test.ts similarity index 66% rename from imxweb/projects/o3t/src/test.ts rename to imxweb/projects/custom-app/src/test.ts index f7ac7d548..159c87d7d 100644 --- a/imxweb/projects/o3t/src/test.ts +++ b/imxweb/projects/custom-app/src/test.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,24 +26,14 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'core-js/es7/reflect'; -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +// Zone must be imported first. Be careful with prettier import organizing +import 'zone.js'; +import 'zone.js/testing'; + import { getTestBed } from '@angular/core/testing'; -import { - BrowserDynamicTestingModule, - platformBrowserDynamicTesting -} from '@angular/platform-browser-dynamic/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; +import 'core-js/es/reflect'; import { TestHelperModule } from 'qbm'; -declare const require: any; - // First, initialize the Angular testing environment. -getTestBed().initTestEnvironment( - [BrowserDynamicTestingModule, TestHelperModule], - platformBrowserDynamicTesting() -); -// Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); -// And load the modules. -context.keys().map(context); +getTestBed().initTestEnvironment([BrowserDynamicTestingModule, TestHelperModule], platformBrowserDynamicTesting()); diff --git a/imxweb/projects/custom-app/tsconfig-es5.app.json b/imxweb/projects/custom-app/tsconfig-es5.app.json index e1d0d6abd..036b8797e 100644 --- a/imxweb/projects/custom-app/tsconfig-es5.app.json +++ b/imxweb/projects/custom-app/tsconfig-es5.app.json @@ -1,6 +1,6 @@ { - "extends": "./tsconfig.app.json", - "compilerOptions": { - "target": "es2020" + "extends": "./tsconfig.app.json", + "compilerOptions": { + "target": "es2020" } -} \ No newline at end of file +} diff --git a/imxweb/projects/custom-app/tsconfig.app.json b/imxweb/projects/custom-app/tsconfig.app.json index d69a5c154..136843ce2 100644 --- a/imxweb/projects/custom-app/tsconfig.app.json +++ b/imxweb/projects/custom-app/tsconfig.app.json @@ -1,14 +1,13 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "strictNullChecks": true, "outDir": "../../out-tsc/app", "types": [] }, - "files": [ - "src/main.ts", - "src/polyfills.ts" - ], - "include": [ - "projects/custom-app/src/**/*.d.ts" - ] + "angularCompilerOptions": { + "strictTemplates": true + }, + "files": ["src/main.ts", "src/polyfills.ts"], + "include": ["projects/custom-app/src/**/*.d.ts"] } diff --git a/imxweb/projects/custom-app/tsconfig.spec.json b/imxweb/projects/custom-app/tsconfig.spec.json index a809b0a66..143838d98 100644 --- a/imxweb/projects/custom-app/tsconfig.spec.json +++ b/imxweb/projects/custom-app/tsconfig.spec.json @@ -2,17 +2,8 @@ "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "../../out-tsc/spec", - "types": [ - "jasmine", - "node" - ] + "types": ["jasmine", "node"] }, - "files": [ - "src/test.ts", - "src/polyfills.ts" - ], - "include": [ - "**/*.spec.ts", - "**/*.d.ts" - ] + "files": ["src/test.ts", "src/polyfills.ts"], + "include": ["**/*.spec.ts", "**/*.d.ts"] } diff --git a/imxweb/projects/custom-app/tslint.json b/imxweb/projects/custom-app/tslint.json deleted file mode 100644 index 8bab15f53..000000000 --- a/imxweb/projects/custom-app/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "imx", - "camelCase" - ], - "component-selector": [ - true, - "element", - "imx", - "kebab-case" - ] - } -} diff --git a/imxweb/projects/dpr/.compodocrc.json b/imxweb/projects/dpr/.compodocrc.json index f76fe1406..0ea099834 100644 --- a/imxweb/projects/dpr/.compodocrc.json +++ b/imxweb/projects/dpr/.compodocrc.json @@ -1,6 +1,6 @@ { "name": "IMX Web - DPR Library", - "output": "../../documentation/v92/dpr", + "output": "../../documentation/v93/dpr", "theme": "material", "assetsFolder": "../../compodoc/assets", "customLogo": "../../compodoc/assets/images/oneidentity-logo.png", diff --git a/imxweb/projects/dpr/.eslintrc.json b/imxweb/projects/dpr/.eslintrc.json new file mode 100644 index 000000000..b0e65b644 --- /dev/null +++ b/imxweb/projects/dpr/.eslintrc.json @@ -0,0 +1,35 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*", "**/*.spec.ts"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": ["imxweb/projects/dpr/tsconfig.lib.json", "imxweb/projects/dpr/tsconfig.spec.json"], + "createDefaultProgram": true + }, + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "imx", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "imx", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "rules": {} + } + ] +} diff --git a/imxweb/projects/dpr/karma.conf.js b/imxweb/projects/dpr/karma.conf.js index d7a9db062..3e53a3ac8 100644 --- a/imxweb/projects/dpr/karma.conf.js +++ b/imxweb/projects/dpr/karma.conf.js @@ -15,7 +15,7 @@ module.exports = function (config) { require('karma-junit-reporter'), ], client: { - clearContext: false // leave Jasmine Spec Runner output visible in browser + clearContext: false, // leave Jasmine Spec Runner output visible in browser }, coverageIstanbulReporter: { dir: require('path').join(__dirname, 'results'), @@ -23,14 +23,14 @@ module.exports = function (config) { fixWebpackSourcePaths: true, 'report-config': { html: { - subdir: 'coverage-html' + subdir: 'coverage-html', }, }, thresholds: { statements: 0, branches: 0, functions: 0, - lines: 0 + lines: 0, }, }, junitReporter: { @@ -40,13 +40,14 @@ module.exports = function (config) { port: 9876, captureTimeout: 210000, browserDisconnectTolerance: 3, - browserDisconnectTimeout : 210000, - browserNoActivityTimeout : 210000, + browserDisconnectTimeout: 210000, + browserNoActivityTimeout: 210000, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], singleRun: false, - restartOnFileChange: true + failOnEmptyTestSuite: false, + restartOnFileChange: true, }); }; diff --git a/imxweb/projects/dpr/ng-package.dynamic.json b/imxweb/projects/dpr/ng-package.dynamic.json index be54fd15a..a9cc85ea0 100644 --- a/imxweb/projects/dpr/ng-package.dynamic.json +++ b/imxweb/projects/dpr/ng-package.dynamic.json @@ -16,12 +16,11 @@ "@elemental-ui/core", "@ngx-translate/core", "@ngx-translate/http-loader", - "applicationinsights-js", "billboard.js" ], "dest": "../../html/dpr", "lib": { "entryFile": "src/public_api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] } } diff --git a/imxweb/projects/dpr/ng-package.json b/imxweb/projects/dpr/ng-package.json index 06cadfbe8..ba9a58230 100644 --- a/imxweb/projects/dpr/ng-package.json +++ b/imxweb/projects/dpr/ng-package.json @@ -3,6 +3,6 @@ "dest": "../../dist/dpr", "lib": { "entryFile": "src/public_api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] } } diff --git a/imxweb/projects/dpr/package.json b/imxweb/projects/dpr/package.json index e33682b8d..6c17aaa10 100644 --- a/imxweb/projects/dpr/package.json +++ b/imxweb/projects/dpr/package.json @@ -1,6 +1,6 @@ { "name": "dpr", - "version": "9.2.1", + "version": "9.3.0", "private": true, "bundledDependencies": [ "imx-api" diff --git a/imxweb/projects/dpr/project.json b/imxweb/projects/dpr/project.json new file mode 100644 index 000000000..248022787 --- /dev/null +++ b/imxweb/projects/dpr/project.json @@ -0,0 +1,64 @@ +{ + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "name": "dpr", + "sourceRoot": "projects/dpr/src", + "implicitDependencies": ["qbm"], + "projectType": "library", + "prefix": "imx", + "generators": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:ng-packagr", + "options": { + "tsConfig": "projects/dpr/tsconfig.lib.json", + "project": "projects/dpr/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "projects/dpr/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "projects/dpr/tsconfig.lib.json" + }, + "dynamic": { + "project": "projects/dpr/ng-package.dynamic.json" + }, + "remote-dev": { + "tsConfig": "projects/dpr/tsconfig.lib.json" + }, + "remote-qs": { + "tsConfig": "projects/dpr/tsconfig.lib.json" + } + }, + "outputs": ["{workspaceRoot}/dist/dpr"] + }, + "test": { + "executor": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/dpr/src/test.ts", + "tsConfig": "projects/dpr/tsconfig.spec.json", + "karmaConfig": "projects/dpr/karma.conf.js", + "stylePreprocessorOptions": { + "includePaths": [ + "./shared/assets", + "./shared/scss", + "./node_modules", + "./node_modules/@elemental-ui/cadence-icon", + "./node_modules/@elemental-ui/core" + ] + } + } + }, + "lint": { + "executor": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": ["projects/dpr/tsconfig.lib.json", "projects/dpr/tsconfig.spec.json"], + "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + } + } + } +} diff --git a/imxweb/projects/dpr/src/lib/api.service.ts b/imxweb/projects/dpr/src/lib/api.service.ts index 2395b0415..855d094da 100644 --- a/imxweb/projects/dpr/src/lib/api.service.ts +++ b/imxweb/projects/dpr/src/lib/api.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,12 +26,12 @@ import { Injectable } from '@angular/core'; -import { V2Client, TypedClient } from 'imx-api-dpr'; -import { ApiClient } from 'imx-qbm-dbts'; +import { V2Client, TypedClient } from '@imx-modules/imx-api-dpr'; +import { ApiClient } from '@imx-modules/imx-qbm-dbts'; import { AppConfigService, ClassloggerService, ImxTranslationProviderService } from 'qbm'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ApiService { private tc: TypedClient; @@ -51,7 +51,8 @@ export class ApiService { constructor( private readonly config: AppConfigService, private readonly logger: ClassloggerService, - private readonly translationProvider: ImxTranslationProviderService) { + private readonly translationProvider: ImxTranslationProviderService, + ) { try { this.logger.debug(this, 'Initializing DPR API service'); diff --git a/imxweb/projects/dpr/src/lib/outstanding/dependencies.component.html b/imxweb/projects/dpr/src/lib/outstanding/dependencies.component.html index ff1e978e1..15cfe2c60 100644 --- a/imxweb/projects/dpr/src/lib/outstanding/dependencies.component.html +++ b/imxweb/projects/dpr/src/lib/outstanding/dependencies.component.html @@ -1,13 +1,13 @@ -
    - +
    + #LDS#The following objects must also be added to the target system, as they depend on the objects to be added. - + #LDS#The following objects must also be deleted, as they depend on the objects to be deleted. - + {{ u.Display }} - {{ u.ObjectType }} @@ -15,7 +15,7 @@
    -
    diff --git a/imxweb/projects/dpr/src/lib/outstanding/dependencies.component.scss b/imxweb/projects/dpr/src/lib/outstanding/dependencies.component.scss index a81c04c69..b37f4f201 100644 --- a/imxweb/projects/dpr/src/lib/outstanding/dependencies.component.scss +++ b/imxweb/projects/dpr/src/lib/outstanding/dependencies.component.scss @@ -1,15 +1,8 @@ -.helper-alert -{ - display: block; -} - -.helper-alert, -.helper-list +.sidesheet-margin { margin: 1em; } -.object-type -{ +.object-type { font-style: italic; -} \ No newline at end of file +} diff --git a/imxweb/projects/dpr/src/lib/outstanding/dependencies.component.ts b/imxweb/projects/dpr/src/lib/outstanding/dependencies.component.ts index 82ee95337..2e3af0914 100644 --- a/imxweb/projects/dpr/src/lib/outstanding/dependencies.component.ts +++ b/imxweb/projects/dpr/src/lib/outstanding/dependencies.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,22 +26,21 @@ import { Component, Inject } from '@angular/core'; import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; -import { DependencyToConfirm, OutstandingAction } from 'imx-api-dpr'; +import { DependencyToConfirm, OutstandingAction } from '@imx-modules/imx-api-dpr'; @Component({ templateUrl: './dependencies.component.html', - styleUrls: ['./dependencies.component.scss'] + styleUrls: ['./dependencies.component.scss'], }) export class DependenciesComponent { - constructor( - @Inject(EUI_SIDESHEET_DATA) public data: { - dependencies: DependencyToConfirm[], - action: OutstandingAction + @Inject(EUI_SIDESHEET_DATA) + public data: { + dependencies: DependencyToConfirm[]; + action: OutstandingAction; }, - private sidesheetRef: EuiSidesheetRef - ) { - } + private sidesheetRef: EuiSidesheetRef, + ) {} public async save(): Promise { this.sidesheetRef.close(true); diff --git a/imxweb/projects/dpr/src/lib/outstanding/outstanding-object-entity.ts b/imxweb/projects/dpr/src/lib/outstanding/outstanding-object-entity.ts index 9713ec1dc..d5f488f58 100644 --- a/imxweb/projects/dpr/src/lib/outstanding/outstanding-object-entity.ts +++ b/imxweb/projects/dpr/src/lib/outstanding/outstanding-object-entity.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -23,16 +23,14 @@ * THIS SOFTWARE OR ITS DERIVATIVES. * */ -import { IReadValue, TypedEntity } from 'imx-qbm-dbts'; +import { IReadValue, TypedEntity } from '@imx-modules/imx-qbm-dbts'; export class OutstandingObjectEntity extends TypedEntity { - public readonly ObjectType: IReadValue = this.GetEntityValue('ObjectType'); public readonly ObjectKey: IReadValue = this.GetEntityValue('ObjectKey'); public readonly Display: IReadValue = this.GetEntityValue('Display'); public readonly CanPublish: IReadValue = this.GetEntityValue('CanPublish'); public readonly CanDelete: IReadValue = this.GetEntityValue('CanDelete'); - public readonly CanDeleteRestrictionReason: IReadValue = this.GetEntityValue("CanDeleteRestrictionReason"); - public readonly CanPublishRestrictionReason: IReadValue = this.GetEntityValue("CanPublishRestrictionReason"); - + public readonly CanDeleteRestrictionReason: IReadValue = this.GetEntityValue('CanDeleteRestrictionReason'); + public readonly CanPublishRestrictionReason: IReadValue = this.GetEntityValue('CanPublishRestrictionReason'); } diff --git a/imxweb/projects/dpr/src/lib/outstanding/outstanding.component.html b/imxweb/projects/dpr/src/lib/outstanding/outstanding.component.html index 2c3c2b645..be6fa5f7a 100644 --- a/imxweb/projects/dpr/src/lib/outstanding/outstanding.component.html +++ b/imxweb/projects/dpr/src/lib/outstanding/outstanding.component.html @@ -1,54 +1,77 @@ - - -

    {{ '#LDS#Heading Outstanding Objects' | translate }}

    - +

    {{ '#LDS#Heading Outstanding Objects' | translate }}

    + {{ LdsOutstandingText | translate }} -
    - + +
    + {{ '#LDS#Target system type' | translate }} - - + + {{ pr.GetEntity().GetDisplay() }} - - + + {{ '#LDS#Object type' | translate }} - - + + {{ pr.GetEntity().GetDisplay() }} ({{ pr.CountOutstanding.value }}) - - {{ !!selectedNamespaceTitle && !loadingTableData ? - ('#LDS#Show all outstanding objects of {0} ({1})' | translate | ldsReplace: selectedNamespaceTitle : tableDataCount): - ('#LDS#Show all outstanding objects of the target system type' | translate) + + {{ + !!selectedNamespaceTitle && !loadingTableData + ? ('#LDS#Show all outstanding objects of {0} ({1})' | translate | ldsReplace: selectedNamespaceTitle : tableDataCount) + : ('#LDS#Show all outstanding objects of the target system type' | translate) }}
    - + + - - + @@ -66,15 +89,13 @@

    {{ '#LDS#Heading Outstanding Objects' | translate }} - - + + -
    - -
    +
    + {{ '#LDS#Heading Outstanding Objects' | translate }} - - - - - - - - +
    +
    + + + + + + + + + +
    - + - +
    -
    +
    {{ node.name }}
    - + {{ node.name }}
    - +

    {{ '#LDS#Upload file attachments' | translate }}

    @@ -58,7 +83,7 @@

    {{ '#LDS#Upload file attachments' | translate }}

    • {{ '#LDS#Here you can attach files to this ticket.' | translate }}
    • {{ '#LDS#Optionally, you can create folders and add files to these folders.' | translate }}
    • -
    • {{ '#LDS#The maximum file size is:' | translate }} {{convertedMaxAttachmentSize}}
    • +
    • {{ '#LDS#The maximum file size is:' | translate }} {{ convertedMaxAttachmentSize }}
    diff --git a/imxweb/projects/hds/src/lib/calls/calls-attachment/calls-attachment.component.scss b/imxweb/projects/hds/src/lib/calls/calls-attachment/calls-attachment.component.scss index a7cf47ffc..1bd9de2f2 100644 --- a/imxweb/projects/hds/src/lib/calls/calls-attachment/calls-attachment.component.scss +++ b/imxweb/projects/hds/src/lib/calls/calls-attachment/calls-attachment.component.scss @@ -1,23 +1,25 @@ @import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/mixins'; + +:host{ + @include flex-column-container-fill(); +} .calls-attachment { - height: 100%; + @include flex-column-container($height: 100%); position: relative; - display: flex; - flex-direction: column; gap: 20px; - .mat-card { - display: flex; - flex-direction: column; - height: 100%; + + .mat-mdc-card { + @include flex-column-container($height: 100%); gap: 10px; border: 1px solid rgba($color-gray-100, 0.12); + margin: 3px; } + &__information { - display: flex; - flex-direction: column; + @include flex-column-container($height: 100%); justify-content: center; - height: 100%; background-color: $color-blue-10; border: 1px solid $color-blue-20; border-radius: 4px; @@ -35,10 +37,6 @@ font-size: 48px; font-weight: 700; } - .eui-icon { - font-size: 200px !important; - line-height: 200px !important; - } } .file-size { @@ -52,41 +50,10 @@ } } } - &__new-folder { - display: flex; - align-items: flex-start; - gap: 10px; - .eui-icon { - color: $color-blue-60; - cursor: pointer; - font-size: 20px; - padding-right: 4px; - &--disabled { - color: $color-gray-30; - cursor: default; - } - } - - .deleteIcon { - margin-left: auto; - .eui-icon{ - color:inherit - } - .eui-icon--disabled { - color: inherit; - } - } - input[type='file'] { - display: none; - } - .mat-primary{ - color: $color-gray-80; - } - } &__attachments-tree { height: 100%; - &--hidden{ + &--hidden { display: none; } } @@ -95,11 +62,7 @@ font-weight: 600; } - imx-sidenav-tree { - .snavigation { - width: 100% !important; - } - } + .loading-overlay { position: absolute; z-index: 999; @@ -113,36 +76,14 @@ } :host { .imx-tree-root > eui-icon { - color: $color-blue-60; margin-right: 10px; } - .imx-tree-root--selected > eui-icon { - color: $color-orange-60; - } - ::ng-deep{ - .calls-attachment{ - &__new-folder{ - button{ - .mat-button-wrapper{ - display: flex; - align-items: center; - } - } - } - } - } } // Theming .eui-dark-theme { :host { - .imx-tree-root > eui-icon { - color: $color-blue-40; - } - .imx-tree-root--selected > eui-icon { - color: $color-orange-40; - } .calls-attachment { &__information { background-color: $color-blue-90; @@ -153,7 +94,7 @@ } } &__new-folder { - .mat-primary{ + .mat-primary { color: $color-gray-0; } } diff --git a/imxweb/projects/hds/src/lib/calls/calls-attachment/calls-attachment.component.ts b/imxweb/projects/hds/src/lib/calls/calls-attachment/calls-attachment.component.ts index 1486cbeb3..b6c71dc8a 100644 --- a/imxweb/projects/hds/src/lib/calls/calls-attachment/calls-attachment.component.ts +++ b/imxweb/projects/hds/src/lib/calls/calls-attachment/calls-attachment.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,12 +25,14 @@ */ import { Overlay, OverlayRef } from '@angular/cdk/overlay'; -import { Component, ErrorHandler, Injector, Input, OnInit } from '@angular/core'; import { FlatTreeControl } from '@angular/cdk/tree'; -import { EuiDownloadDirective } from '@elemental-ui/core'; +import { HttpClient } from '@angular/common/http'; +import { Component, ElementRef, ErrorHandler, Injector, Input, OnInit } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; -import { HelpdeskConfig, V2ApiClientMethodFactory } from 'imx-api-hds'; -import { MethodDefinition } from 'imx-qbm-dbts'; +import { EuiDownloadDirective, EuiDownloadService } from '@elemental-ui/core'; +import { HelpdeskConfig, V2ApiClientMethodFactory } from '@imx-modules/imx-api-hds'; +import { MethodDefinition } from '@imx-modules/imx-qbm-dbts'; +import { TranslateService } from '@ngx-translate/core'; import { AppConfigService, DataSourceToolbarSettings, @@ -39,11 +41,9 @@ import { ElementalUiConfigService, LdsReplacePipe, } from 'qbm'; -import { HttpClient } from '@angular/common/http'; -import { CallsAttachmentActionType, CallsAttachmentNode, CallsAttachmentType } from './calls-attachment.model'; -import { CallsAttachmentServiceService } from './calls-attachment-service.service'; import { CallsAttachmentDialogComponent } from './calls-attachment-dialog/calls-attachment-dialog.component'; -import { TranslateService } from '@ngx-translate/core'; +import { CallsAttachmentServiceService } from './calls-attachment-service.service'; +import { CallsAttachmentActionType, CallsAttachmentNode, CallsAttachmentType } from './calls-attachment.model'; @Component({ selector: 'imx-calls-attachment', @@ -55,9 +55,9 @@ export class CallsAttachmentComponent implements OnInit { // Set FlatTreeControl node order and node expendable public treeControl = new FlatTreeControl( (node) => node.level, - (node) => node.type === CallsAttachmentType.folder + (node) => node.type === CallsAttachmentType.folder, ); - public attachmentTreeData: CallsAttachmentNode = null; + public attachmentTreeData: CallsAttachmentNode; public selectedNode: CallsAttachmentNode | null = null; public showAlert = false; public overlayRef: OverlayRef; @@ -69,7 +69,7 @@ export class CallsAttachmentComponent implements OnInit { public callsConfig: HelpdeskConfig; public hasMaxAttachmentSize: boolean; public convertedMaxAttachmentSize: string; - public noResultText = '#LDS#There are no attachments.' + public noResultText = '#LDS#There are no attachments.'; public initialized: boolean; constructor( @@ -83,7 +83,8 @@ export class CallsAttachmentComponent implements OnInit { private readonly elementalUiConfigService: ElementalUiConfigService, private readonly translator: TranslateService, private readonly errorHandler: ErrorHandler, - private readonly ldsReplace: LdsReplacePipe + private readonly ldsReplace: LdsReplacePipe, + private readonly downloadService: EuiDownloadService, ) {} // Setup apiControls and dynamicDataSource and get initial attachment tree data @@ -102,7 +103,7 @@ export class CallsAttachmentComponent implements OnInit { }, getChildren: async (node: CallsAttachmentNode, onlyCheck: boolean = true): Promise => { try { - if (node.children && (node.children.length > 0) && onlyCheck) { + if (node.children && node.children.length > 0 && onlyCheck) { return node.children; } node.isLoading = true; @@ -128,7 +129,7 @@ export class CallsAttachmentComponent implements OnInit { } // Open attachment dialog and call createFolder action - public async onAddFolder(node: CallsAttachmentNode) { + public async onAddFolder(node: CallsAttachmentNode | null) { this.dialog .open(CallsAttachmentDialogComponent, { data: { @@ -139,16 +140,18 @@ export class CallsAttachmentComponent implements OnInit { .afterClosed() .subscribe((dialogResult) => { if (!!dialogResult && !!dialogResult.action && !!dialogResult.value) { - this.createFolder(dialogResult.value, node); + this.createFolder(dialogResult.value, node || undefined); } }); } - public onDeleteItem(node: CallsAttachmentNode) { - if (node.type === CallsAttachmentType.file) { - this.openDeleteFileDialog(node); - } else { - this.openDeleteFolderDialog(node); + public onDeleteItem(node: CallsAttachmentNode | null) { + if (!!node) { + if (node.type === CallsAttachmentType.file) { + this.openDeleteFileDialog(node); + } else { + this.openDeleteFolderDialog(node); + } } } @@ -175,10 +178,14 @@ export class CallsAttachmentComponent implements OnInit { * @param node Selected node */ public async openDeleteFolderDialog(node: CallsAttachmentNode): Promise { - if(!node.children || node.children.length == 0){ + if (!node.children || node.children.length == 0) { const children = await this.attachmentService.getFilesOfDirectory(this.callId, node); - if(children.length > 0){ - this.errorHandler.handleError(this.translator.instant('#LDS#You cannot delete the folder. The folder is not empty. First delete the contents of the folder and try again.')) + if (children.length > 0) { + this.errorHandler.handleError( + this.translator.instant( + '#LDS#You cannot delete the folder. The folder is not empty. First delete the contents of the folder and try again.', + ), + ); return; } } @@ -198,21 +205,29 @@ export class CallsAttachmentComponent implements OnInit { } // Attaching a file inside a folder or at parent level - public async attachFile(val) { - if(!val){ + public async attachFile(event: EventTarget | null) { + const val = (event as HTMLInputElement)?.files; + if (!val) { return; } this.changeLoadingOverlayStatus(true); const fileToUpload: File | null = val[0]; - const fileExtension: string = fileToUpload?.name.split('.').pop(); + const fileExtension = fileToUpload?.name.split('.').pop(); try { - if (fileToUpload && this.callsConfig.AttachmentFileTypes.includes(`.${fileExtension}`)) { + if (fileToUpload && this.callsConfig.AttachmentFileTypes?.includes(`.${fileExtension}`)) { const obj = { name: fileToUpload?.name, type: CallsAttachmentType.file, path: '', file: fileToUpload }; const blob = new Blob([fileToUpload], { type: fileToUpload.type }); if (this.hasMaxAttachmentSize && fileToUpload.size > this.callsConfig?.AttachmentMaxSize) { - throw new Error(this.ldsReplace.transform(await this.translator.get('#LDS#You cannot upload the file. The file is too big. You can upload files up to {0}.').toPromise(), this.convertedMaxAttachmentSize)); + throw new Error( + this.ldsReplace.transform( + await this.translator + .get('#LDS#You cannot upload the file. The file is too big. You can upload files up to {0}.') + .toPromise(), + this.convertedMaxAttachmentSize, + ), + ); } //when attaching inside a parent if (this.selectedNode) { @@ -224,7 +239,14 @@ export class CallsAttachmentComponent implements OnInit { fileToUpload && this.uploadFile(obj.path, blob, this.attachmentTreeData); } } else { - throw new Error(this.ldsReplace.transform(await this.translator.get('#LDS#You cannot upload the file. The maximum number of files has been reached. You can attach a total of {0} files.').toPromise(), this.callsConfig.AttachmentFileTypes.join(", "))); + throw new Error( + this.ldsReplace.transform( + await this.translator + .get('#LDS#You cannot upload the file. The maximum number of files has been reached. You can attach a total of {0} files.') + .toPromise(), + this.callsConfig.AttachmentFileTypes?.join(', '), + ), + ); } } catch (error) { this.errorHandler.handleError(error); @@ -237,13 +259,19 @@ export class CallsAttachmentComponent implements OnInit { public async downloadFile(node: CallsAttachmentNode) { const def = new MethodDefinition(new V2ApiClientMethodFactory().portal_calls_attachments_file_get(this.callId, node.path)); - const directive = new EuiDownloadDirective(null /* no element */, this.http, this.overlay, this.injector); + const directive = new EuiDownloadDirective( + new ElementRef('') /* no element */, + this.http, + this.overlay, + this.injector, + this.downloadService, + ); directive.downloadOptions = { ...this.elementalUiConfigService.Config.downloadOptions, fileMimeType: '', url: this.config.BaseUrl + def.path, disableElement: false, - fileName: node.name + fileName: node.name, }; directive.onClick(); @@ -251,7 +279,7 @@ export class CallsAttachmentComponent implements OnInit { public onNodeSelection(node: CallsAttachmentNode) { this.attachmentTreeData.isSelected = false; - this.updateSelectionState(this.attachmentTreeData.children); + this.updateSelectionState(this.attachmentTreeData.children || []); if (this.selectedNode == node) { this.selectedNode = null; } else { @@ -273,8 +301,8 @@ export class CallsAttachmentComponent implements OnInit { return elementFound; } - public hasChild(_: number, node: CallsAttachmentNode): CallsAttachmentNode[] { - return node.children; + public hasChild(_: number, node: CallsAttachmentNode): boolean { + return !!node.children ? node.children?.length > 0 : false; } public onAlertDismissed() { @@ -289,46 +317,59 @@ export class CallsAttachmentComponent implements OnInit { // If isLoading return true the progressbar next to the node will shown public isLoading(node: CallsAttachmentNode): boolean { - return node.isLoading; + return !!node.isLoading; } // Only empty folders and files can be deleted - public isNodeDeletable(node: CallsAttachmentNode): boolean { + public isNodeDeletable(node: CallsAttachmentNode | null): boolean { return this.isNodeFolderEmpty(node) || this.isNodeFile(node); - } // Show information instead of attachment file tree - public get showInformation():boolean{ - return this.attachmentTreeData?.children.length === 0; + public get showInformation(): boolean { + return this.attachmentTreeData?.children?.length === 0; } - private async uploadFile(path: string, blob: Blob, node: CallsAttachmentNode): Promise{ - try{ - await this.attachmentService.uploadFile(this.callId,path, blob); + private async uploadFile(path: string, blob: Blob, node: CallsAttachmentNode): Promise { + try { + await this.attachmentService.uploadFile(this.callId, path, blob); await this.apiControls.getChildren(node, false); this.expandNode(node); } catch { - this.errorHandler.handleError(this.translator.instant('#LDS#You cannot upload the file. You do not have permission to upload files.')) + this.errorHandler.handleError( + this.translator.instant('#LDS#You cannot upload the file. You do not have permission to upload files.'), + ); } } private async byteConverter(byte: number): Promise { - if (byte >= 1073741824) return this.ldsReplace.transform(await this.translator.get('#LDS#{0} GB').toPromise(), this.getFlooredFixed(byte / Math.pow(1024,3))); else - if (byte >= 1048576) return this.ldsReplace.transform(await this.translator.get('#LDS#{0} MB').toPromise(), this.getFlooredFixed(byte / Math.pow(1024,2))); else - if (byte >= 1024) return this.ldsReplace.transform(await this.translator.get('#LDS#{0} kB').toPromise(), this.getFlooredFixed(byte / Math.pow(1024,1))); else - return this.ldsReplace.transform(await this.translator.get('#LDS#{0} byte').toPromise(), byte); + if (byte >= 1073741824) + return this.ldsReplace.transform( + await this.translator.get('#LDS#{0} GB').toPromise(), + this.getFlooredFixed(byte / Math.pow(1024, 3)), + ); + else if (byte >= 1048576) + return this.ldsReplace.transform( + await this.translator.get('#LDS#{0} MB').toPromise(), + this.getFlooredFixed(byte / Math.pow(1024, 2)), + ); + else if (byte >= 1024) + return this.ldsReplace.transform( + await this.translator.get('#LDS#{0} kB').toPromise(), + this.getFlooredFixed(byte / Math.pow(1024, 1)), + ); + else return this.ldsReplace.transform(await this.translator.get('#LDS#{0} byte').toPromise(), byte); } private getFlooredFixed(value: number, fractionDigits: number = 2): string { return (Math.floor(value * Math.pow(10, fractionDigits)) / Math.pow(10, fractionDigits)).toFixed(fractionDigits); } - private isNodeFolderEmpty(node: CallsAttachmentNode): boolean { + private isNodeFolderEmpty(node: CallsAttachmentNode | null): boolean { return !!node && node.type === CallsAttachmentType.folder && (!node.children || node.children.length === 0); } - private isNodeFile(node: CallsAttachmentNode): boolean { + private isNodeFile(node: CallsAttachmentNode | null): boolean { return !!node && node.type === CallsAttachmentType.file; } @@ -340,12 +381,14 @@ export class CallsAttachmentComponent implements OnInit { if (parent) { await this.apiControls.getChildren(parent, false); this.expandNode(parent); - }else{ - this.attachmentTreeData.children = this.attachmentTreeData.children.filter((childNode) => childNode.name !== node.name); + } else { + this.attachmentTreeData.children = this.attachmentTreeData.children?.filter((childNode) => childNode.name !== node.name); this.expandNode(this.attachmentTreeData); } } catch { - this.errorHandler.handleError(this.translator.instant('#LDS#You cannot delete the folder. You do not have permission to delete folders.')) + this.errorHandler.handleError( + this.translator.instant('#LDS#You cannot delete the folder. You do not have permission to delete folders.'), + ); } finally { this.changeLoadingOverlayStatus(false); this.selectedNode = null; @@ -353,7 +396,7 @@ export class CallsAttachmentComponent implements OnInit { } private async deleteFile(node: CallsAttachmentNode) { - try{ + try { this.changeLoadingOverlayStatus(true); await this.attachmentService.deleteFile(this.callId, node.path); if (node.parent) { @@ -361,9 +404,11 @@ export class CallsAttachmentComponent implements OnInit { this.expandNode(node.parent); } this.selectedNode = null; - }catch{ - this.errorHandler.handleError(this.translator.instant('#LDS#You cannot delete the attachment. You do not have permission to delete attachments.')) - }finally{ + } catch { + this.errorHandler.handleError( + this.translator.instant('#LDS#You cannot delete the attachment. You do not have permission to delete attachments.'), + ); + } finally { this.changeLoadingOverlayStatus(false); } } @@ -381,22 +426,26 @@ export class CallsAttachmentComponent implements OnInit { }; if (node) { if (this.isDuplicateName(val, node.children)) { - this.showAlertMessage('#LDS#You cannot create the folder. A folder with the entered name already exists. Enter a different folder name.'); + this.showAlertMessage( + '#LDS#You cannot create the folder. A folder with the entered name already exists. Enter a different folder name.', + ); } else { obj.path = node.path + '/' + val; obj.level = node.level + 1; await this.apiControls.getChildren(node); await this.attachmentService.createDirectory(this.callId, obj.path); - node.children.unshift(obj); + node.children?.unshift(obj); this.expandNode(node); } } else { if (this.isDuplicateName(val, this.attachmentTreeData.children)) { - this.showAlertMessage('#LDS#You cannot create the folder. A folder with the entered name already exists. Enter a different folder name.'); + this.showAlertMessage( + '#LDS#You cannot create the folder. A folder with the entered name already exists. Enter a different folder name.', + ); } else { obj.path = `${val}`; await this.attachmentService.createDirectory(this.callId, obj.path); - this.attachmentTreeData.children.unshift(obj); + this.attachmentTreeData.children?.unshift(obj); this.expandNode(this.attachmentTreeData); } } diff --git a/imxweb/projects/hds/src/lib/calls/calls-attachment/calls-attachment.model.ts b/imxweb/projects/hds/src/lib/calls/calls-attachment/calls-attachment.model.ts index d6ffc5992..1fcd956a1 100644 --- a/imxweb/projects/hds/src/lib/calls/calls-attachment/calls-attachment.model.ts +++ b/imxweb/projects/hds/src/lib/calls/calls-attachment/calls-attachment.model.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/hds/src/lib/calls/calls-history-sidesheet/calls-history-sidesheet.component.html b/imxweb/projects/hds/src/lib/calls/calls-history-sidesheet/calls-history-sidesheet.component.html index 0a88b51ea..b7df58f19 100644 --- a/imxweb/projects/hds/src/lib/calls/calls-history-sidesheet/calls-history-sidesheet.component.html +++ b/imxweb/projects/hds/src/lib/calls/calls-history-sidesheet/calls-history-sidesheet.component.html @@ -1,12 +1,11 @@ -
    -
    -
    - -
    - -
    -
    -
    -
    -
    \ No newline at end of file +
    + +
    + +
    +
    +
    diff --git a/imxweb/projects/hds/src/lib/calls/calls-history-sidesheet/calls-history-sidesheet.component.scss b/imxweb/projects/hds/src/lib/calls/calls-history-sidesheet/calls-history-sidesheet.component.scss index 42b005ccf..e7de9633e 100644 --- a/imxweb/projects/hds/src/lib/calls/calls-history-sidesheet/calls-history-sidesheet.component.scss +++ b/imxweb/projects/hds/src/lib/calls/calls-history-sidesheet/calls-history-sidesheet.component.scss @@ -7,73 +7,6 @@ display: flex; flex-direction: column; overflow: hidden; - - &__tab-content { - display: flex; - flex-direction: column; - height: 100%; - } - - &__tab-content-body { - flex: 1; - padding: 32px; - overflow: auto; - - &.no-padding { - padding: 0; - } - - .helper-alert { - display: flex; - margin-bottom: 20px; - - - span { - display: block; - } - } - } - - &__action-buttons { - display: flex; - justify-content: flex-end; - background-color: $white; - padding: 16px 32px; - margin: 16px 32px; - - .justify-start { - margin-right: auto; - } - - button:not(:last-of-type):not(.justify-start) { - margin-right: 10px; - } - - button:disabled:hover { - cursor: not-allowed; - } - - .help-icon { - color: $iris-blue; - font-size: 16px; - margin-left: 5px; - cursor: auto; - } - - } - - ::ng-deep .mat-tab-group { - height: 100%; - - .mat-tab-header { - padding: 0 32px; - background-color: $white; - } - - .mat-tab-body-wrapper { - height: 100%; - } - } } @media screen and (max-width: 480px) { @@ -81,55 +14,5 @@ &__tab-content { display: block; } - - &__action-buttons { - flex-direction: column-reverse; - - button { - width: 100%; - margin-bottom: 10px; - margin-right: 0; - } - - .justify-start { - margin-right: 0; - } - } - } -} - -.eui-dark-theme { - :host { - .calls-history-sidesheet { - background-color: $color-gray-80; - - ::ng-deep .mat-tab-group { - .mat-tab-header { - background-color: $color-gray-80; - } - } - - &__action-buttons { - background-color: $color-gray-70; - } - } - } -} - -.eui-contrast-theme { - :host { - .calls-history-sidesheet { - background-color: $color-gray-100; - - ::ng-deep .mat-tab-group { - .mat-tab-header { - background-color: $color-gray-90; - } - } - - &__action-buttons { - background-color: $color-gray-80; - } - } } } diff --git a/imxweb/projects/hds/src/lib/calls/calls-history-sidesheet/calls-history-sidesheet.component.ts b/imxweb/projects/hds/src/lib/calls/calls-history-sidesheet/calls-history-sidesheet.component.ts index 052471e60..7da8bf745 100644 --- a/imxweb/projects/hds/src/lib/calls/calls-history-sidesheet/calls-history-sidesheet.component.ts +++ b/imxweb/projects/hds/src/lib/calls/calls-history-sidesheet/calls-history-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,38 +26,36 @@ import { Component, Inject, OnInit } from '@angular/core'; import { EuiLoadingService, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { BaseCdr, ColumnDependentReference} from 'qbm'; -import { PortalCallsHistory } from 'imx-api-hds'; +import { BaseCdr, ColumnDependentReference } from 'qbm'; +import { PortalCallsHistory } from '@imx-modules/imx-api-hds'; @Component({ selector: 'imx-calls-history-sidesheet', templateUrl: './calls-history-sidesheet.component.html', - styleUrls: ['./calls-history-sidesheet.component.scss'] + styleUrls: ['./calls-history-sidesheet.component.scss'], }) export class CallsHistorySidesheetComponent implements OnInit { - public readonly detailsFormGroup: UntypedFormGroup; public cdrList: ColumnDependentReference[] = []; - constructor( + constructor( @Inject(EUI_SIDESHEET_DATA) public portalCallsHistory: PortalCallsHistory, private readonly euiLoadingService: EuiLoadingService, public formBuilder: UntypedFormBuilder, ) { this.detailsFormGroup = new UntypedFormGroup({ formArray: formBuilder.array([]) }); - } + } - public async ngOnInit(): Promise { + public async ngOnInit(): Promise { await this.setup(); } public async setup(): Promise { let entity = this.portalCallsHistory.GetEntity(); let columnNames = await this.getColumnNames(); - columnNames.forEach(columnName => { + columnNames.forEach((columnName) => { let column = entity.GetColumn(columnName); - if (column) - this.cdrList.push(new BaseCdr(column)); + if (column) this.cdrList.push(new BaseCdr(column)); }); } diff --git a/imxweb/projects/hds/src/lib/calls/calls-history/calls-history.component.html b/imxweb/projects/hds/src/lib/calls/calls-history/calls-history.component.html index 4f5a75613..d7c976d10 100644 --- a/imxweb/projects/hds/src/lib/calls/calls-history/calls-history.component.html +++ b/imxweb/projects/hds/src/lib/calls/calls-history/calls-history.component.html @@ -1,17 +1,8 @@ - - + {{ '#LDS#Here you can get an overview of all changes to this ticket.' | translate }} - - - -
    - - -
    - - -
    \ No newline at end of file + + + + + diff --git a/imxweb/projects/hds/src/lib/calls/calls-history/calls-history.component.scss b/imxweb/projects/hds/src/lib/calls/calls-history/calls-history.component.scss index 59a630f8f..b188afba9 100644 --- a/imxweb/projects/hds/src/lib/calls/calls-history/calls-history.component.scss +++ b/imxweb/projects/hds/src/lib/calls/calls-history/calls-history.component.scss @@ -1,8 +1,6 @@ -.helper-alert { - display: flex; - margin-bottom: 20px; +@import 'base/mixins'; - span { - display: block; - } - } \ No newline at end of file + +:host { +@include flex-column-container-fill(); +} \ No newline at end of file diff --git a/imxweb/projects/hds/src/lib/calls/calls-history/calls-history.component.ts b/imxweb/projects/hds/src/lib/calls/calls-history/calls-history.component.ts index 845c47d8a..ee528c8f4 100644 --- a/imxweb/projects/hds/src/lib/calls/calls-history/calls-history.component.ts +++ b/imxweb/projects/hds/src/lib/calls/calls-history/calls-history.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,40 +24,35 @@ * */ -import { Component, OnInit, Input, ErrorHandler } from '@angular/core'; -import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; +import { Component, Input, OnInit } from '@angular/core'; +import { EuiSidesheetService } from '@elemental-ui/core'; +import { PortalCalls, PortalCallsHistory } from '@imx-modules/imx-api-hds'; +import { CollectionLoadParameters, EntitySchema, IClientProperty, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { TranslateService } from '@ngx-translate/core'; -import { PortalCalls, PortalCallsHistory } from 'imx-api-hds'; +import { DataViewInitParameters, DataViewSource, calculateSidesheetWidth } from 'qbm'; import { HdsApiService } from '../../hds-api-client.service'; -import { DataSourceToolbarSettings, ClassloggerService, SettingsService, DataSourceWrapper, DataTableGroupedData, DataSourceToolbarFilter } from 'qbm'; -import { CollectionLoadParameters, EntitySchema, IClientProperty } from 'imx-qbm-dbts'; import { CallsHistorySidesheetComponent } from '../calls-history-sidesheet/calls-history-sidesheet.component'; @Component({ selector: 'imx-calls-history', templateUrl: './calls-history.component.html', - styleUrls: ['./calls-history.component.scss'] + styleUrls: ['./calls-history.component.scss'], + providers: [DataViewSource], }) export class CallsHistoryComponent implements OnInit { - - @Input() public ticket: any; + @Input() public ticket: PortalCalls; public entitySchema: EntitySchema; - public dstSettings: DataSourceToolbarSettings; public displayedColumns: IClientProperty[] = []; - public collectionLoadParameters: CollectionLoadParameters; - public filterOptions: DataSourceToolbarFilter[] = []; constructor( private readonly sidesheet: EuiSidesheetService, private readonly translate: TranslateService, - private readonly errorHandler: ErrorHandler, private readonly hdsApiService: HdsApiService, - private readonly settingsService: SettingsService, - private readonly euiLoadingService: EuiLoadingService) { - this.collectionLoadParameters = { PageSize: this.settingsService.DefaultPageSize, StartIndex: 0 }; - this.entitySchema = this.hdsApiService.typedClient.PortalCallsHistory.GetSchema(); - } - + public dataSource: DataViewSource, + ) { + this.entitySchema = this.hdsApiService.typedClient.PortalCallsHistory.GetSchema(); + } + public async ngOnInit() { this.displayedColumns = [ this.entitySchema.Columns['UID_TroubleTicket'], @@ -66,53 +61,27 @@ export class CallsHistoryComponent implements OnInit { this.entitySchema.Columns['UID_TroubleCallState'], this.entitySchema.Columns['ObjectKeySupporter'], ]; - await this.loadHistory(); - } - - private async loadHistory(): Promise { - let overlayRef = this.euiLoadingService.show(); - try { - if (this.ticket?.EntityKeysData?.Keys?.length > 0) - { - let history = await this.hdsApiService.typedClient.PortalCallsHistory.Get(this.ticket.EntityKeysData.Keys[0],this.collectionLoadParameters); - this.dstSettings = { - displayedColumns: this.displayedColumns, - dataSource: history, - entitySchema: this.entitySchema, - navigationState: this.collectionLoadParameters, - filters: this.filterOptions, - }; - } - } - catch (error) { - this.errorHandler.handleError(error); - } - finally { - this.euiLoadingService.hide(overlayRef); - } - } - - public async onNavigationStateChanged(collectionLoadParameters?: CollectionLoadParameters): Promise { - if (collectionLoadParameters) - this.collectionLoadParameters = collectionLoadParameters; - - await this.loadHistory(); - } - - public async onSearch(keywords: string): Promise { - this.collectionLoadParameters.StartIndex = 0; - this.collectionLoadParameters.search = keywords; - await this.loadHistory(); + const dataViewInitParameters: DataViewInitParameters = { + execute: (params: CollectionLoadParameters, signal: AbortSignal): Promise> => + this.hdsApiService.typedClient.PortalCallsHistory.Get(this.ticket?.EntityKeysData?.Keys?.[0] || '', params), + schema: this.entitySchema, + columnsToDisplay: this.displayedColumns, + highlightEntity: (identity: PortalCallsHistory) => { + this.onSelected(identity); + }, + }; + this.dataSource.init(dataViewInitParameters); } public async onSelected(history: PortalCallsHistory): Promise { if (history) { let title = await this.translate.get('#LDS#Heading View Change Details').toPromise(); - let sidesheetRef = this.sidesheet.open(CallsHistorySidesheetComponent, { + this.sidesheet.open(CallsHistorySidesheetComponent, { testId: 'calls-history-sidesheet', title: title, + subTitle: history.GetEntity().GetDisplay(), padding: '0px', - width: 'max(400px, 40%)', + width: calculateSidesheetWidth(700, 0.4), data: history, }); } diff --git a/imxweb/projects/hds/src/lib/calls/calls-sidesheet/calls-sidesheet.component.html b/imxweb/projects/hds/src/lib/calls/calls-sidesheet/calls-sidesheet.component.html index 13c9a836a..f1751138b 100644 --- a/imxweb/projects/hds/src/lib/calls/calls-sidesheet/calls-sidesheet.component.html +++ b/imxweb/projects/hds/src/lib/calls/calls-sidesheet/calls-sidesheet.component.html @@ -1,35 +1,43 @@
    - + -
    -
    - - -
    - -
    -
    -
    -
    - -
    +
    + + +
    + +
    +
    +
    +
    +
    -
    -
    - -
    +
    +
    -
    -
    - -
    +
    +
    diff --git a/imxweb/projects/hds/src/lib/calls/calls-sidesheet/calls-sidesheet.component.scss b/imxweb/projects/hds/src/lib/calls/calls-sidesheet/calls-sidesheet.component.scss index 2c2546751..991eed524 100644 --- a/imxweb/projects/hds/src/lib/calls/calls-sidesheet/calls-sidesheet.component.scss +++ b/imxweb/projects/hds/src/lib/calls/calls-sidesheet/calls-sidesheet.component.scss @@ -3,38 +3,13 @@ .calls-sidesheet { overflow: hidden; - height:100%; - - &__tab-content { - display: flex; - flex-direction: column; - height: 100%; - } - - &__tab-content-body { - - .helper-alert { - display: flex; - margin-bottom: 20px; - } - } - - ::ng-deep .mat-tab-group { - height: 100%; - .mat-tab-header { - padding: 0 20px; - } - - .mat-tab-body-wrapper { - height: 100%; - } - } + height: 100%; } -imx-help-contextual{ +imx-help-contextual { justify-content: end; } -.eui-sidesheet-content.eui-sidesheet-content--no-padding-top{ +.eui-sidesheet-content.eui-sidesheet-content--no-padding-top { padding-top: 0; } @@ -43,55 +18,5 @@ imx-help-contextual{ &__tab-content { display: block; } - - &__action-buttons { - flex-direction: column-reverse; - - button { - width: 100%; - margin-bottom: 10px; - margin-right: 0; - } - - .justify-start { - margin-right: 0; - } - } - } -} - -.eui-dark-theme { - :host { - .calls-sidesheet { - background-color: $color-gray-80; - - ::ng-deep .mat-tab-group { - .mat-tab-header { - background-color: $color-gray-80; - } - } - - &__action-buttons { - background-color: $color-gray-70; - } - } - } -} - -.eui-contrast-theme { - :host { - .calls-sidesheet { - background-color: $color-gray-100; - - ::ng-deep .mat-tab-group { - .mat-tab-header { - background-color: $color-gray-100; - } - } - - &__action-buttons { - background-color: $color-gray-90; - } - } } } diff --git a/imxweb/projects/hds/src/lib/calls/calls-sidesheet/calls-sidesheet.component.ts b/imxweb/projects/hds/src/lib/calls/calls-sidesheet/calls-sidesheet.component.ts index 890e967cf..af8229993 100644 --- a/imxweb/projects/hds/src/lib/calls/calls-sidesheet/calls-sidesheet.component.ts +++ b/imxweb/projects/hds/src/lib/calls/calls-sidesheet/calls-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,25 +25,24 @@ */ import { Component, ErrorHandler, Inject, OnInit } from '@angular/core'; -import { EuiLoadingService, EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { PortalCalls } from 'imx-api-hds'; -import { BaseCdr, ColumnDependentReference, SnackBarService, ConfirmationService, TextContainer, HELP_CONTEXTUAL } from 'qbm'; +import { EUI_SIDESHEET_DATA, EuiLoadingService, EuiSidesheetRef } from '@elemental-ui/core'; +import { PortalCalls } from '@imx-modules/imx-api-hds'; +import { BaseCdr, ColumnDependentReference, ConfirmationService, HELP_CONTEXTUAL, SnackBarService, TextContainer } from 'qbm'; import { ProjectConfigurationService } from 'qer'; import { HdsApiService } from '../../hds-api-client.service'; export interface CallsSidesheetData { isNew: boolean; - ticket: PortalCalls + ticket: PortalCalls; } @Component({ selector: 'imx-calls-sidesheet', templateUrl: './calls-sidesheet.component.html', - styleUrls: ['./calls-sidesheet.component.scss'] + styleUrls: ['./calls-sidesheet.component.scss'], }) export class CallsSidesheetComponent implements OnInit { - public readonly detailsFormGroup: UntypedFormGroup; public cdrList: ColumnDependentReference[] = []; public ticket: PortalCalls; @@ -63,7 +62,7 @@ export class CallsSidesheetComponent implements OnInit { this.detailsFormGroup = new UntypedFormGroup({ formArray: formBuilder.array([]) }); this.sidesheetRef.closeClicked().subscribe(async () => { - if (!this.detailsFormGroup.dirty || await this.confirmationService.confirmLeaveWithUnsavedChanges()) { + if (!this.detailsFormGroup.dirty || (await this.confirmationService.confirmLeaveWithUnsavedChanges())) { this.sidesheetRef.close(); } }); @@ -77,11 +76,10 @@ export class CallsSidesheetComponent implements OnInit { this.ticket = this.sidesheetData.ticket; let entity = this.sidesheetData.ticket.GetEntity(); let columnNames = await this.getColumnNames(); - columnNames.forEach(columnName => { + columnNames.forEach((columnName) => { let column = entity.GetColumn(columnName); - if (column) - this.cdrList.push(new BaseCdr(column)); + if (column) this.cdrList.push(new BaseCdr(column)); }); } @@ -91,15 +89,11 @@ export class CallsSidesheetComponent implements OnInit { try { let config = await this.projectConfigurationService.getConfig(); - if (this.sidesheetData.isNew) - columnNames = config.OwnershipConfig.PrimaryFields['TroubleTicket']; - else - columnNames = config.OwnershipConfig.EditableFields['TroubleTicket']; - } - catch (error) { + if (this.sidesheetData.isNew) columnNames = config.OwnershipConfig?.PrimaryFields?.['TroubleTicket'] || []; + else columnNames = config.OwnershipConfig?.EditableFields?.['TroubleTicket'] || []; + } catch (error) { this.errorHandler.handleError(error); - } - finally { + } finally { this.euiLoadingService.hide(overlayRef); } return columnNames; @@ -109,23 +103,30 @@ export class CallsSidesheetComponent implements OnInit { if (this.detailsFormGroup.valid) { let overlayRef = this.euiLoadingService.show(); try { - await this.sidesheetData.ticket.GetEntity().Commit(true).then(() => { - this.detailsFormGroup.markAsPristine(); - this.sidesheetRef.close(); - let textContainer: TextContainer; - - if (this.sidesheetData.isNew) - textContainer = { key: '#LDS#The ticket with the number {0} has been successfully created.', parameters: [this.sidesheetData.ticket.CallNumber.value.toString()] }; - else - textContainer = { key: '#LDS#The ticket has been successfully saved.', parameters: [this.sidesheetData.ticket.CallNumber.value.toString()] }; - - this.snackBarService.open(textContainer, '#LDS#Close', { duration: 6000 }); - }); - } - catch (error) { + await this.sidesheetData.ticket + .GetEntity() + .Commit(true) + .then(() => { + this.detailsFormGroup.markAsPristine(); + this.sidesheetRef.close(); + let textContainer: TextContainer; + + if (this.sidesheetData.isNew) + textContainer = { + key: '#LDS#The ticket with the number {0} has been successfully created.', + parameters: [this.sidesheetData.ticket.CallNumber.value.toString()], + }; + else + textContainer = { + key: '#LDS#The ticket has been successfully saved.', + parameters: [this.sidesheetData.ticket.CallNumber.value.toString()], + }; + + this.snackBarService.open(textContainer, '#LDS#Close', { duration: 6000 }); + }); + } catch (error) { this.errorHandler.handleError(error); - } - finally { + } finally { this.euiLoadingService.hide(overlayRef); } } @@ -135,7 +136,7 @@ export class CallsSidesheetComponent implements OnInit { this.sidesheetRef.close(); } - private get getTicketId():string{ + public get getTicketId(): string { return !!this.sidesheetData.ticket.EntityKeysData.Keys ? this.sidesheetData.ticket.EntityKeysData.Keys[0] : ''; } } diff --git a/imxweb/projects/hds/src/lib/calls/calls.component.html b/imxweb/projects/hds/src/lib/calls/calls.component.html index dfeba9923..9297621cf 100644 --- a/imxweb/projects/hds/src/lib/calls/calls.component.html +++ b/imxweb/projects/hds/src/lib/calls/calls.component.html @@ -1,60 +1,145 @@ -

    - {{ '#LDS#Heading Tickets' | translate }} - -

    +
    +

    + {{ '#LDS#Heading Tickets' | translate }} + +

    + +
    - - + - -
    - - - - -
    - {{ getTicketItemValue(item, 'DescriptionHotline') }} -
    -
    -
    - - - - - - - - - - - - - -
    - {{ getTicketItemValue(item, 'ActionHotline') }} -
    -
    -
    -
    -
    - + + + {{ entitySchema?.Columns?.DescriptionHotline?.Display }} + + +
    + {{ getTicketItemValue(item, 'DescriptionHotline') }} +
    + +
    + + + {{ entitySchema?.Columns?.ActionHotline?.Display }} + + +
    + {{ getTicketItemValue(item, 'ActionHotline') }} +
    + +
    + + + {{ entitySchema?.Columns?.CallNumber?.Display }} + + +
    {{ item.CallNumber.Column.GetDisplayValue() }}
    + +
    + + + {{ entitySchema?.Columns?.IsClosed?.Display }} + + +
    {{ item.entity.columns.IsClosed.GetDisplayValue() }}
    + +
    + + + {{ entitySchema?.Columns?.IsHistory?.Display }} + + +
    {{ item.entity.columns.IsHistory.GetDisplayValue() }}
    + +
    + + + {{ entitySchema?.Columns?.IsHold?.Display }} + + +
    {{ item.entity.columns.IsHold.GetDisplayValue() }}
    + +
    + + + {{ entitySchema?.Columns?.UID_PersonHotline?.Display }} + + +
    {{ item.entity.columns.UID_PersonHotline.GetDisplayValue() }}
    + +
    + + + {{ entitySchema?.Columns?.UID_PersonInTrouble?.Display }} + + +
    {{ item.UID_PersonInTrouble.Column.GetDisplayValue() }}
    + +
    + + + {{ entitySchema?.Columns?.UID_PersonInTroubleAdditional?.Display }} + + +
    {{ item.entity.columns.UID_PersonInTroubleAdditional.GetDisplayValue() }}
    + +
    + + + {{ entitySchema?.Columns?.UID_TroubleCallState?.Display }} + + +
    {{ item.entity.columns.UID_TroubleCallState.GetDisplayValue() }}
    + +
    + + + {{ entitySchema?.Columns?.UID_TroubleEscalationLevel_E?.Display }} + + +
    {{ item.entity.columns.UID_TroubleEscalationLevel_E.GetDisplayValue() }}
    + +
    + + + {{ entitySchema?.Columns?.UID_TroubleProduct?.Display }} + + +
    {{ item.entity.columns.UID_TroubleProduct.GetDisplayValue() }}
    + +
    + + + {{ entitySchema?.Columns?.UID_TroubleSeverity?.Display }} + + +
    {{ item.UID_TroubleSeverity.Column.GetDisplayValue() }}
    + +
    + + + {{ entitySchema?.Columns?.UID_TroubleTypeHotline?.Display }} + + +
    {{ item.entity.columns.UID_TroubleTypeHotline.GetDisplayValue() }}
    + +
    + +
    -
    - +
    - - - -
    - - - - -
    diff --git a/imxweb/projects/o3t/src/lib/teams/team-channel-details/team-channel-details.component.ts b/imxweb/projects/o3t/src/lib/teams/team-channel-details/team-channel-details.component.ts deleted file mode 100644 index 16450c824..000000000 --- a/imxweb/projects/o3t/src/lib/teams/team-channel-details/team-channel-details.component.ts +++ /dev/null @@ -1,83 +0,0 @@ -/* - * ONE IDENTITY LLC. PROPRIETARY INFORMATION - * - * This software is confidential. One Identity, LLC. or one of its affiliates or - * subsidiaries, has supplied this software to you under terms of a - * license agreement, nondisclosure agreement or both. - * - * You may not copy, disclose, or use this software except in accordance with - * those terms. - * - * - * Copyright 2023 One Identity LLC. - * ALL RIGHTS RESERVED. - * - * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR - * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, OR - * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE - * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE - * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING - * THIS SOFTWARE OR ITS DERIVATIVES. - * - */ - -import { Component, Inject, OnInit } from '@angular/core'; -import { UntypedFormArray, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; -import { PortalTargetsystemTeamsChannels } from 'imx-api-o3t'; -import { BaseCdr, ColumnDependentReference, SnackBarService } from 'qbm'; -import { TeamsService } from '../teams.service'; - -@Component({ - selector: 'imx-team-channel-details', - templateUrl: './team-channel-details.component.html' -}) -export class TeamChannelDetailsComponent implements OnInit { - - public readonly formGroup: UntypedFormGroup; - public cdrList: ColumnDependentReference[] = []; - - constructor( - formBuilder: UntypedFormBuilder, - @Inject(EUI_SIDESHEET_DATA) public teamChannel: PortalTargetsystemTeamsChannels, - private teamsService: TeamsService, - private readonly sidesheetRef: EuiSidesheetRef, - private readonly snackbar: SnackBarService, - ) { - this.formGroup = new UntypedFormGroup({ formArray: formBuilder.array([]) }); - } - - get formArray(): UntypedFormArray { - return this.formGroup.get('formArray') as UntypedFormArray; - } - - public ngOnInit(): void { - this.setup(); - } - - public async saveChanges(): Promise { - this.teamsService.handleOpenLoader(); - try { - await this.teamChannel.GetEntity().Commit(true); - this.formGroup.markAsPristine(); - this.snackbar.open({ key: '#LDS#Your changes have been successfully saved.' }); - } finally { - this.teamsService.handleCloseLoader(); - } - } - - public cancel(): void { - this.sidesheetRef.close(); - } - - private setup(): void { - this.cdrList = [ - new BaseCdr(this.teamChannel.DisplayName.Column), - new BaseCdr(this.teamChannel.Description.Column), - ]; - } - -} diff --git a/imxweb/projects/o3t/src/lib/teams/team-channels/team-channels.component.html b/imxweb/projects/o3t/src/lib/teams/team-channels/team-channels.component.html deleted file mode 100644 index ce4453afc..000000000 --- a/imxweb/projects/o3t/src/lib/teams/team-channels/team-channels.component.html +++ /dev/null @@ -1,34 +0,0 @@ - -
    - - - - - -
    - - - - - - - - - - - - -
    - -
    diff --git a/imxweb/projects/o3t/src/lib/teams/team-channels/team-channels.component.ts b/imxweb/projects/o3t/src/lib/teams/team-channels/team-channels.component.ts deleted file mode 100644 index c63b5c01a..000000000 --- a/imxweb/projects/o3t/src/lib/teams/team-channels/team-channels.component.ts +++ /dev/null @@ -1,123 +0,0 @@ -/* - * ONE IDENTITY LLC. PROPRIETARY INFORMATION - * - * This software is confidential. One Identity, LLC. or one of its affiliates or - * subsidiaries, has supplied this software to you under terms of a - * license agreement, nondisclosure agreement or both. - * - * You may not copy, disclose, or use this software except in accordance with - * those terms. - * - * - * Copyright 2023 One Identity LLC. - * ALL RIGHTS RESERVED. - * - * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR - * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, OR - * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE - * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE - * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING - * THIS SOFTWARE OR ITS DERIVATIVES. - * - */ - -import { Component, Input, OnInit } from '@angular/core'; -import { EuiSidesheetService } from '@elemental-ui/core'; -import { TranslateService } from '@ngx-translate/core'; -import { PortalTargetsystemTeamsChannels } from 'imx-api-o3t'; -import { CollectionLoadParameters, DisplayColumns, EntitySchema, IClientProperty } from 'imx-qbm-dbts'; -import { ClassloggerService, DataSourceToolbarFilter, DataSourceToolbarSettings, SettingsService } from 'qbm'; -import { TeamChannelDetailsComponent } from '../team-channel-details/team-channel-details.component'; -import { TeamsService } from '../teams.service'; - -@Component({ - selector: 'imx-team-channels', - templateUrl: './team-channels.component.html' -}) -export class TeamChannelsComponent implements OnInit { - - @Input() public uidO3t: string; - public dstSettings: DataSourceToolbarSettings; - public navigationState: CollectionLoadParameters; - public filterOptions: DataSourceToolbarFilter[] = []; - public readonly entitySchemaTeamChannels: EntitySchema; - public readonly DisplayColumns = DisplayColumns; - - private displayedColumns: IClientProperty[] = []; - - constructor( - private readonly sideSheet: EuiSidesheetService, - private readonly logger: ClassloggerService, - settingsService: SettingsService, - private readonly teamsService: TeamsService, - private readonly translate: TranslateService - ) { - this.navigationState = { PageSize: settingsService.DefaultPageSize, StartIndex: 0 }; - this.entitySchemaTeamChannels = this.teamsService.teamChannelsSchema; - } - - public async ngOnInit(): Promise { - this.displayedColumns = [ - this.entitySchemaTeamChannels.Columns.DisplayName, - this.entitySchemaTeamChannels.Columns.Description, - this.entitySchemaTeamChannels.Columns.WebUrl, - ]; - - await this.navigate(); - } - - public async onNavigationStateChanged(newState?: CollectionLoadParameters): Promise { - if (newState) { - this.navigationState = newState; - } - await this.navigate(); - } - - public async onSearch(keywords: string): Promise { - this.logger.debug(this, `Searching for: ${keywords}`); - this.navigationState.StartIndex = 0; - this.navigationState.search = keywords; - await this.navigate(); - } - - public async onTeamChannelChanged(channel: PortalTargetsystemTeamsChannels): Promise { - this.logger.debug(this, `Selected channel changed`); - this.logger.trace(this, `New channel selected`, channel); - this.openDetailsSidesheet(channel); - } - - private async navigate(): Promise { - this.teamsService.handleOpenLoader(); - - try { - const data = await this.teamsService.getTeamChannels(this.uidO3t, this.navigationState); - this.dstSettings = { - displayedColumns: this.displayedColumns, - dataSource: data, - entitySchema: this.entitySchemaTeamChannels, - navigationState: this.navigationState, - filters: this.filterOptions, - }; - this.logger.debug(this, `Head at ${data.Data.length + this.navigationState.StartIndex} of ${data.totalCount} item(s)`); - } finally { - this.teamsService.handleCloseLoader(); - } - } - - private async openDetailsSidesheet(channel: PortalTargetsystemTeamsChannels): Promise { - const sidesheetRef = this.sideSheet.open(TeamChannelDetailsComponent, { - title: await this.translate.get('#LDS#Heading View Microsoft Teams Channel Details').toPromise(), - subTitle: channel.GetEntity().GetDisplay(), - padding: '0px', - width: `max(650px, 60%)`, - icon: 'usergroup', - testId: 'teams-channel-view-team-channel-details', - data: channel - }); - // After the sidesheet closes, reload the current data to refresh any changes that might have been made - sidesheetRef.afterClosed().subscribe(() => this.navigate()); - } -} diff --git a/imxweb/projects/o3t/src/lib/teams/team-details/team-details.component.html b/imxweb/projects/o3t/src/lib/teams/team-details/team-details.component.html deleted file mode 100644 index abaee2a08..000000000 --- a/imxweb/projects/o3t/src/lib/teams/team-details/team-details.component.html +++ /dev/null @@ -1,60 +0,0 @@ -
    - - - -
    - #LDS#Heading Details - - -
    -
    -
    -
    - -
    - -
    -
    -
    - - - - - - - - -
    -
    - - - -
    -
    - -
    - - - - - -
    -
    -
    - -
    - -
    diff --git a/imxweb/projects/o3t/src/lib/teams/team-details/team-details.component.ts b/imxweb/projects/o3t/src/lib/teams/team-details/team-details.component.ts deleted file mode 100644 index ef7bb6d92..000000000 --- a/imxweb/projects/o3t/src/lib/teams/team-details/team-details.component.ts +++ /dev/null @@ -1,140 +0,0 @@ -/* - * ONE IDENTITY LLC. PROPRIETARY INFORMATION - * - * This software is confidential. One Identity, LLC. or one of its affiliates or - * subsidiaries, has supplied this software to you under terms of a - * license agreement, nondisclosure agreement or both. - * - * You may not copy, disclose, or use this software except in accordance with - * those terms. - * - * - * Copyright 2023 One Identity LLC. - * ALL RIGHTS RESERVED. - * - * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR - * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, OR - * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE - * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE - * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING - * THIS SOFTWARE OR ITS DERIVATIVES. - * - */ - -import { Component, Inject, OnInit } from '@angular/core'; -import { UntypedFormArray, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { EuiSidesheetRef, EuiSidesheetService, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; -import { TranslateService } from '@ngx-translate/core'; -import { PortalTargetsystemTeams } from 'imx-api-o3t'; -import { BaseCdr, ColumnDependentReference, SnackBarService } from 'qbm'; -import { GroupSidesheetComponent, GroupSidesheetData, GroupsService } from 'tsb'; -import { TeamsService } from '../teams.service'; - -@Component({ - selector: 'imx-team-details', - templateUrl: './team-details.component.html' -}) -export class TeamDetailsComponent implements OnInit { - public readonly formGroup: UntypedFormGroup; - public cdrList: ColumnDependentReference[] = []; - - constructor( - formBuilder: UntypedFormBuilder, - @Inject(EUI_SIDESHEET_DATA) public team: PortalTargetsystemTeams, - private teamsService: TeamsService, - private readonly sidesheet: EuiSidesheetService, - private readonly sidesheetRef: EuiSidesheetRef, - private readonly groupsService: GroupsService, - private readonly snackbar: SnackBarService, - private readonly translate: TranslateService - ) { - this.formGroup = new UntypedFormGroup({ formArray: formBuilder.array([]) }); - } - - get formArray(): UntypedFormArray { - return this.formGroup.get('formArray') as UntypedFormArray; - } - - get teamId(): string { - return this.team?.GetEntity().GetKeys().join(''); - } - - public ngOnInit(): void { - this.setup(); - } - - public async saveChanges(): Promise { - this.teamsService.handleOpenLoader(); - try { - this.team.GetEntity().Commit(true); - this.formGroup.markAsPristine(); - this.snackbar.open({ key: '#LDS#Your changes have been successfully saved.' }); - } finally { - this.teamsService.handleCloseLoader(); - } - } - - public cancel(): void { - this.sidesheetRef.close(); - } - - public async viewGroup(): Promise { - - let data: GroupSidesheetData; - this.teamsService.handleOpenLoader(); - try { - const unsGroupDbObjectKey: any = { TableName: 'O3EUnifiedGroup', Keys: [this.team.UID_O3EUnifiedGroup.value] }; - const o3tGroup = await this.groupsService.getGroupDetails(unsGroupDbObjectKey); - const uidAccProduct = o3tGroup?.GetEntity().GetColumn('UID_AccProduct').GetValue(); - const serviceItem = await this.groupsService.getGroupServiceItem(uidAccProduct); - - data = { - uidAccProduct, - unsGroupDbObjectKey, - group: o3tGroup, - groupServiceItem: serviceItem - }; - } finally { - this.teamsService.handleCloseLoader(); - } - - this.openGroupSidesheet(data); - } - - private async openGroupSidesheet(data: GroupSidesheetData): Promise { - this.sidesheet.open(GroupSidesheetComponent, { - title: await this.translate.get('#LDS#Heading Edit System Entitlement').toPromise(), - subTitle: this.team.GetEntity().GetDisplay(), - padding: '0px', - width: 'max(650px, 60%)', - icon: 'usergroup', - testId: 'teams-details-view-group-details', - data - }); - } - - private setup(): void { - this.cdrList = [ - new BaseCdr(this.team.tmsAllowDeleteChannels.Column), - new BaseCdr(this.team.tmsAllowCreateUpdateRemoveTabs.Column), - new BaseCdr(this.team.tmsAllowCreateUpdateRemoveConn.Column), - new BaseCdr(this.team.tmsAllowCreateUpdateChannels.Column), - new BaseCdr(this.team.tmsAllowAddRemoveApps.Column), - new BaseCdr(this.team.msAllowUserEditMessages.Column), - new BaseCdr(this.team.msAllowUserDeleteMessages.Column), - new BaseCdr(this.team.msAllowTeamMentions.Column), - new BaseCdr(this.team.msAllowOwnerDeleteMessages.Column), - new BaseCdr(this.team.msAllowChannelMentions.Column), - new BaseCdr(this.team.gsAllowDeleteChannels.Column), - new BaseCdr(this.team.IsArchived.Column), - new BaseCdr(this.team.gsAllowCreateUpdateChannels.Column), - new BaseCdr(this.team.fsGiphyContentRating.Column), - new BaseCdr(this.team.fsAllowStickersAndMemes.Column), - new BaseCdr(this.team.fsAllowGiphy.Column), - new BaseCdr(this.team.fsAllowCustomMemes.Column) - ]; - } -} diff --git a/imxweb/projects/o3t/src/lib/teams/teams.component.html b/imxweb/projects/o3t/src/lib/teams/teams.component.html deleted file mode 100644 index 3a5d7f833..000000000 --- a/imxweb/projects/o3t/src/lib/teams/teams.component.html +++ /dev/null @@ -1,42 +0,0 @@ - - -
    -
    - - #LDS#Heading Teams -
    -
    - - - - -
    - - - - - - - - - - - - -
    - - -
    diff --git a/imxweb/projects/o3t/src/lib/teams/teams.component.ts b/imxweb/projects/o3t/src/lib/teams/teams.component.ts deleted file mode 100644 index 9b4802ac4..000000000 --- a/imxweb/projects/o3t/src/lib/teams/teams.component.ts +++ /dev/null @@ -1,123 +0,0 @@ -/* - * ONE IDENTITY LLC. PROPRIETARY INFORMATION - * - * This software is confidential. One Identity, LLC. or one of its affiliates or - * subsidiaries, has supplied this software to you under terms of a - * license agreement, nondisclosure agreement or both. - * - * You may not copy, disclose, or use this software except in accordance with - * those terms. - * - * - * Copyright 2023 One Identity LLC. - * ALL RIGHTS RESERVED. - * - * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR - * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, OR - * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE - * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE - * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING - * THIS SOFTWARE OR ITS DERIVATIVES. - * - */ - -import { Component, Input, OnInit } from '@angular/core'; -import { EuiSidesheetService } from '@elemental-ui/core'; -import { TranslateService } from '@ngx-translate/core'; -import { PortalTargetsystemTeams } from 'imx-api-o3t'; -import { CollectionLoadParameters, DisplayColumns, EntitySchema, IClientProperty } from 'imx-qbm-dbts'; -import { ClassloggerService, DataSourceToolbarFilter, DataSourceToolbarSettings, SettingsService } from 'qbm'; -import { TeamDetailsComponent } from './team-details/team-details.component'; -import { TeamsService } from './teams.service'; - -@Component({ - selector: 'imx-o3t-teams', - templateUrl: './teams.component.html' -}) -export class TeamsComponent implements OnInit { - - @Input() public sidesheetWidth = '65%'; - public dstSettings: DataSourceToolbarSettings; - public navigationState: CollectionLoadParameters; - public filterOptions: DataSourceToolbarFilter[] = []; - public readonly entitySchemaTeams: EntitySchema; - public readonly DisplayColumns = DisplayColumns; - - private displayedColumns: IClientProperty[] = []; - - constructor( - private readonly sideSheet: EuiSidesheetService, - private readonly logger: ClassloggerService, - settingsService: SettingsService, - private readonly teamsService: TeamsService, - private readonly translate: TranslateService - ) { - this.navigationState = { PageSize: settingsService.DefaultPageSize, StartIndex: 0 }; - this.entitySchemaTeams = this.teamsService.teamsSchema; - } - - public async ngOnInit(): Promise { - this.displayedColumns = [ - this.entitySchemaTeams.Columns[DisplayColumns.DISPLAY_PROPERTYNAME], - this.entitySchemaTeams.Columns.UID_O3EUnifiedGroup, - this.entitySchemaTeams.Columns.WebUrl, - ]; - - await this.navigate(); - } - - public async onNavigationStateChanged(newState?: CollectionLoadParameters): Promise { - if (newState) { - this.navigationState = newState; - } - await this.navigate(); - } - - public async onSearch(keywords: string): Promise { - this.logger.debug(this, `Searching for: ${keywords}`); - this.navigationState.StartIndex = 0; - this.navigationState.search = keywords; - await this.navigate(); - } - - public async onTeamChanged(team: PortalTargetsystemTeams): Promise { - this.logger.debug(this, `Selected team changed`); - this.logger.trace(this, `New team selected`, team); - - this.openDetailsSidesheet(team); - } - - private async navigate(): Promise { - - this.teamsService.handleOpenLoader(); - try { - const data = await this.teamsService.getTeams(this.navigationState); - this.dstSettings = { - displayedColumns: this.displayedColumns, - dataSource: data, - entitySchema: this.entitySchemaTeams, - navigationState: this.navigationState, - filters: this.filterOptions, - }; - this.logger.debug(this, `Head at ${data.Data.length + this.navigationState.StartIndex} of ${data.totalCount} item(s)`); - } finally { - this.teamsService.handleCloseLoader(); - } - } - - private async openDetailsSidesheet( data: PortalTargetsystemTeams): Promise { - const sidesheetRef = this.sideSheet.open(TeamDetailsComponent, { - title: await this.translate.get('#LDS#Heading View Microsoft Teams Team Details').toPromise(), - subTitle: data.GetEntity().GetDisplay(), - padding: '0px', - width: `max(650px, ${this.sidesheetWidth})`, - icon: 'usergroup', - testId: 'teams-view-team-details', - data - }); - sidesheetRef.afterClosed().subscribe(() => this.navigate()); - } -} diff --git a/imxweb/projects/o3t/src/lib/teams/teams.service.ts b/imxweb/projects/o3t/src/lib/teams/teams.service.ts deleted file mode 100644 index 56976cd99..000000000 --- a/imxweb/projects/o3t/src/lib/teams/teams.service.ts +++ /dev/null @@ -1,75 +0,0 @@ -/* - * ONE IDENTITY LLC. PROPRIETARY INFORMATION - * - * This software is confidential. One Identity, LLC. or one of its affiliates or - * subsidiaries, has supplied this software to you under terms of a - * license agreement, nondisclosure agreement or both. - * - * You may not copy, disclose, or use this software except in accordance with - * those terms. - * - * - * Copyright 2023 One Identity LLC. - * ALL RIGHTS RESERVED. - * - * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR - * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, OR - * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE - * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE - * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING - * THIS SOFTWARE OR ITS DERIVATIVES. - * - */ - -import { Injectable } from '@angular/core'; -import { ApiService } from '../api.service'; -import { PortalTargetsystemTeams, PortalTargetsystemTeamsChannels } from 'imx-api-o3t'; -import { CollectionLoadParameters, EntitySchema, TypedEntityCollectionData } from 'imx-qbm-dbts'; -import { OverlayRef } from '@angular/cdk/overlay'; -import { EuiLoadingService } from '@elemental-ui/core'; - -@Injectable({ - providedIn: 'root', -}) -export class TeamsService { - private busyIndicator: OverlayRef; - - constructor(private readonly o3tApiClient: ApiService, private readonly busyService: EuiLoadingService) {} - - public get teamsSchema(): EntitySchema { - return this.o3tApiClient.typedClient.PortalTargetsystemTeams.GetSchema(); - } - - public get teamChannelsSchema(): EntitySchema { - return this.o3tApiClient.typedClient.PortalTargetsystemTeamsChannels.GetSchema(); - } - - public async getTeams(navigationState: CollectionLoadParameters): Promise> { - return this.o3tApiClient.typedClient.PortalTargetsystemTeams.Get(navigationState); - } - - public async getTeamChannels( - uidO3tTeam: string, - navigationState: CollectionLoadParameters - ): Promise> { - return this.o3tApiClient.typedClient.PortalTargetsystemTeamsChannels.Get(uidO3tTeam, navigationState); - } - - public handleOpenLoader(): void { - if (!this.busyIndicator) { - this.busyIndicator = this.busyService.show(); - } - } - - public handleCloseLoader(): void { - if (this.busyIndicator) { - setTimeout(() => { - this.busyService.hide(this.busyIndicator); - this.busyIndicator = undefined; - }); - } - } -} diff --git a/imxweb/projects/o3t/src/test/o3t-common-test-mocks.ts b/imxweb/projects/o3t/src/test/o3t-common-test-mocks.ts deleted file mode 100644 index df47453a2..000000000 --- a/imxweb/projects/o3t/src/test/o3t-common-test-mocks.ts +++ /dev/null @@ -1,165 +0,0 @@ -/* - * ONE IDENTITY LLC. PROPRIETARY INFORMATION - * - * This software is confidential. One Identity, LLC. or one of its affiliates or - * subsidiaries, has supplied this software to you under terms of a - * license agreement, nondisclosure agreement or both. - * - * You may not copy, disclose, or use this software except in accordance with - * those terms. - * - * - * Copyright 2023 One Identity LLC. - * ALL RIGHTS RESERVED. - * - * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR - * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, OR - * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE - * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE - * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING - * THIS SOFTWARE OR ITS DERIVATIVES. - * - */ - -import { PortalTargetsystemTeams } from 'imx-api-o3t'; -import { IEntityColumn, IEntity } from 'imx-qbm-dbts'; -import { ISessionState } from 'qbm'; -import { Subject } from 'rxjs'; - -export class O3tCommonTestData { - - public static mockConfigService: any = { - getConfig: () => { - return Promise.resolve({ - PersonConfig: { - VI_MyData_WhitePages_ResultAttributes: { - Columns: ['col1', 'col2'] - }, - VI_PersonalData_Fields: { - Columns: ['col1', 'col2'] - } - } - }); - } - }; - - public static mockAppConfigService: any = { - Config: { - Title: '', - routeConfig: { - start: 'dashboard' - } - }, - client: { - imx_multilanguage_getcaptions_get: () => Promise.resolve({}), - imx_multilanguage_translations_get: () => Promise.resolve({}) - } - }; - - public static mockAuthenticationServiceStub = { - onSessionResponse: new Subject(), - update: jasmine.createSpy('update') - }; - - public static mockSessionService: any = { - TypedClient: { - PortalTargetsystemUnsGroup: { - Get: () => Promise.resolve({}) - }, - PortalTargetsystemUnsAccount: { - Get: () => Promise.resolve({}) - }, - PortalPersonAll: { - Get: () => Promise.resolve({}) - }, - PortalAdminPerson: { - Get: () => Promise.resolve({}) - }, - PortalPerson: { - Get: () => Promise.resolve({Data: ['test1', 'test2']}) - }, - } - }; - - public static teamSchemaMock: any = { - Columns: { - __Display: {}, - UID_O3EUnifiedGroup: {}, - WebUrl: {} - } - }; - - public static teamChannelSchemaMock: any = { - Columns: { - DisplayName: {}, - Description: {}, - WebUrl: {} - } - }; - - public static mockTeamsService = { - teamsSchema: O3tCommonTestData.teamSchemaMock, - teamChannelsSchema: O3tCommonTestData.teamChannelSchemaMock, - getTeams: jasmine.createSpy('getTeams').and.returnValue(Promise.resolve({ Data: []})), - getTeamChannels: jasmine.createSpy('getTeamChannels').and.returnValue(Promise.resolve({ Data: []})), - handleOpenLoader: jasmine.createSpy('handleOpenLoader'), - handleCloseLoader: jasmine.createSpy('handleCloseLoader'), - }; - - public static mockEntityColumn = { - ColumnName: '', - GetMetadata: () => { - return { - CanEdit: () => false, - GetDisplay: () => '', - GetMinLength: () => 0 - }; - }, - GetValue: () => '' - - } as IEntityColumn; - - public static mockEntityColumnWithValue = { - ColumnName: '', - GetMetadata: () => { - return { - CanEdit: () => false, - GetDisplay: () => '', - GetMinLength: () => 0 - }; - }, - GetValue: () => 'Test value 1' - - } as IEntityColumn; - - public static mockEntity = { - GetDisplay: () => 'Display value', - GetKeys: () => ['1'], - GetColumn: (name) => O3tCommonTestData.mockEntityColumn, - Commit: () => Promise.resolve() - } as IEntity; - - public static mockTeam = { - GetEntity: () => O3tCommonTestData.mockEntity, - tmsAllowDeleteChannels: { Column: O3tCommonTestData.mockEntityColumn }, - tmsAllowCreateUpdateRemoveTabs: { Column: O3tCommonTestData.mockEntityColumn }, - tmsAllowCreateUpdateRemoveConn: { Column: O3tCommonTestData.mockEntityColumn }, - tmsAllowCreateUpdateChannels: { Column: O3tCommonTestData.mockEntityColumn }, - tmsAllowAddRemoveApps: { Column: O3tCommonTestData.mockEntityColumn }, - msAllowUserEditMessages: { Column: O3tCommonTestData.mockEntityColumn }, - msAllowUserDeleteMessages: { Column: O3tCommonTestData.mockEntityColumn }, - msAllowTeamMentions: { Column: O3tCommonTestData.mockEntityColumn }, - msAllowOwnerDeleteMessages: { Column: O3tCommonTestData.mockEntityColumn }, - msAllowChannelMentions: { Column: O3tCommonTestData.mockEntityColumn }, - gsAllowDeleteChannels: { Column: O3tCommonTestData.mockEntityColumn }, - IsArchived: { Column: O3tCommonTestData.mockEntityColumn }, - gsAllowCreateUpdateChannels: { Column: O3tCommonTestData.mockEntityColumn }, - fsGiphyContentRating: { Column: O3tCommonTestData.mockEntityColumn }, - fsAllowStickersAndMemes: { Column: O3tCommonTestData.mockEntityColumn }, - fsAllowGiphy: { Column: O3tCommonTestData.mockEntityColumn }, - fsAllowCustomMemes: { Column: O3tCommonTestData.mockEntityColumn }, - } as PortalTargetsystemTeams; -} diff --git a/imxweb/projects/o3t/tslint.json b/imxweb/projects/o3t/tslint.json deleted file mode 100644 index 8bab15f53..000000000 --- a/imxweb/projects/o3t/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "imx", - "camelCase" - ], - "component-selector": [ - true, - "element", - "imx", - "kebab-case" - ] - } -} diff --git a/imxweb/projects/olg/.compodocrc.json b/imxweb/projects/olg/.compodocrc.json index 9919bd6cc..2163ad64a 100644 --- a/imxweb/projects/olg/.compodocrc.json +++ b/imxweb/projects/olg/.compodocrc.json @@ -1,6 +1,6 @@ { "name": "IMX Web - olg Library", - "output": "../../documentation/v92/olg", + "output": "../../documentation/v93/olg", "theme": "material", "assetsFolder": "../../compodoc/assets", "customLogo": "../../compodoc/assets/images/oneidentity-logo.png", diff --git a/imxweb/projects/olg/.eslintrc.json b/imxweb/projects/olg/.eslintrc.json new file mode 100644 index 000000000..627c6270d --- /dev/null +++ b/imxweb/projects/olg/.eslintrc.json @@ -0,0 +1,35 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*", "**/*.spec.ts"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": ["imxweb/projects/olg/tsconfig.lib.json", "imxweb/projects/olg/tsconfig.spec.json"], + "createDefaultProgram": true + }, + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "imx", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "imx", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "rules": {} + } + ] +} diff --git a/imxweb/projects/olg/imx-plugin-config.json b/imxweb/projects/olg/imx-plugin-config.json index db07225b9..29f9d31b6 100644 --- a/imxweb/projects/olg/imx-plugin-config.json +++ b/imxweb/projects/olg/imx-plugin-config.json @@ -5,4 +5,4 @@ "Name": "OlgConfigModule" } ] -} \ No newline at end of file +} diff --git a/imxweb/projects/olg/karma.conf.js b/imxweb/projects/olg/karma.conf.js index 211193c9d..3e53a3ac8 100644 --- a/imxweb/projects/olg/karma.conf.js +++ b/imxweb/projects/olg/karma.conf.js @@ -15,7 +15,7 @@ module.exports = function (config) { require('karma-junit-reporter'), ], client: { - clearContext: false // leave Jasmine Spec Runner output visible in browser + clearContext: false, // leave Jasmine Spec Runner output visible in browser }, coverageIstanbulReporter: { dir: require('path').join(__dirname, 'results'), @@ -23,7 +23,7 @@ module.exports = function (config) { fixWebpackSourcePaths: true, 'report-config': { html: { - subdir: 'coverage-html' + subdir: 'coverage-html', }, }, thresholds: { @@ -40,13 +40,14 @@ module.exports = function (config) { port: 9876, captureTimeout: 210000, browserDisconnectTolerance: 3, - browserDisconnectTimeout : 210000, - browserNoActivityTimeout : 210000, + browserDisconnectTimeout: 210000, + browserNoActivityTimeout: 210000, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], singleRun: false, - restartOnFileChange: true + failOnEmptyTestSuite: false, + restartOnFileChange: true, }); }; diff --git a/imxweb/projects/olg/ng-package.dynamic.json b/imxweb/projects/olg/ng-package.dynamic.json index f45879d9f..7b54797e4 100644 --- a/imxweb/projects/olg/ng-package.dynamic.json +++ b/imxweb/projects/olg/ng-package.dynamic.json @@ -16,12 +16,11 @@ "@elemental-ui/core", "@ngx-translate/core", "@ngx-translate/http-loader", - "applicationinsights-js", "billboard.js" ], - "dest": "../../html/olg", + "dest": "../../html/qer-app-portal/olg", "lib": { "entryFile": "src/public_api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] } } diff --git a/imxweb/projects/olg/ng-package.json b/imxweb/projects/olg/ng-package.json index b529b1d33..311edcb17 100644 --- a/imxweb/projects/olg/ng-package.json +++ b/imxweb/projects/olg/ng-package.json @@ -3,6 +3,6 @@ "dest": "../../dist/olg", "lib": { "entryFile": "src/public_api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] } } diff --git a/imxweb/projects/olg/package.json b/imxweb/projects/olg/package.json index 020943ccb..d2a3b1fca 100644 --- a/imxweb/projects/olg/package.json +++ b/imxweb/projects/olg/package.json @@ -1,6 +1,6 @@ { "name": "olg", - "version": "9.2.1", + "version": "9.3.0", "private": true, "bundledDependencies": [ "imx-api-olg" diff --git a/imxweb/projects/olg/project.json b/imxweb/projects/olg/project.json new file mode 100644 index 000000000..cd78c3518 --- /dev/null +++ b/imxweb/projects/olg/project.json @@ -0,0 +1,65 @@ +{ + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "name": "olg", + "sourceRoot": "projects/olg/src", + "implicitDependencies": ["qer"], + "projectType": "library", + "generators": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "prefix": "imx", + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:ng-packagr", + "options": { + "tsConfig": "projects/olg/tsconfig.lib.json", + "project": "projects/olg/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "projects/olg/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "projects/olg/tsconfig.lib.json" + }, + "dynamic": { + "project": "projects/olg/ng-package.dynamic.json" + }, + "remote-dev": { + "tsConfig": "projects/olg/tsconfig.lib.json" + }, + "remote-qs": { + "tsConfig": "projects/olg/tsconfig.lib.json" + } + }, + "outputs": ["{workspaceRoot}/dist/olg"] + }, + "test": { + "executor": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/olg/src/test.ts", + "tsConfig": "projects/olg/tsconfig.spec.json", + "karmaConfig": "projects/olg/karma.conf.js", + "codeCoverageExclude": ["projects/olg/src/**/test/*"], + "stylePreprocessorOptions": { + "includePaths": [ + "./shared/assets", + "./shared/scss", + "./node_modules", + "./node_modules/@elemental-ui/cadence-icon", + "./node_modules/@elemental-ui/core" + ] + } + } + }, + "lint": { + "executor": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": ["projects/olg/tsconfig.lib.json", "projects/olg/tsconfig.spec.json"], + "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + } + } + } +} diff --git a/imxweb/projects/olg/src/lib/init.service.spec.ts b/imxweb/projects/olg/src/lib/init.service.spec.ts index 976ceb3c9..f5eeea165 100644 --- a/imxweb/projects/olg/src/lib/init.service.spec.ts +++ b/imxweb/projects/olg/src/lib/init.service.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/olg/src/lib/init.service.ts b/imxweb/projects/olg/src/lib/init.service.ts index 6c3cd6684..f67ccc39b 100644 --- a/imxweb/projects/olg/src/lib/init.service.ts +++ b/imxweb/projects/olg/src/lib/init.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -34,7 +34,10 @@ import { MfaFormControlComponent } from './mfa-form-control/mfa-form-control.com providedIn: 'root', }) export class InitService { - constructor(private readonly extService: ExtService, private readonly router: Router) {} + constructor( + private readonly extService: ExtService, + private readonly router: Router, + ) {} public onInit(routes: Route[]): void { this.addRoutes(routes); @@ -42,7 +45,7 @@ export class InitService { this.extService.register('mfaComponent', { instance: MfaComponent, }); - + this.extService.register('authenticator', { instance: MfaFormControlComponent, }); diff --git a/imxweb/projects/olg/src/lib/mfa-form-control/mfa-form-control.component.html b/imxweb/projects/olg/src/lib/mfa-form-control/mfa-form-control.component.html index 34b0fc872..46beae954 100644 --- a/imxweb/projects/olg/src/lib/mfa-form-control/mfa-form-control.component.html +++ b/imxweb/projects/olg/src/lib/mfa-form-control/mfa-form-control.component.html @@ -8,12 +8,16 @@

    {{ '#LDS#To authenticate, use the one-time password.' | translate }}

    - {{ '#LDS#To authenticate, use either the push notification or the one-time password of your OneLogin Protector app.' | translate }} + {{ + '#LDS#To authenticate, use either the push notification or the one-time password of your OneLogin Protector app.' | translate + }} +

    +

    + {{ '#LDS#To authenticate, use the one-time password of your authentication app.' | translate }}

    -

    {{ '#LDS#To authenticate, use the one-time password of your authentication app.' | translate }}

    - +

    {{ info.authMessage | translate }}

    @@ -26,17 +30,17 @@ > {{ '#LDS#Use authentication method' | translate }} - +
    @@ -18,7 +23,10 @@
    - +
    @@ -26,7 +34,10 @@
    - +
    @@ -41,7 +52,11 @@
    - + +
    diff --git a/imxweb/projects/pol/src/lib/policies/policies-sidesheet/policies-sidesheet.component.scss b/imxweb/projects/pol/src/lib/policies/policies-sidesheet/policies-sidesheet.component.scss deleted file mode 100644 index 886de866f..000000000 --- a/imxweb/projects/pol/src/lib/policies/policies-sidesheet/policies-sidesheet.component.scss +++ /dev/null @@ -1,48 +0,0 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; - -:host { - .mat-tab-group, ::ng-deep .mat-tab-body-wrapper { - height: 100%; - } - - ::ng-deep .mat-tab-body-content { - display: flex; - flex-direction: column; - justify-content: space-between; - } - - .imx-mat-tab-container { - padding: 20px; - display: flex; - flex-direction: column; - overflow: auto; - } -} - -.eui-sidesheet-actions { - .justify-start { - margin-right: auto; - } -} - -:host { - ::ng-deep .mat-tab-labels { - background-color: $color-gray-0; - } -} - -.eui-dark-theme { - :host { - ::ng-deep .mat-tab-labels { - background-color: $color-gray-80; - } - } -} - -.eui-contrast-theme { - :host { - ::ng-deep .mat-tab-labels { - background-color: $color-gray-100; - } - } -} diff --git a/imxweb/projects/pol/src/lib/policies/policies-sidesheet/policies-sidesheet.component.ts b/imxweb/projects/pol/src/lib/policies/policies-sidesheet/policies-sidesheet.component.ts index 61345423a..fb4e0c31e 100644 --- a/imxweb/projects/pol/src/lib/policies/policies-sidesheet/policies-sidesheet.component.ts +++ b/imxweb/projects/pol/src/lib/policies/policies-sidesheet/policies-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,18 +25,17 @@ */ import { Component, Inject, OnDestroy } from '@angular/core'; -import { EuiDownloadOptions, EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { EUI_SIDESHEET_DATA, EuiDownloadOptions, EuiSidesheetRef } from '@elemental-ui/core'; import { Subscription } from 'rxjs'; -import { PortalPolicies } from 'imx-api-pol'; -import { DisplayColumns } from 'imx-qbm-dbts'; +import { PortalPolicies } from '@imx-modules/imx-api-pol'; +import { DisplayColumns } from '@imx-modules/imx-qbm-dbts'; import { BaseReadonlyCdr, ColumnDependentReference, ElementalUiConfigService } from 'qbm'; import { PoliciesService } from '../policies.service'; @Component({ selector: 'imx-policies-sidesheet', templateUrl: './policies-sidesheet.component.html', - styleUrls: ['./policies-sidesheet.component.scss'], }) export class PoliciesSidesheetComponent implements OnDestroy { public reportDownload: EuiDownloadOptions; @@ -55,7 +54,7 @@ export class PoliciesSidesheetComponent implements OnDestroy { }, public sidesheetRef: EuiSidesheetRef, private readonly policiesProvider: PoliciesService, - elementalUiConfigService: ElementalUiConfigService + elementalUiConfigService: ElementalUiConfigService, ) { this.uidCompliance = data.selectedPolicy.GetEntity().GetKeys().join(','); this.cdrList = [ @@ -63,13 +62,16 @@ export class PoliciesSidesheetComponent implements OnDestroy { new BaseReadonlyCdr(this.data.selectedPolicy.Description.Column), new BaseReadonlyCdr(this.data.selectedPolicy.IsExceptionAllowed.Column), new BaseReadonlyCdr(this.data.selectedPolicy.IsInActive.Column), - data.hasRiskIndex ? new BaseReadonlyCdr(this.data.selectedPolicy.RiskIndex.Column) : null, new BaseReadonlyCdr(this.data.selectedPolicy.RuleSeverity.Column), new BaseReadonlyCdr(this.data.selectedPolicy.RuleViolationThreshold.Column), new BaseReadonlyCdr(this.data.selectedPolicy.SignificancyClass.Column), new BaseReadonlyCdr(this.data.selectedPolicy.TransparencyIndex.Column), new BaseReadonlyCdr(this.data.selectedPolicy.UID_QERPolicyGroup.Column), - ].filter((elem) => elem != null); + ]; + if (data.hasRiskIndex) { + // Stick this in position 4 + this.cdrList.splice(4, 0, new BaseReadonlyCdr(this.data.selectedPolicy.RiskIndex.Column)); + } this.reportDownload = { ...elementalUiConfigService.Config.downloadOptions, diff --git a/imxweb/projects/pol/src/lib/policies/policies.component.html b/imxweb/projects/pol/src/lib/policies/policies.component.html index 49f9e55e0..d83e4b4b7 100644 --- a/imxweb/projects/pol/src/lib/policies/policies.component.html +++ b/imxweb/projects/pol/src/lib/policies/policies.component.html @@ -1,41 +1,52 @@ -

    - {{ '#LDS#Heading Company Policies' | translate }} - -

    - +
    +

    + {{ '#LDS#Heading Company Policies' | translate }} + +

    + +
    +
    - - -
    - - - -
    -
    -
    {{ item.GetEntity().GetDisplay() }}
    -
    {{ item.Description.Column.GetDisplayValue() }}
    -
    - {{ '#LDS#Deactivated' | translate }} + + + + {{ policySchema?.Columns?.[DisplayColumns.DISPLAY_PROPERTYNAME]?.Display }} + + +
    +
    +
    {{ item.GetEntity().GetDisplay() }}
    +
    {{ item.Description.Column.GetDisplayValue() }}
    - - - - - - {{ policy.CountOpen.value + policy.CountClosed.value }} - - - - - -
    - + {{ '#LDS#Deactivated' | translate }} +
    + + + + + + {{ '#LDS#Policy violations' | translate }} + + + + + {{ item.CountOpen.value + item.CountClosed.value }} + + + + + + + {{ policySchema?.Columns?.CountOpen?.Display }} + + + + + {{ item.CountOpen.Column.GetDisplayValue() }} + + + + +
    diff --git a/imxweb/projects/pol/src/lib/policies/policies.component.scss b/imxweb/projects/pol/src/lib/policies/policies.component.scss index ffe8c184e..826e8b1b2 100644 --- a/imxweb/projects/pol/src/lib/policies/policies.component.scss +++ b/imxweb/projects/pol/src/lib/policies/policies.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; div[subtitle] { font-size: smaller; @@ -10,31 +10,16 @@ div[subtitle] { flex-direction: column; overflow: hidden; height: inherit; - - .mat-card { - flex: 1 1 auto ; - display: flex; - flex-direction: column; - overflow: hidden; - margin: 3px; - } -} - -.imx-table-container { - display: flex; - flex-direction: column; - overflow: hidden; - flex: 1 1 auto; } .imx-display-column { - display: flex; - flex-direction: row; - - :first-child { - flex: 1 1 auto; + width: 100% !important; + cursor: inherit !important; + &-main { + flex: 1 1 auto; + display: block !important; + cursor: inherit !important; } - .imx-badge { align-self: flex-end; } diff --git a/imxweb/projects/pol/src/lib/policies/policies.component.ts b/imxweb/projects/pol/src/lib/policies/policies.component.ts index fe15744cd..a8d393da2 100644 --- a/imxweb/projects/pol/src/lib/policies/policies.component.ts +++ b/imxweb/projects/pol/src/lib/policies/policies.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,29 +25,45 @@ */ import { Component, OnInit } from '@angular/core'; -import { PortalPolicies } from 'imx-api-pol'; -import { CollectionLoadParameters, DisplayColumns, EntitySchema, ValType } from 'imx-qbm-dbts'; -import { BusyService, ClientPropertyForTableColumns, DataSourceToolbarFilter, DataSourceToolbarSettings, SystemInfoService } from 'qbm'; -import { PolicyParameter } from './policy-parameter'; -import { PoliciesService } from './policies.service'; import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; +import { PortalPolicies } from '@imx-modules/imx-api-pol'; +import { + CollectionLoadParameters, + DataModel, + DisplayColumns, + EntitySchema, + TypedEntityCollectionData, + ValType, +} from '@imx-modules/imx-qbm-dbts'; import { TranslateService } from '@ngx-translate/core'; +import { + BusyService, + ClientPropertyForTableColumns, + DataSourceToolbarFilter, + DataSourceToolbarSettings, + DataViewInitParameters, + DataViewSource, + SystemInfoService, + calculateSidesheetWidth, +} from 'qbm'; import { PoliciesSidesheetComponent } from './policies-sidesheet/policies-sidesheet.component'; +import { PoliciesService } from './policies.service'; @Component({ selector: 'imx-policies', templateUrl: './policies.component.html', styleUrls: ['./policies.component.scss'], + providers: [DataViewSource], }) export class PoliciesComponent implements OnInit { public dstSettings: DataSourceToolbarSettings; public readonly DisplayColumns = DisplayColumns; public policySchema: EntitySchema; public busyService = new BusyService(); + public dataModel: DataModel; private displayedColumns: ClientPropertyForTableColumns[] = []; private filterOptions: DataSourceToolbarFilter[] = []; - private navigationState: PolicyParameter = {}; private isMControlPerViolation: boolean; constructor( @@ -55,7 +71,8 @@ export class PoliciesComponent implements OnInit { private readonly sideSheetService: EuiSidesheetService, private readonly systemInfoService: SystemInfoService, private readonly translate: TranslateService, - private readonly euiBusysService: EuiLoadingService + private readonly euiBusysService: EuiLoadingService, + public dataSource: DataViewSource, ) { this.policySchema = policiesProvider.policySchema; this.displayedColumns = [ @@ -71,32 +88,26 @@ export class PoliciesComponent implements OnInit { public async ngOnInit(): Promise { this.euiBusysService.show(); try { - this.filterOptions = (await this.policiesProvider.getDataModel()).Filters; + this.dataModel = await this.policiesProvider.getDataModel(); this.isMControlPerViolation = (await this.policiesProvider.featureConfig()).MitigatingControlsPerViolation; } finally { this.euiBusysService.hide(); } - - const indexActive = this.filterOptions.findIndex((elem) => elem.Name === 'active'); - if (indexActive > -1) { - this.filterOptions[indexActive].InitialValue = '1'; - this.navigationState.active = '1'; - } - await this.navigate({}); + await this.getData(); } public async showDetails(selectedPolicy: PortalPolicies): Promise { - const hasRiskIndex = (await this.systemInfoService.get()).PreProps.includes('RISKINDEX'); + const hasRiskIndex = (await this.systemInfoService.get())?.PreProps?.includes('RISKINDEX'); await this.sideSheetService .open(PoliciesSidesheetComponent, { title: await this.translate.get('#LDS#Heading View Company Policy Details').toPromise(), subTitle: selectedPolicy.GetEntity().GetDisplay(), padding: '0px', - width: 'max(700px, 60%)', + width: calculateSidesheetWidth(700, 0.4), testId: 'policy-details-sidesheet', data: { - selectedPolicy, + selectedPolicy: selectedPolicy as PortalPolicies, hasRiskIndex, isMControlPerViolation: this.isMControlPerViolation, }, @@ -105,22 +116,24 @@ export class PoliciesComponent implements OnInit { .toPromise(); } - public async navigate(parameter: CollectionLoadParameters): Promise { - const isBusy = this.busyService.beginBusy(); - - this.navigationState = { ...this.navigationState, ...parameter }; - - try { - const data = await this.policiesProvider.getPolicies(this.navigationState); - this.dstSettings = { - displayedColumns: this.displayedColumns, - dataSource: data, - entitySchema: this.policySchema, - navigationState: this.navigationState, - filters: this.filterOptions, - }; - } finally { - isBusy.endBusy(); - } + public async getData(): Promise { + this.filterOptions = this.dataModel?.Filters || []; + this.filterOptions.map((filter) => { + if (filter.Name === 'active') { + filter.CurrentValue = '1'; + } + }); + this.dataSource.state.update((state) => ({ ...state, active: '1' })); + const dataViewInitParameters: DataViewInitParameters = { + execute: (params: CollectionLoadParameters, signal: AbortSignal): Promise> => + this.policiesProvider.getPolicies(params, signal), + schema: this.policySchema, + columnsToDisplay: this.displayedColumns, + dataModel: this.dataModel, + highlightEntity: (entity: PortalPolicies) => { + this.showDetails(entity); + }, + }; + this.dataSource.init(dataViewInitParameters); } } diff --git a/imxweb/projects/pol/src/lib/policies/policies.module.ts b/imxweb/projects/pol/src/lib/policies/policies.module.ts index 116d9950a..b89ba0131 100644 --- a/imxweb/projects/pol/src/lib/policies/policies.module.ts +++ b/imxweb/projects/pol/src/lib/policies/policies.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,28 +24,23 @@ * */ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { PoliciesComponent } from './policies.component'; -import { PoliciesSidesheetComponent } from './policies-sidesheet/policies-sidesheet.component'; -import { CdrModule, DataSourceToolbarModule, DataTableModule, HelpContextualModule } from 'qbm'; +import { NgModule } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; -import { TranslateModule } from '@ngx-translate/core'; -import { EuiCoreModule } from '@elemental-ui/core'; +import { MatTableModule } from '@angular/material/table'; import { MatTabsModule } from '@angular/material/tabs'; +import { EuiCoreModule } from '@elemental-ui/core'; +import { TranslateModule } from '@ngx-translate/core'; +import { CdrModule, DataSourceToolbarModule, DataTableModule, DataViewModule, HelpContextualModule } from 'qbm'; import { ObjectHyperviewModule, StatisticsModule } from 'qer'; import { PolicyViolationsModule } from '../policy-violations/policy-violations.module'; import { MitigatingControlsPolicyComponent } from './mitigating-controls-policy/mitigating-controls-policy.component'; - - +import { PoliciesSidesheetComponent } from './policies-sidesheet/policies-sidesheet.component'; +import { PoliciesComponent } from './policies.component'; @NgModule({ - declarations: [ - PoliciesComponent, - PoliciesSidesheetComponent, - MitigatingControlsPolicyComponent, - ], + declarations: [PoliciesComponent, PoliciesSidesheetComponent, MitigatingControlsPolicyComponent], imports: [ CommonModule, EuiCoreModule, @@ -60,6 +55,8 @@ import { MitigatingControlsPolicyComponent } from './mitigating-controls-policy/ ObjectHyperviewModule, HelpContextualModule, PolicyViolationsModule, - ] + DataViewModule, + MatTableModule, + ], }) -export class PoliciesModule { } +export class PoliciesModule {} diff --git a/imxweb/projects/pol/src/lib/policies/policies.service.ts b/imxweb/projects/pol/src/lib/policies/policies.service.ts index cc0652ae8..569440e01 100644 --- a/imxweb/projects/pol/src/lib/policies/policies.service.ts +++ b/imxweb/projects/pol/src/lib/policies/policies.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,8 +25,8 @@ */ import { Injectable } from '@angular/core'; -import { PolicyConfig, PortalPoliciesMitigatingcontrols, PortalPolicies } from 'imx-api-pol'; -import { CollectionLoadParameters, DataModel, EntitySchema, ExtendedTypedEntityCollection } from 'imx-qbm-dbts'; +import { PolicyConfig, PortalPolicies, PortalPoliciesMitigatingcontrols } from '@imx-modules/imx-api-pol'; +import { CollectionLoadParameters, DataModel, EntitySchema, ExtendedTypedEntityCollection } from '@imx-modules/imx-qbm-dbts'; import { AppConfigService } from 'qbm'; import { ApiService } from '../api.service'; @@ -34,7 +34,10 @@ import { ApiService } from '../api.service'; providedIn: 'root', }) export class PoliciesService { - constructor(private apiservice: ApiService, private appConfig: AppConfigService) {} + constructor( + private apiservice: ApiService, + private appConfig: AppConfigService, + ) {} public get policySchema(): EntitySchema { return this.apiservice.typedClient.PortalPolicies.GetSchema(); @@ -44,8 +47,11 @@ export class PoliciesService { return this.apiservice.client.portal_policy_config_get(); } - public async getPolicies(parameter?: CollectionLoadParameters): Promise> { - return this.apiservice.typedClient.PortalPolicies.Get(parameter); + public async getPolicies( + parameter?: CollectionLoadParameters, + signal?: AbortSignal, + ): Promise> { + return this.apiservice.typedClient.PortalPolicies.Get(parameter, { signal }); } public async getDataModel(): Promise { diff --git a/imxweb/projects/pol/src/lib/policies/policy-parameter.ts b/imxweb/projects/pol/src/lib/policies/policy-parameter.ts index 357f8d702..fee293e1e 100644 --- a/imxweb/projects/pol/src/lib/policies/policy-parameter.ts +++ b/imxweb/projects/pol/src/lib/policies/policy-parameter.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,8 +24,8 @@ * */ -import { CollectionLoadParameters } from 'imx-qbm-dbts'; +import { CollectionLoadParameters } from '@imx-modules/imx-qbm-dbts'; -export interface PolicyParameter extends CollectionLoadParameters{ +export interface PolicyParameter extends CollectionLoadParameters { active?: string; } diff --git a/imxweb/projects/pol/src/lib/policy-violations/mitigating-controls/mitigating-controls.component.html b/imxweb/projects/pol/src/lib/policy-violations/mitigating-controls/mitigating-controls.component.html index fe5af5044..aad79a72c 100644 --- a/imxweb/projects/pol/src/lib/policy-violations/mitigating-controls/mitigating-controls.component.html +++ b/imxweb/projects/pol/src/lib/policy-violations/mitigating-controls/mitigating-controls.component.html @@ -1,27 +1,37 @@ -
    - +
    + {{ '#LDS#Heading Mitigating Controls Can Be Assigned Individually' | translate }} - {{ '#LDS#Here you can assign mitigating controls to the policy violation. NOTE: If no mitigating controls have been assigned to the violated company policy, you cannot assign any mitigating controls to the policy violation either.' | translate }} + {{ + '#LDS#Here you can assign mitigating controls to the policy violation. NOTE: If no mitigating controls have been assigned to the violated company policy, you cannot assign any mitigating controls to the policy violation either.' + | translate + }} - -
    + +

    - +
    -

    +

    - -
    -
    - - -
    - -
    +
    +
    +
    diff --git a/imxweb/projects/pol/src/lib/policy-violations/mitigating-controls/mitigating-controls.component.scss b/imxweb/projects/pol/src/lib/policy-violations/mitigating-controls/mitigating-controls.component.scss index be50b8950..4de661ee8 100644 --- a/imxweb/projects/pol/src/lib/policy-violations/mitigating-controls/mitigating-controls.component.scss +++ b/imxweb/projects/pol/src/lib/policy-violations/mitigating-controls/mitigating-controls.component.scss @@ -1,153 +1 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; - -:host { - display: flex; - flex-direction: column; - flex: 1 1 auto; - height: 100%; - - mat-card { - display: flex; - } - - .imx-helper-alert { - margin: 20px; - } - - .imx-mitigating-control { - display: flex; - flex-direction: row; - margin: 3px 3px 10px 3px; - - :first-child { - flex: 1 1 auto; - } - - button { - margin-top: 10px; - } - } - - .imx-mitigating-controls-card { - flex: 1 1 auto; - display: flex; - margin: 20px; - align-items: center; - overflow: hidden; - } - - .imx-spacer { - flex: 1 1 auto; - } - - .imx-content { - display: flex; - flex-direction: column; - overflow: hidden; - flex: 1 1 auto; - } - - .eui-sidesheet-actions { - margin: 0; - } - - .imx-save-button { - align-self: flex-end; - } - - .imx-data-no-results { - text-align: center; - flex: 1 1 auto; - - .eui-icon { - font-size: 100px; - } - - p { - margin: 0; - font-size: 18px; - } - button { - margin-top : 10px; - } - } - - .imx-mitigating-form{ - flex: 1 1 auto; - display: flex; - flex-direction: column; - align-self: flex-start; - overflow: auto; - max-height: 100%; - padding: 2px; - } - - .mat-error { - margin-top: 0.6666666667em; - margin-left: 1em; - } -} - -:host { - .delete-icon { - color: $color-red-60; - } - - .add-icon { - color: $color-blue-60; - } - - .imx-data-no-results { - .eui-icon { - color: $color-gray-10; - } - - p { - color: $color-gray-50; - } - } -} - -.eui-dark-theme { - :host { - .delete-icon { - color: $color-red-40; - } - - .add-icon { - color: $color-blue-40; - } - - .imx-data-no-results { - .eui-icon { - color: $color-gray-20; - } - - p { - color: $color-gray-10; - } - } - } -} - -.eui-contrast-theme { - :host { - .delete-icon { - color: $color-red-60; - } - - .add-icon { - color: $color-blue-60; - } - - .imx-data-no-results { - .eui-icon { - color: $color-gray-10; - } - - p { - color: $color-gray-0; - } - } - } -} +@import 'common/mitigating-controls'; diff --git a/imxweb/projects/pol/src/lib/policy-violations/mitigating-controls/mitigating-controls.component.ts b/imxweb/projects/pol/src/lib/policy-violations/mitigating-controls/mitigating-controls.component.ts index cc3553878..cf201ece2 100644 --- a/imxweb/projects/pol/src/lib/policy-violations/mitigating-controls/mitigating-controls.component.ts +++ b/imxweb/projects/pol/src/lib/policy-violations/mitigating-controls/mitigating-controls.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -56,10 +56,10 @@ export class MitigatingControlsComponent implements OnInit { private confirmationService: ConfirmationService, private cd: ChangeDetectorRef, private snackbar: SnackBarService, - formBuilder: UntypedFormBuilder + formBuilder: UntypedFormBuilder, ) { this.mitigatingForm = new UntypedFormGroup({ formArray: formBuilder.array([]) }); - this.mitigatingCaption = violationService.mitigationSchema.Columns.UID_MitigatingControl.Display; + this.mitigatingCaption = violationService.mitigationSchema.Columns.UID_MitigatingControl.Display ?? ''; } public filter = (option: EuiSelectOption, searchInputValue: string): boolean => @@ -99,13 +99,12 @@ export class MitigatingControlsComponent implements OnInit { } public async ngOnInit(): Promise { - this.sidesheetRef.componentInstance.disableClose = true; this.subscriptions$.push( this.sidesheetRef.closeClicked().subscribe(async () => { if (!this.isDirty || (await this.confirmationService.confirmLeaveWithUnsavedChanges())) { this.sidesheetRef.close(); } - }) + }), ); return this.loadMitigationControls(); } @@ -121,7 +120,7 @@ export class MitigatingControlsComponent implements OnInit { ); } - public async delete(index: number): Promise { + public async onDelete(index: number): Promise { const elem = this.mControls[index]; if (elem.UID_MitigatingControl.value !== '' && !(await this.confirmationService.confirmDelete())) { return; @@ -146,6 +145,8 @@ export class MitigatingControlsComponent implements OnInit { (control: FormControl) => (this.isDuplicate(control) ? { duplicated: true } : null), ]); this.cd.detectChanges(); + + this.mitigatingForm.markAsDirty(); } public async save() { @@ -172,7 +173,7 @@ export class MitigatingControlsComponent implements OnInit { try { //reload this.mControls = (await this.violationService.getMitigatingContols(this.uidViolation)).Data.map( - (elem) => new PolicyViolationExtended(false, elem) + (elem) => new PolicyViolationExtended(false, elem), ); this.mControls.forEach((elem) => this.formArray.push(elem.formControl)); await this.initOptions(); @@ -182,14 +183,15 @@ export class MitigatingControlsComponent implements OnInit { } private async initOptions(): Promise { - this.options = ( - await this.violationService.getCandidates( - this.uidViolation, - {}, - { - PageSize: 100000, - } - ) - ).Entities?.map((elem) => ({ display: elem.Display, value: elem.Keys[0] })); + this.options = + ( + await this.violationService.getCandidates( + this.uidViolation, + {}, + { + PageSize: 100000, + }, + ) + ).Entities?.map((elem) => ({ display: elem.Display ?? '', value: elem.Keys?.[0] ?? '' })) ?? []; } } diff --git a/imxweb/projects/pol/src/lib/policy-violations/mitigating-controls/policy-violation-extended.ts b/imxweb/projects/pol/src/lib/policy-violations/mitigating-controls/policy-violation-extended.ts index 330499d32..b55ef73e6 100644 --- a/imxweb/projects/pol/src/lib/policy-violations/mitigating-controls/policy-violation-extended.ts +++ b/imxweb/projects/pol/src/lib/policy-violations/mitigating-controls/policy-violation-extended.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,14 +25,17 @@ */ import { FormControl } from '@angular/forms'; -import { PortalPoliciesViolations } from 'imx-api-pol'; +import { PortalPoliciesViolations } from '@imx-modules/imx-api-pol'; import { BaseReadonlyCdr, ColumnDependentReference } from 'qbm'; export class PolicyViolationExtended extends PortalPoliciesViolations { - public formControl = new FormControl(undefined); + public formControl = new FormControl('', { nonNullable: true }); public mitigatingCdr: ColumnDependentReference; - constructor(public editable, readonly original: PortalPoliciesViolations) { + constructor( + public editable, + readonly original: PortalPoliciesViolations, + ) { super(original.GetEntity()); if (!editable) { this.mitigatingCdr = new BaseReadonlyCdr(original.UID_MitigatingControl.Column); diff --git a/imxweb/projects/pol/src/lib/policy-violations/policy-violation.ts b/imxweb/projects/pol/src/lib/policy-violations/policy-violation.ts index 6e97a953c..69ab12162 100644 --- a/imxweb/projects/pol/src/lib/policy-violations/policy-violation.ts +++ b/imxweb/projects/pol/src/lib/policy-violations/policy-violation.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,29 +24,27 @@ * */ -import { ObjectInfo, PortalPoliciesViolationslist } from 'imx-api-pol'; +import { ObjectInfo, PortalPoliciesViolationslist } from '@imx-modules/imx-api-pol'; import { BaseReadonlyCdr, ColumnDependentReference } from 'qbm'; export class PolicyViolation extends PortalPoliciesViolationslist { - public properties: ColumnDependentReference[]; /** * The color and the caption depending on the value of the state of a {@link PortalPoliciesViolationslist}. */ - public get stateBadge(): { color: 'blue' | 'orange' | 'green', caption: string } { + public get stateBadge(): { color: 'blue' | 'orange' | 'green'; caption: string } { return { color: this.stateBadgeColor, - caption: this.stateCaption + caption: this.stateCaption, }; - }; + } public readonly data: ObjectInfo[]; private stateBadgeColor: 'blue' | 'orange' | 'green'; private stateCaption: string; - constructor( private readonly baseObject: PortalPoliciesViolationslist, - extendedData: ObjectInfo[] + extendedData: ObjectInfo[], ) { super(baseObject.GetEntity()); this.initPropertyInfo(); @@ -59,25 +57,15 @@ export class PolicyViolation extends PortalPoliciesViolationslist { } private initPropertyInfo(): void { - const props: any[] = - [ - this.UID_QERPolicy, - this.Description, - this.ObjectKey - ]; + const props: any[] = [this.UID_QERPolicy, this.Description, this.ObjectKey]; if (this.State.value.toLowerCase() !== 'pending') { - props.push(...[ - this.UID_PersonDecisionMade, - this.UID_QERJustification, - this.DecisionReason, - this.DecisionDate - ]); + props.push(...[this.UID_PersonDecisionMade, this.UID_QERJustification, this.DecisionReason, this.DecisionDate]); } this.properties = props - .filter(property => property.value != null && property.value !== '') - .map(property => new BaseReadonlyCdr(property.Column)); + .filter((property) => property.value != null && property.value !== '') + .map((property) => new BaseReadonlyCdr(property.Column)); } private async initStateBadge(): Promise { diff --git a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-multi-action/policy-violations-action-multi-action.component.html b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-multi-action/policy-violations-action-multi-action.component.html index 770f59261..af0465eda 100644 --- a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-multi-action/policy-violations-action-multi-action.component.html +++ b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-multi-action/policy-violations-action-multi-action.component.html @@ -1,12 +1,16 @@ {{ '#LDS#Heading General Settings' | translate }} - {{ '#LDS#Here you can make settings that are applied to each selected policy violation.' | translate }} + {{ '#LDS#Here you can make settings that are applied to each selected policy violation.' | translate }}
    - +
    @@ -15,14 +19,15 @@ {{ '#LDS#Heading Selected Policy Violations' | translate }} -
    - - -
    - {{ policyViolationApproval?.GetEntity()?.GetDisplay() }} -
    -
    -
    -
    + + +
    + {{ policyViolationApproval?.GetEntity()?.GetDisplay() }} +
    +
    + {{ policyViolationApproval.stateBadge.caption | translate }} +
    +
    +
    -
    \ No newline at end of file +
    diff --git a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-multi-action/policy-violations-action-multi-action.component.scss b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-multi-action/policy-violations-action-multi-action.component.scss index 7ba872959..509e02aae 100644 --- a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-multi-action/policy-violations-action-multi-action.component.scss +++ b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-multi-action/policy-violations-action-multi-action.component.scss @@ -2,15 +2,6 @@ display: flex; flex-direction: column; overflow: hidden; - - ::ng-deep .mat-list-base .mat-list-item .mat-list-item-content{ - padding-left: 0%; - } - - ::ng-deep .mat-list-base .mat-list-item{ - height: auto; - margin-bottom: 16px; - } } mat-card { @@ -31,12 +22,3 @@ mat-card-content { imx-bulk-editor { margin-top: 20px; } - - -.mat-list-base .mat-list-item .mat-line{ - margin-left: 0px; - overflow: auto; - white-space: inherit; - word-wrap: break-word; - height: auto; -} diff --git a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-multi-action/policy-violations-action-multi-action.component.ts b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-multi-action/policy-violations-action-multi-action.component.ts index c2ddc10a2..e882fcdeb 100644 --- a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-multi-action/policy-violations-action-multi-action.component.ts +++ b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-multi-action/policy-violations-action-multi-action.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -39,10 +39,9 @@ import { PolicyViolationsAction } from '../policy-violations-action.interface'; @Component({ selector: 'imx-policy-violations-action-multi-action', templateUrl: './policy-violations-action-multi-action.component.html', - styleUrls: ['./policy-violations-action-multi-action.component.scss'] + styleUrls: ['./policy-violations-action-multi-action.component.scss'], }) -export class PolicyViolationsActionMultiActionComponent{ - +export class PolicyViolationsActionMultiActionComponent { /** * @ignore since this is only an internal component. * diff --git a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-parameters.interface.ts b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-parameters.interface.ts index 83f9cb52f..c485baf06 100644 --- a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-parameters.interface.ts +++ b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-parameters.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,19 +24,19 @@ * */ -import { ColumnDependentReference } from 'qbm'; +import { BaseCdr } from 'qbm'; /** - * Defines the {@link ColumnDependentReference} for an action that can be performed on a policy violation + * Defines the {@link BaseCdr} for an action that can be performed on a policy violation */ export interface PolicyViolationsActionParameters { /** * A free text field for a reason */ - reason: ColumnDependentReference; + reason: BaseCdr; /** * A FK-Editor for standard justifications */ - justification?: ColumnDependentReference; + justification?: BaseCdr; } diff --git a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-single-action/policy-violations-action-single-action.component.html b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-single-action/policy-violations-action-single-action.component.html index 880cc8072..c2d0861ac 100644 --- a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-single-action/policy-violations-action-single-action.component.html +++ b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-single-action/policy-violations-action-single-action.component.html @@ -1,10 +1,13 @@ - {{ policyViolation.GetEntity()?.GetDisplay() }} + {{ policyViolation.GetEntity()?.GetDisplay() }} {{ policyViolation.State.Column.GetDisplayValue() }} - + - \ No newline at end of file +
    diff --git a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-single-action/policy-violations-action-single-action.component.scss b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-single-action/policy-violations-action-single-action.component.scss index fed2b0eb2..65cdb0a91 100644 --- a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-single-action/policy-violations-action-single-action.component.scss +++ b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-single-action/policy-violations-action-single-action.component.scss @@ -1,16 +1,8 @@ // Create a config with the default typography levels. // see: https://material.angular.io/guide/typography -@import "~@angular/material/theming"; -$config: mat-typography-config(); +@use '@angular/material' as mat; +$config: mat.m2-define-typography-config(); mat-card-content { overflow: hidden; } - -mat-card-title { - font-size: mat-font-size($config, subheading-2); -} - -mat-card-subtitle { - font-size: mat-font-size($config, subheading-1); -} diff --git a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-single-action/policy-violations-action-single-action.component.ts b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-single-action/policy-violations-action-single-action.component.ts index aee990624..ec9c5bd15 100644 --- a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-single-action/policy-violations-action-single-action.component.ts +++ b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action-single-action/policy-violations-action-single-action.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,7 +27,6 @@ import { Component, Input, OnInit } from '@angular/core'; import { AbstractControl, UntypedFormGroup } from '@angular/forms'; -import { ColumnDependentReference } from 'qbm'; import { PolicyViolation } from '../../policy-violation'; import { PolicyViolationsAction } from '../policy-violations-action.interface'; @@ -41,10 +40,9 @@ import { PolicyViolationsAction } from '../policy-violations-action.interface'; @Component({ selector: 'imx-policy-violations-action-single-action', templateUrl: './policy-violations-action-single-action.component.html', - styleUrls: ['./policy-violations-action-single-action.component.scss'] + styleUrls: ['./policy-violations-action-single-action.component.scss'], }) export class PolicyViolationsActionSingleActionComponent implements OnInit { - /** * @ignore since this is only an internal component. * @@ -85,8 +83,6 @@ export class PolicyViolationsActionSingleActionComponent implements OnInit { */ public onFormControlCreated(name: string, control: AbstractControl): void { // use setTimeout to make addControl asynchronous in order to avoid "NG0100: Expression has changed after it was checked" - setTimeout(() => - this.formGroup.addControl(name, control) - ); + setTimeout(() => this.formGroup.addControl(name, control)); } } diff --git a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action.component.html b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action.component.html index e13d358b9..b18ef992c 100644 --- a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action.component.html +++ b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action.component.html @@ -1,4 +1,4 @@ -
    +

    {{ data.description | translate }}

    @@ -9,8 +9,15 @@
    -
    -
    diff --git a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action.component.scss b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action.component.scss index d8efa71bd..7e84805b0 100644 --- a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action.component.scss +++ b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action.component.scss @@ -1,18 +1,16 @@ +@import 'base/mixins'; + .eui-sidesheet-content { - padding: 32px; - overflow: hidden; - height: 100%; + @include flex-column-container($overflow: hidden, $height: 100%); flex: 1; - display: flex; - flex-direction: column; } -.eui-sidesheet-actions.mat-card { +.eui-sidesheet-actions.mat-mdc-card { margin: 16px 32px; } -.imx-policy-violation-content{ +.imx-policy-violation-content { overflow: hidden; display: flex; flex-direction: column; -} \ No newline at end of file +} diff --git a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action.component.ts b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action.component.ts index 74ce203a1..d2edac239 100644 --- a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action.component.ts +++ b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -45,7 +45,7 @@ import { PolicyViolationsAction } from './policy-violations-action.interface'; */ @Component({ templateUrl: './policy-violations-action.component.html', - styleUrls: ['./policy-violations-action.component.scss'] + styleUrls: ['./policy-violations-action.component.scss'], }) export class PolicyViolationsActionComponent { /** @@ -60,7 +60,6 @@ export class PolicyViolationsActionComponent { */ constructor( @Inject(EUI_SIDESHEET_DATA) public readonly data: PolicyViolationsAction, - public readonly sideSheetRef: EuiSidesheetRef - ) { - } + public readonly sideSheetRef: EuiSidesheetRef, + ) {} } diff --git a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action.interface.ts b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action.interface.ts index 76f9409c2..43f771b47 100644 --- a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action.interface.ts +++ b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-action/policy-violations-action.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-sidesheet/policy-violations-sidesheet.component.html b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-sidesheet/policy-violations-sidesheet.component.html index f46b6410e..0d8fbe3ac 100644 --- a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-sidesheet/policy-violations-sidesheet.component.html +++ b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-sidesheet/policy-violations-sidesheet.component.html @@ -1,4 +1,4 @@ - + @@ -6,15 +6,26 @@ - {{'#LDS#Heading Mitigating Controls' | translate}} - + {{ '#LDS#Heading Mitigating Controls' | translate }} + - + - +
    - + {{ '#LDS#Related objects' | translate }} @@ -22,9 +33,7 @@ - +
    @@ -37,13 +46,16 @@
    -
    {{ data?.policyViolation?.State?.GetMetadata()?.GetDisplay() }}
    - - {{ data?.policyViolation?.stateBadge?.caption | translate }} + + {{ data.policyViolation.stateBadge.caption | translate }}
    @@ -51,12 +63,14 @@ -
    - - +
    diff --git a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-sidesheet/policy-violations-sidesheet.component.scss b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-sidesheet/policy-violations-sidesheet.component.scss index e33fd5c3f..d15bd2ba1 100644 --- a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-sidesheet/policy-violations-sidesheet.component.scss +++ b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-sidesheet/policy-violations-sidesheet.component.scss @@ -6,82 +6,30 @@ overflow: hidden; height: 100%; - .tab-group-wrapper { - display: flex; - overflow: auto; - flex: 1; - - .mat-tab-group { - width: 100%; - } - - ::ng-deep .mat-tab-body-content { - display: flex; - flex-direction: column; - } - - ::ng-deep .mat-tab-body-wrapper { - flex: 1; - } - } - - ::ng-deep .mat-tab-group { - height: 100%; - flex-grow: 1; - overflow: auto; - - .mat-tab-header { - padding: 0 32px; - } - - ::ng-deep .mat-tab-body-wrapper { - height: 100%; - } - - ::ng-deep .mat-tab-body-content { - height: 100%; - display: flex; - flex-direction: column; - } - - } - .imx-dirty-indicator { margin-left: 5px; } - .imx-content{ + .imx-content { display: flex; flex: 1 1 auto; flex-direction: column; } - .eui-sidesheet-actions{ + .eui-sidesheet-actions { margin-top: auto; } - .eui-sidesheet-actions.mat-card { + .eui-sidesheet-actions.mat-mdc-card { margin: 16px 32px; } - .eui-badge ::ng-deep .eui-badge-content { - font-size: 12px; - line-height: 12px; - margin-bottom: 20px; - } - .imx-state-caption { font-size: 12px; padding-bottom: 5px; } - .eui-sidesheet-actions { - button:not(:last-of-type) { - margin-right: 10px; - } - } - - .imx-policy-violations-hyperview{ + .imx-policy-violations-hyperview { display: flex; flex-direction: column; } @@ -89,11 +37,7 @@ //Theming :host { - ::ng-deep .mat-tab-header { - background-color: $color-gray-0; - } - - .imx-state-caption { + .imx-state-caption { color: $color-gray-40; } @@ -104,10 +48,6 @@ .eui-dark-theme { :host { - ::ng-deep .mat-tab-header { - background-color: $color-gray-80; - } - .imx-state-caption { color: $color-gray-10; } @@ -120,10 +60,6 @@ .eui-contrast-theme { :host { - ::ng-deep .mat-tab-header { - background-color: $color-gray-90; - } - .imx-state-caption { color: $color-gray-0; } diff --git a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-sidesheet/policy-violations-sidesheet.component.ts b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-sidesheet/policy-violations-sidesheet.component.ts index 2008c08a7..6f150f5d3 100644 --- a/imxweb/projects/pol/src/lib/policy-violations/policy-violations-sidesheet/policy-violations-sidesheet.component.ts +++ b/imxweb/projects/pol/src/lib/policy-violations/policy-violations-sidesheet/policy-violations-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,26 +24,28 @@ * */ -import { Component, Inject } from '@angular/core'; -import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { Component, Inject, OnDestroy } from '@angular/core'; +import { EUI_SIDESHEET_DATA, EuiLoadingService, EuiSidesheetRef } from '@elemental-ui/core'; +import { ObjectInfo } from '@imx-modules/imx-api-pol'; +import { DbObjectKey } from '@imx-modules/imx-qbm-dbts'; import { ColumnDependentReference } from 'qbm'; +import { Subscription } from 'rxjs'; import { PolicyViolation } from '../policy-violation'; import { PolicyViolationsService } from '../policy-violations.service'; -import { ObjectInfo } from 'imx-api-pol'; -import { DbObjectKey } from 'imx-qbm-dbts'; @Component({ selector: 'imx-policy-violations-sidesheet', templateUrl: './policy-violations-sidesheet.component.html', - styleUrls: ['./policy-violations-sidesheet.component.scss'] + styleUrls: ['./policy-violations-sidesheet.component.scss'], }) -export class PolicyViolationsSidesheetComponent { - +export class PolicyViolationsSidesheetComponent implements OnDestroy { public cdrList: ColumnDependentReference[] = []; public selectedHyperviewType: string; public selectedHyperviewUID: string; - public selectedOption: ObjectInfo + public selectedOption: ObjectInfo; + public result: boolean = false; + public closeSubscription: Subscription; public get isPending(): boolean { return this.data.policyViolation.State.value?.toLocaleLowerCase() === 'pending'; @@ -58,44 +60,69 @@ export class PolicyViolationsSidesheetComponent { } constructor( - @Inject(EUI_SIDESHEET_DATA) public data: { + @Inject(EUI_SIDESHEET_DATA) + public data: { policyViolation: PolicyViolation; isMControlPerViolation: boolean; isReadOnly: boolean; }, private readonly policyViolationService: PolicyViolationsService, - public readonly sideSheetRef: EuiSidesheetRef + public readonly sideSheetRef: EuiSidesheetRef, + private readonly euiLoadingService: EuiLoadingService, ) { this.cdrList = this.data.policyViolation.properties; + this.closeSubscription = sideSheetRef.closeClicked().subscribe(() => { + sideSheetRef.close(this.result); + }); + } + + public ngOnDestroy(): void { + this.closeSubscription?.unsubscribe(); } /** - * Opens the Approve-Sidesheet for the current selected rules violations and closes the sidesheet afterwards. + * Opens the Approve-Sidesheet for the current selected rule violations. */ public async approve(): Promise { - await this.policyViolationService.approve([this.data.policyViolation]); - return this.sideSheetRef.close(true); + if (await this.policyViolationService.approve([this.data.policyViolation])) { + return this.reloadData(); + } } /** - * Opens the Deny-Sidesheet for the current selected rules violations and closes the sidesheet afterwards. + * Opens the Deny-Sidesheet for the current selected rule violations. */ public async deny(): Promise { - await this.policyViolationService.deny([this.data.policyViolation]); - return this.sideSheetRef.close(true); + if (await this.policyViolationService.deny([this.data.policyViolation])) { + return this.reloadData(); + } } - public get relatedOptions(): ObjectInfo[]{ + public get relatedOptions(): ObjectInfo[] { return this.data.policyViolation.data || []; } - public setHyperviewObject(selectedRelatedObject: ObjectInfo): void{ - const dbKey = DbObjectKey.FromXml(selectedRelatedObject.ObjectKey); + public setHyperviewObject(selectedRelatedObject: ObjectInfo): void { + const dbKey = DbObjectKey.FromXml(selectedRelatedObject.ObjectKey ?? ''); this.selectedHyperviewType = dbKey.TableName; this.selectedHyperviewUID = dbKey.Keys.join(','); } public onHyperviewOptionSelected(): void { - this.setHyperviewObject(this.selectedOption) + this.setHyperviewObject(this.selectedOption); + } + + private async reloadData() { + this.result = true; + this.euiLoadingService.show(); + try { + const value = await this.policyViolationService.get(true, { + filter: [{ ColumnName: 'ObjectKey', Value1: this.data.policyViolation.ObjectKey.value }], + }); + this.data.policyViolation = value.Data[0]; + this.cdrList = this.data.policyViolation.properties; + } finally { + this.euiLoadingService.hide(); + } } } diff --git a/imxweb/projects/pol/src/lib/policy-violations/policy-violations.component.html b/imxweb/projects/pol/src/lib/policy-violations/policy-violations.component.html index bc4266e93..a64fa8f0f 100644 --- a/imxweb/projects/pol/src/lib/policy-violations/policy-violations.component.html +++ b/imxweb/projects/pol/src/lib/policy-violations/policy-violations.component.html @@ -1,101 +1,111 @@
    -

    - {{ '#LDS#Heading Policy Violations' | translate }} - -

    - -
    - - -
    - - - -
    -
    {{ item.UID_QERPolicy.Column.GetDisplayValue() }}
    -
    {{ item.Description.Column.GetDisplayValue() }}
    -
    -
    -
    - - - - - - -
    - - -
    -
    -
    -
    -
    - -
    +
    +

    + {{ '#LDS#Heading Policy Violations' | translate }} + +

    + +
    + + + + + + + + + {{ entitySchema?.Columns?.UID_QERPolicy?.Display }} + + + +
    +
    {{ item.UID_QERPolicy.Column.GetDisplayValue() }}
    +
    {{ item.Description.Column.GetDisplayValue() }}
    +
    + +
    + + + + {{ '#LDS#Violating object' | translate }} + + + + + {{ item.ObjectKey.Column.GetDisplayValue() }} + + + + + + + {{ entitySchema?.Columns?.State?.Display }} + + + + + {{ item.State.Column.GetDisplayValue() }} + + + + + + + + +
    + + +
    + +
    +
    +
    -
    - -
    - +
    + +
    +
    + + +
    + + +
    +
    + +
    diff --git a/imxweb/projects/qam/src/lib/access-request/access-request-sidesheet.component.scss b/imxweb/projects/qam/src/lib/access-request/access-request-sidesheet.component.scss new file mode 100644 index 000000000..8b73c063a --- /dev/null +++ b/imxweb/projects/qam/src/lib/access-request/access-request-sidesheet.component.scss @@ -0,0 +1,3 @@ +.imx-manual-folder-container { + margin-top: 10px; +} \ No newline at end of file diff --git a/imxweb/projects/qam/src/lib/access-request/access-request-sidesheet.component.ts b/imxweb/projects/qam/src/lib/access-request/access-request-sidesheet.component.ts new file mode 100644 index 000000000..13402720d --- /dev/null +++ b/imxweb/projects/qam/src/lib/access-request/access-request-sidesheet.component.ts @@ -0,0 +1,209 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Component, Inject, OnInit, ViewChild } from '@angular/core'; +import { FormArray, FormControl, FormGroup } from '@angular/forms'; +import { EUI_SIDESHEET_DATA, EuiLoadingService, EuiSidesheetRef } from '@elemental-ui/core'; +import { TranslateService } from '@ngx-translate/core'; +import { Subscription } from 'rxjs'; + +import { CollectionLoadParameters, DataModel, IEntity, ValType } from '@imx-modules/imx-qbm-dbts'; +import { + BaseCdr, + ColumnDependentReference, + ConfirmationService, + DataSourceToolbarFilter, + DataTreeWrapperComponent, + EntityService, + HELPER_ALERT_KEY_PREFIX, + LdsReplacePipe, + StorageService, +} from 'qbm'; +import { AccessRequestDataService } from './access-request-data.service'; +import { AccessRequestSidesheetData } from './access-request-sidesheet-data.interface'; +import { AccessRequestTreeDatabase } from './access-request-tree-database'; +import { QamTreeNode } from './qam-resourcetree'; + +const helperAlertKey = `${HELPER_ALERT_KEY_PREFIX}_requestingFileSystemAccess`; + +interface FolderFormGroup { + folderArray: FormArray; + enterFolderManually: FormControl; +} + +/** + * Component that let the user select multiple resource for his request. + * The user can select the resources from a tree or can enter them manually. + */ +@Component({ + templateUrl: './access-request-sidesheet.component.html', + styleUrls: ['./access-request-sidesheet.component.scss'], +}) +export class AccessRequestSidesheetComponent implements OnInit { + @ViewChild('dataTreeWrapper', { static: true }) dataTreeWrapper: DataTreeWrapperComponent; + + public get showHelperAlert(): boolean { + return !this.storageService.isHelperAlertDismissed(helperAlertKey); + } + public formGroup: FormGroup; + public folderList: ColumnDependentReference[] = []; + + public treeDatabase: AccessRequestTreeDatabase; + public filterOptions: DataSourceToolbarFilter[] = []; + + public selectedNodes: IEntity[] = []; + public showTree = true; + + private dataModel: DataModel; + private readonly subscriptions: Subscription[] = []; + private dgeResourcesNodes: QamTreeNode[] = []; + + constructor( + @Inject(EUI_SIDESHEET_DATA) public data: AccessRequestSidesheetData, + confirmationService: ConfirmationService, + private readonly accessRequestDataService: AccessRequestDataService, + private readonly entityService: EntityService, + private readonly euiBusyService: EuiLoadingService, + private readonly ldsReplace: LdsReplacePipe, + private readonly sidesheetRef: EuiSidesheetRef, + private readonly storageService: StorageService, + private readonly translate: TranslateService, + ) { + this.subscriptions.push( + this.sidesheetRef.closeClicked().subscribe(async () => { + const folders = await this.getFolders(); + if (folders.length === 0 || folders.filter((folder) => folder.length === 0).length > 0) { + const close = await confirmationService.confirmLeaveWithUnsavedChanges(); + if (close) { + this.sidesheetRef.close([]); + } else { + return; + } + } else { + this.sidesheetRef.close(folders); + } + }), + ); + + this.initializeTree(); + } + + public async ngOnInit(): Promise { + this.showTree = this.data.uidAccProduct !== undefined && this.data.uidAccProduct.length > 0; + + this.formGroup = new FormGroup({ + folderArray: new FormArray([]), + enterFolderManually: new FormControl(!this.showTree, { nonNullable: true }), + }); + this.createNewCdr(); + + this.dataModel = this.data.dataModel; + this.filterOptions = this.dataModel?.Filters || []; + } + + protected createNewCdr() { + const cdr = new BaseCdr( + this.entityService.createLocalEntityColumn({ + ColumnName: 'folder', + Type: ValType.Text, + IsMultiLine: true, + }), + this.ldsReplace.transform(this.translate.instant('#LDS#Folder #{0}'), this.folderList.length + 1), + ); + this.folderList.push(cdr); + } + + /** + * Checks, if at least one folder was manually entered or selected from the tree + * @returns true, if one or more folder were entered/selected, otherwise false + */ + public foldersAreValid(): boolean { + if (this.formGroup.controls.enterFolderManually.value) { + // return the manually entered folders + return this.formGroup.controls.folderArray.value.length > 0 && this.formGroup.controls.folderArray.value[0].length > 0; + } else { + // return the selected folders from the tree + return this.selectedNodes.length > 0; + } + } + + protected submitValues(): void { + this.sidesheetRef.close(this.getFolders()); + } + + private async initializeTree(): Promise { + if (this.data.uidAccProduct && this.data.uidAccProduct.length > 0) { + this.dgeResourcesNodes = await this.getData(true); + this.treeDatabase = new AccessRequestTreeDatabase(this.dgeResourcesNodes); + } + } + + /** Returns the list of all selected or manually entered folders. */ + private async getFolders(): Promise { + if (this.formGroup.controls.enterFolderManually.value) { + // return the manually entered folders + return this.formGroup.controls.folderArray.value; + } else { + // return the selected folders from the tree + let folders: string[] = []; + + for (const node of this.selectedNodes) { + const uidQamDug = node.GetColumn('UidQamDug').GetValue(); + // get the fullpath of this dgeResource + const opts: CollectionLoadParameters = { + PageSize: 1, + StartIndex: 0, + filter: [ + { + ColumnName: 'UID_QAMDug', + Value1: uidQamDug, + }, + ], + }; + const dgeResources = await this.accessRequestDataService.getDgeResources(opts); + if (dgeResources?.Data[0]?.FullPath?.value?.length > 0) { + folders.push(dgeResources.Data[0].FullPath.value); + } + } + return folders; + } + } + + private async getData(showLoading: boolean, parameters: CollectionLoadParameters = { ParentKey: '' }): Promise { + let nodes: QamTreeNode[]; + if (showLoading && this.euiBusyService.overlayRefs.length === 0) { + this.euiBusyService.show(); + } + try { + nodes = this.data.uidAccProduct ? await this.accessRequestDataService.getDgeResourceTree(this.data.uidAccProduct) : []; + } finally { + if (showLoading) { + this.euiBusyService.hide(); + } + } + return nodes; + } +} diff --git a/imxweb/projects/qam/src/lib/access-request/access-request-tree-database.ts b/imxweb/projects/qam/src/lib/access-request/access-request-tree-database.ts new file mode 100644 index 000000000..e1745ebf5 --- /dev/null +++ b/imxweb/projects/qam/src/lib/access-request/access-request-tree-database.ts @@ -0,0 +1,89 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { IEntity, TypedEntityBuilder } from '@imx-modules/imx-qbm-dbts'; + +import { TreeDatabase, TreeNode } from 'qbm'; +import { QamResourcetree, QamTreeNode } from './qam-resourcetree'; + +/** Provider of DgeResources for the imx-data-tree */ +export class AccessRequestTreeDatabase extends TreeDatabase { + private readonly builder = new TypedEntityBuilder(QamResourcetree); + + constructor(private readonly dgeResourcesNodes: QamTreeNode[]) { + super(); + this.identifierColumnName = 'UidQamDug'; + this.canSearch = false; + } + + /** Initial data from database */ + public async initialize(): Promise { + let rootNodes: TreeNode[] = []; + rootNodes = this.createTreeNodes(this.dgeResourcesNodes, 0); + this.initialized.next(); + return rootNodes; + } + /** creates recursively all {@link TreeNode | tree nodes} from the given {@link QamTreeNode | qamTree nodes} */ + public createTreeNodes(qamTreeNodes: QamTreeNode[], levelNumber: number): TreeNode[] { + let treeNodes: TreeNode[] = []; + + for (const qamTreeNode of qamTreeNodes) { + // create IEntity + const qamResourcetree = this.builder.buildReadWriteEntity({ + entitySchema: QamResourcetree.GetEntitySchema(), + entityData: QamResourcetree.buildSingleEntityData(qamTreeNode), + }); + + // create TreeNode + const treeNode = this.createNode(qamResourcetree.GetEntity(), levelNumber); + treeNode.isSelectable = qamResourcetree.IsTarget.value; + + // create TreeNodes also for the chiild nodes + if (qamTreeNode.Nodes && qamTreeNode.Nodes.length > 0) { + const childNodes = this.createTreeNodes(qamTreeNode.Nodes, levelNumber + 1); + treeNode.nodes = childNodes; + } + + // push to result + treeNodes.push(treeNode); + } + return treeNodes; + } + + /** return children for a given tree node including the information, if more elements are available on the server */ + public async getChildren(node: TreeNode, startIndex: number): Promise<{ nodes: TreeNode[]; canLoadMore: boolean }> { + return { + nodes: node.nodes as TreeNode[], + canLoadMore: false, + }; + } + + /** Returns the UID of the QamDug */ + public getId(entity: IEntity): string { + const qamNode = entity.GetColumn('UidQamDug')?.GetValue(); + return qamNode.length > 0 ? qamNode : entity.GetDisplay(); + } +} diff --git a/imxweb/projects/qbm/src/lib/sidenav-tree/sidenav-tree.module.ts b/imxweb/projects/qam/src/lib/access-request/access-request.module.ts similarity index 63% rename from imxweb/projects/qbm/src/lib/sidenav-tree/sidenav-tree.module.ts rename to imxweb/projects/qam/src/lib/access-request/access-request.module.ts index 45c8d1705..839ae13a0 100644 --- a/imxweb/projects/qbm/src/lib/sidenav-tree/sidenav-tree.module.ts +++ b/imxweb/projects/qam/src/lib/access-request/access-request.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,28 +26,31 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { MatTreeModule } from '@angular/material/tree'; -import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; -import { MatSidenavModule } from '@angular/material/sidenav'; -import { SidenavTreeComponent } from './sidenav-tree.component'; +import { AccessRequestSidesheetComponent } from './access-request-sidesheet.component'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { TranslateModule } from '@ngx-translate/core'; -import { DataSourceToolbarModule } from '../data-source-toolbar/data-source-toolbar.module'; +import { CdrModule, DataTreeWrapperModule } from 'qbm'; +import { MatButtonModule } from '@angular/material/button'; +import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; +import { MatCardModule } from '@angular/material/card'; +import { MatSelectModule } from '@angular/material/select'; @NgModule({ declarations: [ - SidenavTreeComponent + AccessRequestSidesheetComponent, ], imports: [ CommonModule, - MatTreeModule, + CdrModule, + DataTreeWrapperModule, EuiCoreModule, EuiMaterialModule, - MatSidenavModule, - TranslateModule, - DataSourceToolbarModule - ], - exports: [ - SidenavTreeComponent + MatButtonModule, + MatCardModule, + MatSelectModule, + FormsModule, + ReactiveFormsModule, + TranslateModule ] }) -export class SidenavTreeModule { } +export class AccessRequestModule { } diff --git a/imxweb/projects/qam/src/lib/access-request/access-request.service.ts b/imxweb/projects/qam/src/lib/access-request/access-request.service.ts new file mode 100644 index 000000000..cecf9e3d1 --- /dev/null +++ b/imxweb/projects/qam/src/lib/access-request/access-request.service.ts @@ -0,0 +1,120 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Injectable } from '@angular/core'; +import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; +import { TranslateService } from '@ngx-translate/core'; +import _ from 'lodash'; + +import { PortalCartitem } from '@imx-modules/imx-api-qer'; +import { calculateSidesheetWidth } from 'qbm'; +import { ExtendedEntityWrapper, ICartItemsExtensionService, RequestableProduct } from 'qer'; +import { AccessRequestDataService } from './access-request-data.service'; +import { AccessRequestSidesheetData } from './access-request-sidesheet-data.interface'; +import { AccessRequestSidesheetComponent } from './access-request-sidesheet.component'; + +@Injectable({ + providedIn: 'root', +}) +export class AccessRequestService implements ICartItemsExtensionService { + private folders: string[] = []; + + // TODO #460645: replace UidAccProduct-workaround with if (requestable.DisplayType === 'DGEAccessRequest' ) + private fileSystemAccessUidAccProduct = 'QAM-4B31152BD53849CFBCEA4B27570BD947'; + + constructor( + private readonly accessRequestDataService: AccessRequestDataService, + private readonly busyIndicator: EuiLoadingService, + private readonly sidesheetService: EuiSidesheetService, + private readonly translate: TranslateService, + ) {} + + public async OnBeforeCreateCartItems(products: RequestableProduct[]): Promise { + this.folders = []; + let requestableProducts: RequestableProduct[] = []; + + const entitySchema = this.accessRequestDataService.getSchema(); + const dataModel = await this.accessRequestDataService.getDataModel(); + + const data: AccessRequestSidesheetData = { + entitySchema, + dataModel, + }; + this.busyIndicator.hide(); + + // TODO #460645: replace UidAccProduct-workaround with if (requestable.DisplayType === 'DGEAccessRequest' ) + const countFileSystemAccessProducts = products.filter((product) => product.UidAccProduct === this.fileSystemAccessUidAccProduct).length; + + if (countFileSystemAccessProducts > 0) { + // the user should specify which folders he want to request + data.uidAccProduct = this.fileSystemAccessUidAccProduct; + this.folders = await this.sidesheetService + .open(AccessRequestSidesheetComponent, { + title: this.translate.instant('#LDS#Heading Requesting File System Access'), + width: calculateSidesheetWidth(800, 0.5), + disableClose: true, + testId: 'access-request-sidesheet', + padding: '0px', + data, + }) + .afterClosed() + .toPromise(); + } + for (const product of products) { + if (product.UidAccProduct === this.fileSystemAccessUidAccProduct) { + if (this.folders?.length > 0) { + // create a product for each folder + for (const folder of this.folders) { + const requestable = _.cloneDeep(product); + requestable.Folder = folder; + requestableProducts.push(requestable); + } + } + } else { + requestableProducts.push(product); + } + } + + return requestableProducts; + } + + public async OnAfterCreateCartItem( + product: RequestableProduct, + cartItem: ExtendedEntityWrapper, + ): Promise> { + if ((product.Folder?.length ?? 0) > 0) { + for (const parameterCategoryColumn of cartItem.parameterCategoryColumns) { + if (parameterCategoryColumn.column.ColumnName === 'Resource') { + parameterCategoryColumn.column.PutValue(product.Folder); + } + if (parameterCategoryColumn.column.ColumnName === 'Access') { + parameterCategoryColumn.column.PutValue('Read'); + } + } + } + return cartItem; + } +} diff --git a/imxweb/projects/qam/src/lib/access-request/qam-resourcetree.ts b/imxweb/projects/qam/src/lib/access-request/qam-resourcetree.ts new file mode 100644 index 000000000..c0e28f606 --- /dev/null +++ b/imxweb/projects/qam/src/lib/access-request/qam-resourcetree.ts @@ -0,0 +1,124 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { + EntityColumnData, + EntityData, + EntitySchema, + ExtendedTypedEntityCollection, + IClientProperty, + IReadValue, + TypedEntity, + TypedEntityBuilder, + ValType, +} from '@imx-modules/imx-qbm-dbts'; +import { TreeNode } from '../TypedClient'; + +import { TranslateService } from '@ngx-translate/core'; + +export class QamTreeNode implements TreeNode { + UidQamDug?: string; + Display?: string; + IsTarget: boolean; + Nodes?: TreeNode[]; +} + +export class QamResourcetree extends TypedEntity { + public readonly Display: IReadValue = this.GetEntityValue('Display'); + public readonly UidQamDug: IReadValue = this.GetEntityValue('UidQamDug'); + public readonly IsTarget: IReadValue = this.GetEntityValue('IsTarget'); + // public readonly HasChildren: IWriteValue = this.GetEntityValue('HasChildren'); + + public Nodes: QamResourcetree[] = []; + public static GetEntitySchema(translate?: TranslateService): EntitySchema { + const returnColumns: { [key: string]: IClientProperty } = {}; + + returnColumns.Display = { + Type: ValType.String, + ColumnName: 'Display', + Display: translate ? translate.instant('#LDS#Display name') : '#LDS#Display name', + }; + + returnColumns.UidQamDug = { + Type: ValType.String, + ColumnName: 'UidQamDug', + Display: translate ? translate.instant('#LDS#UidQamDug') : '#LDS#UidQamDug', + }; + + returnColumns.IsTarget = { + Type: ValType.Bool, + ColumnName: 'IsTarget', + Display: translate ? translate.instant('#LDS#Is Target') : '#LDS#Is Target', + }; + + returnColumns.HasChildren = { + Type: ValType.Bool, + ColumnName: 'HasChildren', + }; + + return { + TypeName: 'QamResourcetree', + Display: translate ? translate.instant('#LDS#QamResourcetree') : '#LDS#QamResourcetree', + Columns: returnColumns, + }; + } + + public static buildEntities( + entityData: EntityData[], + entitySchema: EntitySchema, + ): ExtendedTypedEntityCollection { + const builder = new TypedEntityBuilder(QamResourcetree); + const typedEntityCollection = builder.buildReadWriteEntities( + { + TotalCount: entityData.length, + Entities: entityData, + }, + entitySchema, + ); + return typedEntityCollection; + } + + public static buildEntityData(nodes: TreeNode[]): EntityData[] { + return nodes.map((elem) => { + const returnColumns: { [key: string]: EntityColumnData } = {}; + returnColumns.Display = { Value: elem.Display, IsReadOnly: true }; + returnColumns.IsTarget = { Value: elem.IsTarget, IsReadOnly: true }; + returnColumns.UidQamDug = { Value: elem.UidQamDug, IsReadOnly: true }; + returnColumns.HasChildren = { Value: elem.Nodes && elem.Nodes.length > 0, IsReadOnly: true }; + returnColumns.nodes = { Value: elem.Nodes, IsReadOnly: true }; + return { Columns: returnColumns, Display: elem.Display }; + }); + } + + public static buildSingleEntityData(node: TreeNode): EntityData { + const returnColumns: { [key: string]: EntityColumnData } = {}; + returnColumns.Display = { Value: node.Display, IsReadOnly: true }; + returnColumns.IsTarget = { Value: node.IsTarget, IsReadOnly: true }; + returnColumns.UidQamDug = { Value: node.UidQamDug, IsReadOnly: true }; + returnColumns.HasChildren = { Value: node.Nodes && node.Nodes.length > 0, IsReadOnly: true }; + return { Columns: returnColumns, Display: node.Display }; + } +} diff --git a/imxweb/projects/qam/src/lib/access/access.component.html b/imxweb/projects/qam/src/lib/access/access.component.html new file mode 100644 index 000000000..7910497a9 --- /dev/null +++ b/imxweb/projects/qam/src/lib/access/access.component.html @@ -0,0 +1,13 @@ +
    + + +
    +
    + {{ "#LDS#'{0}' has access to the following governed data resources." | translate | ldsReplace: referrer.display }} +
    + + +
    + +
    +
    diff --git a/imxweb/projects/qam/src/lib/access/access.component.scss b/imxweb/projects/qam/src/lib/access/access.component.scss new file mode 100644 index 000000000..4c054a88d --- /dev/null +++ b/imxweb/projects/qam/src/lib/access/access.component.scss @@ -0,0 +1,21 @@ +@import 'base/mixins'; + +:host { + @include flex-column-container-fill(); +} + +.imx-card-content.hidden { + display: none; +} + +.imx-busy { + padding: 10px; + display: flex; + flex-direction: column; + align-items: center; + height: 100%; +} + +.imx-card-content { + @include flex-column-container-fill(); +} diff --git a/imxweb/projects/qam/src/lib/access/access.component.ts b/imxweb/projects/qam/src/lib/access/access.component.ts new file mode 100644 index 000000000..1e0d77b7f --- /dev/null +++ b/imxweb/projects/qam/src/lib/access/access.component.ts @@ -0,0 +1,69 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Subscription } from 'rxjs'; + +import { BusyService, DynamicTabDataProviderDirective } from 'qbm'; +import { QamApiService } from '../qam-api-client.service'; +import { TrusteeAccessData } from '../TypedClient'; + +/** Shows access information for an account or a system entitlement. */ +@Component({ + templateUrl: './access.component.html', + styleUrls: ['./access.component.scss'], +}) +export class AccessComponent implements OnInit, OnDestroy { + public data: TrusteeAccessData; + + public referrer: { objecttable: string; objectuid: string; display: string }; + + public busyService: BusyService; + public isLoading: boolean = true; + private subscription: Subscription | undefined; + + constructor( + dataProvider: DynamicTabDataProviderDirective, + private readonly qamApi: QamApiService, + ) { + this.referrer = dataProvider.data; + this.busyService = new BusyService(); + this.subscription = this.busyService.busyStateChanged.subscribe((elem) => (this.isLoading = elem)); + } + + public ngOnDestroy(): void { + this.subscription?.unsubscribe(); + } + + public async ngOnInit(): Promise { + const isBusy = this.busyService.beginBusy(); + try { + this.data = await this.qamApi.client.portal_dge_access_get(this.referrer.objecttable, this.referrer.objectuid); + } finally { + isBusy?.endBusy(); + } + } +} diff --git a/imxweb/projects/qam/src/lib/access/trustee-view.component.html b/imxweb/projects/qam/src/lib/access/trustee-view.component.html new file mode 100644 index 000000000..385b864a3 --- /dev/null +++ b/imxweb/projects/qam/src/lib/access/trustee-view.component.html @@ -0,0 +1,61 @@ + +
    + + + + + + + + +
    + + +
    + +
    + +
    +
    +
    +
    +
    + + + + {{ perm.path }} + + + + + + + + + + +
    +
    + + +
    + +

    #LDS#No groups or accounts have access to governed data.

    +
    +
    + + +
    + +

    #LDS#Please select a trustee.

    +
    +
    diff --git a/imxweb/projects/qam/src/lib/access/trustee-view.component.scss b/imxweb/projects/qam/src/lib/access/trustee-view.component.scss new file mode 100644 index 000000000..6ce1b99ba --- /dev/null +++ b/imxweb/projects/qam/src/lib/access/trustee-view.component.scss @@ -0,0 +1,73 @@ +@import 'base/mixins'; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; + +:host { + @include flex-row-container-fill(); + overflow: hidden; +} + +.imx-trustee-tree { + background-color: transparent; +} + +.imx-trustee-content { + @include flex-row-container-fill(); + overflow: hidden; + gap: 20px; + padding: 3px; + > :first-child { + width: 300px; + } +} + +.imx-selected-trustee { + @include flex-column-container-fill(); +} + +.imx-trustee-card { + box-shadow: inherit; +} + +.imx-permission-card { + margin: 20px; + .mat-mdc-card-header { + margin-bottom: 20px; + font-weight: 600; + align-items: center; + @include flex-column-container(); + } + .mat-mdc-card-content { + @include flex-row-container-fill(); + } +} + +.imx-trustee-tree-invisible { + display: none; +} + +.imx-trustee-tree ul, +.imx-trustee-tree li { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +/* + * This padding sets alignment of the nested nodes. + */ +.imx-trustee-tree .mat-nested-tree-node div[role='group'] { + padding-left: 40px; +} + +/* + * Padding for leaf nodes. + * Leaf nodes need to have padding so as to align with other non-leaf nodes + * under the same parent. + */ +.imx-trustee-tree div[role='group'] > .mat-tree-node { + padding-left: 20px; +} + +.tree-button-node { + color: inherit; +} diff --git a/imxweb/projects/qam/src/lib/access/trustee-view.component.ts b/imxweb/projects/qam/src/lib/access/trustee-view.component.ts new file mode 100644 index 000000000..24af54630 --- /dev/null +++ b/imxweb/projects/qam/src/lib/access/trustee-view.component.ts @@ -0,0 +1,97 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { NestedTreeControl } from '@angular/cdk/tree'; +import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; +import { MatTreeNestedDataSource } from '@angular/material/tree'; +import { BusyService } from 'qbm'; +import { TrusteeAccessData, TrusteeData } from '../TypedClient'; +import { QamApiService } from '../qam-api-client.service'; + +interface TrusteePermissionData { + path: string; + allowRead: boolean; + allowWrite: boolean; + anyAllow: boolean; + allowFullControl: boolean; + allowChangePermissions: boolean; +} + +/** Component to display a TrusteeAccessData structure. */ +@Component({ + templateUrl: './trustee-view.component.html', + styleUrls: ['../qam.scss', './trustee-view.component.scss'], + selector: 'imx-trustee-view', +}) +export class TrusteeViewComponent implements OnInit, OnChanges { + public currentSelectedTrustee: TrusteeData | undefined; + public currentSelectedTrusteePermissions: TrusteePermissionData[] = []; + public treeControl = new NestedTreeControl((node) => node.Children); + public dataSource = new MatTreeNestedDataSource(); + @Input() public data: TrusteeAccessData | undefined; + @Input() public busyService: BusyService | undefined; + + public trusteeTypes: { [id: number]: string }; + + constructor(private readonly api: QamApiService) {} + + public async ngOnInit(): Promise { + const isBusy = this.busyService?.beginBusy(); + try { + this.trusteeTypes = await this.api.getTrusteeTypes(); + } finally { + isBusy?.endBusy(); + } + + this.dataSource.data = this.data?.Trustees ?? []; + } + + public ngOnChanges(changes: SimpleChanges) { + if (changes.data) { + this.dataSource.data = this.data?.Trustees ?? []; + } + } + + public hasChild = (_: number, node: TrusteeData) => !!node.Children && node.Children.length > 0; + + public isSelected = (node: TrusteeData) => { + return node.UidQamTrustee === this.currentSelectedTrustee?.UidQamTrustee; + }; + + public updateView(data: TrusteeData) { + this.currentSelectedTrustee = data; + this.currentSelectedTrusteePermissions = + data.Paths?.map((elem) => ({ + path: elem.Path ?? '', + allowRead: elem.Permissions?.includes('AllowRead') ?? false, + allowWrite: elem.Permissions?.includes('AllowWrite') ?? false, + allowChangePermissions: elem.Permissions?.includes('AllowChangePermissions') ?? false, + allowFullControl: elem.Permissions?.includes('AllowFullControl') ?? false, + anyAllow: elem.Permissions?.includes('AnyAllow') ?? false, + })) ?? []; + console.log('martina', this.currentSelectedTrustee, this.currentSelectedTrusteePermissions); + } +} diff --git a/imxweb/projects/qam/src/lib/access/user-access.component.html b/imxweb/projects/qam/src/lib/access/user-access.component.html new file mode 100644 index 000000000..f22394787 --- /dev/null +++ b/imxweb/projects/qam/src/lib/access/user-access.component.html @@ -0,0 +1 @@ + diff --git a/imxweb/projects/qer/src/lib/approval-workflows/approval-workflow-edit/approval-workflow-edit-info/approval-workflow-edit-info.component.ts b/imxweb/projects/qam/src/lib/access/user-access.component.ts similarity index 66% rename from imxweb/projects/qer/src/lib/approval-workflows/approval-workflow-edit/approval-workflow-edit-info/approval-workflow-edit-info.component.ts rename to imxweb/projects/qam/src/lib/access/user-access.component.ts index f981b5481..934280d7c 100644 --- a/imxweb/projects/qer/src/lib/approval-workflows/approval-workflow-edit/approval-workflow-edit-info/approval-workflow-edit-info.component.ts +++ b/imxweb/projects/qam/src/lib/access/user-access.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,20 +25,18 @@ */ import { Component, OnInit } from '@angular/core'; -import { MatDialogRef } from '@angular/material/dialog'; +import { imx_SessionService } from 'qbm'; +/** Shows access information for the authenticated user's identity. */ @Component({ - selector: 'imx-approval-workflow-edit-info', - templateUrl: './approval-workflow-edit-info.component.html', - styleUrls: ['./approval-workflow-edit-info.component.scss'] + templateUrl: './user-access.component.html', }) -export class ApprovalWorkflowEditInfoComponent { +export class UserAccessComponent implements OnInit { + constructor(private readonly session: imx_SessionService) {} - constructor( - public dialogRef: MatDialogRef - ) { } + userUid: string; - public close(): void { - this.dialogRef.close(); + ngOnInit(): void { + this.userUid = this.session.SessionState.UserUid ?? ''; } } diff --git a/imxweb/projects/qam/src/lib/dug-activities/dug-activities.component.html b/imxweb/projects/qam/src/lib/dug-activities/dug-activities.component.html new file mode 100644 index 000000000..371104b06 --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug-activities/dug-activities.component.html @@ -0,0 +1,61 @@ + + + + + {{ '#LDS# Most active resources' | translate }} + + + +
    +

    + {{ + '#LDS#The following list shows the {0} most active governed resources which you are responsible for. Activity is aggregated for the last {1} days.' + | translate + | ldsReplace: topCountResource : interval + }} +

    + + + + + +
    {{ item.GetEntity().GetDisplay() }}
    +
    {{ item.GetEntity().GetDisplayLong() }}
    +
    +
    + +
    +
    +
    +
    + + + + + {{ '#LDS# Most active users' | translate }} + + + +
    +

    + {{ + '#LDS#The following list shows the {0} most active users on resources which you are responsible for. Activity is aggregated for the last {1} days.' + | translate + | ldsReplace: topCountTrustee : interval + }} +

    + + + + + +
    {{ item.GetEntity().GetDisplay() }}
    +
    {{ item.GetEntity().GetDisplayLong() }}
    +
    +
    + +
    +
    +
    +
    +
    diff --git a/imxweb/projects/qam/src/lib/dug-activities/dug-activities.component.scss b/imxweb/projects/qam/src/lib/dug-activities/dug-activities.component.scss new file mode 100644 index 000000000..b2efe27d5 --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug-activities/dug-activities.component.scss @@ -0,0 +1,3 @@ +:host{ + padding-top: 16px; +} \ No newline at end of file diff --git a/imxweb/projects/qam/src/lib/dug-activities/dug-activities.component.ts b/imxweb/projects/qam/src/lib/dug-activities/dug-activities.component.ts new file mode 100644 index 000000000..f0c907272 --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug-activities/dug-activities.component.ts @@ -0,0 +1,116 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Component, OnInit } from '@angular/core'; +import { EuiLoadingService } from '@elemental-ui/core'; +import { DugActivitiesService } from './dug-activities.service'; +import { ResourceActivityData, TrusteeActivityData } from '../TypedClient'; +import { DugActivityEntity } from './dug-activity-entity'; +import { TranslateService } from '@ngx-translate/core'; +import { DisplayColumns, EntitySchema } from '@imx-modules/imx-qbm-dbts'; +import { DataSourceToolbarSettings } from 'qbm'; + +@Component({ + selector: 'imx-dug-activities', + templateUrl: './dug-activities.component.html', + styleUrls: ['./dug-activities.component.scss'], +}) +export class DugActivitiesComponent implements OnInit { + private mostActiveTrustees: TrusteeActivityData[]; + private mostActiveResources: ResourceActivityData[]; + + public entitySchemaTrustee: EntitySchema; + public entitySchemaResource: EntitySchema; + + public dstSettingsTrustee: DataSourceToolbarSettings; + public dstSettingsResource: DataSourceToolbarSettings; + public DisplayColumns = DisplayColumns; + + private displayColumnsTrustee; + private displayColumnsResource; + + public topCountResource: number = 10; + public topCountTrustee: number = 10; + + public interval: number = 0; + + constructor( + public readonly activityService: DugActivitiesService, + private readonly loadingServiceEui: EuiLoadingService, + private readonly translateService: TranslateService, + ) {} + + public async ngOnInit(): Promise { + const over = this.loadingServiceEui.show(); + try { + this.entitySchemaTrustee = DugActivityEntity.GetEntitySchema('Trustee', '#LDS#Trustee', this.translateService); + this.entitySchemaResource = DugActivityEntity.GetEntitySchema('Resources', '#LDS#Resources', this.translateService); + this.mostActiveTrustees = await this.activityService.getMostActiveTrustees(); + this.mostActiveResources = await this.activityService.getMostActiveResources(); + const config = await this.activityService.getConfig(); + this.interval = config.ActivityAggregationIntervalDays; + this.displayColumnsTrustee = [ + this.entitySchemaTrustee.Columns[DisplayColumns.DISPLAY_PROPERTYNAME], + this.entitySchemaTrustee.Columns['CountActivities'], + ]; + this.displayColumnsResource = [ + this.entitySchemaResource.Columns[DisplayColumns.DISPLAY_PROPERTYNAME], + this.entitySchemaResource.Columns['CountActivities'], + ]; + this.initTrustee(); + this.initResources(); + + } finally { + this.loadingServiceEui.hide(over); + } + } + + public initTrustee() { + const data = DugActivityEntity.buildEntities( + DugActivityEntity.buildEntityDataTrustee(this.mostActiveTrustees), + this.entitySchemaTrustee, + ); + this.dstSettingsTrustee = { + displayedColumns:this.displayColumnsTrustee, + dataSource: data, + entitySchema: this.entitySchemaTrustee, + navigationState: {}, + }; + } + + public initResources() { + const data = DugActivityEntity.buildEntities( + DugActivityEntity.buildEntityDataTrustee(this.mostActiveResources), + this.entitySchemaResource, + ); + this.dstSettingsResource = { + displayedColumns:this.displayColumnsResource, + dataSource: data, + entitySchema: this.entitySchemaResource, + navigationState: {}, + }; + } +} diff --git a/imxweb/projects/qer/src/lib/statistics/statistics-home-page/statistics-ordering-sidesheet/statistics-ordering-sidesheet-dialog/statistics-ordering-sidesheet-dialog.component.ts b/imxweb/projects/qam/src/lib/dug-activities/dug-activities.service.ts similarity index 53% rename from imxweb/projects/qer/src/lib/statistics/statistics-home-page/statistics-ordering-sidesheet/statistics-ordering-sidesheet-dialog/statistics-ordering-sidesheet-dialog.component.ts rename to imxweb/projects/qam/src/lib/dug-activities/dug-activities.service.ts index c35e1a8cc..851554661 100644 --- a/imxweb/projects/qer/src/lib/statistics/statistics-home-page/statistics-ordering-sidesheet/statistics-ordering-sidesheet-dialog/statistics-ordering-sidesheet-dialog.component.ts +++ b/imxweb/projects/qam/src/lib/dug-activities/dug-activities.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,22 +24,26 @@ * */ -import { Component } from '@angular/core'; -import { MatDialogRef } from '@angular/material/dialog'; - -@Component({ - selector: 'imx-statistics-ordering-sidesheet-dialog', - templateUrl: './statistics-ordering-sidesheet-dialog.component.html', - styleUrls: ['./statistics-ordering-sidesheet-dialog.component.scss'] -}) -export class StatisticsOrderingSidesheetDialogComponent { +import { Injectable } from '@angular/core'; +import { QamApiService } from '../qam-api-client.service'; +import { DgeConfigData, ResourceActivityData, TrusteeActivityData } from '../TypedClient'; +@Injectable({ providedIn: 'root' }) +export class DugActivitiesService { constructor( - public dialogRef: MatDialogRef - ) { } + private readonly api: QamApiService, + ) {} + - public close(): void { - this.dialogRef.close(); + public async getMostActiveTrustees(): Promise { + return this.api.client.portal_dge_mostactivetrustees_get(); } -} + public async getMostActiveResources(): Promise { + return this.api.client.portal_dge_mostactiveresources_get(); + } + + public async getConfig(): Promise{ + return this.api.client.portal_dgeconfig_get(); + } +} \ No newline at end of file diff --git a/imxweb/projects/qam/src/lib/dug-activities/dug-activity-entity.ts b/imxweb/projects/qam/src/lib/dug-activities/dug-activity-entity.ts new file mode 100644 index 000000000..9c8e408e9 --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug-activities/dug-activity-entity.ts @@ -0,0 +1,91 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { + DisplayColumns, + EntityColumnData, + EntityData, + EntitySchema, + ExtendedTypedEntityCollection, + IClientProperty, + TypedEntity, + TypedEntityBuilder, + ValType, +} from '@imx-modules/imx-qbm-dbts'; +import { TranslateService } from '@ngx-translate/core'; +import { ResourceActivityData, TrusteeActivityData } from '../TypedClient'; + +export class DugActivityEntity extends TypedEntity { + public static GetEntitySchema(typeName: string, typeDisplay: string, translate?: TranslateService): EntitySchema { + const returnColumns: { [key: string]: IClientProperty } = {}; + + returnColumns.CountActivities = { + Type: ValType.Int, + ColumnName: 'CountActivities', + Display: translate ? translate.instant('#LDS#Activity count') : '#LDS#Activity count', + }; + + returnColumns[DisplayColumns.DISPLAY_PROPERTYNAME] = DisplayColumns.DISPLAY_PROPERTY; + + return { + TypeName: typeName, + Display: translate ? translate.instant(typeDisplay) : typeDisplay, + Columns: returnColumns, + }; + } + + public static buildEntities( + entityData: EntityData[], + entitySchema: EntitySchema, + ): ExtendedTypedEntityCollection { + const builder = new TypedEntityBuilder(DugActivityEntity); + return builder.buildReadWriteEntities( + { + TotalCount: entityData.length, + Entities: entityData, + }, + entitySchema, + ); + } + + public static buildEntityDataTrustee(trustees: TrusteeActivityData[]): EntityData[] { + return trustees.map((elem) => { + const returnColumns: { [key: string]: EntityColumnData } = {}; + returnColumns.CountActivities = { Value: elem.CountActivities, IsReadOnly: true }; + + return { Columns: returnColumns, Display: elem.Display ?? '', LongDisplay: elem.LongDisplay ?? '', Keys: [elem.UidTrustee ?? ''] }; + }); + } + + public static buildEntityDataResource(resources: ResourceActivityData[]): EntityData[] { + return resources.map((elem) => { + const returnColumns: { [key: string]: EntityColumnData } = {}; + returnColumns.CountActivities = { Value: elem.CountActivities, IsReadOnly: true }; + + return { Columns: returnColumns, Display: elem.Display ?? '', LongDisplay: elem.LongDisplay ?? '', Keys: [elem.UidQamDug ?? ''] }; + }); + } +} diff --git a/imxweb/projects/qam/src/lib/dug-dashboards/dug-dashboards.component.html b/imxweb/projects/qam/src/lib/dug-dashboards/dug-dashboards.component.html new file mode 100644 index 000000000..6be0894bc --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug-dashboards/dug-dashboards.component.html @@ -0,0 +1,12 @@ +
    + + + {{ stat?.Display }} + + +
    + +
    +
    +
    +
    diff --git a/imxweb/projects/qam/src/lib/dug-dashboards/dug-dashboards.component.scss b/imxweb/projects/qam/src/lib/dug-dashboards/dug-dashboards.component.scss new file mode 100644 index 000000000..121d0768f --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug-dashboards/dug-dashboards.component.scss @@ -0,0 +1,41 @@ +@import 'base/mixins'; + +:host { + height: 100%; + width: 100%; + display: flex; + flex-direction: column; + overflow: hidden; +} + +.chart-container { + display: flex; + height: fit-content; + flex-wrap: wrap; + grid-auto-rows: auto; + gap: 15px; + grid-auto-flow: dense; + padding: 10px; + overflow: auto; +} + +.mat-card-header { + justify-content: center; +} + +.center-content { + display: flex; + flex-direction: column; + margin: auto; +} + +.stat-card { + min-height: 300px; + min-width: 500px; + max-width: 600px; + display: flex; + flex-direction: column; + text-align: center; + border: 1px solid transparent; + @include eui-elevation-1; +} diff --git a/imxweb/projects/qam/src/lib/dug-dashboards/dug-dashboards.component.ts b/imxweb/projects/qam/src/lib/dug-dashboards/dug-dashboards.component.ts new file mode 100644 index 000000000..faeac10bd --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug-dashboards/dug-dashboards.component.ts @@ -0,0 +1,57 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Component, OnInit } from '@angular/core'; +import { EuiLoadingService } from '@elemental-ui/core'; +import { ChartInfoTyped } from 'qer'; +import { ChartDto } from '../TypedClient'; +import { DugDashboardsService } from './dug-dashboards.service'; + +@Component({ + selector: 'imx-dug-dashboards', + templateUrl: './dug-dashboards.component.html', + styleUrls: ['./dug-dashboards.component.scss'], +}) +export class DugDashboardsComponent implements OnInit { + public stats: ChartDto[] = []; + public info: ChartInfoTyped[] = []; + constructor( + public readonly dashboardsService: DugDashboardsService, + private readonly loadingServiceEui: EuiLoadingService, + ) {} + + public async ngOnInit(): Promise { + const over = this.loadingServiceEui.show(); + try { + const test = await this.dashboardsService.getDashboards(); + this.stats = test.Data ?? []; + this.info = ChartInfoTyped.buildEntities((test.Charts ?? []).map((elem) => ChartInfoTyped.buildEntityData(elem))).Data; + console.log(this.info, this.stats); + } finally { + this.loadingServiceEui.hide(over); + } + } +} diff --git a/imxweb/projects/qbm/src/lib/select/check-match.validator.ts b/imxweb/projects/qam/src/lib/dug-dashboards/dug-dashboards.service.ts similarity index 65% rename from imxweb/projects/qbm/src/lib/select/check-match.validator.ts rename to imxweb/projects/qam/src/lib/dug-dashboards/dug-dashboards.service.ts index 99b680560..097b71090 100644 --- a/imxweb/projects/qbm/src/lib/select/check-match.validator.ts +++ b/imxweb/projects/qam/src/lib/dug-dashboards/dug-dashboards.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,18 +24,15 @@ * */ -import { AbstractControl } from '@angular/forms'; +import { Injectable } from '@angular/core'; +import { PersonalStatsData } from '../TypedClient'; +import { QamApiService } from '../qam-api-client.service'; -// tslint:disable-next-line: typedef -export function CheckMatchValidator(control: AbstractControl) { - if (control.disabled || control.pristine) { - return null; - } +@Injectable({ providedIn: 'root' }) +export class DugDashboardsService { + constructor(private readonly api: QamApiService) {} - const selection: any = control.value; - if (typeof selection === 'string' && selection !== '') { - return { matchFailed: true }; + public async getDashboards(): Promise { + return this.api.client.portal_dge_personalstats_get(); } - - return null; } diff --git a/imxweb/projects/qam/src/lib/dug-overview/dug-overview.component.html b/imxweb/projects/qam/src/lib/dug-overview/dug-overview.component.html new file mode 100644 index 000000000..129473c33 --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug-overview/dug-overview.component.html @@ -0,0 +1,47 @@ + +
    +
    +

    #LDS#Heading Governed Data

    + +
    +
    + + +
    + + + + + + +
    {{ item.GetEntity().GetDisplay() }}
    +
    {{ item.GetEntity().GetDisplayLong() }}
    +
    +
    + + +
    + +
    +
    +
    + + + + + + + + + + + +
    +
    diff --git a/imxweb/projects/qam/src/lib/dug-overview/dug-overview.component.scss b/imxweb/projects/qam/src/lib/dug-overview/dug-overview.component.scss new file mode 100644 index 000000000..5f52e225e --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug-overview/dug-overview.component.scss @@ -0,0 +1,11 @@ +@import 'base/mixins'; + +:host { + @include flex-column-container($overflow: hidden, $height: 100%, $max-width: 100%); + padding-bottom: 3px; +} + +.heading-wrapper { + @include flex-row-container(); + flex: 0 0 auto; +} diff --git a/imxweb/projects/qam/src/lib/dug-overview/dug-overview.component.ts b/imxweb/projects/qam/src/lib/dug-overview/dug-overview.component.ts new file mode 100644 index 000000000..031090961 --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug-overview/dug-overview.component.ts @@ -0,0 +1,128 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Component, OnInit } from '@angular/core'; +import { BusyService, calculateSidesheetWidth, DataSourceToolbarSettings, HelpContextualValues, SideNavigationComponent } from 'qbm'; + +import { EuiSidesheetService } from '@elemental-ui/core'; +import { CollectionLoadParameters, DataModel, DisplayColumns, EntitySchema, IClientProperty, TypedEntity } from '@imx-modules/imx-qbm-dbts'; +import { TranslateService } from '@ngx-translate/core'; +import { DugSidesheetComponent } from '../dug/dug-sidesheet.component'; +import { DugOverviewService } from './dug-overview.service'; + +@Component({ + selector: 'imx-dug-overview', + templateUrl: './dug-overview.component.html', + styleUrls: ['./dug-overview.component.scss'], +}) +export class DugOverviewComponent implements OnInit, SideNavigationComponent { + public data?: any; + public contextId?: HelpContextualValues; + private dataModel: DataModel; + + public busyService = new BusyService(); + public navigationState: CollectionLoadParameters = {}; + public dstSettings: DataSourceToolbarSettings; + public entitySchema: EntitySchema; + private displayedColumns: IClientProperty[] = []; + public readonly DisplayColumns = DisplayColumns; + + constructor( + private readonly overviewService: DugOverviewService, + private readonly sideSheet: EuiSidesheetService, + private readonly translate: TranslateService, + ) { + this.entitySchema = overviewService.DugResourceSchema; + this.displayedColumns = [ + this.entitySchema.Columns[DisplayColumns.DISPLAY_PROPERTYNAME], + this.entitySchema.Columns.UID_QAMResourceType, + this.entitySchema.Columns.RiskIndexCalculated, + ]; + } + + public async ngOnInit(): Promise { + const isBusy = this.busyService.beginBusy(); + try { + this.dataModel = await this.overviewService.getDataModel(); + await this.getData(); + } finally { + isBusy.endBusy(); + } + } + + /** + * Occurs when the navigation state has changed - e.g. users clicks on the next page button. + * + */ + public async onNavigationStateChanged(newState?: CollectionLoadParameters): Promise { + await this.getData(newState); + } + + /** + * Occurs when user triggers search. + * + * @param keywords Search keywords. + */ + public async onSearch(keywords: string): Promise { + await this.getData({ ...this.navigationState, StartIndex: 0, search: keywords }); + } + + public async showDugResource(resource: TypedEntity): Promise { + const sidesheetRef = this.sideSheet.open(DugSidesheetComponent, { + title: this.translate.instant('#LDS#Heading Edit Governed Data'), + subTitle: resource.GetEntity().GetDisplay(), + width: calculateSidesheetWidth(1000, 0.9), + disableClose: true, + padding: '0', + testId: 'edit-dug-resource-sidesheet', + data: { uid: resource.GetEntity().GetKeys()[0] }, + }); + sidesheetRef.afterClosed().subscribe((result) => { + if (result) { + this.getData(this.navigationState); + } + }); + } + + private async getData(parameter: CollectionLoadParameters = {}): Promise { + const isBusy = this.busyService.beginBusy(); + this.navigationState = { ...parameter, owned: '1' }; + try { + const data = await this.overviewService.getData(this.navigationState); + + this.dstSettings = { + displayedColumns: this.displayedColumns, + dataSource: data, + dataModel: this.dataModel, + filters: this.dataModel?.Filters ?? [], + entitySchema: this.entitySchema, + navigationState: this.navigationState, + }; + } finally { + isBusy.endBusy(); + } + } +} diff --git a/imxweb/projects/qam/src/lib/dug-overview/dug-overview.service.ts b/imxweb/projects/qam/src/lib/dug-overview/dug-overview.service.ts new file mode 100644 index 000000000..767948248 --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug-overview/dug-overview.service.ts @@ -0,0 +1,67 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Injectable } from '@angular/core'; +import { QamApiService } from '../qam-api-client.service'; +import { CollectionLoadParameters, DataModel, EntitySchema, ExtendedTypedEntityCollection } from '@imx-modules/imx-qbm-dbts'; +import { ChangeRequestType, DgeConfigData, PortalDgeResources, PortalDgeResourcesActivity, PortalDgeResourcesbyid } from '../TypedClient'; +import { SettingsService } from 'qbm'; + +@Injectable({ providedIn: 'root' }) +export class DugOverviewService { + constructor( + private readonly api: QamApiService, + private readonly settings: SettingsService, + ) {} + + public async getData(parameter: CollectionLoadParameters): Promise> { + return this.api.typedClient.PortalDgeResources.Get(parameter); + } + + public async getDataModel(): Promise{ + return this.api.client.portal_dge_resources_datamodel_get(); + } + + public get DugResourceSchema(): EntitySchema { + return this.api.typedClient.PortalDgeResources.GetSchema(); + } + + public async getDgeConfig(): Promise { + return this.api.getDgeConfig(); + } + + public async getDugResourceById(key: string): Promise { + return (await this.api.typedClient.PortalDgeResourcesInteractivebyid.Get_byid(key)).Data[0]; + } + + public async getAllResourceActivities(key: string): Promise> { + return this.api.typedClient.PortalDgeResourcesActivity.Get(key, { PageSize: this.settings.PageSizeForAllElements }); + } + + public async makeRequest(uid: string, type: ChangeRequestType, reason: string): Promise { + return this.api.client.portal_dge_resources_request_post(uid, { ChangeRequestType: type, Reason: reason }); + } +} diff --git a/imxweb/projects/qam/src/lib/dug/access-comparison.component.html b/imxweb/projects/qam/src/lib/dug/access-comparison.component.html new file mode 100644 index 000000000..cd804decf --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/access-comparison.component.html @@ -0,0 +1,13 @@ + +

    {{ LdsHeader | translate | ldsReplace: [dug.GetEntity().GetDisplay(), dugB.GetEntity().GetDisplay()] }}

    + +
    {{ LdsTitle | translate }}
    + +
    {{ LdsSubTitle | translate | ldsReplace: [dugB.GetEntity().GetDisplay(), dug.GetEntity().GetDisplay()]}}
    + +
    • + {{ trustee.display}} ({{ trusteeTypes[trustee.trusteeType] }}): A = {{trustee.accessA}}, B = {{trustee.accessB}} +
    + + +
    \ No newline at end of file diff --git a/imxweb/projects/qam/src/lib/dug/access-comparison.component.ts b/imxweb/projects/qam/src/lib/dug/access-comparison.component.ts new file mode 100644 index 000000000..cb0b56eac --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/access-comparison.component.ts @@ -0,0 +1,120 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Component, Input, OnInit } from '@angular/core'; +import { EuiLoadingService } from '@elemental-ui/core'; +import { PortalDgeResourcesbyid } from '../TypedClient'; +import { QamApiService } from '../qam-api-client.service'; + +type ComparisonData = { display: string; trusteeType: number; accessA?: string[]; accessB?: string[] }; + +/** Displays a comparison between security entries on two resources. */ +@Component({ + templateUrl: './access-comparison.component.html', + styleUrls: ['../qam.scss'], + selector: 'imx-dge-access-comparison', +}) +export class AccessComparisonComponent implements OnInit { + constructor( + private readonly qam: QamApiService, + private loadingService: EuiLoadingService, + ) {} + + @Input() dug: PortalDgeResourcesbyid; + + uidDugA: string; + uidDugB: string; + + dugB: PortalDgeResourcesbyid; + + loading = false; + + comparisonData: ComparisonData[]; + trusteeTypes: { [id: number]: string }; + + async ngOnInit() { + const over = this.loadingService.show(); + try { + this.trusteeTypes = await this.qam.getTrusteeTypes(); + const parent = this.dug.UID_QAMDuGParent.value; + this.dugB = (await this.qam.typedClient.PortalDgeResourcesInteractivebyid.Get_byid(parent)).Data[0]; + + // combine data by trustee + const map = new Map(); + + await this.mapTrusteeA(map); + await this.mapTrusteeB(map, parent); + + this.comparisonData = Array.from(map.values()); + } finally { + this.loadingService.hide(over); + } + } + + private async mapTrusteeA(map: Map): Promise { + const accessA = await this.qam.client.portal_dge_resources_trusteeandpolicyrightset_get(this.dug.UID_QAMDuG.value); + if (accessA.Trustees) { + accessA.Trustees.forEach((element) => { + if (!map.has(element.Display ?? '')) + map.set(element.Display ?? '', { + display: element.Display ?? '', + trusteeType: element.TrusteeType, + }); + + const mapA = map.get(element.Display ?? ''); + if (mapA) { + mapA.accessA = element.Permissions; + } + }); + } + } + + private async mapTrusteeB(map: Map, parent: string): Promise { + const accessB = await this.qam.client.portal_dge_resources_trusteeandpolicyrightset_get(parent); + if (accessB.Trustees) { + accessB.Trustees.forEach((element) => { + if (!map.has(element.Display ?? '')) + map.set(element.Display ?? '', { + display: element.Display ?? '', + trusteeType: element.TrusteeType, + }); + + const mapB = map.get(element.Display ?? ''); + if (mapB) { + mapB.accessB = element.Permissions; + } + }); + } + } + + public LdsHeader = "#LDS#Access rights comparison between '{0}' and '{1}'"; + + public LdsTitle = + '#LDS#The following displays a comparison of the access rights assigned on a security deviated resource and its parent structure.'; + + public LdsSubTitle = + "#LDS#The resource '{0}' has been identified as the parent resource of '{1}'. Without deviated security indexes, the resource '{1}' would have inherited the access permissions which are assigned to '{0}'."; +} diff --git a/imxweb/projects/qam/src/lib/dug/dug-access-analysis.component.html b/imxweb/projects/qam/src/lib/dug/dug-access-analysis.component.html new file mode 100644 index 000000000..515b4d608 --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-access-analysis.component.html @@ -0,0 +1,17 @@ + + + + + + + + + + + +

    {{ldsAccess | translate}}

    +
      +
    • {{ identity.Display }}
    • +
    +
    +
    \ No newline at end of file diff --git a/imxweb/projects/qam/src/lib/dug/dug-access-analysis.component.ts b/imxweb/projects/qam/src/lib/dug/dug-access-analysis.component.ts new file mode 100644 index 000000000..68fd9f202 --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-access-analysis.component.ts @@ -0,0 +1,58 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Component, Input, OnInit } from '@angular/core'; +import { PortalDgeResourcesbyid, ResourceAccessExpansionPerson } from '../TypedClient'; +import { DugAccessAnalysisService } from './dug-access-analysis.service'; + +@Component({ + templateUrl: './dug-access-analysis.component.html', + selector: 'imx-dge-access-analysis', +}) +export class DugAccessAnalysisComponent implements OnInit { + @Input() public dug: PortalDgeResourcesbyid; + public displayResource: string; + public dugBackingFolder: PortalDgeResourcesbyid; + public identities: ResourceAccessExpansionPerson[]; + public ldsAccess = '#LDS#The following identities have access to the selected resource.'; + + constructor(private readonly dugAccessAnalysisService: DugAccessAnalysisService) {} + + public async ngOnInit(): Promise { + this.displayResource = this.dug.UID_QAMResourceType.Column.GetDisplayValue(); + + if (this.dug.UID_QAMResourceType.value == 'QAM-52F4B02EFBCAEB7A2EE35B8A4636FAEA') { + // load backing folder for windows computer share + + const uidBackingFolder = this.dug.UID_BackingFolder.value; + if (uidBackingFolder) { + this.dugBackingFolder = await this.dugAccessAnalysisService.getBackingFolder(uidBackingFolder); + } + } + + this.identities = await this.dugAccessAnalysisService.getIdentities(this.dug.UID_QAMDuG.value); + } +} diff --git a/imxweb/projects/qam/src/lib/dug/dug-access-analysis.service.ts b/imxweb/projects/qam/src/lib/dug/dug-access-analysis.service.ts new file mode 100644 index 000000000..6e57cba80 --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-access-analysis.service.ts @@ -0,0 +1,46 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Injectable } from '@angular/core'; +import { QamApiService } from '../qam-api-client.service'; +import { ChartDto, PortalDgeResourcesbyid, ResourceAccessExpansionPerson } from '../TypedClient'; + +@Injectable({ providedIn: 'root' }) +export class DugAccessAnalysisService { + constructor(private readonly api: QamApiService) {} + + public async getIdentities(uid): Promise { + return (await this.api.client.portal_dge_resources_identities_get(uid))?.Identities ?? []; + } + + public async getBackingFolder(uid): Promise { + return (await this.api.typedClient.PortalDgeResourcesInteractivebyid.Get_byid(uid)).Data[0]; + } + + public async getChartData(uid, chartid): Promise { + return this.api.client.portal_dge_resources_accesschart_get(uid, chartid); + } +} diff --git a/imxweb/projects/qam/src/lib/dug/dug-access-detail.component.html b/imxweb/projects/qam/src/lib/dug/dug-access-detail.component.html new file mode 100644 index 000000000..018fe3d33 --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-access-detail.component.html @@ -0,0 +1,27 @@ + + + {{ LdsAccessDeviation | translate }} + {{ LdsInheritanceBlocked | translate }} + + + + {{ LdsAdditionalRights | translate }} + + + + + + +

    {{ LdsViewTitle | translate }}

    + +
    + + + {{ chart?.title }} + + + + + +
    +
    diff --git a/imxweb/projects/qam/src/lib/dug/dug-access-detail.component.scss b/imxweb/projects/qam/src/lib/dug/dug-access-detail.component.scss new file mode 100644 index 000000000..07c88a8e9 --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-access-detail.component.scss @@ -0,0 +1,46 @@ +@import 'base/mixins'; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; + +:host { + @include flex-column-container-fill(); + height: 100%; + + .imx-wrapped-chart-container { + @include flex-row-container(); + flex-wrap: wrap; + gap: 10px; + } + + .imx-chart-container { + @include flex-column-container-fill(); + } + + .mat-card-content { + display: flex; + justify-content: center; + } +} + +/* light theme*/ + +:host { + .imx-chart-container { + background-color: $color-gray-2; + } +} + +.eui-dark-theme { + :host { + .imx-chart-container { + background-color: $color-gray-80; + } + } +} + +.eui-contrast-theme { + :host { + .imx-chart-container { + background-color: $color-gray-100; + } + } +} diff --git a/imxweb/projects/qam/src/lib/dug/dug-access-detail.component.ts b/imxweb/projects/qam/src/lib/dug/dug-access-detail.component.ts new file mode 100644 index 000000000..52a99c5ad --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-access-detail.component.ts @@ -0,0 +1,151 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { KeyValue } from '@angular/common'; +import { Component, Input, OnInit } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { ChartOptions, donut } from 'billboard.js'; +import { SystemInfoService } from 'qbm'; +import { ChartDto, PortalDgeResourcesbyid } from '../TypedClient'; +import { DugAccessAnalysisService } from './dug-access-analysis.service'; + +@Component({ + templateUrl: './dug-access-detail.component.html', + styleUrls: ['./dug-access-detail.component.scss'], + selector: 'imx-dge-access-detail', +}) +export class DugAccessDetailComponent implements OnInit { + public LdsViewTitle = + '#LDS#Analysis of access rights assigned to people using the organizational context such as department structures, locations and titles'; + + public LdsAnalysisTitle2 = '#LDS#Analysis of access rights assigned to people using the access right assignment methods and permissions'; + + public LdsAccessDeviation = '#LDS#The access rights at this folder are deviated from the share/parent folder.'; + + public LdsInheritanceBlocked = '#LDS#The inheritance of access rights (i.e. security inheritance) has been blocked at this folder.'; + + public LdsAdditionalRights = '#LDS#This folder has additional access rights assigned which are not on the share/parent folder.'; + + public charts: ({ + title: string; + noDataMessage: string; + data: ChartOptions; + } | null)[] = []; + + @Input() public dug: PortalDgeResourcesbyid; + + constructor( + private readonly dugAccessAnalysisService: DugAccessAnalysisService, + private readonly systemInfoService: SystemInfoService, + private readonly translate: TranslateService, + ) {} + + public async ngOnInit(): Promise { + this.charts = ( + await Promise.all([ + this.getChart( + 'QAM_AccessAnalysis_Department', + '#LDS#Access analysis by department', + '#LDS#Analysis of access rights, grouped by department, is currently unavailable.', + ), + this.getChart( + 'QAM_AccessAnalysis_PrimaryRoleTitle', + '#LDS#Access analysis by primary role title', + '#LDS#Analysis of access rights, grouped by primary role title, is currently unavailable.', + 'ORG', + ), + this.getChart( + 'QAM_AccessAnalysis_Location', + '#LDS#Access analysis by location', + '#LDS#Analysis of access rights, grouped by location, is currently unavailable.', + ), + ]) + ).filter((x) => x != null); + } + + private async getChart(id: string, titleLds: string, noDataLds: string, preprop?: string) { + if (preprop != null) { + if (!(await this.systemInfoService.get()).PreProps?.includes(preprop)) return null; + } + const noDataMessage = await this.translate.get(noDataLds).toPromise(); + return { + title: await this.translate.get(titleLds).toPromise(), + noDataMessage, + data: this.buildChartOptions(await this.dugAccessAnalysisService.getChartData(this.dug.UID_QAMDuG.value, id), noDataMessage), + }; + } + //#region chart data helper + + private buildChartOptions(chartData: ChartDto, noDataText: string): ChartOptions { + return { + data: { + type: donut(), + columns: chartData?.Data?.map((ch, index) => ['data' + index, ch?.Points?.[0].Value]) as any[], + names: this.toObject(chartData?.Data?.map((ch, index) => ({ key: 'data' + index, value: ch.Name ?? '' })) ?? []), + empty: { + label: { + text: noDataText, + }, + }, + }, + donut: { + padAngle: 0.05, + expand: { + rate: 1.005, + }, + label: { + show: false, + }, + }, + size: { width: 200, height: 200 }, + resize: { + auto: true, + }, + legend: { + show: true, + item: { + onclick: () => false, + }, + }, + padding: { + bottom: 1, + top: 0, + left: 0, + right: 0, + }, + }; + } + + private toObject(array: KeyValue[]): { [key: string]: string } { + const ret = {}; + + for (const elem of array) { + ret[elem.key] = elem.value; + } + return ret; + } + //#endregion +} diff --git a/imxweb/projects/qam/src/lib/dug/dug-access/assigned-permissions/assigned-permissions.component.html b/imxweb/projects/qam/src/lib/dug/dug-access/assigned-permissions/assigned-permissions.component.html new file mode 100644 index 000000000..72829072b --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-access/assigned-permissions/assigned-permissions.component.html @@ -0,0 +1,30 @@ + + +
    + + + +
    {{ item.GetEntity().GetDisplay() }}
    +
    {{ item.TypeDisplay.value }}
    +
    +
    + + + + + + + + + + + + + +
    +
    +
    diff --git a/imxweb/projects/qam/src/lib/dug/dug-access/assigned-permissions/assigned-permissions.component.scss b/imxweb/projects/qam/src/lib/dug/dug-access/assigned-permissions/assigned-permissions.component.scss new file mode 100644 index 000000000..1be454154 --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-access/assigned-permissions/assigned-permissions.component.scss @@ -0,0 +1,5 @@ +@import 'base/mixins'; + +:host { + @include flex-column-container-fill(); +} diff --git a/imxweb/projects/qam/src/lib/dug/dug-access/assigned-permissions/assigned-permissions.component.ts b/imxweb/projects/qam/src/lib/dug/dug-access/assigned-permissions/assigned-permissions.component.ts new file mode 100644 index 000000000..9c366ec9c --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-access/assigned-permissions/assigned-permissions.component.ts @@ -0,0 +1,71 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; +import { DisplayColumns, EntitySchema, IClientProperty } from '@imx-modules/imx-qbm-dbts'; +import { TranslateService } from '@ngx-translate/core'; +import { DataSourceToolbarSettings } from 'qbm'; +import { AssignedResourceAccessData } from '../../../TypedClient'; +import { TrusteeEntity } from './trustee-entity'; + +@Component({ + selector: 'imx-assigned-permissions', + templateUrl: './assigned-permissions.component.html', + styleUrls: ['./assigned-permissions.component.scss'], +}) +export class AssignedPermissionsComponent implements OnChanges { + @Input() trustees: AssignedResourceAccessData; + + public dstSettings: DataSourceToolbarSettings; + public entitySchema: EntitySchema; + private displayedColumns: IClientProperty[] = []; + public readonly DisplayColumns = DisplayColumns; + + constructor(translate: TranslateService) { + this.entitySchema = TrusteeEntity.GetEntitySchema(translate); + this.displayedColumns = [ + this.entitySchema.Columns.Display, + this.entitySchema.Columns.AllowChangePermissions, + this.entitySchema.Columns.AllowFullControl, + this.entitySchema.Columns.AllowWrite, + this.entitySchema.Columns.AllowRead, + this.entitySchema.Columns.AnyAllow, + ]; + } + + public ngOnChanges(changes: SimpleChanges): void { + if (!changes?.trustees == null) { + return; + } + const data = TrusteeEntity.buildEntities(TrusteeEntity.buildEntityData(this.trustees.Trustees), this.entitySchema); + this.dstSettings = { + displayedColumns: this.displayedColumns, + dataSource: data, + entitySchema: this.entitySchema, + navigationState: {}, + }; + } +} diff --git a/imxweb/projects/qam/src/lib/dug/dug-access/assigned-permissions/trustee-entity.ts b/imxweb/projects/qam/src/lib/dug/dug-access/assigned-permissions/trustee-entity.ts new file mode 100644 index 000000000..7b4b24efb --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-access/assigned-permissions/trustee-entity.ts @@ -0,0 +1,142 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { + EntityColumnData, + EntityData, + EntitySchema, + ExtendedTypedEntityCollection, + IClientProperty, + IReadValue, + TypedEntity, + TypedEntityBuilder, + ValType, +} from '@imx-modules/imx-qbm-dbts'; + +import { TranslateService } from '@ngx-translate/core'; +import { AssignedResourceAccess } from '../../../TypedClient'; + +export class TrusteeEntity extends TypedEntity { + public readonly Display: IReadValue = this.GetEntityValue('Display'); + public readonly TypeDisplay: IReadValue = this.GetEntityValue('TypeDisplay'); + public readonly TrusteeType: IReadValue = this.GetEntityValue('TrusteeType'); + public readonly AllowWrite: IReadValue = this.GetEntityValue('AllowWrite'); + public readonly AllowRead: IReadValue = this.GetEntityValue('AllowRead'); + public readonly AnyAllow: IReadValue = this.GetEntityValue('AnyAllow'); + public readonly AllowFullControl: IReadValue = this.GetEntityValue('AllowFullControl'); + public readonly AllowChangePermissions: IReadValue = this.GetEntityValue('AllowChangePermissions'); + + public GetDisplay(): string { + return this.Display.value; + } + + public static GetEntitySchema(translate?: TranslateService): EntitySchema { + const returnColumns: { [key: string]: IClientProperty } = {}; + + returnColumns.Display = { + Type: ValType.String, + ColumnName: 'Display', + Display: translate ? translate.instant('#LDS#Display name') : '#LDS#Display name', + }; + + returnColumns.TypeDisplay = { + Type: ValType.String, + ColumnName: 'TypeDisplay', + Display: translate ? translate.instant('#LDS#Type display') : '#LDS#Type display', + }; + + returnColumns.TrusteeType = { + Type: ValType.Int, + ColumnName: 'TrusteeType', + Display: translate ? translate.instant('#LDS#Trustee type') : '#LDS#Trustee type', + }; + + returnColumns.AllowWrite = { + Type: ValType.Bool, + ColumnName: 'AllowWrite', + Display: translate ? translate.instant('#LDS#Allow write') : '#LDS#Allow write', + }; + + returnColumns.AllowRead = { + Type: ValType.Bool, + ColumnName: 'AllowRead', + Display: translate ? translate.instant('#LDS#Allow read') : '#LDS#Allow read', + }; + + returnColumns.AnyAllow = { + Type: ValType.Bool, + ColumnName: 'AnyAllow', + Display: translate ? translate.instant('#LDS#Any allow') : '#LDS#Any allow', + }; + + returnColumns.AllowFullControl = { + Type: ValType.Bool, + ColumnName: 'AllowFullControl', + Display: translate ? translate.instant('#LDS#Allow full control') : '#LDS#Allow full control', + }; + + returnColumns.AllowChangePermissions = { + Type: ValType.Bool, + ColumnName: 'AllowChangePermissions', + Display: translate ? translate.instant('#LDS#Allow change permissions') : '#LDS#Allow change permissions', + }; + + return { + TypeName: 'Trustee', + Display: translate ? translate.instant('#LDS#Trustee') : '#LDS#Trustee', + Columns: returnColumns, + }; + } + + public static buildEntities(entityData: EntityData[], entitySchema: EntitySchema): ExtendedTypedEntityCollection { + const builder = new TypedEntityBuilder(TrusteeEntity); + return builder.buildReadWriteEntities( + { + TotalCount: entityData.length, + Entities: entityData, + }, + entitySchema, + ); + } + + public static buildEntityData(trustees: AssignedResourceAccess[] | undefined): EntityData[] { + return !trustees + ? [] + : trustees.map((elem) => { + const returnColumns: { [key: string]: EntityColumnData } = {}; + returnColumns.Display = { Value: elem.Display, IsReadOnly: true }; + returnColumns.TrusteeType = { Value: elem.TrusteeType, IsReadOnly: true }; + returnColumns.TypeDisplay = { Value: elem.TypeDisplay, IsReadOnly: true }; + returnColumns.AllowWrite = { Value: elem.Permissions?.includes('AllowWrite'), IsReadOnly: true }; + returnColumns.AllowRead = { Value: elem.Permissions?.includes('AllowRead'), IsReadOnly: true }; + returnColumns.AnyAllow = { Value: elem.Permissions?.includes('AnyAllow'), IsReadOnly: true }; + returnColumns.AllowFullControl = { Value: elem.Permissions?.includes('AllowFullControl'), IsReadOnly: true }; + returnColumns.AllowChangePermissions = { Value: elem.Permissions?.includes('AllowChangePermissions'), IsReadOnly: true }; + + return { Columns: returnColumns, Display: elem.Display }; + }); + } +} diff --git a/imxweb/projects/qam/src/lib/dug/dug-access/box-icon/box-icon.component.html b/imxweb/projects/qam/src/lib/dug/dug-access/box-icon/box-icon.component.html new file mode 100644 index 000000000..118d744cb --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-access/box-icon/box-icon.component.html @@ -0,0 +1,8 @@ +
    +
    {{ display }}
    + +
    + + + + diff --git a/imxweb/projects/qam/src/lib/dug/dug-access/box-icon/box-icon.component.scss b/imxweb/projects/qam/src/lib/dug/dug-access/box-icon/box-icon.component.scss new file mode 100644 index 000000000..68bd73218 --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-access/box-icon/box-icon.component.scss @@ -0,0 +1,10 @@ +@import 'base/mixins'; + +.imx-single-permission { + @include flex-column-container($overflow: hidden); + align-items: center; + + > div { + text-align: center; + } +} diff --git a/imxweb/projects/qam/src/lib/dug/dug-access/box-icon/box-icon.component.ts b/imxweb/projects/qam/src/lib/dug/dug-access/box-icon/box-icon.component.ts new file mode 100644 index 000000000..5c4761ad7 --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-access/box-icon/box-icon.component.ts @@ -0,0 +1,37 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Component, Input } from '@angular/core'; + +@Component({ + selector: 'imx-box-icon', + templateUrl: './box-icon.component.html', + styleUrls: ['./box-icon.component.scss'], +}) +export class BoxIconComponent { + @Input() public isChecked: boolean; + @Input() public display: string | undefined; +} diff --git a/imxweb/projects/qam/src/lib/dug/dug-access/dug-access.component.html b/imxweb/projects/qam/src/lib/dug/dug-access/dug-access.component.html new file mode 100644 index 000000000..e04683ce4 --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-access/dug-access.component.html @@ -0,0 +1,21 @@ + + + {{ '#LDS#Show assigned permissions' | translate }} + + + {{ '#LDS#Show effective permissions' | translate }} + + + + +

    {{ ldsAssignedAccess | translate }}

    + + +
    + + +

    #LDS#The following accounts and groups have access to this resource.

    + + + +
    diff --git a/imxweb/projects/qam/src/lib/dug/dug-access/dug-access.component.scss b/imxweb/projects/qam/src/lib/dug/dug-access/dug-access.component.scss new file mode 100644 index 000000000..6f2c8adac --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-access/dug-access.component.scss @@ -0,0 +1,12 @@ + +@import 'base/mixins'; +:host{ + @include flex-column-container-fill(); + height: 100% +} + + +.trusteetype { + font-style: italic; +} + diff --git a/imxweb/projects/qam/src/lib/dug/dug-access/dug-access.component.ts b/imxweb/projects/qam/src/lib/dug/dug-access/dug-access.component.ts new file mode 100644 index 000000000..0c62d7c7d --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-access/dug-access.component.ts @@ -0,0 +1,86 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { QamApiService } from '../../qam-api-client.service'; +import { AssignedResourceAccessData, PortalDgeResources, ResourceAccessData } from '../../TypedClient'; +import { BusyService } from 'qbm'; +import { Subscription } from 'rxjs'; + +@Component({ + templateUrl: './dug-access.component.html', + styleUrls: ['./dug-access.component.scss'], + selector: 'imx-dge-access', +}) +export class DugAccessComponent implements OnInit, OnDestroy { + @Input() public dug: PortalDgeResources; + @Input() public IsReadOnly: boolean = false; + + public isShowEffectivePermissions: boolean = false; + + public trusteeTypes: { [id: number]: string }; + + /** assigned access permissions */ + public assigned: AssignedResourceAccessData; + + /** effective access permissions */ + public data: ResourceAccessData; + + public isLoading: boolean = false; + + public busyService = new BusyService(); + private busySubscription: Subscription; + + constructor( + private readonly api: QamApiService, + private readonly changeDetector: ChangeDetectorRef, + ) { + this.busySubscription = this.busyService.busyStateChanged.subscribe((state: boolean) => { + this.isLoading = state; + this.changeDetector.detectChanges(); + }); + } + + public ngOnDestroy(): void { + this.busySubscription?.unsubscribe(); + } + + public async ngOnInit() { + const isBusy = this.busyService.beginBusy(); + try { + const uidDug = this.dug.GetEntity().GetKeys()[0]; + this.trusteeTypes = await this.api.getTrusteeTypes(); + this.data = await this.api.client.portal_dge_resources_access_get(uidDug); + this.assigned = await this.api.client.portal_dge_resources_trusteeandpolicyrightset_get(uidDug); + } finally { + isBusy.endBusy(); + } + } + + + public ldsAssignedAccess = + '#LDS#Access permissions are assigned to the following accounts and groups. If the access permissions are not correct, you can request a modification.'; +} diff --git a/imxweb/projects/qbm/src/lib/select/select.module.ts b/imxweb/projects/qam/src/lib/dug/dug-access/dug-access.module.ts similarity index 50% rename from imxweb/projects/qbm/src/lib/select/select.module.ts rename to imxweb/projects/qam/src/lib/dug/dug-access/dug-access.module.ts index c64e27534..bda69da63 100644 --- a/imxweb/projects/qbm/src/lib/select/select.module.ts +++ b/imxweb/projects/qam/src/lib/dug/dug-access/dug-access.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,48 +24,48 @@ * */ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { ReactiveFormsModule, FormsModule } from '@angular/forms'; -import { MatAutocompleteModule } from '@angular/material/autocomplete'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; -import { MatCheckboxModule } from '@angular/material/checkbox'; -import { MatChipsModule } from '@angular/material/chips'; +import { MatCardModule } from '@angular/material/card'; import { MatIconModule } from '@angular/material/icon'; -import { MatInputModule } from '@angular/material/input'; -import { MatPaginatorModule } from '@angular/material/paginator'; -import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { MatRadioModule } from '@angular/material/radio'; +import { MatSidenavModule } from '@angular/material/sidenav'; +import { MatTreeModule } from '@angular/material/tree'; import { EuiCoreModule } from '@elemental-ui/core'; -import { ScrollingModule } from '@angular/cdk/scrolling'; import { TranslateModule } from '@ngx-translate/core'; -import { OverlayModule } from '@angular/cdk/overlay'; - -import { SelectComponent } from './select.component'; -import { AutocompleteComponent } from './autocomplete.component'; +import { BusyIndicatorModule, DataSourceToolbarModule, DataTableModule, DataTreeModule } from 'qbm'; +import { AssignedPermissionsComponent } from './assigned-permissions/assigned-permissions.component'; +import { BoxIconComponent } from './box-icon/box-icon.component'; +import { DugAccessComponent } from './dug-access.component'; +import { EffectivePermissionComponent } from './effective-permission/effective-permission.component'; +import { PermissionMemberComponent } from './effective-permission/permission-member/permission-member.component'; @NgModule({ declarations: [ - SelectComponent, - AutocompleteComponent + DugAccessComponent, + AssignedPermissionsComponent, + EffectivePermissionComponent, + PermissionMemberComponent, + BoxIconComponent, ], imports: [ CommonModule, - EuiCoreModule, - MatAutocompleteModule, - MatChipsModule, - MatIconModule, - MatInputModule, + DataTableModule, + DataSourceToolbarModule, + MatRadioModule, MatButtonModule, - MatCheckboxModule, - ScrollingModule, - MatProgressSpinnerModule, + MatCardModule, + MatTreeModule, + MatIconModule, + TranslateModule, FormsModule, - MatPaginatorModule, - ReactiveFormsModule, - TranslateModule - ], - exports: [ - SelectComponent + EuiCoreModule, + DataTreeModule, + MatSidenavModule, + BusyIndicatorModule, ], + exports: [DugAccessComponent, BoxIconComponent], }) -export class SelectModule { } +export class DugAccessModule {} diff --git a/imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/effective-permission-tree-database.ts b/imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/effective-permission-tree-database.ts new file mode 100644 index 000000000..49104b5cf --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/effective-permission-tree-database.ts @@ -0,0 +1,52 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { TreeDatabase, TreeNodeResultParameter } from 'qbm'; +import { TrusteeEntityHierarchy } from './trustee-entity-hierarchy'; +import { CollectionLoadParameters, IEntity } from '@imx-modules/imx-qbm-dbts'; + +export class EffectivePermissionTreeDatabase extends TreeDatabase { + public constructor(private entities: TrusteeEntityHierarchy[]) { + super(); + this.entities.forEach((entity) => (entity.HasChildren.value = this.entities.some((ent) => ent.Parent.value === entity.Display.value))); + } + + public async getData(showLoading: boolean, parameters: CollectionLoadParameters = {}): Promise { + + const entities = this.entities.filter(element => element.Parent.value === parameters.ParentKey) + return { + entities: entities.map((element) => element.GetEntity()), + canLoadMore: false, + totalCount: entities.length, + }; + } + + public getId(entity: IEntity): string { + return entity.GetColumn('Display')?.GetValue() ?? ''; + } + + +} diff --git a/imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/effective-permission.component.html b/imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/effective-permission.component.html new file mode 100644 index 000000000..b4d8b26f2 --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/effective-permission.component.html @@ -0,0 +1,50 @@ + +

    {{ '#LDS#Trustee' | translate }}

    + + + +
    + {{ node.item?.GetDisplay() }} + + ( + {{ node.item?.columns?.TypeDisplay.GetValue() }} + ) + +
    +
    +
    +
    +
    +
    +

    {{ displayedEntity?.GetDisplay() }}

    + + + Permissions + +
    + +
    +
    +
    + +
    +
    + + +
    + +

    +
    +
    diff --git a/imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/effective-permission.component.scss b/imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/effective-permission.component.scss new file mode 100644 index 000000000..15b9062de --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/effective-permission.component.scss @@ -0,0 +1,153 @@ +@import 'base/mixins'; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; + +:host { + @include flex-row-container-fill(); + overflow: hidden; + + h3 { + font-size: 16px; + font-weight: 600; + line-height: 20px; + padding-bottom: 5px; + } + + .dug-tree-card { + @include flex-column-container-fill(); + max-width: 500px; + min-width: 500px; + margin: 3px; + } + + .dug-tree-content { + @include flex-column-container-fill(); + margin: 3px; + } + + .effective-permission-content { + @include flex-column-container-fill(); + + h3 { + font-size: 20px; + font-weight: 600; + } + } + + .effective-permission-content-card { + @include flex-column-container-fill(); + } + + .imx-single-permission { + @include flex-column-container($overflow: hidden); + align-items: center; + } + + .imx-no-results { + flex: 1 1 auto; + } + + .imx-trustee-text { + display: flex; + justify-content: center; + align-items: center; + padding: 10px; + } + span[italic] { + font-style: italic; + } + + span[caption] { + margin-right: 10px; + font-weight: 600; + } + + .mat-mdc-card { + .mat-mdc-card-header { + margin-bottom: 20px; + font-weight: 600; + align-items: center; + @include flex-column-container(); + } + .mat-mdc-card-content { + @include flex-row-container-fill(); + } + } + + .imx-scrollable { + overflow: auto; + } +} + +:host { + .imx-no-results { + p { + color: $color-gray-50; + } + } + + .effective-permission-content { + h3 { + color: $color-gray-40; + } + } + + ::ng-deep .mat-tree { + background-color: transparent; + } + + .effective-permission-content-card { + background-color: $color-gray-2; + border: 1px solid $color-gray-10; + } +} + +.eui-dark-theme { + :host { + .imx-no-results { + p { + color: $color-gray-10; + } + } + + .effective-permission-content { + h3 { + color: $color-gray-10; + } + } + + .effective-permission-content-card { + background-color: $color-gray-80; + border: 1px solid $color-gray-90; + } + } +} + +.eui-contrast-theme { + :host { + .imx-no-results { + p { + color: $color-gray-0; + } + } + + .effective-permission-content { + h3 { + color: $color-gray-0; + } + } + + .effective-permission-content-card { + background-color: $color-gray-100; + border: 1px solid $color-gray-0; + } + } +} + +@media screen and (max-width: 1024px) { + :host { + .dug-tree-card { + max-width: 300px; + min-width: 300px; + } + } +} diff --git a/imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/effective-permission.component.ts b/imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/effective-permission.component.ts new file mode 100644 index 000000000..9f4d4ebaa --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/effective-permission.component.ts @@ -0,0 +1,89 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; +import { EntitySchema, IClientProperty, IEntity } from '@imx-modules/imx-qbm-dbts'; +import { TranslateService } from '@ngx-translate/core'; +import { AssignedResourceAccessData, ResourceAccessMembersData, ResourceAccessTrusteeData } from '../../../TypedClient'; + +import { TreeDatabase } from 'qbm'; +import { EffectivePermissionTreeDatabase } from './effective-permission-tree-database'; +import { TrusteeEntityHierarchy } from './trustee-entity-hierarchy'; + +@Component({ + selector: 'imx-effective-permission', + templateUrl: './effective-permission.component.html', + styleUrls: ['./effective-permission.component.scss', '../../../qam.scss'], +}) +export class EffectivePermissionComponent implements OnChanges { + @Input() trustees: AssignedResourceAccessData; + @Input() accessData: ResourceAccessTrusteeData[]; + + @Input() trusteeTypes: { [id: number]: string }; + + public entitySchema: EntitySchema; + + public displayedEntity: IEntity; + public permissionColumns: IClientProperty[]; + + public treeDataBase: TreeDatabase; + + public entities: TrusteeEntityHierarchy[]; + + public members: ResourceAccessMembersData[]; + + /* */ + + constructor(translate: TranslateService) { + this.entitySchema = TrusteeEntityHierarchy.GetEntitySchema(translate); + + this.permissionColumns = [ + this.entitySchema.Columns.AllowWrite, + this.entitySchema.Columns.AllowRead, + this.entitySchema.Columns.AnyAllow, + this.entitySchema.Columns.AllowFullControl, + this.entitySchema.Columns.AllowChangePermissions, + ]; + } + + public ngOnChanges(changes: SimpleChanges): void { + if (changes?.trustees?.currentValue?.Trustees == null) { + return; + } + this.entities = TrusteeEntityHierarchy.buildEntities( + TrusteeEntityHierarchy.buildEntityData(this.trustees.Trustees), + this.entitySchema, + ).Data; + this.treeDataBase = new EffectivePermissionTreeDatabase(this.entities); + + this.treeDataBase.dataReloaded$.next(true); + } + + public async showDetails(entity: IEntity): Promise { + this.displayedEntity = entity; + this.members = this.accessData.find((elem) => elem.Display === entity.GetColumn('Display').GetValue())?.Members ?? []; + } +} diff --git a/imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/permission-member/permission-member.component.html b/imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/permission-member/permission-member.component.html new file mode 100644 index 000000000..a6f0f63d1 --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/permission-member/permission-member.component.html @@ -0,0 +1,46 @@ + + Members + + + + + + + + + + + + + + + + + + +
    + {{ node.name }} + + ( + {{ node.item.TypeDisplay }} + ) + +
    + + + +
    + + +
    + +

    +
    +
    diff --git a/imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/permission-member/permission-member.component.scss b/imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/permission-member/permission-member.component.scss new file mode 100644 index 000000000..848628059 --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/permission-member/permission-member.component.scss @@ -0,0 +1,85 @@ +@import 'base/mixins'; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; + +:host { + @include flex-column-container-fill(); + + .effective-permission-content-card-members { + @include flex-column-container-fill(); + } + + .imx-member-text { + display: flex; + justify-content: center; + align-items: center; + padding: 10px; + } + + .imx-warning-text { + margin-left: auto; + margin-bottom: 3px; + display: flex; + justify-content: center; + align-items: center; + + ::ng-deep .eui-alert { + padding: 10px; + } + } + .imx-no-results { + flex: 1 1 auto; + p { + font-size: 18px; + } + } + + .mat-card { + .mat-card-header { + padding-bottom: 20px; + font-weight: 600; + align-items: center; + @include flex-column-container(); + } + .mat-card-content { + @include flex-row-container-fill(); + overflow: auto; + } + } + + span[italic] { + font-style: italic; + } + + span[caption] { + margin-right: 10px; + font-weight: 600; + } +} + +:host { + .imx-no-results { + p { + color: $color-gray-50; + } + } +} + +.eui-dark-theme { + :host { + .imx-no-results { + p { + color: $color-gray-10; + } + } + } +} + +.eui-contrast-theme { + :host { + .imx-no-results { + p { + color: $color-gray-0; + } + } + } +} diff --git a/imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/permission-member/permission-member.component.ts b/imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/permission-member/permission-member.component.ts new file mode 100644 index 000000000..20e0040e4 --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/permission-member/permission-member.component.ts @@ -0,0 +1,91 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { FlatTreeControl } from '@angular/cdk/tree'; +import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; +import { MatTreeFlatDataSource, MatTreeFlattener } from '@angular/material/tree'; +import { IEntity } from '@imx-modules/imx-qbm-dbts'; +import { ResourceAccessMembersData } from '../../../../TypedClient'; + +interface ExampleFlatNode { + expandable: boolean; + name: string; + item: ResourceAccessMembersData; + level: number; +} + +@Component({ + selector: 'imx-permission-member', + templateUrl: './permission-member.component.html', + styleUrls: ['./permission-member.component.scss'], +}) +export class PermissionMemberComponent implements OnChanges { + /** Methods and functions for the tree*/ + + public treeControl = new FlatTreeControl( + (node) => node.level, + (node) => node.expandable, + ); + + private _transformer = (node: ResourceAccessMembersData, level: number) => { + return { + expandable: !!node.Members && node.Members.length > 0, + name: node.Display ?? '', + item: node, + level: level, + }; + }; + + private treeFlattener = new MatTreeFlattener( + this._transformer, + (node) => node.level, + (node) => node.expandable, + (node) => node.Members, + ); + public dataSource: MatTreeFlatDataSource | undefined = + new MatTreeFlatDataSource(this.treeControl, this.treeFlattener); + + public hasChild = (_: number, node: ExampleFlatNode) => node.expandable; + + /** Inputs */ + @Input() public members: ResourceAccessMembersData[]; + @Input() public displayedEntity: IEntity; + + /**LDS texts */ + public circularWarningText = '#LDS#There is a circular membership dependency. Please check the members inheritance.'; + + public ngOnChanges(changes: SimpleChanges): void { + if (!changes.members) { + return; + } + if (this.members == null || this.members.length <= 0) { + this.dataSource = undefined; + } else { + this.dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener); + this.dataSource.data = this.members; + } + } +} diff --git a/imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/trustee-entity-hierarchy.ts b/imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/trustee-entity-hierarchy.ts new file mode 100644 index 000000000..aeb4d9cff --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-access/effective-permission/trustee-entity-hierarchy.ts @@ -0,0 +1,190 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { + EntityColumnData, + EntityData, + EntitySchema, + ExtendedTypedEntityCollection, + IClientProperty, + IReadValue, + IWriteValue, + TypedEntity, + TypedEntityBuilder, + ValType, +} from '@imx-modules/imx-qbm-dbts'; + +import { TranslateService } from '@ngx-translate/core'; +import { AssignedResourceAccess } from '../../../TypedClient'; + +export class TrusteeEntityHierarchy extends TypedEntity { + public readonly Display: IReadValue = this.GetEntityValue('Display'); + public readonly TypeDisplay: IReadValue = this.GetEntityValue('TypeDisplay'); + public readonly TrusteeType: IReadValue = this.GetEntityValue('TrusteeType'); + public readonly AllowWrite: IReadValue = this.GetEntityValue('AllowWrite'); + public readonly AllowRead: IReadValue = this.GetEntityValue('AllowRead'); + public readonly AnyAllow: IReadValue = this.GetEntityValue('AnyAllow'); + public readonly AllowFullControl: IReadValue = this.GetEntityValue('AllowFullControl'); + public readonly AllowChangePermissions: IReadValue = this.GetEntityValue('AllowChangePermissions'); + public readonly Parent: IReadValue = this.GetEntityValue('Parent'); + public readonly HasChildren: IWriteValue = this.GetEntityValue('HasChildren'); + public readonly HasPermissions: IWriteValue = this.GetEntityValue('HasPermissions'); + + public static GetEntitySchema(translate?: TranslateService): EntitySchema { + const returnColumns: { [key: string]: IClientProperty } = {}; + + returnColumns.Display = { + Type: ValType.String, + ColumnName: 'Display', + Display: translate ? translate.instant('#LDS#Display name') : '#LDS#Display name', + }; + + returnColumns.TypeDisplay = { + Type: ValType.String, + ColumnName: 'TypeDisplay', + Display: translate ? translate.instant('#LDS#Type display') : '#LDS#Type display', + }; + + returnColumns.TrusteeType = { + Type: ValType.Int, + ColumnName: 'TrusteeType', + Display: translate ? translate.instant('#LDS#Trustee type') : '#LDS#Trustee type', + }; + + returnColumns.HasPermissions = { + Type: ValType.Bool, + ColumnName: 'HasPermissions', + }; + + returnColumns.AllowWrite = { + Type: ValType.Bool, + ColumnName: 'AllowWrite', + Display: translate ? translate.instant('#LDS#Allow write') : '#LDS#Allow write', + }; + + returnColumns.AllowRead = { + Type: ValType.Bool, + ColumnName: 'AllowRead', + Display: translate ? translate.instant('#LDS#Allow read') : '#LDS#Allow read', + }; + + returnColumns.AnyAllow = { + Type: ValType.Bool, + ColumnName: 'AnyAllow', + Display: translate ? translate.instant('#LDS#Any allow') : '#LDS#Any allow', + }; + + returnColumns.AllowFullControl = { + Type: ValType.Bool, + ColumnName: 'AllowFullControl', + Display: translate ? translate.instant('#LDS#Allow full control') : '#LDS#Allow full control', + }; + + returnColumns.AllowChangePermissions = { + Type: ValType.Bool, + ColumnName: 'AllowChangePermissions', + Display: translate ? translate.instant('#LDS#Allow change permissions') : '#LDS#Allow change permissions', + }; + + returnColumns.Parent = { + Type: ValType.String, + ColumnName: 'Parent', + }; + + returnColumns.HasChildren = { + Type: ValType.Bool, + ColumnName: 'HasChildren', + }; + + return { + TypeName: 'Trustee', + Display: translate ? translate.instant('#LDS#Trustee') : '#LDS#Trustee', + Columns: returnColumns, + }; + } + + public static buildEntities( + entityData: EntityData[], + entitySchema: EntitySchema, + ): ExtendedTypedEntityCollection { + const builder = new TypedEntityBuilder(TrusteeEntityHierarchy); + return builder.buildReadWriteEntities( + { + TotalCount: entityData.length, + Entities: entityData, + }, + entitySchema, + ); + } + + public static buildEntityData(trustees: AssignedResourceAccess[] | undefined): EntityData[] { + return !trustees + ? [] + : trustees.concat(this.getMissingParent(trustees)).map((elem) => { + const returnColumns: { [key: string]: EntityColumnData } = {}; + returnColumns.Display = { Value: elem.Display, IsReadOnly: true }; + returnColumns.TrusteeType = { Value: elem.TrusteeType, IsReadOnly: true }; + returnColumns.TypeDisplay = { Value: elem.TypeDisplay, IsReadOnly: true }; + returnColumns.HasPermissions = { Value: elem.Permissions != null, IsReadOnly: true }; + returnColumns.AllowWrite = { Value: elem.Permissions?.includes('AllowWrite'), IsReadOnly: true }; + returnColumns.AllowRead = { Value: elem.Permissions?.includes('AllowRead'), IsReadOnly: true }; + returnColumns.AnyAllow = { Value: elem.Permissions?.includes('AnyAllow'), IsReadOnly: true }; + returnColumns.AllowFullControl = { Value: elem.Permissions?.includes('AllowFullControl'), IsReadOnly: true }; + returnColumns.AllowChangePermissions = { Value: elem.Permissions?.includes('AllowChangePermissions'), IsReadOnly: true }; + returnColumns.Parent = { + Value: this.getParentValue(elem.Display), + IsReadOnly: true, + DisplayValue: this.getParentDisplay(elem.Display), + }; + returnColumns.HasChildren = { IsReadOnly: false }; + + return { Columns: returnColumns, Display: elem.Display?.split('\\').pop() }; + }); + } + + private static getMissingParent(trustees: AssignedResourceAccess[]): AssignedResourceAccess[] { + const parentValues = trustees.map((elem) => this.getParentValue(elem.Display)); + const missing = [...new Set(parentValues.filter((elem) => trustees.every((trus) => trus.Display !== elem)))]; + return missing.map((elem) => ({ TrusteeType: -1, Display: elem })).filter((elem) => elem.Display !== ''); + } + + private static getParentDisplay(parent: string | undefined): string { + if (parent == null || parent?.indexOf('\\') === -1) { + return ''; + } + const parts = parent.split('\\'); + return parts[parts.length - 2]; + } + + private static getParentValue(parent: string | undefined): string { + if (parent == null || parent?.indexOf('\\') === -1) { + return ''; + } + const parts = parent.split('\\'); + parts.pop(); + return parts.join('\\'); + } +} diff --git a/imxweb/projects/qam/src/lib/dug/dug-activity/dug-activity.component.html b/imxweb/projects/qam/src/lib/dug/dug-activity/dug-activity.component.html new file mode 100644 index 000000000..7811bfe33 --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-activity/dug-activity.component.html @@ -0,0 +1,14 @@ +

    + {{ '#LDS#The following activity has been logged for this resource in the last {0} days.' | translate | ldsReplace: interval }} +

    + + + + + + + + diff --git a/imxweb/projects/qam/src/lib/dug/dug-activity/dug-activity.component.scss b/imxweb/projects/qam/src/lib/dug/dug-activity/dug-activity.component.scss new file mode 100644 index 000000000..4f71d1c8b --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-activity/dug-activity.component.scss @@ -0,0 +1,9 @@ + +@import 'base/mixins'; +:host{ + @include flex-column-container-fill(); + height: 100% +} +.imx-no-results .eui-icon { + font-size: 100px; +} \ No newline at end of file diff --git a/imxweb/projects/qam/src/lib/dug/dug-activity/dug-activity.component.ts b/imxweb/projects/qam/src/lib/dug/dug-activity/dug-activity.component.ts new file mode 100644 index 000000000..bd59d1dbc --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-activity/dug-activity.component.ts @@ -0,0 +1,64 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Component, Input, OnInit } from '@angular/core'; +import { DisplayColumns, EntitySchema, IClientProperty } from '@imx-modules/imx-qbm-dbts'; +import { DataSourceToolbarSettings } from 'qbm'; +import { PortalDgeResourcesActivity } from '../../TypedClient'; + +@Component({ + selector: 'imx-dug-activity', + templateUrl: './dug-activity.component.html', + styleUrls: ['./dug-activity.component.scss'], +}) +export class DugActivityComponent implements OnInit { + @Input() public interval: number; + @Input() public activities: PortalDgeResourcesActivity[]; + + private displayedColumns: IClientProperty[] = []; + public readonly DisplayColumns = DisplayColumns; + public entitySchema: EntitySchema; + public dstSettings: DataSourceToolbarSettings; + + constructor() { + this.entitySchema = PortalDgeResourcesActivity.GetEntitySchema(); + this.displayedColumns = [ + this.entitySchema.Columns.UID_QAMTrustee, + this.entitySchema.Columns.Operation, + this.entitySchema.Columns.Resources, + this.entitySchema.Columns.Activities, + ]; + } + + public async ngOnInit(): Promise { + this.dstSettings = { + dataSource: { Data: this.activities, totalCount: this.activities.length }, + entitySchema: this.entitySchema, + navigationState: {}, + displayedColumns: this.displayedColumns, + }; + } +} diff --git a/imxweb/projects/o3t/src/lib/o3t-config.module.ts b/imxweb/projects/qam/src/lib/dug/dug-activity/dug-activity.module.ts similarity index 74% rename from imxweb/projects/o3t/src/lib/o3t-config.module.ts rename to imxweb/projects/qam/src/lib/dug/dug-activity/dug-activity.module.ts index ae1278783..c7a5cba5c 100644 --- a/imxweb/projects/o3t/src/lib/o3t-config.module.ts +++ b/imxweb/projects/qam/src/lib/dug/dug-activity/dug-activity.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,28 +24,27 @@ * */ -import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; - +import { CommonModule } from '@angular/common'; +import { DugActivityComponent } from '../dug-activity/dug-activity.component'; import { TranslateModule } from '@ngx-translate/core'; +import { DataSourceToolbarModule, DataTableModule, LdsReplaceModule } from 'qbm'; -import { EuiCoreModule } from '@elemental-ui/core'; -import { ClassloggerService } from 'qbm'; @NgModule({ declarations: [ + DugActivityComponent ], imports: [ CommonModule, TranslateModule, - EuiCoreModule + LdsReplaceModule, + DataSourceToolbarModule, + DataTableModule + ], + exports: [ + DugActivityComponent ] }) -export class O3TConfigModule { - constructor(private readonly logger: ClassloggerService) { - this.logger.info(this, '🔥 O3T loaded'); - - this.logger.info(this, '▶️ O3T initialized'); - } -} +export class DugActivityModule { } diff --git a/imxweb/projects/qam/src/lib/dug/dug-reports/dug-report-entity.ts b/imxweb/projects/qam/src/lib/dug/dug-reports/dug-report-entity.ts new file mode 100644 index 000000000..c7d460976 --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-reports/dug-report-entity.ts @@ -0,0 +1,96 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { + EntityColumnData, + EntityData, + EntitySchema, + ExtendedTypedEntityCollection, + IClientProperty, + TypedEntity, + TypedEntityBuilder, + ValType, +} from '@imx-modules/imx-qbm-dbts'; +import { TranslateService } from '@ngx-translate/core'; +import { ResourceReportData } from '../../TypedClient'; + +export class DugReportEntity extends TypedEntity { + public reportInfo: { uid: string; presetParameters: { [key: string]: string } }; + + public static GetEntitySchema(translate?: TranslateService): EntitySchema { + const returnColumns: { [key: string]: IClientProperty } = {}; + + returnColumns.ReportDisplayName = { + Type: ValType.String, + ColumnName: 'ReportDisplayName', + Display: translate ? translate.instant('#LDS#Display name') : '#LDS#Display name', + }; + + returnColumns.UidReport = { + Type: ValType.String, + ColumnName: 'UidReport', + }; + + return { + TypeName: 'Reports', + Display: translate ? translate.instant('#LDS#Reports') : '#LDS#Reports', + Columns: returnColumns, + }; + } + + public static buildEntities( + entityData: EntityData[], + extended: { [key: string]: string }[], + entitySchema: EntitySchema, + ): ExtendedTypedEntityCollection { + const builder = new TypedEntityBuilder(DugReportEntity); + const returnColumns = builder.buildReadWriteEntities( + { + TotalCount: entityData.length, + Entities: entityData, + ExtendedData: extended, + }, + entitySchema, + ); + + returnColumns.Data.forEach( + (elem, index) => + (elem.reportInfo = { uid: elem.entity.GetColumn('UidReport').GetValue(), presetParameters: returnColumns?.extendedData?.[index]! }), + ); + + return returnColumns; + } + + public static buildEntityData(trustees: ResourceReportData[]): EntityData[] { + return trustees.map((elem) => { + const returnColumns: { [key: string]: EntityColumnData } = {}; + returnColumns.ReportDisplayName = { Value: elem.ReportDisplayName, IsReadOnly: true }; + returnColumns.UidReport = { Value: elem.UidReport, IsReadOnly: true }; + + return { Columns: returnColumns, Display: elem.ReportDisplayName }; + }); + } +} diff --git a/imxweb/projects/qam/src/lib/dug/dug-reports/dug-reports.component.html b/imxweb/projects/qam/src/lib/dug/dug-reports/dug-reports.component.html new file mode 100644 index 000000000..98b0ab4dd --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-reports/dug-reports.component.html @@ -0,0 +1,15 @@ +
    + + + + + + + + + + + + + +
    diff --git a/imxweb/projects/qam/src/lib/dug/dug-reports/dug-reports.component.scss b/imxweb/projects/qam/src/lib/dug/dug-reports/dug-reports.component.scss new file mode 100644 index 000000000..761d8b43e --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-reports/dug-reports.component.scss @@ -0,0 +1,3 @@ +.preset-report-extension { + justify-content: end; +} \ No newline at end of file diff --git a/imxweb/projects/qam/src/lib/dug/dug-reports/dug-reports.component.ts b/imxweb/projects/qam/src/lib/dug/dug-reports/dug-reports.component.ts new file mode 100644 index 000000000..4bae81d65 --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-reports/dug-reports.component.ts @@ -0,0 +1,73 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Component, Input, OnInit } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; + + +import { BusyService, DataSourceToolbarSettings } from 'qbm'; +import { DisplayColumns, EntitySchema, IClientProperty, ValType } from '@imx-modules/imx-qbm-dbts'; +import { DugReportEntity } from './dug-report-entity'; +import { DugReportService } from './dug-reports.service'; + +@Component({ + selector: 'imx-dug-reports', + templateUrl: './dug-reports.component.html', + styleUrls: ['./dug-reports.component.scss'], +}) +export class DugReportsComponent implements OnInit { + @Input() public dugUid: string; + public busyService = new BusyService(); + public dstSettings: DataSourceToolbarSettings; + public entitySchema: EntitySchema; + private displayedColumns: IClientProperty[] = []; + public readonly DisplayColumns = DisplayColumns; + + constructor(private readonly dugReportService: DugReportService, translateService: TranslateService) { + this.entitySchema = DugReportEntity.GetEntitySchema(translateService); + this.displayedColumns = [this.entitySchema.Columns.ReportDisplayName, {ColumnName: 'action', Type: ValType.String}]; + } + + public async ngOnInit(): Promise { + return this.getData(); + } + + private async getData(): Promise { + const isBusy = this.busyService.beginBusy(); + try { + const data = await this.dugReportService.getReports(this.dugUid); + + this.dstSettings = { + displayedColumns: this.displayedColumns, + dataSource: data, + entitySchema: this.entitySchema, + navigationState: {}, + }; + } finally { + isBusy.endBusy(); + } + } +} diff --git a/imxweb/projects/qam/src/lib/dug/dug-reports/dug-reports.service.ts b/imxweb/projects/qam/src/lib/dug/dug-reports/dug-reports.service.ts new file mode 100644 index 000000000..ee38b6aae --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-reports/dug-reports.service.ts @@ -0,0 +1,49 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Injectable } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; + +import { ExtendedTypedEntityCollection } from '@imx-modules/imx-qbm-dbts'; +import { QamApiService } from '../../qam-api-client.service'; +import { DugReportEntity } from './dug-report-entity'; + +@Injectable({ providedIn: 'root' }) +export class DugReportService { + constructor( + private readonly api: QamApiService, + private readonly translate: TranslateService, + ) {} + + public async getReports(uid: string): Promise> { + const resource = await this.api.client.portal_dge_resources_reports_get(uid); + return DugReportEntity.buildEntities( + DugReportEntity.buildEntityData(resource), + resource.map((elem) => elem.PresetParameters!).filter((elem) => elem.presetParameters), + DugReportEntity.GetEntitySchema(this.translate), + ); + } +} diff --git a/imxweb/projects/qam/src/lib/dug/dug-sidesheet.component.html b/imxweb/projects/qam/src/lib/dug/dug-sidesheet.component.html new file mode 100644 index 000000000..1ad6b296c --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-sidesheet.component.html @@ -0,0 +1,150 @@ + + + #LDS#Heading Main Data  +
    +
    +
    + + +
    + +
    +
    + + {{ orderRestrictionHint | translate }} +
    + +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    +
    +
    + + + +
    +
    +
    + + + +
    + +
    +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    +
    +
    + + + +
    + +
    +
    +
    +
    +
    +
    + + + + + + + +
    diff --git a/imxweb/projects/qam/src/lib/dug/dug-sidesheet.component.scss b/imxweb/projects/qam/src/lib/dug/dug-sidesheet.component.scss new file mode 100644 index 000000000..388277934 --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-sidesheet.component.scss @@ -0,0 +1,30 @@ +@import 'base/mixins'; + +:host { + @include flex-column-container-fill(); + padding-bottom: 3px; +} + +.governance-sidesheet__tab-content-body { + .mat-card { + @include flex-column-container-fill(); + } +} + +.governance-sidesheet__tab-content { + imx-hyperview { + overflow: hidden; + } +} + +.mat-card-title { + font-size: medium ; +} + +.hidden{ + display: none; +} + +.imx-busy-loader { + height: 100%; +} \ No newline at end of file diff --git a/imxweb/projects/qam/src/lib/dug/dug-sidesheet.component.ts b/imxweb/projects/qam/src/lib/dug/dug-sidesheet.component.ts new file mode 100644 index 000000000..8e18155a4 --- /dev/null +++ b/imxweb/projects/qam/src/lib/dug/dug-sidesheet.component.ts @@ -0,0 +1,256 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { ChangeDetectorRef, Component, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { FormGroup, UntypedFormArray } from '@angular/forms'; +import { MatTab, MatTabChangeEvent } from '@angular/material/tabs'; +import { EUI_SIDESHEET_DATA, EuiLoadingService, EuiSidesheetRef, EuiSidesheetService } from '@elemental-ui/core'; +import { DbObjectKey, ValType } from '@imx-modules/imx-qbm-dbts'; +import { TranslateService } from '@ngx-translate/core'; +import { + BaseCdr, + BusyService, + CdrFactoryService, + ColumnDependentReference, + ConfirmationService, + EntityService, + ExtService, + SnackBarService, + SystemInfoService, + TabItem, + calculateSidesheetWidth, +} from 'qbm'; +import { RiskAnalysisSidesheetComponent, WorkflowActionComponent } from 'qer'; +import { Subscription } from 'rxjs'; +import { DugOverviewService } from '../dug-overview/dug-overview.service'; +import { ChangeRequestType, DgeConfigData, PortalDgeResourcesActivity, PortalDgeResourcesbyid } from '../TypedClient'; + +interface DugResourceFormGroup { + array: UntypedFormArray; +} + +@Component({ + selector: 'imx-dug-sidesheet', + styleUrls: ['./dug-sidesheet.component.scss'], + templateUrl: './dug-sidesheet.component.html', +}) +export class DugSidesheetComponent implements OnInit, OnDestroy { + public dug: PortalDgeResourcesbyid; + public config: DgeConfigData; + public busyService = new BusyService(); + public subscriptions: Subscription[] = []; + + public supportsActivity: boolean; + public isShare: boolean; + public canAnalyzeRisk: boolean; + public isRpsEnabled: boolean; + public isAttEnabled: boolean; + public activity: PortalDgeResourcesActivity[] = []; + public ChangeRequestType = ChangeRequestType; + public showChangePropertyButton: boolean = false; + public hideActionBar: boolean = false; + + public dugResourceFormGroup = new FormGroup({ array: new UntypedFormArray([]) }); + public dugResourceConditionsFormGroup = new FormGroup({ array: new UntypedFormArray([]) }); + public cdrList: (ColumnDependentReference | undefined)[] = []; + public isLoading = true; + public dynamicTabs: TabItem[] = []; + public readonly parameters: { objecttable: string; objectuid: string }; + + public get saveDisabled(): boolean { + return ( + (!this.dugResourceFormGroup.dirty && !this.dugResourceConditionsFormGroup.dirty) || + this.dugResourceFormGroup.invalid || + this.dugResourceConditionsFormGroup.invalid + ); + } + + public cdrListOrderConditions: (ColumnDependentReference | undefined)[] = []; + public orderRestrictionHint = + '#LDS#You can specify who can order access to this resource. If nothing is assigned, the resource can be ordered by all employees.'; + + @ViewChild('accessTab') private accessTab: MatTab; + + constructor( + @Inject(EUI_SIDESHEET_DATA) public readonly data: { uid: string }, + private readonly dugOverviewProvider: DugOverviewService, + private readonly sideSheet: EuiSidesheetService, + private readonly loadingService: EuiLoadingService, + private readonly systemInfoService: SystemInfoService, + private readonly translate: TranslateService, + private readonly cdrFactory: CdrFactoryService, + private readonly extService: ExtService, + private readonly entityService: EntityService, + private readonly snackbar: SnackBarService, + private readonly sidesheetRef: EuiSidesheetRef, + private readonly changeDetector: ChangeDetectorRef, // private readonly sidesheetRef: EuiSidesheetRef, //ToDO: DGE add apply, if Main Data changed + confirm: ConfirmationService, + ) { + this.parameters = { + objecttable: dugOverviewProvider.DugResourceSchema.TypeName ?? '', + objectuid: data.uid, + }; + + this.subscriptions.push( + this.busyService.busyStateChanged.subscribe((state: boolean) => { + this.isLoading = state; + this.changeDetector.detectChanges(); + }), + ); + this.subscriptions.push( + sidesheetRef.closeClicked().subscribe(async () => { + if (!this.saveDisabled && !(await confirm.confirmLeaveWithUnsavedChanges())) { + return; + } + + sidesheetRef.close(false); + }), + ); + } + + public async ngOnInit(): Promise { + const isBusy = this.busyService.beginBusy(); + try { + const info = await this.systemInfoService.get(); + this.canAnalyzeRisk = info.PreProps?.includes('RISKINDEX') ?? false; + this.isRpsEnabled = info.PreProps?.includes('REPORT_SUBSCRIPTION') ?? false; + this.isAttEnabled = info.PreProps?.includes('ATTESTATION') ?? false; + + this.config = await this.dugOverviewProvider.getDgeConfig(); + this.dug = await this.dugOverviewProvider.getDugResourceById(this.data.uid); + this.supportsActivity = this.dug.UID_QAMResourceType.value != 'QAM-A2EB93DC78054195837671623098181F'; + this.isShare = this.dug.UID_QAMResourceType.value == 'QAM-52F4B02EFBCAEB7A2EE35B8A4636FAEA'; + + this.cdrList = this.cdrFactory + .buildCdrFromColumnList(this.dug.GetEntity(), [ + 'UID_QAMDuG', + 'UID_QAMNode', + 'UID_QAMResourceType', + 'UID_PersonResponsible', + 'UID_AERoleOwner', + 'IsSecurityInformationIndexed', + 'UID_BackingFolder', + 'UID_QAMDuGParent', + 'DisplayName', + 'FullPath', + 'DisplayPath', + 'RiskIndexCalculated', + 'RequiresOwnership', + 'UID_QAMClassificationLevelMan', + 'IsSecurityInheritanceBlocked', + ]) + .filter((elem) => elem); + + this.cdrListOrderConditions = this.cdrFactory + .buildCdrFromColumnList(this.dug.GetEntity(), ['InProfitCenter', 'InDepartment', 'InAERole', 'InLocality', 'InOrg']) + .filter((elem) => elem); + + if (this.supportsActivity) { + this.activity = (await this.dugOverviewProvider.getAllResourceActivities(this.data.uid)).Data; + } else { + this.activity = []; + } + + this.dynamicTabs = ( + await this.extService.getFittingComponents('dugSidesheet', (ext) => ext.inputData.checkVisibility(this.parameters)) + ).sort((tab1: TabItem, tab2: TabItem) => (tab1.sortOrder ?? 0) - (tab2.sortOrder ?? 0)); + } finally { + isBusy.endBusy(); + } + } + + public ngOnDestroy(): void { + this.subscriptions?.forEach((elem) => elem?.unsubscribe()); + } + + public async save(): Promise { + const overlay = this.loadingService.show(); + try { + await this.dug.GetEntity().Commit(); + this.snackbar.open({ key: '#LDS#The resource has been saved' }); + this.sidesheetRef.close(true); + } finally { + this.loadingService.hide(overlay); + } + } + + public async analyzeRisk(): Promise { + this.sideSheet.open(RiskAnalysisSidesheetComponent, { + title: await this.translate.get('#LDS#Heading Analyze Risk').toPromise(), + padding: '0px', + width: calculateSidesheetWidth(600, 0.4), + data: { objectKey: new DbObjectKey('QAMDuG', this.data.uid).ToXmlString() }, + }); + } + + public updateCurrentTab(event: MatTabChangeEvent): void { + this.hideActionBar = this.dynamicTabs.some((elem) => this.translate.instant(elem.inputData.label) === event.tab.textLabel); + this.showChangePropertyButton = event.tab === this.accessTab; + } + + public async makeRequest(type: ChangeRequestType): Promise { + const actionParameters = { + reason: this.createCdrReason(undefined, 2), + }; + const result = await this.sideSheet + .open(WorkflowActionComponent, { + title: this.translate.instant( + type === ChangeRequestType.Change ? '#LDS#Heading Request Property Change' : '#LDS#Heading Reject Ownership', + ), + subTitle: this.dug.DisplayName.value, + padding: '0', + width: calculateSidesheetWidth(), + testId: 'dug-sidesheet-' + (type === ChangeRequestType.Change ? 'change' : 'reject-ownership'), + data: { + requests: [this.dug], + actionParameters, + }, + }) + .afterClosed() + .toPromise(); + + if (result) { + await this.dugOverviewProvider.makeRequest(this.data.uid, type, actionParameters.reason.column.GetValue()); + this.snackbar.open({ + key: + type === ChangeRequestType.Change + ? '#LDS#The change of a property has been requested' + : '#LDS#A rejection of the ownership has been requested', + }); + } + } + + private createCdrReason(display?: string, reasonType: number = 0): BaseCdr { + const column = this.entityService.createLocalEntityColumn({ + ColumnName: 'ReasonHead', + Type: ValType.Text, + IsMultiLine: true, + MinLen: reasonType === 2 ? 1 : 0, + }); + + return new BaseCdr(column, display || '#LDS#Reason for your decision'); + } +} diff --git a/imxweb/projects/qam/src/lib/identity/identity.component.html b/imxweb/projects/qam/src/lib/identity/identity.component.html new file mode 100644 index 000000000..26896c9db --- /dev/null +++ b/imxweb/projects/qam/src/lib/identity/identity.component.html @@ -0,0 +1,73 @@ +
    + + + + +
    +
    + {{ "#LDS#'{0}' has access to the following governed data resources." | translate | ldsReplace: referrer?.display }} +
    + + +
    +
    + +
    + +
    +
    + + +
    +
    + + + + + + {{ '#LDS#Path' | translate }}: + {{ path.DisplayPath }} + +
    + {{ trustee?.Display }} + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +
    + +

    #LDS#There are no resources assigned to the identity.

    +
    +
    diff --git a/imxweb/projects/qam/src/lib/identity/identity.component.scss b/imxweb/projects/qam/src/lib/identity/identity.component.scss new file mode 100644 index 000000000..61f8c2125 --- /dev/null +++ b/imxweb/projects/qam/src/lib/identity/identity.component.scss @@ -0,0 +1,42 @@ +@import 'base/mixins'; + +:host { + flex: 1 1 auto; + overflow: hidden; +} + +.imx-resource-line { + @include flex-row-container($overflow: hidden); +} + +.imx-resource-toolbar { + @include flex-row-container(); +} + +.imx-resource-line:not(:last-of-type) { + margin-bottom: 10px; +} + +span[bold] { + font-weight: 700; +} + +.imx-resource-content { + @include flex-column-container-fill(); +} + +.imx-scrollable-resoures { + @include flex-column-container-fill(); +} + +.hidden { + display: none; +} + +.imx-busy { + padding: 10px; + display: flex; + flex-direction: column; + align-items: center; + height: 100%; +} diff --git a/imxweb/projects/qam/src/lib/identity/identity.component.ts b/imxweb/projects/qam/src/lib/identity/identity.component.ts new file mode 100644 index 000000000..301f5750a --- /dev/null +++ b/imxweb/projects/qam/src/lib/identity/identity.component.ts @@ -0,0 +1,156 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit, Optional, ViewChild } from '@angular/core'; +import { MatAccordion } from '@angular/material/expansion'; +import { EuiSidesheetService } from '@elemental-ui/core'; +import { TranslateService } from '@ngx-translate/core'; +import { BusyService, calculateSidesheetWidth, DynamicTabDataProviderDirective } from 'qbm'; +import { SourceDetectiveSidesheetComponent, SourceDetectiveSidesheetData, SourceDetectiveType } from 'qer'; +import { Subscription } from 'rxjs'; +import { QamApiService } from '../qam-api-client.service'; +import { TrusteeAccessData, TrusteeData } from '../TypedClient'; + +interface AccessForPath { + DisplayPath: string; + Accesses: AccessData[]; +} + +interface AccessData { + Display: string; + UidQamTrustee: string; + Permissions: string[]; +} + +/** Shows access information for an identity. */ +@Component({ + templateUrl: './identity.component.html', + styleUrls: ['./identity.component.scss', '../qam.scss'], + selector: 'imx-dge-identity', +}) +export class IdentityComponent implements OnInit, OnDestroy { + public data: TrusteeAccessData; + + public byPath: AccessForPath[] = []; + + public referrer: { objecttable: string; objectuid: string; display: string }; + + public busyService: BusyService; + public isLoading: boolean = true; + @Input() public uid: string; + @ViewChild(MatAccordion) public accordion: MatAccordion; + private subscription: Subscription | undefined; + + constructor( + @Optional() dataProvider: DynamicTabDataProviderDirective, + private translate: TranslateService, + private readonly sideSheet: EuiSidesheetService, + private readonly qamApi: QamApiService, + private change: ChangeDetectorRef, + ) { + this.referrer = dataProvider?.data; + this.busyService = new BusyService(); + this.subscription = this.busyService.busyStateChanged.subscribe((elem) => { + this.isLoading = elem; + }); + } + + public async ngOnInit() { + const isBusy = this.busyService.beginBusy(); + try { + this.data = await this.qamApi.client.portal_dge_access_identity_get(this.uid || this.referrer.objectuid); + } finally { + isBusy?.endBusy(); + } + + // pivot the data and store as byPath + var allTrustees: TrusteeData[] = []; + this.iterateRecursively(this.data.Trustees ?? [], allTrustees); + const paths = [ + ...new Set( + allTrustees + .map((d) => d.Paths) + .flat() + .map((d) => d?.Path ?? ''), + ), + ]; + this.byPath = paths.map((path) => { + return { + DisplayPath: path, + Accesses: allTrustees + .map((t) => this.buildAccessData(t, path)) + .flat() + .filter((x) => x.Permissions.length > 0), + }; + }); + this.change.detectChanges(); + } + + public ngOnDestroy(): void { + this.subscription?.unsubscribe(); + } + + public openSourceDetective(uidQamTrustee: string) { + const data: SourceDetectiveSidesheetData = { + UID_Person: this.referrer.objectuid, + Type: SourceDetectiveType.MembershipOfEntitlement, + UID: uidQamTrustee, + TableName: 'QAMTrustee', + }; + this.sideSheet.open(SourceDetectiveSidesheetComponent, { + title: this.translate.instant('#LDS#Heading View Assignment Analysis'), + padding: '0px', + width: calculateSidesheetWidth(600, 0.4), + disableClose: false, + data, + }); + } + + private iterateRecursively(t: TrusteeData[], result: TrusteeData[]) { + t.forEach((trustee) => { + result.push(trustee); + if (trustee.Children) this.iterateRecursively(trustee.Children, result); + }); + } + + private buildAccessData(t: TrusteeData, path: string): AccessData[] { + return ( + t?.Paths?.map((p) => { + if (p.Path != path) + return { + Display: '__WithoutPath', + UidQamTrustee: '', + Permissions: [], + }; + return { + Display: t.Display ?? '', + UidQamTrustee: t.UidQamTrustee || '', + Permissions: p.Permissions || [], + }; + }).filter((x) => x.Display !== '__WithoutPath') ?? [] + ); + } +} diff --git a/imxweb/projects/qam/src/lib/init.service.ts b/imxweb/projects/qam/src/lib/init.service.ts new file mode 100644 index 000000000..60abcdcd3 --- /dev/null +++ b/imxweb/projects/qam/src/lib/init.service.ts @@ -0,0 +1,118 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Injectable } from '@angular/core'; +import { Route, Router } from '@angular/router'; +import { ExtService, HELP_CONTEXTUAL, MenuService } from 'qbm'; +import { CartItemsExtensionService, MyResponsibilitiesRegistryService } from 'qer'; +import { AccessRequestService } from './access-request/access-request.service'; +import { AccessComponent } from './access/access.component'; +import { UserAccessComponent } from './access/user-access.component'; +import { DugOverviewComponent } from './dug-overview/dug-overview.component'; +import { IdentityComponent } from './identity/identity.component'; + +@Injectable({ providedIn: 'root' }) +export class InitService { + protected readonly dgeTag = 'QAMDuG'; + + constructor( + private readonly router: Router, + private readonly extService: ExtService, + private readonly cartItemExtService: CartItemsExtensionService, + private readonly menuService: MenuService, + private readonly myResponsibilitiesRegistryService: MyResponsibilitiesRegistryService, + ) {} + + public onInit(routes: Route[]): void { + // register the extension on the group sidesheet + this.extService.register('groupSidesheet', { + instance: AccessComponent, + inputData: { + id: 'access', + label: '#LDS#Access', + checkVisibility: async (ref) => this.supportsAccess(ref), + }, + }); + + // register the extension on the account sidesheet + this.extService.register('accountSidesheet', { + instance: AccessComponent, + inputData: { + id: 'access', + label: '#LDS#Access', + checkVisibility: async (ref) => this.supportsAccess(ref), + }, + }); + + // register the extension on the identity sidesheet + this.extService.register('identitySidesheet', { + instance: IdentityComponent, + inputData: { + id: 'access', + label: '#LDS#Resource Access', + checkVisibility: async (ref) => true, + }, + }); + + // register the extension on the profile + this.extService.register('profile', { + instance: UserAccessComponent, + inputData: { + id: 'access', + label: '#LDS#Resource Access', + checkVisibility: async (_) => true, + }, + }); + + this.cartItemExtService.register('AccessRequestService', AccessRequestService); + + this.myResponsibilitiesRegistryService.registerFactory(() => ({ + instance: DugOverviewComponent, + sortOrder: 7, + name: this.dgeTag, + caption: '#LDS#Menu Entry Governed Data', + data: { + TableName: this.dgeTag, + Count: 0, + }, + contextId: HELP_CONTEXTUAL.Default, //ToDo Indian Team: DGE Provide context help + })); + + this.addRoutes(routes); + } + + private supportsAccess(referrer: any): boolean { + return referrer ? ['SPSIdentity', 'SPSGroup', 'ADSAccount', 'ADSGroup'].includes(referrer.objecttable) : false; + } + + private addRoutes(routes: Route[]): void { + const config = this.router.config; + routes.forEach((route) => { + config.unshift(route); + }); + this.router.resetConfig(config); + } +} diff --git a/imxweb/projects/o3t/src/lib/api.service.ts b/imxweb/projects/qam/src/lib/qam-api-client.service.ts similarity index 63% rename from imxweb/projects/o3t/src/lib/api.service.ts rename to imxweb/projects/qam/src/lib/qam-api-client.service.ts index d9561f8a4..c642cb846 100644 --- a/imxweb/projects/o3t/src/lib/api.service.ts +++ b/imxweb/projects/qam/src/lib/qam-api-client.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,14 +25,14 @@ */ import { Injectable } from '@angular/core'; -import { V2Client, TypedClient } from 'imx-api-o3t'; -import { ApiClient } from 'imx-qbm-dbts'; +import { CachedPromise } from '@imx-modules/imx-qbm-dbts'; import { AppConfigService, ClassloggerService, ImxTranslationProviderService } from 'qbm'; +import { DgeConfigData, TypedClient, V2Client } from './TypedClient'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) -export class ApiService { +export class QamApiService { private tc: TypedClient; public get typedClient(): TypedClient { return this.tc; @@ -43,16 +43,13 @@ export class ApiService { return this.c; } - public get apiClient(): ApiClient { - return this.config.apiClient; - } - constructor( private readonly config: AppConfigService, private readonly logger: ClassloggerService, - private readonly translationProvider: ImxTranslationProviderService) { + private readonly translationProvider: ImxTranslationProviderService, + ) { try { - this.logger.debug(this, 'Initializing O3T API service'); + this.logger.debug(this, 'Initializing QAM API service'); // Use schema loaded by QBM client const schemaProvider = config.client; @@ -62,4 +59,24 @@ export class ApiService { this.logger.error(this, e); } } + + private cachedDgeConfig: CachedPromise = new CachedPromise(() => this.client.portal_dgeconfig_get()); + + public getDgeConfig() { + return this.cachedDgeConfig.get(); + } + + private cachedTrusteeTypes: CachedPromise<{ [id: number]: string }> = new CachedPromise(async () => { + const data = await this.client.portal_dge_trustees_types_get(); + var result = {}; + data.forEach((e) => { + result[e.Value] = e.Description; + }); + return result; + }); + + /** Returns the display names of the known trustee types. */ + public getTrusteeTypes(): Promise<{ [id: number]: string }> { + return this.cachedTrusteeTypes.get(); + } } diff --git a/imxweb/projects/qam/src/lib/qam-config.module.ts b/imxweb/projects/qam/src/lib/qam-config.module.ts new file mode 100644 index 000000000..d1c18ce30 --- /dev/null +++ b/imxweb/projects/qam/src/lib/qam-config.module.ts @@ -0,0 +1,131 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatExpansionModule } from '@angular/material/expansion'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatRadioModule } from '@angular/material/radio'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatTreeModule } from '@angular/material/tree'; +import { RouterModule, Routes } from '@angular/router'; +import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; +import { TranslateModule } from '@ngx-translate/core'; +import { + BusyIndicatorModule, + CdrModule, + DataSourceToolbarModule, + DataTableModule, + DynamicTabsModule, + ExtModule, + HelpContextualModule, + LdsReplaceModule, + ObjectHistoryModule, + RouteGuardService, +} from 'qbm'; +import { ObjectHyperviewModule, StatisticsModule } from 'qer'; +import { AccessRequestModule } from './access-request/access-request.module'; +import { AccessComponent } from './access/access.component'; +import { TrusteeViewComponent } from './access/trustee-view.component'; +import { UserAccessComponent } from './access/user-access.component'; +import { DugActivitiesComponent } from './dug-activities/dug-activities.component'; +import { DugDashboardsComponent } from './dug-dashboards/dug-dashboards.component'; +import { DugOverviewComponent } from './dug-overview/dug-overview.component'; +import { AccessComparisonComponent } from './dug/access-comparison.component'; +import { DugAccessAnalysisComponent } from './dug/dug-access-analysis.component'; +import { DugAccessDetailComponent } from './dug/dug-access-detail.component'; +import { DugAccessModule } from './dug/dug-access/dug-access.module'; +import { DugActivityModule } from './dug/dug-activity/dug-activity.module'; +import { DugReportsComponent } from './dug/dug-reports/dug-reports.component'; +import { DugSidesheetComponent } from './dug/dug-sidesheet.component'; +import { IdentityComponent } from './identity/identity.component'; +import { InitService } from './init.service'; + +const routes: Routes = [ + { + path: 'dge', + canActivate: [RouteGuardService], + resolve: [RouteGuardService], + component: DugSidesheetComponent, // as an example + }, +]; + +@NgModule({ + declarations: [ + AccessComponent, + TrusteeViewComponent, + IdentityComponent, + DugSidesheetComponent, + DugAccessAnalysisComponent, + DugAccessDetailComponent, + UserAccessComponent, + AccessComparisonComponent, + DugOverviewComponent, + DugReportsComponent, + DugDashboardsComponent, + DugActivitiesComponent, + ], + imports: [ + AccessRequestModule, + CommonModule, + DataSourceToolbarModule, + DataTableModule, + CdrModule, + HelpContextualModule, + DynamicTabsModule, + FormsModule, + ReactiveFormsModule, + RouterModule.forChild(routes), + TranslateModule, + LdsReplaceModule, + MatButtonModule, + MatRadioModule, + MatMenuModule, + MatTabsModule, + MatExpansionModule, + MatTreeModule, + ObjectHyperviewModule, + ObjectHistoryModule, + EuiCoreModule, + EuiMaterialModule, + DugAccessModule, + DugActivityModule, + BusyIndicatorModule, + ExtModule, + StatisticsModule, + MatTooltipModule, + ], +}) +export class QamConfigModule { + constructor(private readonly initializer: InitService) { + this.initializer.onInit(routes); + + console.log('▶️ QAM initialized'); + } +} diff --git a/imxweb/projects/qam/src/lib/qam.scss b/imxweb/projects/qam/src/lib/qam.scss new file mode 100644 index 000000000..598adc766 --- /dev/null +++ b/imxweb/projects/qam/src/lib/qam.scss @@ -0,0 +1,50 @@ +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/mixins'; + +.trusteetype { + font-style: italic; +} + +.imx-no-results { + display: flex; + flex-direction: column; + flex: 1 1 auto; + align-items: center; + justify-content: center; + + p { + margin: 0; + font-size: 18px; + } +} + +.imx-flex-line { + @include flex-row-container(); +} + +.eui-light-theme { + :host { + .qam-tree-content { + background-color: $color-blue-10; + border: 1px solid $color-blue-20; + } + } +} + +.eui-dark-theme { + :host { + .qam-tree-content { + background-color: $color-blue-90; + border: 1px solid $color-blue-80; + } + } +} + +.eui-contrast-theme { + :host { + .qam-tree-content { + background-color: $color-gray-90; + border: 1px solid $color-gray-0; + } + } +} diff --git a/imxweb/projects/o3t/src/lib/api.service.spec.ts b/imxweb/projects/qam/src/public_api.ts similarity index 84% rename from imxweb/projects/o3t/src/lib/api.service.spec.ts rename to imxweb/projects/qam/src/public_api.ts index 1b61024ea..73dda2de7 100644 --- a/imxweb/projects/o3t/src/lib/api.service.spec.ts +++ b/imxweb/projects/qam/src/public_api.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,10 +24,4 @@ * */ -describe('ApiService', () => { - - it('is a dummy test', async () => { - - expect({}).toBeDefined(); - }); -}); +export { QamConfigModule } from './lib/qam-config.module'; diff --git a/imxweb/projects/qam/src/test.ts b/imxweb/projects/qam/src/test.ts new file mode 100644 index 000000000..a10861796 --- /dev/null +++ b/imxweb/projects/qam/src/test.ts @@ -0,0 +1,40 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js'; +import 'zone.js/testing'; + +import { getTestBed } from '@angular/core/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; +import 'core-js/es/reflect'; +import { TestHelperModule } from 'qbm'; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment([BrowserDynamicTestingModule, TestHelperModule], platformBrowserDynamicTesting(), { + teardown: { destroyAfterEach: false }, +}); diff --git a/imxweb/projects/o3t/tsconfig.lib.json b/imxweb/projects/qam/tsconfig.lib.json similarity index 68% rename from imxweb/projects/o3t/tsconfig.lib.json rename to imxweb/projects/qam/tsconfig.lib.json index 6bc419f84..f863d8bff 100644 --- a/imxweb/projects/o3t/tsconfig.lib.json +++ b/imxweb/projects/qam/tsconfig.lib.json @@ -7,10 +7,11 @@ "declarationMap": true, "sourceMap": true, "inlineSources": true, + "strictNullChecks": true, "types": [] }, - "exclude": [ - "src/test.ts", - "**/*.spec.ts" - ] + "angularCompilerOptions": { + "strictTemplates": true + }, + "exclude": ["src/test.ts", "**/*.spec.ts"] } diff --git a/imxweb/projects/o3t/tsconfig.lib.prod.json b/imxweb/projects/qam/tsconfig.lib.prod.json similarity index 75% rename from imxweb/projects/o3t/tsconfig.lib.prod.json rename to imxweb/projects/qam/tsconfig.lib.prod.json index 61c7592e7..fb40dbbb0 100644 --- a/imxweb/projects/o3t/tsconfig.lib.prod.json +++ b/imxweb/projects/qam/tsconfig.lib.prod.json @@ -3,8 +3,5 @@ "extends": "./tsconfig.lib.json", "compilerOptions": { "declarationMap": false - }, - "angularCompilerOptions": { - "enableIvy": true } } diff --git a/imxweb/projects/o3t/tsconfig.spec.json b/imxweb/projects/qam/tsconfig.spec.json similarity index 100% rename from imxweb/projects/o3t/tsconfig.spec.json rename to imxweb/projects/qam/tsconfig.spec.json diff --git a/imxweb/projects/aad/tslint.json b/imxweb/projects/qam/tslint.json similarity index 100% rename from imxweb/projects/aad/tslint.json rename to imxweb/projects/qam/tslint.json diff --git a/imxweb/projects/qbm-app-landingpage/.compodocrc.json b/imxweb/projects/qbm-app-landingpage/.compodocrc.json index 162019b61..5caeccd1c 100644 --- a/imxweb/projects/qbm-app-landingpage/.compodocrc.json +++ b/imxweb/projects/qbm-app-landingpage/.compodocrc.json @@ -1,6 +1,6 @@ { "name": "IMX Web - QBM Landing Page", - "output": "../../documentation/v92/qbm-app-landingpage", + "output": "../../documentation/v93/qbm-app-landingpage", "theme": "material", "assetsFolder": "../../compodoc/assets", "customLogo": "../../compodoc/assets/images/oneidentity-logo.png", diff --git a/imxweb/projects/qbm-app-landingpage/.eslintrc.json b/imxweb/projects/qbm-app-landingpage/.eslintrc.json new file mode 100644 index 000000000..584d9666a --- /dev/null +++ b/imxweb/projects/qbm-app-landingpage/.eslintrc.json @@ -0,0 +1,35 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*", "**/*.spec.ts"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": ["imxweb/projects/qbm-app-landingpage/tsconfig.app.json", "imxweb/projects/qbm-app-landingpage/tsconfig.spec.json"], + "createDefaultProgram": true + }, + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "imx", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "imx", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "rules": {} + } + ] +} diff --git a/imxweb/projects/qbm-app-landingpage/README.md b/imxweb/projects/qbm-app-landingpage/README.md new file mode 100644 index 000000000..d2d86589f --- /dev/null +++ b/imxweb/projects/qbm-app-landingpage/README.md @@ -0,0 +1 @@ +# IMX Web - Qbm Landing Page diff --git a/imxweb/projects/qbm-app-landingpage/karma.conf.js b/imxweb/projects/qbm-app-landingpage/karma.conf.js index 65aa5dd4e..1f8775dfc 100644 --- a/imxweb/projects/qbm-app-landingpage/karma.conf.js +++ b/imxweb/projects/qbm-app-landingpage/karma.conf.js @@ -13,10 +13,10 @@ module.exports = function (config) { require('karma-coverage-istanbul-reporter'), require('@angular-devkit/build-angular/plugins/karma'), require('karma-junit-reporter'), - require('karma-viewport') + require('karma-viewport'), ], client: { - clearContext: false // leave Jasmine Spec Runner output visible in browser + clearContext: false, // leave Jasmine Spec Runner output visible in browser }, coverageIstanbulReporter: { dir: require('path').join(__dirname, 'results'), @@ -24,7 +24,7 @@ module.exports = function (config) { fixWebpackSourcePaths: true, 'report-config': { html: { - subdir: 'coverage-html' + subdir: 'coverage-html', }, }, thresholds: { @@ -41,13 +41,14 @@ module.exports = function (config) { port: 9876, captureTimeout: 210000, browserDisconnectTolerance: 3, - browserDisconnectTimeout : 210000, - browserNoActivityTimeout : 210000, + browserDisconnectTimeout: 210000, + browserNoActivityTimeout: 210000, colors: true, logLevel: config.LOG_INFO, autoWatch: true, restartOnFileChange: true, singleRun: false, - browsers: ['Chrome'] + failOnEmptyTestSuite: false, + browsers: ['Chrome'], }); }; diff --git a/imxweb/projects/qbm-app-landingpage/package.json b/imxweb/projects/qbm-app-landingpage/package.json index f5397e23a..b6ac98c62 100644 --- a/imxweb/projects/qbm-app-landingpage/package.json +++ b/imxweb/projects/qbm-app-landingpage/package.json @@ -1,8 +1,6 @@ { "name": "qbm-app-landingpage", - "version": "9.2.1", + "version": "9.3.0", "private": true, - "peerDependencies": { - - } + "peerDependencies": {} } diff --git a/imxweb/projects/qbm-app-landingpage/project.json b/imxweb/projects/qbm-app-landingpage/project.json new file mode 100644 index 000000000..5eb09a50b --- /dev/null +++ b/imxweb/projects/qbm-app-landingpage/project.json @@ -0,0 +1,209 @@ +{ + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "name": "qbm-app-landingpage", + "sourceRoot": "projects/qbm-app-landingpage/src", + "implicitDependencies": ["qbm"], + "projectType": "application", + "prefix": "imx", + "generators": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:application", + "options": { + "aot": true, + "outputPath": { + "base": "dist/qbm-app-landingpage", + "browser": "" + }, + "allowedCommonJsDependencies": [ + "lodash", + "highlight.js", + "file-saver", + "billboard.js", + "moment-timezone", + "core-js/fn/map", + "core-js/fn/set", + "core-js/fn/weak-map", + "core-js/fn/array/from", + "core-js/fn/object/assign", + "core-js/es/array/from", + "core-js/es/object/assign", + "core-js/es/map", + "core-js/es/set", + "core-js/es/weak-map", + "lodash.debounce", + "lodash.clamp", + "moment", + "@elemental-ui/cadence-icon/codepoints" + ], + "index": "projects/qbm-app-landingpage/src/index.html", + "browser": "projects/qbm-app-landingpage/src/main.ts", + "polyfills": ["projects/qbm-app-landingpage/src/polyfills.ts"], + "tsConfig": "projects/qbm-app-landingpage/tsconfig.app.json", + "assets": [ + "projects/qbm-app-landingpage/src/assets", + "projects/qbm-app-landingpage/src/appconfig.json", + { + "glob": "**/*", + "input": "./shared/assets/", + "output": "./assets" + }, + { + "glob": "**/*", + "input": "./node_modules/@elemental-ui/core/assets", + "output": "./assets" + } + ], + "styles": [ + "shared/scss/styles.scss", + "projects/qbm-app-landingpage/src/styles.scss", + "node_modules/swagger-ui-dist/swagger-ui.css" + ], + "stylePreprocessorOptions": { + "includePaths": [ + "./shared/scss", + "./node_modules", + "./node_modules/@elemental-ui/cadence-icon", + "./node_modules/@elemental-ui/core" + ] + }, + "scripts": ["node_modules/swagger-ui-dist/swagger-ui-bundle.js", "node_modules/swagger-ui-dist/swagger-ui-standalone-preset.js"] + }, + "configurations": { + "production": { + "fileReplacements": [ + { + "replace": "projects/qbm-app-landingpage/src/environments/environment.ts", + "with": "projects/qbm-app-landingpage/src/environments/environment.prod.ts" + } + ], + "optimization": true, + "outputHashing": "all", + "sourceMap": false, + "namedChunks": true, + "aot": true, + "extractLicenses": true, + "budgets": [ + { + "type": "initial", + "maximumWarning": "20mb", + "maximumError": "40mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "6kb" + } + ] + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + }, + "remote-dev": { + "fileReplacements": [ + { + "replace": "projects/qbm-app-landingpage/src/environments/environment.ts", + "with": "../imxweb_envs/qbm-app-landingpage/environments/environment.remote-dev.ts" + } + ], + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + }, + "remote-qs": { + "fileReplacements": [ + { + "replace": "projects/qbm-app-landingpage/src/environments/environment.ts", + "with": "../imxweb_envs/qbm-app-landingpage/environments/environment.remote-qs.ts" + } + ], + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + }, + "es5": { + "tsConfig": "./projects/qbm-app-landingpage/tsconfig-es5.app.json" + } + }, + "outputs": ["{options.outputPath}"] + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "options": { + "browserTarget": "qbm-app-landingpage:build", + "disableHostCheck": true + }, + "configurations": { + "production": { + "browserTarget": "qbm-app-landingpage:build:production" + }, + "development": { + "browserTarget": "qbm-app-landingpage:build:development" + }, + "remote-dev": { + "browserTarget": "qbm-app-landingpage:build:remote-dev" + }, + "remote-qs": { + "browserTarget": "qbm-app-landingpage:build:remote-qs" + }, + "es5": { + "browserTarget": "qbm-app-landingpage:build:es5" + } + } + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "qbm-app-landingpage:build" + } + }, + "test": { + "executor": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/qbm-app-landingpage/src/test.ts", + "polyfills": "projects/qbm-app-landingpage/src/polyfills.ts", + "tsConfig": "projects/qbm-app-landingpage/tsconfig.spec.json", + "karmaConfig": "projects/qbm-app-landingpage/karma.conf.js", + "scripts": [], + "assets": [ + "projects/qbm-app-landingpage/src/assets", + "projects/qbm-app-landingpage/src/appconfig.json", + { + "glob": "**/*", + "input": "./shared/assets/", + "output": "./assets" + } + ], + "styles": ["projects/qbm-app-landingpage/src/styles.scss"], + "stylePreprocessorOptions": { + "includePaths": [ + "./shared/assets", + "./shared/scss", + "./node_modules", + "./node_modules/@elemental-ui/cadence-icon", + "./node_modules/@elemental-ui/core" + ] + } + } + }, + "lint": { + "executor": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": ["projects/qbm-app-landingpage/tsconfig.app.json", "projects/qbm-app-landingpage/tsconfig.spec.json"], + "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + } + } + } +} diff --git a/imxweb/projects/qbm-app-landingpage/src/app/app-routing.module.ts b/imxweb/projects/qbm-app-landingpage/src/app/app-routing.module.ts index 8d25aef91..03ee9b1c8 100644 --- a/imxweb/projects/qbm-app-landingpage/src/app/app-routing.module.ts +++ b/imxweb/projects/qbm-app-landingpage/src/app/app-routing.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,10 +25,9 @@ */ import { NgModule } from '@angular/core'; -import { Routes, RouterModule } from '@angular/router'; -import { AuthenticationGuardService, LoginComponent, RouteGuardService } from 'qbm'; +import { RouterModule, Routes } from '@angular/router'; +import { AdminRoutes, AuthenticationGuardService, LoginComponent, RouteGuardService } from 'qbm'; import { StartComponent } from './start/start.component'; -import { AdminRoutes } from 'qbm'; const routes: Routes = [ ...AdminRoutes, @@ -45,7 +44,7 @@ const routes: Routes = [ ]; @NgModule({ - imports: [RouterModule.forRoot(routes, { useHash: true, relativeLinkResolution: 'legacy' })], + imports: [RouterModule.forRoot(routes, { useHash: true })], exports: [RouterModule], }) export class AppRoutingModule {} diff --git a/imxweb/projects/qbm-app-landingpage/src/app/app.component.html b/imxweb/projects/qbm-app-landingpage/src/app/app.component.html index 02e1cf47a..6ba785e1a 100644 --- a/imxweb/projects/qbm-app-landingpage/src/app/app.component.html +++ b/imxweb/projects/qbm-app-landingpage/src/app/app.component.html @@ -1,8 +1,7 @@
    - - +
    -
    \ No newline at end of file +
    diff --git a/imxweb/projects/qbm-app-landingpage/src/app/app.component.scss b/imxweb/projects/qbm-app-landingpage/src/app/app.component.scss index 7c5e545e3..dff596b9d 100644 --- a/imxweb/projects/qbm-app-landingpage/src/app/app.component.scss +++ b/imxweb/projects/qbm-app-landingpage/src/app/app.component.scss @@ -1,14 +1,13 @@ .page { - display: flex; - flex-direction: column; - overflow: hidden; - height: 100vh; - } - - .pageContent { - display: flex; - flex-direction: column; - overflow: hidden; - height: inherit; - } - \ No newline at end of file + display: flex; + flex-direction: column; + overflow: hidden; + height: 100vh; +} + +.pageContent { + display: flex; + flex-direction: column; + overflow: hidden; + height: inherit; +} diff --git a/imxweb/projects/qbm-app-landingpage/src/app/app.component.ts b/imxweb/projects/qbm-app-landingpage/src/app/app.component.ts index 0f5b937c7..b14acbd06 100644 --- a/imxweb/projects/qbm-app-landingpage/src/app/app.component.ts +++ b/imxweb/projects/qbm-app-landingpage/src/app/app.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,39 +26,46 @@ import { Component } from '@angular/core'; import { Title } from '@angular/platform-browser'; -import { NavigationEnd, Router, RouterEvent } from '@angular/router'; +import { Event, NavigationEnd, Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; -import { Globals } from 'imx-qbm-dbts'; -import { AppConfigService, AuthenticationService, ISessionState } from 'qbm'; +import { Globals } from '@imx-modules/imx-qbm-dbts'; +import { + AppConfigService, + AuthenticationService, + ClassloggerService, + ConfirmationService, + ISessionState, + Message, + UserMessageService, +} from 'qbm'; import { Subscription } from 'rxjs'; @Component({ selector: 'imx-root', templateUrl: './app.component.html', - styleUrls: ['./app.component.scss'] + styleUrls: ['./app.component.scss'], }) export class AppComponent { - public isLoggedIn = false; public hideUserMessage = false; private readonly subscriptions: Subscription[] = []; private overviewTitle: string; private adminPortalTitle: string; + public message: Message | undefined; constructor( + private readonly logger: ClassloggerService, private readonly translate: TranslateService, private readonly titleService: Title, private readonly appConfigService: AppConfigService, private readonly router: Router, private readonly translateService: TranslateService, - private readonly authentication: AuthenticationService + private readonly authentication: AuthenticationService, + private readonly confirmationService: ConfirmationService, + private readonly userMessageService: UserMessageService, ) { - this.translate.addLangs(['en-US', 'de-DE', 'de', 'en']); - const browserCulture = this.translate.getBrowserCultureLang(); - this.translate.setDefaultLang(browserCulture); - this.translate.use(browserCulture); this.titleService.setTitle(Globals.QIM_ProductNameFull + ' ' + this.appConfigService.Config.Title); this.translateService.onLangChange.subscribe(() => { @@ -66,56 +73,68 @@ export class AppComponent { this.setTitle(this.router.url); }); - this.subscriptions.push(this.authentication.onSessionResponse.subscribe(async (sessionState: ISessionState) => { - // Close the splash screen that opened in app service initialisation - this.isLoggedIn = sessionState.IsLoggedIn; - if (this.isLoggedIn) { - this.titleService.setTitle(this.adminPortalTitle); - } - })); + this.subscriptions.push( + this.authentication.onSessionResponse.subscribe(async (sessionState: ISessionState) => { + // Close the splash screen that opened in app service initialisation + this.isLoggedIn = sessionState?.IsLoggedIn ?? false; + if (this.isLoggedIn) { + this.titleService.setTitle(this.adminPortalTitle); + } + }), + ); + + this.subscriptions.push( + this.userMessageService.subject.subscribe((message) => { + this.message = message; + if (!!this.message && this.message.type === 'error' && !this.message.target) { + this.confirmationService.showErrorMessage({ + Message: this.message?.text, + }); + } + }), + ); this.setupRouter(); - } public ngOnDestroy(): void { - this.subscriptions.forEach(s => s.unsubscribe()); + this.subscriptions.forEach((s) => s.unsubscribe()); } public async ngOnInit(): Promise { this.initTitles(); - this.authentication.update(); + await this.authentication.update(); } /** - * Logs out and kills the session. - */ + * Logs out and kills the session. + */ public async logout(): Promise { await this.authentication.logout(); this.router.navigate([''], { queryParams: {} }); } private setupRouter(): void { - this.router.events.subscribe((event: RouterEvent) => { + this.router.events.subscribe((event: Event) => { if (event instanceof NavigationEnd) { - this.setTitle(this.router.url); + this.setTitle(this.router.url); } }); } - private async initTitles(): Promise { - this.overviewTitle = await this.translate.get('#LDS#Heading Web Applications Overview').toPromise(); - this.adminPortalTitle = await this.translate.get('#LDS#Heading Administration Portal').toPromise(); + private initTitles(): void { + this.overviewTitle = this.translate.instant('#LDS#Heading Web Applications Overview'); + this.adminPortalTitle = this.translate.instant('#LDS#Heading Administration Portal'); } private setTitle(url: string): void { - if (url === "/") { - // show another title on the startpage + if (url === '/') { + // show another title on the startpage this.titleService.setTitle(Globals.QIM_ProductNameFull + ' ' + this.overviewTitle); this.appConfigService.setTitle(this.overviewTitle); - } else { + } else { this.titleService.setTitle(Globals.QIM_ProductNameFull + ' ' + this.adminPortalTitle); this.appConfigService.setTitle(this.adminPortalTitle); - } + } } } diff --git a/imxweb/projects/qbm-app-landingpage/src/app/app.module.ts b/imxweb/projects/qbm-app-landingpage/src/app/app.module.ts index b94a5d03c..11bcb8ae4 100644 --- a/imxweb/projects/qbm-app-landingpage/src/app/app.module.ts +++ b/imxweb/projects/qbm-app-landingpage/src/app/app.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,39 +24,41 @@ * */ +import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; +import { APP_INITIALIZER, ErrorHandler, NgModule } from '@angular/core'; +import { MatCardModule } from '@angular/material/card'; import { BrowserModule } from '@angular/platform-browser'; -import { NgModule, APP_INITIALIZER, ErrorHandler } from '@angular/core'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { HttpClientModule } from '@angular/common/http'; -import { MatCardModule } from '@angular/material/card'; import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; -import { TranslateModule, TranslateLoader, MissingTranslationHandler } from '@ngx-translate/core'; +import { MissingTranslationHandler, TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { LoggerModule, NgxLoggerLevel } from 'ngx-logger'; -import { AppRoutingModule } from './app-routing.module'; -import { AppComponent } from './app.component'; -import { AppcontainerService } from './appcontainer.service'; +import { RECAPTCHA_V3_SITE_KEY } from 'ng-recaptcha-2'; +import { CustomThemeModule } from 'projects/qbm/src/lib/custom-theme/custom-theme.module'; import { + AdminModule, AppConfigService, AuthenticationModule, - imx_SessionService, + ExtModule, + GlobalErrorHandler, ImxMissingTranslationHandler, ImxTranslateLoader, MastHeadModule, - UserMessageModule, - AdminModule, - ExtModule, - GlobalErrorHandler, QbmModule, + UserMessageModule, + imx_SessionService, } from 'qbm'; +import appConfigJson from '../appconfig.json'; +import { environment } from '../environments/environment'; +import { AppRoutingModule } from './app-routing.module'; +import { AppComponent } from './app.component'; import { AppService } from './app.service'; +import { AppcontainerService } from './appcontainer.service'; import { StartComponent } from './start/start.component'; -import { environment } from '../environments/environment'; -import appConfigJson from '../appconfig.json'; -import { CustomThemeModule } from 'projects/qbm/src/lib/custom-theme/custom-theme.module'; @NgModule({ declarations: [AppComponent, StartComponent], + bootstrap: [AppComponent], imports: [ AdminModule, AppRoutingModule, @@ -66,7 +68,6 @@ import { CustomThemeModule } from 'projects/qbm/src/lib/custom-theme/custom-them CustomThemeModule, EuiCoreModule, EuiMaterialModule, - HttpClientModule, MastHeadModule, UserMessageModule, TranslateModule.forRoot({ @@ -82,7 +83,7 @@ import { CustomThemeModule } from 'projects/qbm/src/lib/custom-theme/custom-them ExtModule, MatCardModule, LoggerModule.forRoot({ level: NgxLoggerLevel.DEBUG, serverLogLevel: NgxLoggerLevel.OFF }), - QbmModule + QbmModule, ], providers: [ { provide: 'environment', useValue: environment }, @@ -100,7 +101,14 @@ import { CustomThemeModule } from 'projects/qbm/src/lib/custom-theme/custom-them AppcontainerService, AppConfigService, imx_SessionService, + { + provide: RECAPTCHA_V3_SITE_KEY, + useFactory: (config: AppService) => { + return config.recaptchaSiteKeyV3; + }, + deps: [AppService], + }, + provideHttpClient(withInterceptorsFromDi()), ], - bootstrap: [AppComponent], }) export class AppModule {} diff --git a/imxweb/projects/qbm-app-landingpage/src/app/app.service.ts b/imxweb/projects/qbm-app-landingpage/src/app/app.service.ts index 2eb7b9ef2..1b5bbcc0d 100644 --- a/imxweb/projects/qbm-app-landingpage/src/app/app.service.ts +++ b/imxweb/projects/qbm-app-landingpage/src/app/app.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,15 +25,25 @@ */ import { Injectable } from '@angular/core'; +import { TypedClient } from '@imx-modules/imx-api-qbm'; import { TranslateService } from '@ngx-translate/core'; -import { TypedClient } from 'imx-api-qbm'; -import { AppConfigService, CdrRegistryService, ImxTranslationProviderService, ClassloggerService, imx_SessionService, AuthenticationService } from 'qbm'; +import { + AppConfigService, + AuthenticationService, + CaptchaService, + CdrRegistryService, + ClassloggerService, + ImxTranslationProviderService, + SystemInfoService, + imx_SessionService, +} from 'qbm'; import { environment } from '../environments/environment'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class AppService { + public recaptchaSiteKeyV3: string | null = null; constructor( private logger: ClassloggerService, private readonly config: AppConfigService, @@ -41,21 +51,33 @@ export class AppService { private readonly session: imx_SessionService, private readonly translationProvider: ImxTranslationProviderService, public readonly registry: CdrRegistryService, - private readonly authentication: AuthenticationService - ) { } + private readonly authentication: AuthenticationService, + private readonly systemInfoService: SystemInfoService, + private readonly captchaService: CaptchaService, + ) {} public async init(): Promise { await this.config.init(environment.clientUrl); - this.translateService.addLangs(this.config.Config.Translation.Langs); - const browserCulture = this.translateService.getBrowserCultureLang(); + if (this.config.Config.Translation?.Langs) { + this.translateService.addLangs(this.config.Config.Translation.Langs); + } + const browserCulture = this.translateService.getBrowserCultureLang() as string; this.logger.debug(this, 'Set default language to', browserCulture); this.translateService.setDefaultLang(browserCulture); await this.translateService.use(browserCulture).toPromise(); - this.authentication.onSessionResponse.subscribe(sessionState => this.translationProvider.init(sessionState?.culture)); + this.authentication.onSessionResponse.subscribe((sessionState) => this.translationProvider.init(sessionState?.culture)); this.session.TypedClient = new TypedClient(this.config.v2client, this.translationProvider); + + const imxConfig = await this.systemInfoService.getImxConfig(); + + if (imxConfig.RecaptchaPublicKey) { + this.captchaService.enableReCaptcha(imxConfig.RecaptchaPublicKey); + this.recaptchaSiteKeyV3 = imxConfig.RecaptchaPublicKey; + } + this.captchaService.captchaImageUrl = 'admin/captchaimage'; } public static init(app: AppService): () => Promise { diff --git a/imxweb/projects/qbm-app-landingpage/src/app/appcontainer.service.spec.ts b/imxweb/projects/qbm-app-landingpage/src/app/appcontainer.service.spec.ts index c4acc6e2a..7816b6d2e 100644 --- a/imxweb/projects/qbm-app-landingpage/src/app/appcontainer.service.spec.ts +++ b/imxweb/projects/qbm-app-landingpage/src/app/appcontainer.service.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,7 +30,7 @@ import { AppcontainerService } from './appcontainer.service'; import { environment } from '../environments/environment'; import { AppConfigService } from 'qbm'; -[false, true].forEach(environmentIsProduction => { +[false, true].forEach((environmentIsProduction) => { describe('AppcontainerService Constructor with environmentIsProduction=' + environmentIsProduction, () => { let service: AppcontainerService; @@ -44,11 +44,11 @@ import { AppConfigService } from 'qbm'; public Config = { Basepath: '', Title: '', - WebAppIndex: '' + WebAppIndex: '', }; - } - } - ] + }, + }, + ], }).get(AppcontainerService); environment.production = environmentIsProduction; @@ -69,11 +69,11 @@ describe('AppcontainerService Methods', () => { Config: { Basepath: '', Title: '', - WebAppIndex: '' + WebAppIndex: '', }, client: { - imx_applications_get: jasmine.createSpy('imx_applications_get').and.returnValue(Promise.resolve(nodeAppInfo)) - } + imx_applications_get: jasmine.createSpy('imx_applications_get').and.returnValue(Promise.resolve(nodeAppInfo)), + }, }; beforeEach(() => { @@ -84,9 +84,9 @@ describe('AppcontainerService Methods', () => { AppcontainerService, { provide: AppConfigService, - useValue: appConfigServiceStub - } - ] + useValue: appConfigServiceStub, + }, + ], }).get(AppcontainerService); }); diff --git a/imxweb/projects/qbm-app-landingpage/src/app/appcontainer.service.ts b/imxweb/projects/qbm-app-landingpage/src/app/appcontainer.service.ts index 4f152c00d..98ce94d93 100644 --- a/imxweb/projects/qbm-app-landingpage/src/app/appcontainer.service.ts +++ b/imxweb/projects/qbm-app-landingpage/src/app/appcontainer.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,15 +26,15 @@ import { Injectable } from '@angular/core'; -import { NodeAppInfo } from 'imx-api-qbm'; +import { NodeAppInfo } from '@imx-modules/imx-api-qbm'; import { environment } from '../environments/environment'; import { AppConfigService } from 'qbm'; @Injectable() export class AppcontainerService { - constructor(private readonly appConfigService: AppConfigService) { } + constructor(private readonly appConfigService: AppConfigService) {} - /** Gets a flag indicating whether the user can log in to the + /** Gets a flag indicating whether the user can log in to the * Server Administration app. */ public hasServerAdministrationApp = false; @@ -54,7 +54,7 @@ export class AppcontainerService { }) .map((appInfo: NodeAppInfo) => ({ link: this.appConfigService.BaseUrl + '/html/' + appInfo.Name, - app: appInfo + app: appInfo, })); } } diff --git a/imxweb/projects/qbm-app-landingpage/src/app/start/start.component.html b/imxweb/projects/qbm-app-landingpage/src/app/start/start.component.html index f4890badb..b8336ce7e 100644 --- a/imxweb/projects/qbm-app-landingpage/src/app/start/start.component.html +++ b/imxweb/projects/qbm-app-landingpage/src/app/start/start.component.html @@ -18,7 +18,11 @@ {{ '#LDS#Heading Administration Portal' | translate }} -

    {{ '#LDS#Here you can configure your HTML applications. Additionaly, you can reset your changes to default settings.' | translate }}

    +

    + {{ + '#LDS#Here you can configure your HTML applications. Additionaly, you can reset your changes to default settings.' | translate + }} +

    diff --git a/imxweb/projects/qbm-app-landingpage/src/app/start/start.component.scss b/imxweb/projects/qbm-app-landingpage/src/app/start/start.component.scss index 80f2052da..ae48f6773 100644 --- a/imxweb/projects/qbm-app-landingpage/src/app/start/start.component.scss +++ b/imxweb/projects/qbm-app-landingpage/src/app/start/start.component.scss @@ -11,12 +11,17 @@ min-height: 270px; float: left; margin: 10px; - + padding:16px; mat-card-content { - padding: 0px 10px 10px 10px; + padding: 0px 10px 10px 10px !important; + > p { text-align: justify; + font-size:14px; } } + .mat-mdc-card-title{ + font-size: 24px; + } } } diff --git a/imxweb/projects/qbm-app-landingpage/src/app/start/start.component.ts b/imxweb/projects/qbm-app-landingpage/src/app/start/start.component.ts index daff6b514..fa5059769 100644 --- a/imxweb/projects/qbm-app-landingpage/src/app/start/start.component.ts +++ b/imxweb/projects/qbm-app-landingpage/src/app/start/start.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,7 +31,7 @@ import { AppcontainerService, AppContainer } from '../appcontainer.service'; @Component({ selector: 'imx-start', templateUrl: './start.component.html', - styleUrls: ['./start.component.scss'] + styleUrls: ['./start.component.scss'], }) export class StartComponent implements OnInit { public apps: AppContainer[] = []; @@ -39,7 +39,7 @@ export class StartComponent implements OnInit { /** do not display partial results */ public loaded: boolean; - constructor(private appcontainerService: AppcontainerService) { } + constructor(private appcontainerService: AppcontainerService) {} public async ngOnInit(): Promise { this.apps = await this.appcontainerService.getAppContainers(); diff --git a/imxweb/projects/qbm-app-landingpage/src/environments/environment.prod.ts b/imxweb/projects/qbm-app-landingpage/src/environments/environment.prod.ts index 52c300e43..7e86ef2ac 100644 --- a/imxweb/projects/qbm-app-landingpage/src/environments/environment.prod.ts +++ b/imxweb/projects/qbm-app-landingpage/src/environments/environment.prod.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,5 +29,5 @@ export const environment = { production: true, clientUrl: '', appName: packageJson.name, - appVersion: packageJson.version + appVersion: packageJson.version, }; diff --git a/imxweb/projects/qbm-app-landingpage/src/environments/environment.ts b/imxweb/projects/qbm-app-landingpage/src/environments/environment.ts index de9c0390d..1321fa2e3 100644 --- a/imxweb/projects/qbm-app-landingpage/src/environments/environment.ts +++ b/imxweb/projects/qbm-app-landingpage/src/environments/environment.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -35,5 +35,5 @@ export const environment = { production: false, clientUrl: 'http://localhost:8182', appName: packageJson.name, - appVersion: packageJson.version + appVersion: packageJson.version, }; diff --git a/imxweb/projects/qbm-app-landingpage/src/index.html b/imxweb/projects/qbm-app-landingpage/src/index.html index 4b785ba4e..f08db67fd 100644 --- a/imxweb/projects/qbm-app-landingpage/src/index.html +++ b/imxweb/projects/qbm-app-landingpage/src/index.html @@ -1,15 +1,13 @@ + + + + + + - - - - - - - - - - - + + + diff --git a/imxweb/projects/qbm-app-landingpage/src/main.ts b/imxweb/projects/qbm-app-landingpage/src/main.ts index 9fb380327..aa265c318 100644 --- a/imxweb/projects/qbm-app-landingpage/src/main.ts +++ b/imxweb/projects/qbm-app-landingpage/src/main.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -34,5 +34,6 @@ if (environment.production) { enableProdMode(); } -platformBrowserDynamic().bootstrapModule(AppModule) - .catch(err => console.log(err)); +platformBrowserDynamic() + .bootstrapModule(AppModule) + .catch((err) => console.log(err)); diff --git a/imxweb/projects/qbm-app-landingpage/src/polyfills.ts b/imxweb/projects/qbm-app-landingpage/src/polyfills.ts index 64a64e1c0..e38109681 100644 --- a/imxweb/projects/qbm-app-landingpage/src/polyfills.ts +++ b/imxweb/projects/qbm-app-landingpage/src/polyfills.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -41,9 +41,8 @@ */ /*************************************************************************************************** -* BROWSER POLYFILLS -*/ - + * BROWSER POLYFILLS + */ /** IE10 and IE11 requires the following for NgClass support on SVG elements */ // import 'classlist.js'; // Run `npm install --save classlist.js`. @@ -51,16 +50,15 @@ /** IE10 and IE11 requires the following for the Reflect API. */ // import 'core-js/es6/reflect'; - /** Evergreen browsers require these. */ // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. -import 'core-js/es7/reflect'; +import 'core-js/es/reflect'; // Used for support array.includes -import 'core-js/es7/array'; +import 'core-js/es/array'; // Used for support object.values -import 'core-js/es7/object'; +import 'core-js/es/object'; /** * Required to support Web Animations `@angular/platform-browser/animations`. @@ -68,17 +66,13 @@ import 'core-js/es7/object'; */ import 'web-animations-js'; - - /*************************************************************************************************** * Zone JS is required by default for Angular itself. */ -import 'zone.js/dist/zone'; // Included with Angular CLI. - - +import 'zone.js'; // Included with Angular CLI. /*************************************************************************************************** * APPLICATION IMPORTS */ -import 'whatwg-fetch'; import 'url-polyfill'; +import 'whatwg-fetch'; diff --git a/imxweb/projects/qbm-app-landingpage/src/styles.scss b/imxweb/projects/qbm-app-landingpage/src/styles.scss index 2609ec369..ddbe8a4a7 100644 --- a/imxweb/projects/qbm-app-landingpage/src/styles.scss +++ b/imxweb/projects/qbm-app-landingpage/src/styles.scss @@ -1,29 +1,28 @@ /* You can add global styles to this file, and also import other style files */ -@use '@angular/material' as mat; - -$material_icons_font_path: "~node_modules/@elemental-ui/core/assets/MaterialIcons"; -$cadence_font_path: "~node_modules/@elemental-ui/core/assets/Cadence"; -$source_sans_pro_font_path: "~node_modules/@elemental-ui/core/assets/Source_Sans_Pro"; - @import '@elemental-ui/core/src/styles/core.scss'; .eui-dark-theme { - .page { - color: $color-gray-5; - background-color: $color-gray-80; - } - .pageContent { - color: $color-gray-5; - background-color: $color-gray-80; - } - .eui-sidesheet { - .mat-toolbar{ - background-color: $color-blue-80; - } - } - .eui-sidesheet-actions.mat-card.eui-sidesheet-actions--white{ - background-color: $color-gray-70; - } - + .page { + color: $color-gray-5; + background-color: $color-gray-80; + } + .pageContent { + color: $color-gray-5; + background-color: $color-gray-80; + } + .eui-sidesheet-actions.mat-mdc-card.eui-sidesheet-actions--white { + background-color: $color-gray-70; + } + .page { + color: $color-gray-5; + background-color: $color-gray-80; + } + .pageContent { + color: $color-gray-5; + background-color: $color-gray-80; + } + .eui-sidesheet-actions.mat-mdc-card.eui-sidesheet-actions--white { + background-color: $color-gray-70; + } } .eui-contrast-theme { @@ -35,13 +34,7 @@ $source_sans_pro_font_path: "~node_modules/@elemental-ui/core/assets/Source_Sans color: $color-gray-0; background-color: $color-gray-100; } - .eui-sidesheet { - .mat-toolbar{ - background-color: $color-blue-80; - } - } - .eui-sidesheet-actions.mat-card.eui-sidesheet-actions--white{ - background-color: $color-gray-90; + .eui-sidesheet-actions.mat-mdc-card.eui-sidesheet-actions--white { + background-color: $color-gray-90; } - } diff --git a/imxweb/projects/qbm-app-landingpage/src/test.ts b/imxweb/projects/qbm-app-landingpage/src/test.ts index 5b533ce8e..a0cdd3bfc 100644 --- a/imxweb/projects/qbm-app-landingpage/src/test.ts +++ b/imxweb/projects/qbm-app-landingpage/src/test.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,16 +26,13 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone-testing'; +// Zone must be imported first. Be careful with prettier import organizing +import 'zone.js'; +import 'zone.js/testing'; + import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; import { TestHelperModule } from 'qbm'; -declare const require: any; - // First, initialize the Angular testing environment. getTestBed().initTestEnvironment([BrowserDynamicTestingModule, TestHelperModule], platformBrowserDynamicTesting()); -// Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); -// And load the modules. -context.keys().map(context); diff --git a/imxweb/projects/qbm-app-landingpage/tsconfig-es5.app.json b/imxweb/projects/qbm-app-landingpage/tsconfig-es5.app.json index e1d0d6abd..036b8797e 100644 --- a/imxweb/projects/qbm-app-landingpage/tsconfig-es5.app.json +++ b/imxweb/projects/qbm-app-landingpage/tsconfig-es5.app.json @@ -1,6 +1,6 @@ { - "extends": "./tsconfig.app.json", - "compilerOptions": { - "target": "es2020" + "extends": "./tsconfig.app.json", + "compilerOptions": { + "target": "es2020" } -} \ No newline at end of file +} diff --git a/imxweb/projects/qbm-app-landingpage/tsconfig.app.json b/imxweb/projects/qbm-app-landingpage/tsconfig.app.json index 190ca1d58..b7c805879 100644 --- a/imxweb/projects/qbm-app-landingpage/tsconfig.app.json +++ b/imxweb/projects/qbm-app-landingpage/tsconfig.app.json @@ -1,14 +1,13 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "strictNullChecks": true, "outDir": "../../out-tsc/app", "types": [] }, - "files": [ - "src/main.ts", - "src/polyfills.ts" - ], - "include": [ - "projects/qbm-app-landingpage/src/**/*.d.ts" - ] + "angularCompilerOptions": { + "strictTemplates": true + }, + "files": ["src/main.ts", "src/polyfills.ts"], + "include": ["projects/qbm-app-landingpage/src/**/*.d.ts"] } diff --git a/imxweb/projects/qbm-app-landingpage/tsconfig.spec.json b/imxweb/projects/qbm-app-landingpage/tsconfig.spec.json index 4260637ec..eb1ff8a3b 100644 --- a/imxweb/projects/qbm-app-landingpage/tsconfig.spec.json +++ b/imxweb/projects/qbm-app-landingpage/tsconfig.spec.json @@ -2,18 +2,8 @@ "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "../../out-tsc/spec", - "types": [ - "jasmine", - "node", - "karma-viewport" - ] + "types": ["jasmine", "node", "karma-viewport"] }, - "files": [ - "src/test.ts", - "src/polyfills.ts" - ], - "include": [ - "**/*.spec.ts", - "**/*.d.ts" - ] + "files": ["src/test.ts", "src/polyfills.ts"], + "include": ["**/*.spec.ts", "**/*.d.ts"] } diff --git a/imxweb/projects/qbm-app-landingpage/tslint.json b/imxweb/projects/qbm-app-landingpage/tslint.json deleted file mode 100644 index ef4889cbe..000000000 --- a/imxweb/projects/qbm-app-landingpage/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "imx", - "camelCase" - ], - "component-selector": [ - true, - "element", - "imx", - "kebab-case" - ] - } -} diff --git a/imxweb/projects/qbm/.compodocrc.json b/imxweb/projects/qbm/.compodocrc.json index 60cb1367c..2b41d9a4e 100644 --- a/imxweb/projects/qbm/.compodocrc.json +++ b/imxweb/projects/qbm/.compodocrc.json @@ -1,6 +1,6 @@ { "name": "IMX Web - QBM Library", - "output": "../../documentation/v92/qbm", + "output": "../../documentation/v93/qbm", "assetsFolder": "../../compodoc/assets", "theme": "material", "customLogo": "../../compodoc/assets/images/oneidentity-logo.png", diff --git a/imxweb/projects/qbm/.eslintrc.json b/imxweb/projects/qbm/.eslintrc.json new file mode 100644 index 000000000..e384394c6 --- /dev/null +++ b/imxweb/projects/qbm/.eslintrc.json @@ -0,0 +1,35 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*", "**/*.spec.ts"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": ["imxweb/projects/qbm/tsconfig.lib.json", "imxweb/projects/qbm/tsconfig.spec.json"], + "createDefaultProgram": true + }, + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "imx", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "imx", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "rules": {} + } + ] +} diff --git a/imxweb/projects/qbm/karma.conf.js b/imxweb/projects/qbm/karma.conf.js index a0a6c62fd..efaf207a9 100644 --- a/imxweb/projects/qbm/karma.conf.js +++ b/imxweb/projects/qbm/karma.conf.js @@ -13,10 +13,10 @@ module.exports = function (config) { require('karma-coverage-istanbul-reporter'), require('@angular-devkit/build-angular/plugins/karma'), require('karma-junit-reporter'), - require('karma-viewport') + require('karma-viewport'), ], client: { - clearContext: false // leave Jasmine Spec Runner output visible in browser + clearContext: false, // leave Jasmine Spec Runner output visible in browser }, coverageIstanbulReporter: { dir: require('path').join(__dirname, 'results'), @@ -24,7 +24,7 @@ module.exports = function (config) { fixWebpackSourcePaths: true, 'report-config': { html: { - subdir: 'coverage-html' + subdir: 'coverage-html', }, }, thresholds: { @@ -42,41 +42,40 @@ module.exports = function (config) { port: 9876, captureTimeout: 210000, browserDisconnectTolerance: 3, - browserDisconnectTimeout : 210000, - browserNoActivityTimeout : 210000, + browserDisconnectTimeout: 210000, + browserNoActivityTimeout: 210000, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], singleRun: false, - files: [ - '../../node_modules/hammerjs/hammer.min.js' - ], + failOnEmptyTestSuite: false, + files: ['../../node_modules/hammerjs/hammer.min.js'], // Viewport configuration viewport: { breakpoints: [ { - name: "mobile", + name: 'mobile', size: { width: 760, - height: 1000 - } + height: 1000, + }, }, { - name: "tablet", + name: 'tablet', size: { width: 1024, - height: 1000 - } + height: 1000, + }, }, { - name: "small-desktop", + name: 'small-desktop', size: { width: 1200, - height: 1000 - } - } - ] - } + height: 1000, + }, + }, + ], + }, }); }; diff --git a/imxweb/projects/qbm/ng-package.json b/imxweb/projects/qbm/ng-package.json index 939729306..e7a7a8764 100644 --- a/imxweb/projects/qbm/ng-package.json +++ b/imxweb/projects/qbm/ng-package.json @@ -3,7 +3,7 @@ "dest": "../../dist/qbm", "lib": { "entryFile": "src/public_api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] }, "deleteDestPath": false } diff --git a/imxweb/projects/qbm/package.json b/imxweb/projects/qbm/package.json index b7dcf1abb..de818547a 100644 --- a/imxweb/projects/qbm/package.json +++ b/imxweb/projects/qbm/package.json @@ -1,13 +1,9 @@ { "name": "qbm", - "version": "9.2.1", + "version": "9.3.0", "private": true, - "dependencies": { - - }, - "peerDependencies": { - - }, + "dependencies": {}, + "peerDependencies": {}, "bundledDependencies": [ "imx-api-qbm" ] diff --git a/imxweb/projects/qbm/project.json b/imxweb/projects/qbm/project.json new file mode 100644 index 000000000..8fb808af0 --- /dev/null +++ b/imxweb/projects/qbm/project.json @@ -0,0 +1,60 @@ +{ + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "name": "qbm", + "sourceRoot": "projects/qbm/src", + "projectType": "library", + "prefix": "imx", + "generators": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:ng-packagr", + "options": { + "tsConfig": "projects/qbm/tsconfig.lib.json", + "project": "projects/qbm/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "projects/qbm/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "projects/qbm/tsconfig.lib.json" + }, + "remote-dev": { + "tsConfig": "projects/qbm/tsconfig.lib.json" + }, + "remote-qs": { + "tsConfig": "projects/qbm/tsconfig.lib.json" + } + }, + "outputs": ["{workspaceRoot}/dist/qbm"] + }, + "test": { + "executor": "@angular-devkit/build-angular:karma", + "options": { + "stylePreprocessorOptions": { + "includePaths": [ + "./shared/assets", + "./shared/scss", + "./node_modules", + "./node_modules/@elemental-ui/cadence-icon", + "./node_modules/@elemental-ui/core" + ] + }, + "main": "projects/qbm/src/test.ts", + "tsConfig": "projects/qbm/tsconfig.spec.json", + "karmaConfig": "projects/qbm/karma.conf.js" + } + }, + "lint": { + "executor": "@angular-devkit/build-angular:eslint", + "options": { + "tsConfig": ["projects/qbm/tsconfig.lib.json", "projects/qbm/tsconfig.spec.json"], + "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + } + } + } +} diff --git a/imxweb/projects/qbm/src/default-mocks.spec.ts b/imxweb/projects/qbm/src/default-mocks.spec.ts index 178ff600c..be007fd96 100644 --- a/imxweb/projects/qbm/src/default-mocks.spec.ts +++ b/imxweb/projects/qbm/src/default-mocks.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -32,7 +32,7 @@ import { RouteGuardService } from './lib/route-guard/route-guard.service'; import { ISessionState } from './lib/session/session-state'; import { AuthenticationService } from './lib/authentication/authentication.service'; import { CacheService } from './lib/cache/cache.service'; -import { CachedPromise } from 'imx-qbm-dbts'; +import { CachedPromise } from '@imx-modules/imx-qbm-dbts'; export class QbmDefaultMocks { public static readonly afterClosedSubject = new Subject(); @@ -44,8 +44,8 @@ export class QbmDefaultMocks { public static readonly sidesheetRefStub = { close: jasmine.createSpy('close'), - closeClicked: jasmine.createSpy('closeClicked').and.returnValue(of(undefined)) -} + closeClicked: jasmine.createSpy('closeClicked').and.returnValue(of(undefined)), + }; public static readonly authServiceStub = { onSessionResponse: new Subject(), @@ -59,11 +59,11 @@ export class QbmDefaultMocks { ngMocks.defaultMock(TranslateService, () => ({ get: jasmine.createSpy('get').and.returnValue(of()), })); - ngMocks.defaultMock(EuiSidesheetRef,()=> QbmDefaultMocks.sidesheetRefStub); + ngMocks.defaultMock(EuiSidesheetRef, () => QbmDefaultMocks.sidesheetRefStub); ngMocks.defaultMock(CacheService, () => ({ buildCache: (func) => { return new CachedPromise(func); - } + }, })); } } diff --git a/imxweb/projects/qbm/src/lib/about/About.component.html b/imxweb/projects/qbm/src/lib/about/About.component.html index 9ed2ab9ae..1118a2f13 100644 --- a/imxweb/projects/qbm/src/lib/about/About.component.html +++ b/imxweb/projects/qbm/src/lib/about/About.component.html @@ -1,5 +1,5 @@ - + @@ -24,20 +24,24 @@ {{ '#LDS#Third-Party Contributions' | translate }}
    {{ '#LDS#Source code for components marked with an asterisk (*) is available at' | translate }} - {{product['OpenSourceUrl']}} + {{ product['OpenSourceUrl'] }} .
    - +
    @@ -63,11 +67,10 @@ {{ '#LDS#System information' | translate }} - - + - - + + diff --git a/imxweb/projects/qbm/src/lib/about/About.component.scss b/imxweb/projects/qbm/src/lib/about/About.component.scss index b0e8ad0a0..3413d106a 100644 --- a/imxweb/projects/qbm/src/lib/about/About.component.scss +++ b/imxweb/projects/qbm/src/lib/about/About.component.scss @@ -1,5 +1,5 @@ -@import "variables.scss"; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/variables'; ::ng-deep { .imx-AboutPanel { @@ -42,7 +42,7 @@ .imx-productNameVersion { padding: 15px 0; margin: 30px 20px; - border: solid $VI_Common_Color_Gray; + border: solid $color-gray-60; border-width: 1px 0; text-align: center; } @@ -61,88 +61,68 @@ /* avoid scrollbars caused by the rotating image going out of the box */ overflow: hidden; } +} - ::ng-deep .mat-tab-label { - background-color: $white; - } - - .paragraphLargeMargin { - margin-bottom: 40px; - } - - .mat-dialog-content { - max-height: initial; - } - - .imx-legalNotice-Header { - margin-top: 10px; - margin-bottom: 6px; - font-family: $baseFontFamily; - } +.paragraphLargeMargin { + margin-bottom: 40px; +} - .imx-legalNotice { - height: 100%; - display: flex; - overflow: hidden; - flex-direction: column; - } +.mat-mdc-dialog-content { + max-height: initial; +} - .imx-legalNotice-Header > span { - font-size: 1em; - font-weight: bolder; - } +.imx-legalNotice-Header { + margin-top: 10px; + margin-bottom: 6px; + font-family: $IMX_Base_FontFamily; +} - .imx-legalNotice-Content > span { - font-size: 1em; - font-family: $baseFontFamily; - margin-right: 4px; - } +.imx-legalNotice { + height: 100%; + display: flex; + overflow: hidden; + flex-direction: column; +} - .imx-contact > div > div { - padding-top: 5px; - } +.imx-legalNotice-Header > span { + font-size: 1em; + font-weight: bolder; +} - .imx-contact-Header { - margin-top: 10px; - padding-top: 15px; - padding-bottom: 20px; - font-family: $baseFontFamily; - } +.imx-legalNotice-Content > span { + font-size: 1em; + font-family: $IMX_Base_FontFamily; + margin-right: 4px; +} - .imx-contact-Header > span { - font-size: 1.6em; - } +.imx-contact > div > div { + padding-top: 5px; +} - button { - margin-bottom: 20px; - margin-right: 10px; - } +.imx-contact-Header { + margin-top: 10px; + padding-top: 15px; + padding-bottom: 20px; + font-family: $IMX_Base_FontFamily; +} - .mat-card { - box-shadow: none; - } +.imx-contact-Header > span { + font-size: 1.6em; +} - imx-data-source-toolbar { - display: none; - } +button { + margin-bottom: 20px; + margin-right: 10px; +} - .imx-about-tabs { - height: 100%; - } +.mat-mdc-card { + box-shadow: none; } -.eui-dark-theme { - :host { - ::ng-deep .mat-tab-label { - background-color: $color-gray-80; - } - } +imx-data-source-toolbar { + display: none; } -.eui-contrast-theme { - :host { - ::ng-deep .mat-tab-label { - background-color: $color-gray-100; - } - } +.imx-about-tabs { + height: 100%; } diff --git a/imxweb/projects/qbm/src/lib/about/About.component.ts b/imxweb/projects/qbm/src/lib/about/About.component.ts index b4e706234..f98239533 100644 --- a/imxweb/projects/qbm/src/lib/about/About.component.ts +++ b/imxweb/projects/qbm/src/lib/about/About.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,23 +25,21 @@ */ import { Component, HostBinding, OnInit } from '@angular/core'; -import { OverlayRef } from '@angular/cdk/overlay'; import { EuiLoadingService } from '@elemental-ui/core'; -import { AboutService } from './About.service'; -import { ExtService } from '../ext/ext.service'; -import { Globals, CollectionLoadParameters, EntitySchema, IClientProperty } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, EntitySchema, Globals, IClientProperty } from '@imx-modules/imx-qbm-dbts'; import { DataSourceToolbarSettings } from '../data-source-toolbar/data-source-toolbar-settings'; +import { ExtService } from '../ext/ext.service'; import { SettingsService } from '../settings/settings-service'; import { SystemInfoService } from '../system-info/system-info.service'; +import { AboutService } from './About.service'; @Component({ templateUrl: './About.component.html', styleUrls: ['./About.component.scss'], - selector: 'imx-about' + selector: 'imx-about', }) export class AboutComponent implements OnInit { - @HostBinding('class') public defaultHostClasses = 'imx-flex imx-flex-child'; @@ -58,14 +56,14 @@ export class AboutComponent implements OnInit { private readonly systemInfoService: SystemInfoService, private extService: ExtService, private readonly settings: SettingsService, - private readonly busyService: EuiLoadingService + private readonly busyService: EuiLoadingService, ) { this.entitySchema = aboutInfoService.EntitySchema; this.displayedColumns = [ this.entitySchema.Columns['ComponentName'], this.entitySchema.Columns['CopyRight'], this.entitySchema.Columns['EmailOrURl'], - this.entitySchema.Columns['LicenceName'] + this.entitySchema.Columns['LicenceName'], ]; this.product['Name'] = Globals.QIM_ProductNameFull; this.product['Version'] = Globals.Version; @@ -81,8 +79,7 @@ export class AboutComponent implements OnInit { public async ngOnInit(): Promise { const imxConfig = await this.systemInfoService.getImxConfig(); const name = imxConfig.ProductName; - if (name) - this.product['Name'] = name; + if (name) this.product['Name'] = name; await this.update({ PageSize: this.settings.DefaultPageSize, StartIndex: 0, OrderBy: 'ComponentName' }); } @@ -96,18 +93,21 @@ export class AboutComponent implements OnInit { this.parameters = parameters; } - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } try { const data = await this.aboutInfoService.get(this.parameters); - this.dstSettings = { - dataSource: data, - entitySchema: this.entitySchema, - navigationState: this.parameters, - displayedColumns: this.displayedColumns - }; + if (data) { + this.dstSettings = { + dataSource: data, + entitySchema: this.entitySchema, + navigationState: this.parameters, + displayedColumns: this.displayedColumns, + }; + } } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } } } diff --git a/imxweb/projects/qbm/src/lib/about/About.service.ts b/imxweb/projects/qbm/src/lib/about/About.service.ts index e0ad80ec1..3df0feaa0 100644 --- a/imxweb/projects/qbm/src/lib/about/About.service.ts +++ b/imxweb/projects/qbm/src/lib/about/About.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,24 +24,46 @@ * */ -import { Injectable } from '@angular/core'; +import { Injectable, OnDestroy } from '@angular/core'; -import { ImxSysteminfoThirdparty } from 'imx-api-qbm'; -import { imx_SessionService } from '../session/imx-session.service'; -import { TypedEntityCollectionData, CollectionLoadParameters, EntitySchema } from 'imx-qbm-dbts'; +import { AdminSysteminfoThirdparty } from '@imx-modules/imx-api-qbm'; +import { CollectionLoadParameters, EntitySchema, ExtendedTypedEntityCollection, TypedEntity } from '@imx-modules/imx-qbm-dbts'; -@Injectable({ - providedIn: 'root' -}) -export class AboutService { - constructor(private readonly session: imx_SessionService) { } +// Create a more general type from either admin or ops since it is available in qbm - use this in qer later +export type SysteminfoThirdparty = { [K in keyof AdminSysteminfoThirdparty]: AdminSysteminfoThirdparty[K] } & TypedEntity; - public get EntitySchema(): EntitySchema { - return this.session.TypedClient.ImxSysteminfoThirdparty.GetSchema(); +/** + * Abstract implementation for getting portal specific metadata. + */ +@Injectable() +export abstract class AboutService implements OnDestroy { + protected abortController: AbortController; + constructor() { + this.abortController = new AbortController(); + } + + ngOnDestroy(): void { + this.abortCall(); } - - public async get(parameters: CollectionLoadParameters = {}): - Promise> { - return this.session.TypedClient.ImxSysteminfoThirdparty.Get(parameters); + + /** + * Handles aborting any current requests managed by this service. + */ + public abortCall(): void { + this.abortController.abort(); + this.abortController = new AbortController(); } + + /** + * Abstract getter for exposing the entity schema via the service. + */ + abstract get EntitySchema(): EntitySchema; + + /** + * Abstract method for getting data from the server + * @param parameters Additional request parameters for the method + */ + abstract get( + parameters?: CollectionLoadParameters, + ): Promise | null | undefined>; } diff --git a/imxweb/projects/qbm/src/lib/admin/about/admin-about.service.ts b/imxweb/projects/qbm/src/lib/admin/about/admin-about.service.ts new file mode 100644 index 000000000..048d81f0c --- /dev/null +++ b/imxweb/projects/qbm/src/lib/admin/about/admin-about.service.ts @@ -0,0 +1,56 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Injectable } from '@angular/core'; +import { AdminSysteminfoThirdparty } from '@imx-modules/imx-api-qbm'; +import { CollectionLoadParameters, EntitySchema, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; +import { AboutService } from '../../about/About.service'; +import { ApiClientService } from '../../api-client/api-client.service'; +import { imx_SessionService } from '../../session/imx-session.service'; + +@Injectable({ + providedIn: 'root', +}) +export class AdminAboutService extends AboutService { + constructor( + private session: imx_SessionService, + private readonly apiProvider: ApiClientService, + ) { + super(); + } + + get EntitySchema(): EntitySchema { + return this.session.TypedClient.AdminSysteminfoThirdparty.GetSchema(); + } + + async get(parameters?: CollectionLoadParameters): Promise | undefined> { + return this.apiProvider.request(() => + this.session.TypedClient.AdminSysteminfoThirdparty.Get(parameters, { + signal: this.abortController.signal, + }), + ); + } +} diff --git a/imxweb/projects/qbm/src/lib/admin/add-config-sidesheet.component.html b/imxweb/projects/qbm/src/lib/admin/add-config-sidesheet.component.html index c98144ecd..1d34af9e0 100644 --- a/imxweb/projects/qbm/src/lib/admin/add-config-sidesheet.component.html +++ b/imxweb/projects/qbm/src/lib/admin/add-config-sidesheet.component.html @@ -1,8 +1,7 @@
    - - - {{'#LDS#Select where to add the configuration key' | translate}} + + {{ '#LDS#Select where to add the configuration key' | translate }} @@ -12,18 +11,25 @@ - {{selectedKey.Description}} + {{ selectedKey.Description }} - - {{'#LDS#Name of the new configuration key' | translate}} - + + {{ '#LDS#Name of the new configuration key' | translate }} +
    -
    - +
    +
    diff --git a/imxweb/projects/qbm/src/lib/admin/add-config-sidesheet.component.scss b/imxweb/projects/qbm/src/lib/admin/add-config-sidesheet.component.scss index 1db9ee0e1..a9388aef4 100644 --- a/imxweb/projects/qbm/src/lib/admin/add-config-sidesheet.component.scss +++ b/imxweb/projects/qbm/src/lib/admin/add-config-sidesheet.component.scss @@ -1,13 +1,4 @@ -eui-alert { - margin-bottom: 1em; - display: block; -} - .description { padding-left: 2em; margin-bottom: 1em; } - -mat-form-field { - width: 100%; -} diff --git a/imxweb/projects/qbm/src/lib/admin/add-config-sidesheet.component.ts b/imxweb/projects/qbm/src/lib/admin/add-config-sidesheet.component.ts index 73555f076..26338ae8d 100644 --- a/imxweb/projects/qbm/src/lib/admin/add-config-sidesheet.component.ts +++ b/imxweb/projects/qbm/src/lib/admin/add-config-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,30 +24,30 @@ * */ -import { Component, OnInit } from "@angular/core"; -import { EuiLoadingService, EuiSidesheetRef } from "@elemental-ui/core"; -import { SnackBarService } from "../snackbar/snack-bar.service"; -import { KeyData } from "./config-section"; -import { ConfigService } from "./config.service"; +import { Component, OnInit } from '@angular/core'; +import { EuiLoadingService, EuiSidesheetRef } from '@elemental-ui/core'; +import { SnackBarService } from '../snackbar/snack-bar.service'; +import { KeyData } from './config-section'; +import { ConfigService } from './config.service'; @Component({ templateUrl: './add-config-sidesheet.component.html', - styleUrls: ['./add-config-sidesheet.component.scss'] + styleUrls: ['./add-config-sidesheet.component.scss'], }) export class AddConfigSidesheetComponent implements OnInit { - - constructor(private readonly configSvc: ConfigService, + constructor( + private readonly configSvc: ConfigService, private readonly sideSheetRef: EuiSidesheetRef, private readonly busySvc: EuiLoadingService, - private readonly snackbar: SnackBarService - ) { } + private readonly snackbar: SnackBarService, + ) {} keyData: KeyData[]; selectedKey: KeyData; newKey: string; ngOnInit(): void { - this.keyData = this.configSvc.sections.map(s => s.SettingsSupportingAdd).reduce((a, b) => a.concat(b), []); + this.keyData = this.configSvc.sections.map((s) => s.SettingsSupportingAdd).reduce((a, b) => a.concat(b), []); } public isGlobal: boolean = false; @@ -55,13 +55,13 @@ export class AddConfigSidesheetComponent implements OnInit { public async submit(): Promise { const overlay = this.busySvc.show(); try { - await this.configSvc.addKey(this.selectedKey.Path + "/" + this.newKey); + await this.configSvc.addKey(this.selectedKey.Path + '/' + this.newKey); await this.configSvc.load(); - const key = "#LDS#The configuration key has been successfully created."; - this.snackbar.open({key}); + const key = '#LDS#The configuration key has been successfully created.'; + this.snackbar.open({ key }); } finally { this.busySvc.hide(overlay); } this.sideSheetRef.close(); } -} \ No newline at end of file +} diff --git a/imxweb/projects/qbm/src/lib/admin/admin-component.interface.ts b/imxweb/projects/qbm/src/lib/admin/admin-component.interface.ts index 0e232bf62..aa41ac3ce 100644 --- a/imxweb/projects/qbm/src/lib/admin/admin-component.interface.ts +++ b/imxweb/projects/qbm/src/lib/admin/admin-component.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,8 +24,6 @@ * */ - - export interface AdminComponent { isAdmin?: boolean; -} \ No newline at end of file +} diff --git a/imxweb/projects/qbm/src/lib/admin/admin-routes.ts b/imxweb/projects/qbm/src/lib/admin/admin-routes.ts index 5d31cf185..6742feb6b 100644 --- a/imxweb/projects/qbm/src/lib/admin/admin-routes.ts +++ b/imxweb/projects/qbm/src/lib/admin/admin-routes.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,19 +26,19 @@ import { Routes } from '@angular/router'; import { RouteGuardService } from '../route-guard/route-guard.service'; -import { DashboardComponent } from "./dashboard.component"; +import { DashboardComponent } from './dashboard.component'; export const AdminRoutes: Routes = [ - { - path: 'admin', - component: DashboardComponent, - canActivate: [RouteGuardService], - resolve: [RouteGuardService] - }, - { - path: 'admin/:tab', - component: DashboardComponent, - canActivate: [RouteGuardService], - resolve: [RouteGuardService] - }, + { + path: 'admin', + component: DashboardComponent, + canActivate: [RouteGuardService], + resolve: [RouteGuardService], + }, + { + path: 'admin/:tab', + component: DashboardComponent, + canActivate: [RouteGuardService], + resolve: [RouteGuardService], + }, ]; diff --git a/imxweb/projects/qbm/src/lib/admin/admin.module.ts b/imxweb/projects/qbm/src/lib/admin/admin.module.ts index cc1bcb6e2..4c5dded04 100644 --- a/imxweb/projects/qbm/src/lib/admin/admin.module.ts +++ b/imxweb/projects/qbm/src/lib/admin/admin.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -42,11 +42,19 @@ import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTreeModule } from '@angular/material/tree'; import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; import { TranslateModule } from '@ngx-translate/core'; +import { AboutService } from '../about/About.service'; +import { QbmSqlWizardService } from '../base/qbm-sqlwizard.service'; +import { BusyIndicatorModule } from '../busy-indicator/busy-indicator.module'; import { DataSourceToolbarModule } from '../data-source-toolbar/data-source-toolbar.module'; import { DateModule } from '../date/date.module'; import { LdsReplaceModule } from '../lds-replace/lds-replace.module'; +import { SideNavigationViewModule } from '../side-navigation-view/side-navigation-view.module'; +import { SqlWizardApiService } from '../sqlwizard/sqlwizard-api.service'; +import { InfoModalDialogModule } from './../info-modal-dialog/info-modal-dialog.module'; +import { AdminAboutService } from './about/admin-about.service'; import { AddConfigSidesheetComponent } from './add-config-sidesheet.component'; import { ApplyConfigSidesheetComponent } from './apply-config-sidesheet.component'; +import { CacheComponent } from './cache.component'; import { ConfigKeyPathComponent } from './config-key-path.component'; import { ConfigComponent } from './config.component'; import { ConfigService } from './config.service'; @@ -57,17 +65,11 @@ import { ListSettingComponent } from './list-setting.component'; import { LogDetailsSidesheetComponent } from './log-details-sidesheet.component'; import { LogsComponent } from './logs.component'; import { PackagesComponent } from './packages.component'; +import { PluginsComponent } from './plugins.component'; import { SelectValueComponent } from './select-value.component'; import { StatusComponent } from './status.component'; import { StatusService } from './status.service'; import { SwaggerComponent } from './swagger/swagger.component'; -import { CacheComponent } from './cache.component'; -import { PluginsComponent } from './plugins.component'; -import { InfoModalDialogModule } from './../info-modal-dialog/info-modal-dialog.module'; -import { SqlWizardApiService } from '../sqlwizard/sqlwizard-api.service'; -import { QbmSqlWizardService } from '../base/qbm-sqlwizard.service'; -import { SideNavigationViewModule } from '../side-navigation-view/side-navigation-view.module'; - @NgModule({ imports: [ CommonModule, @@ -93,6 +95,7 @@ import { SideNavigationViewModule } from '../side-navigation-view/side-navigatio ScrollingModule, InfoModalDialogModule, SideNavigationViewModule, + BusyIndicatorModule, ], providers: [ ConfigService, @@ -101,6 +104,10 @@ import { SideNavigationViewModule } from '../side-navigation-view/side-navigatio provide: SqlWizardApiService, useClass: QbmSqlWizardService, }, + { + provide: AboutService, + useClass: AdminAboutService, + }, ], declarations: [ AddConfigSidesheetComponent, diff --git a/imxweb/projects/qbm/src/lib/admin/apply-config-sidesheet.component.html b/imxweb/projects/qbm/src/lib/admin/apply-config-sidesheet.component.html index 58d278da1..877144404 100644 --- a/imxweb/projects/qbm/src/lib/admin/apply-config-sidesheet.component.html +++ b/imxweb/projects/qbm/src/lib/admin/apply-config-sidesheet.component.html @@ -1,9 +1,10 @@
    - - +
    - {{'#LDS#You have changed the following configuration settings.' | translate}} - {{'#LDS#Select how the configuration changes should be applied.' | translate}} + {{ '#LDS#You have changed the following configuration settings.' | translate }} + {{ '#LDS#Select how the configuration changes should be applied.' | translate }}
      @@ -14,18 +15,19 @@ - {{ '#LDS#Apply locally' | translate}} + {{ '#LDS#Apply locally' | translate }} -

      {{ (supportsLocalCustomizations ? LdsApplyLocally : LdsApplyLocallyNotPossible) | translate}}

      +

      {{ (supportsLocalCustomizations ? LdsApplyLocally : LdsApplyLocallyNotPossible) | translate }}

      - {{'#LDS#Apply globally' | translate}} + {{ '#LDS#Apply globally' | translate }} -

      {{LdsApplyGlobally | translate}}

      +

      {{ LdsApplyGlobally | translate }}

      - +
    - - - +
    + +
    diff --git a/imxweb/projects/qbm/src/lib/admin/apply-config-sidesheet.component.scss b/imxweb/projects/qbm/src/lib/admin/apply-config-sidesheet.component.scss index a152cc270..eeec17c20 100644 --- a/imxweb/projects/qbm/src/lib/admin/apply-config-sidesheet.component.scss +++ b/imxweb/projects/qbm/src/lib/admin/apply-config-sidesheet.component.scss @@ -1,35 +1,13 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; -@import '@elemental-ui/core/src/styles/_eui_palette.scss'; - .admin-config-apply-sidesheet { - background-color: $asher-gray; - padding: 2em; height: 100%; display: flex; flex-direction: column; overflow: hidden; - - &__action-buttons { - display: flex; - justify-content: flex-end; - background-color: $white; - padding: 16px 32px; - margin: 16px 0; - - .justify-start { - margin-right: auto; - } - - button:not(:last-of-type):not(.justify-start) { - margin-right: 10px; - } + .mat-mdc-radio-group{ + display: block; } } -mat-radio-button { - font-weight: bolder; -} - ul { margin: 1em; max-height: 200px; @@ -44,30 +22,3 @@ li { padding-left: 2em; margin-bottom: 1em; } - - -.eui-dark-theme { - :host { - .admin-config-apply-sidesheet - { - background-color: $color-gray-80; - - &__action-buttons { - background-color: $color-gray-70; - } - } - } -} - -.eui-contrast-theme { - :host { - .admin-config-apply-sidesheet - { - background-color: $color-gray-100; - - &__action-buttons { - background-color: $color-gray-90; - } - } - } -} diff --git a/imxweb/projects/qbm/src/lib/admin/apply-config-sidesheet.component.ts b/imxweb/projects/qbm/src/lib/admin/apply-config-sidesheet.component.ts index 048e1f9f4..518b121a5 100644 --- a/imxweb/projects/qbm/src/lib/admin/apply-config-sidesheet.component.ts +++ b/imxweb/projects/qbm/src/lib/admin/apply-config-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,19 +24,19 @@ * */ -import { Component } from "@angular/core"; -import { EuiSidesheetRef } from "@elemental-ui/core"; -import { ConfigService } from "./config.service"; +import { Component } from '@angular/core'; +import { EuiSidesheetRef } from '@elemental-ui/core'; +import { ConfigService } from './config.service'; @Component({ templateUrl: './apply-config-sidesheet.component.html', - styleUrls: ['./apply-config-sidesheet.component.scss'] + styleUrls: ['./apply-config-sidesheet.component.scss'], }) export class ApplyConfigSidesheetComponent { - - constructor(private readonly configSvc: ConfigService, + constructor( + private readonly configSvc: ConfigService, private readonly sideSheetRef: EuiSidesheetRef, - ) { + ) { this.isGlobal = !configSvc.supportsLocalCustomization; } @@ -51,13 +51,15 @@ export class ApplyConfigSidesheetComponent { this.sideSheetRef.close(); } - public LdsApplyLocally = '#LDS#Use this setting if you want to try configuration changes only on this server. The changes are reset when you restart the server.'; + public LdsApplyLocally = + '#LDS#Use this setting if you want to try configuration changes only on this server. The changes are reset when you restart the server.'; - public LdsApplyGlobally = '#LDS#Use this setting if you want to apply the configuration changes globally. The changes are stored in the global configuration file and distributed to all API servers.'; + public LdsApplyGlobally = + '#LDS#Use this setting if you want to apply the configuration changes globally. The changes are stored in the global configuration file and distributed to all API servers.'; public LdsApplyLocallyNotPossible = '#LDS#This option has been disabled by your administrator.'; - public get pendingChanges(): string[][] { + public get pendingChanges(): (string | undefined)[][] { return this.configSvc.getPendingChanges(); } -} \ No newline at end of file +} diff --git a/imxweb/projects/qbm/src/lib/admin/cache.component.html b/imxweb/projects/qbm/src/lib/admin/cache.component.html index cd19fe5f6..bb8c8a92c 100644 --- a/imxweb/projects/qbm/src/lib/admin/cache.component.html +++ b/imxweb/projects/qbm/src/lib/admin/cache.component.html @@ -1,13 +1,13 @@ -
    -

    #LDS#Heading Caches

    +
    +

    #LDS#Heading Caches

    - + @@ -15,7 +15,7 @@

    #LDS#Heading Caches

    - + @@ -35,17 +35,17 @@

    #LDS#Heading Caches

    {{'#LDS#Name' | translate}}{{ '#LDS#Name' | translate }} {{ '#LDS#Cached items' | translate }} {{ '#LDS#Cache hits' | translate }}
    - {{d}} + {{ d }} {{ cacheData[d].ObjectCount }} @@ -27,7 +27,7 @@

    #LDS#Heading Caches

    {{'#LDS#Total' | translate}}{{ '#LDS#Total' | translate }} {{ totalEntries }} {{ totalHits }}
    -
    - - -
    diff --git a/imxweb/projects/qbm/src/lib/admin/cache.component.ts b/imxweb/projects/qbm/src/lib/admin/cache.component.ts index 4bcb6dd40..f1888d80f 100644 --- a/imxweb/projects/qbm/src/lib/admin/cache.component.ts +++ b/imxweb/projects/qbm/src/lib/admin/cache.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,56 +24,71 @@ * */ -import { Component, OnInit, Input } from "@angular/core"; -import { CacheData } from "imx-api-qbm"; -import { AppConfigService } from "../appConfig/appConfig.service"; -import { SideNavigationComponent } from "../side-navigation-view/side-navigation-view-interfaces"; +import { Component, Input, OnInit } from '@angular/core'; +import { CacheData } from '@imx-modules/imx-api-qbm'; +import { AppConfigService } from '../appConfig/appConfig.service'; +import { ConfirmationService } from '../confirmation/confirmation.service'; +import { MessageParameter } from '../message-dialog/message-parameter.interface'; +import { SideNavigationComponent } from '../side-navigation-view/side-navigation-view-interfaces'; type T = CacheData & { key: string }; @Component({ templateUrl: './cache.component.html', styleUrls: ['./shared.scss'], - selector: 'imx-cache' + selector: 'imx-cache', }) export class CacheComponent implements OnInit, SideNavigationComponent { @Input() public isAdmin: boolean; - constructor(private readonly appConfigService: AppConfigService) { } - + constructor( + private readonly appConfigService: AppConfigService, + private readonly confirmService: ConfirmationService, + ) {} public cacheData: { [key: string]: CacheData } = {}; public totalEntries = 0; public totalHits = 0; - public apiProjects: string[]; + public apiProjects: (string | undefined)[]; public cacheKeys: string[] = []; async ngOnInit() { this.busy = true; - this.apiProjects = (await this.appConfigService.v2client.admin_projects_get()) - .map(p => p.AppId); + this.apiProjects = (await this.appConfigService.v2client.admin_projects_get()).map((p) => p.AppId); await this.reload(); } async reload() { const client = this.appConfigService.client; - var data = this.apiProjects.map(appid => client.admin_cache_get(appid)); + var data = this.apiProjects.map((appid) => client.admin_cache_get(appid ?? '')); await this.apply(data); } async flushall(): Promise { - const data = this.apiProjects.map(appid => this.appConfigService.v2client.admin_cache_post(appid, { - flush: true, - disable: this.isCachingDeactivated() - })); + const data = this.apiProjects.map((appid) => + this.appConfigService.v2client.admin_cache_post(appid ?? '', { + flush: true, + disable: this.isCachingDeactivated(), + }), + ); await this.apply(data); } - async switchall(): Promise { const disable = !this.isCachingDeactivated(); - const data = this.apiProjects.map(appid => this.appConfigService.v2client.admin_cache_post(appid, { disable: disable })); - await this.apply(data); + if (disable) { + const messageParameters: MessageParameter = { + Title: '#LDS#Heading Deactivate Caches', + Message: '#LDS#If you deactivate all caches, performance will be impaired. Are you sure you want to deactivate all caches?', + }; + if (await this.confirmService.confirm(messageParameters)) { + const data = this.apiProjects.map((appid) => this.appConfigService.v2client.admin_cache_post(appid ?? '', { disable: disable })); + await this.apply(data); + } + } else { + const data = this.apiProjects.map((appid) => this.appConfigService.v2client.admin_cache_post(appid ?? '', { disable: disable })); + await this.apply(data); + } } async apply(pdata: Promise<{ [key: string]: CacheData }>[]) { @@ -89,7 +104,7 @@ export class CacheComponent implements OnInit, SideNavigationComponent { IsDisabled: false, AccessCount: 0, HitCount: 0, - ObjectCount: 0 + ObjectCount: 0, }; const obj = this.cacheData[key]; @@ -97,7 +112,7 @@ export class CacheComponent implements OnInit, SideNavigationComponent { IsDisabled: obj.IsDisabled || data[key].IsDisabled, AccessCount: obj.AccessCount + data[key].AccessCount, HitCount: obj.HitCount + data[key].HitCount, - ObjectCount: obj.ObjectCount + data[key].ObjectCount + ObjectCount: obj.ObjectCount + data[key].ObjectCount, }; } } @@ -106,23 +121,22 @@ export class CacheComponent implements OnInit, SideNavigationComponent { } this.totalEntries = Object.keys(this.cacheData) - .map(a => this.cacheData[a].ObjectCount) + .map((a) => this.cacheData[a].ObjectCount) .reduce((a, b) => a + b); this.totalHits = Object.keys(this.cacheData) - .map(a => this.cacheData[a].HitCount) + .map((a) => this.cacheData[a].HitCount) .reduce((a, b) => a + b); this.cacheKeys = Object.keys(this.cacheData); } private isCachingDeactivated() { - return Object.keys(this.cacheData).filter(b => !this.cacheData[b].IsDisabled).length == 0; + return Object.keys(this.cacheData).filter((b) => !this.cacheData[b].IsDisabled).length == 0; } public GetLdsSwitchText() { - return this.isCachingDeactivated() - ? "#LDS#Activate caches" : "#LDS#Deactivate caches"; + return this.isCachingDeactivated() ? '#LDS#Activate caches' : '#LDS#Deactivate caches'; } public LdsFlushAll = '#LDS#Flush all caches'; diff --git a/imxweb/projects/qbm/src/lib/admin/config-key-path.component.html b/imxweb/projects/qbm/src/lib/admin/config-key-path.component.html index 4e9afe56f..d9987d03b 100644 --- a/imxweb/projects/qbm/src/lib/admin/config-key-path.component.html +++ b/imxweb/projects/qbm/src/lib/admin/config-key-path.component.html @@ -1,4 +1,4 @@ {{ elem }} / - \ No newline at end of file + diff --git a/imxweb/projects/qbm/src/lib/admin/config-key-path.component.scss b/imxweb/projects/qbm/src/lib/admin/config-key-path.component.scss index b6812b34d..5e948a784 100644 --- a/imxweb/projects/qbm/src/lib/admin/config-key-path.component.scss +++ b/imxweb/projects/qbm/src/lib/admin/config-key-path.component.scss @@ -1,6 +1,6 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; .key-path-separator { - margin: 0 .15em; + margin: 0 0.15em; color: $black-9; } diff --git a/imxweb/projects/qbm/src/lib/admin/config-key-path.component.ts b/imxweb/projects/qbm/src/lib/admin/config-key-path.component.ts index 27f8da594..7987c5427 100644 --- a/imxweb/projects/qbm/src/lib/admin/config-key-path.component.ts +++ b/imxweb/projects/qbm/src/lib/admin/config-key-path.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,14 +24,13 @@ * */ -import { Component, Input } from "@angular/core"; +import { Component, Input } from '@angular/core'; @Component({ templateUrl: './config-key-path.component.html', selector: 'config-key-path', - styleUrls: ['./config-key-path.component.scss'] + styleUrls: ['./config-key-path.component.scss'], }) export class ConfigKeyPathComponent { - - @Input() public path: string[] = []; -} \ No newline at end of file + @Input() public path: (string | undefined)[] = []; +} diff --git a/imxweb/projects/qbm/src/lib/admin/config-section.ts b/imxweb/projects/qbm/src/lib/admin/config-section.ts index d31cddca5..a97b245a9 100644 --- a/imxweb/projects/qbm/src/lib/admin/config-section.ts +++ b/imxweb/projects/qbm/src/lib/admin/config-section.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,19 +24,20 @@ * */ -import { ConfigSettingData } from 'imx-api-qbm'; +import { ConfigSettingData } from '@imx-modules/imx-api-qbm'; export class ConfigSection { - constructor(public readonly Title: string, + constructor( + public readonly Title: string, public readonly Description: string, public readonly Keys: KeyData[], /** Returns the configuration nodes that support adding a new setting. */ - public readonly SettingsSupportingAdd: KeyData[]) { - } + public readonly SettingsSupportingAdd: KeyData[], + ) {} } export interface KeyData extends ConfigSettingData { Path: string; - DisplayPath: string[]; - searchTerms: string[]; -} \ No newline at end of file + DisplayPath: (string | undefined)[]; + searchTerms: (string | undefined)[]; +} diff --git a/imxweb/projects/qbm/src/lib/admin/config.component.html b/imxweb/projects/qbm/src/lib/admin/config.component.html index bc14afb32..e47e66534 100644 --- a/imxweb/projects/qbm/src/lib/admin/config.component.html +++ b/imxweb/projects/qbm/src/lib/admin/config.component.html @@ -1,10 +1,11 @@
    -

    #LDS#Configuration

    +

    #LDS#Configuration

    - + + {{ '#LDS#Show configuration for the following API project' | translate }} - + {{ pr.DisplayName }} @@ -12,7 +13,7 @@

    #LDS#Configuration

    - @@ -30,13 +31,24 @@

    #LDS#Configuration

    {{ '#LDS#Create configuration key' | translate }} -
    -
    @@ -46,26 +58,27 @@

    #LDS#Configuration

    #dataSourceToolbar [alwaysVisible]="true" [settings]="dstSettings" - (search)="onSearch($event)" - (navigationStateChanged)="getData($event)" + (search)="getData()" + (navigationStateChanged)="getData()" [options]="['search', 'filter']" > - - -
    - - - -
    {{ '#LDS#There are no configuration keys matching your search.' | translate }}
    - - + + +
    + +
    + + {{ '#LDS#There are no configuration keys matching your search.' | translate }} +
    + +

    {{ section.Title }}

    {{ section.Description }}

    - - + + @@ -92,7 +105,9 @@

    {{ section.Title }}

    - {{ conf.DisplayPath[conf.DisplayPath.length - 1] }} + {{ conf.DisplayPath[conf.DisplayPath.length - 1] }} +

    {{ conf.Description }}

    @@ -116,7 +131,15 @@

    {{ section.Title }}

    - + @@ -164,9 +187,9 @@

    {{ section.Title }}

    -
    +
    +
    diff --git a/imxweb/projects/qbm/src/lib/admin/convert-config-sidesheet.component.scss b/imxweb/projects/qbm/src/lib/admin/convert-config-sidesheet.component.scss index 93585a4f6..df2fdd66c 100644 --- a/imxweb/projects/qbm/src/lib/admin/convert-config-sidesheet.component.scss +++ b/imxweb/projects/qbm/src/lib/admin/convert-config-sidesheet.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; ul { margin: 1em; diff --git a/imxweb/projects/qbm/src/lib/admin/convert-config-sidesheet.component.ts b/imxweb/projects/qbm/src/lib/admin/convert-config-sidesheet.component.ts index 27a714191..bd0790c82 100644 --- a/imxweb/projects/qbm/src/lib/admin/convert-config-sidesheet.component.ts +++ b/imxweb/projects/qbm/src/lib/admin/convert-config-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,17 +24,17 @@ * */ -import { Component } from "@angular/core"; -import { EuiSidesheetRef } from "@elemental-ui/core"; -import { ConfigService } from "./config.service"; +import { Component } from '@angular/core'; +import { EuiSidesheetRef } from '@elemental-ui/core'; +import { ConfigService } from './config.service'; @Component({ templateUrl: './convert-config-sidesheet.component.html', - styleUrls: ['./convert-config-sidesheet.component.scss'] + styleUrls: ['./convert-config-sidesheet.component.scss'], }) export class ConvertConfigSidesheetComponent { - - constructor(private readonly configSvc: ConfigService, + constructor( + private readonly configSvc: ConfigService, private readonly sideSheetRef: EuiSidesheetRef, ) { this.customizationsToConvert = this.configSvc.getLocalCustomizations(); @@ -42,12 +42,13 @@ export class ConvertConfigSidesheetComponent { public isGlobal: boolean = false; - public customizationsToConvert: string[][]; + public customizationsToConvert: (string | undefined)[][]; public submit() { this.configSvc.convert(); this.sideSheetRef.close(); } - public LdsInfoText = '#LDS#You can globally apply the following configuration changes that are currently used locally. The changes are stored in the global configuration file and distributed to all API Servers.'; -} \ No newline at end of file + public LdsInfoText = + '#LDS#You can globally apply the following configuration changes that are currently used locally. The changes are stored in the global configuration file and distributed to all API Servers.'; +} diff --git a/imxweb/projects/qbm/src/lib/admin/dashboard.component.scss b/imxweb/projects/qbm/src/lib/admin/dashboard.component.scss index 47d42ab68..683ae23fa 100644 --- a/imxweb/projects/qbm/src/lib/admin/dashboard.component.scss +++ b/imxweb/projects/qbm/src/lib/admin/dashboard.component.scss @@ -1,165 +1,3 @@ -@import '@elemental-ui/core/src/styles/_palette.scss'; -@import '@elemental-ui/core/src/styles/_eui_palette.scss'; -@import '../../../../../shared/scss/side-navigation.scss'; - :host { height: 100%; } - -.snavigation { - height: 100%; - - .mat-sidenav-container { - height: 100%; - background-color: $asher-gray; - - .mat-sidenav { - width: 230px; - - .snavigation-side { - display: flex; - flex-direction: column; - height: 100%; - - .snavigation-side-content { - flex: 1; - padding: 32px; - padding-bottom: 16px; - } - - .snavigation-side-footer { - padding: 16px 32px; - border-top: 1px solid rgba($black-c, 0.5); - } - } - - .snavigation-side-toggle { - display: none; - padding: 16px 12px 0; - margin-bottom: 10px; - - .mat-button { - min-width: 0; - padding: 0 4px; - height: 28px; - - .mat-icon { - margin-top: -12px; - } - } - } - - .snavigation-side-heading { - font-size: 14px; - font-weight: bold; - margin-bottom: 10px; - white-space: nowrap; - } - - .snavigation-item { - margin: 0 -32px; - padding: 10px 32px; - display: flex; - align-items: center; - justify-content: flex-start; - cursor: pointer; - - & > .eui-icon { - margin-right: 8px; - color: rgba($black, 0.4); - } - - & > span { - flex: 1; - } - - &:hover:not(.snavigation-item--selected) { - background-color: rgba($iris-tint, 0.5); - } - - &.snavigation-item--selected { - background-color: $iris-blue; - color: $white; - } - } - } - - .mat-sidenav-content { - padding: 32px; - position: relative; - display: flex; - - &.snavigation--backdrop-showing { - overflow: hidden; - } - - .snavigation-backdrop { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba($black, 0.32); - z-index: 200; - } - - .mat-table tr:hover td { - background-color: rgba($black, 0.04); - cursor: pointer; - } - } - } -} - -@media only screen and (max-width: 768px) { - .snavigation .mat-sidenav-container { - .mat-sidenav { - transition: width 0.5s; - - .snavigation-side { - .snavigation-side-content, - .snavigation-side-footer { - padding: 16px; - } - } - - .snavigation-side-toggle { - display: block; - } - - &:not(.snavigation-side--expanded) { - width: 58px; - - .snavigation-side-content, - .snavigation-side-footer { - display: none; - } - } - } - } -} - -.eui-dark-theme { - :host { - .snavigation { - .mat-sidenav-container{ - background-color: $color-gray-80; - } - } - } -} - -.eui-contrast-theme { - :host { - .snavigation { - .mat-sidenav-container{ - background-color: $color-gray-90; - } - - .snavigation-item--selected { - border-top: 1px solid $color-gray-0; - border-bottom: 1px solid $color-gray-0; - } - } - } -} diff --git a/imxweb/projects/qbm/src/lib/admin/dashboard.component.ts b/imxweb/projects/qbm/src/lib/admin/dashboard.component.ts index f5253bd47..00d8326d3 100644 --- a/imxweb/projects/qbm/src/lib/admin/dashboard.component.ts +++ b/imxweb/projects/qbm/src/lib/admin/dashboard.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,6 +27,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { ConfigService } from './config.service'; +import { EuiTheme, EuiThemeService } from '@elemental-ui/core'; import { Subscription } from 'rxjs'; import { AppConfigService } from '../appConfig/appConfig.service'; import { SideNavigationExtension } from '../side-navigation-view/side-navigation-view-interfaces'; @@ -131,10 +132,17 @@ export class DashboardComponent implements OnInit, OnDestroy { documentationUiEnabled: boolean = true; - constructor(private readonly appConfigService: AppConfigService, private readonly configService: ConfigService) {} + constructor( + private readonly appConfigService: AppConfigService, + private readonly configService: ConfigService, + private readonly euiThemeService: EuiThemeService, + ) { + this.euiThemeService.setTheme(EuiTheme.LIGHT); + } async ngOnInit() { this.loadDocumentationUi(); + this.subscriptions.push(this.configService.submitChanges?.subscribe(() => this.loadDocumentationUi())); } diff --git a/imxweb/projects/qbm/src/lib/admin/delete-config-sidesheet.component.html b/imxweb/projects/qbm/src/lib/admin/delete-config-sidesheet.component.html index abfb5d660..a908cfe99 100644 --- a/imxweb/projects/qbm/src/lib/admin/delete-config-sidesheet.component.html +++ b/imxweb/projects/qbm/src/lib/admin/delete-config-sidesheet.component.html @@ -1,8 +1,7 @@
    - - {{'#LDS#Configuration key to be deleted' | translate}} + {{ '#LDS#Configuration key to be deleted' | translate }} @@ -12,7 +11,8 @@
    -
    - +
    +
    diff --git a/imxweb/projects/qbm/src/lib/admin/delete-config-sidesheet.component.ts b/imxweb/projects/qbm/src/lib/admin/delete-config-sidesheet.component.ts index 40db19e8c..d4a7dea1b 100644 --- a/imxweb/projects/qbm/src/lib/admin/delete-config-sidesheet.component.ts +++ b/imxweb/projects/qbm/src/lib/admin/delete-config-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,29 +24,28 @@ * */ -import { Component, OnInit } from "@angular/core"; -import { EuiLoadingService, EuiSidesheetRef } from "@elemental-ui/core"; -import { SnackBarService } from "../snackbar/snack-bar.service"; -import { KeyData } from "./config-section"; -import { ConfigService } from "./config.service"; +import { Component, OnInit } from '@angular/core'; +import { EuiLoadingService, EuiSidesheetRef } from '@elemental-ui/core'; +import { SnackBarService } from '../snackbar/snack-bar.service'; +import { KeyData } from './config-section'; +import { ConfigService } from './config.service'; @Component({ templateUrl: './delete-config-sidesheet.component.html', - styleUrls: ['./add-config-sidesheet.component.scss'] + styleUrls: ['./add-config-sidesheet.component.scss'], }) export class DeleteConfigSidesheetComponent implements OnInit { - - constructor(private readonly configSvc: ConfigService, + constructor( + private readonly configSvc: ConfigService, private readonly sideSheetRef: EuiSidesheetRef, private readonly busySvc: EuiLoadingService, - private readonly snackbar: SnackBarService - ) { } + private readonly snackbar: SnackBarService, + ) {} keyData: KeyData[]; selectedKey: KeyData; ngOnInit(): void { - this.keyData = this.configSvc.deletableKeys; } @@ -57,11 +56,11 @@ export class DeleteConfigSidesheetComponent implements OnInit { try { await this.configSvc.deleteKey(this.selectedKey.Path); await this.configSvc.load(); - const key = "#LDS#The configuration key has been successfully deleted."; + const key = '#LDS#The configuration key has been successfully deleted.'; this.snackbar.open({ key }); } finally { this.busySvc.hide(overlay); } this.sideSheetRef.close(); } -} \ No newline at end of file +} diff --git a/imxweb/projects/qbm/src/lib/admin/list-setting.component.html b/imxweb/projects/qbm/src/lib/admin/list-setting.component.html index ca9f84287..42a274f44 100644 --- a/imxweb/projects/qbm/src/lib/admin/list-setting.component.html +++ b/imxweb/projects/qbm/src/lib/admin/list-setting.component.html @@ -1,26 +1,29 @@
    -
    +
    - - + - + - {{pr.Display}} ({{pr.Value}}) + {{ pr.Display }} ({{ pr.Value }}) -
    -
    - -
    \ No newline at end of file +
    + +
    diff --git a/imxweb/projects/qbm/src/lib/admin/list-setting.component.scss b/imxweb/projects/qbm/src/lib/admin/list-setting.component.scss index 058d87280..b637e8551 100644 --- a/imxweb/projects/qbm/src/lib/admin/list-setting.component.scss +++ b/imxweb/projects/qbm/src/lib/admin/list-setting.component.scss @@ -1,17 +1,13 @@ -@import "variables.scss"; - -.buttonbar -{ - margin: 1em 0; -} +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/variables'; .setting-value-list { width: 500px; max-width: 100%; - border: solid 1px $VI_Common_Color_LightGray; + border: solid 1px $color-gray-30; min-height: 45px; display: block; - background: white; + background: $color-gray-0; border-radius: 4px; overflow: hidden; cursor: move; @@ -29,21 +25,22 @@ .setting-value-box { padding: 10px; position: relative; - border-bottom: solid 1px $VI_Common_Color_LightGray; + border-bottom: solid 1px $color-gray-30; display: flex; flex-direction: row; align-items: center; justify-content: space-between; box-sizing: border-box; - background: white; + background: $color-gray-0; } .cdk-drag-preview { box-sizing: border-box; border-radius: 4px; - box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), - 0 8px 10px 1px rgba(0, 0, 0, 0.14), - 0 3px 14px 2px rgba(0, 0, 0, 0.12); + box-shadow: + 0 5px 5px -3px rgba(0, 0, 0, 0.2), + 0 8px 10px 1px rgba(0, 0, 0, 0.14), + 0 3px 14px 2px rgba(0, 0, 0, 0.12); } .cdk-drag-placeholder { diff --git a/imxweb/projects/qbm/src/lib/admin/list-setting.component.ts b/imxweb/projects/qbm/src/lib/admin/list-setting.component.ts index 60288c529..12c5a2fef 100644 --- a/imxweb/projects/qbm/src/lib/admin/list-setting.component.ts +++ b/imxweb/projects/qbm/src/lib/admin/list-setting.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,22 +24,23 @@ * */ -import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; +import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; +import { ConfigSettingType, ConfigSettingValidValue } from '@imx-modules/imx-api-qbm'; import { imx_SessionService } from '../session/imx-session.service'; import { KeyData } from './config-section'; -import { ConfigSettingType, ConfigSettingValidValue } from 'imx-api-qbm'; import { ConfigService } from './config.service'; @Component({ - selector: "imx-list-setting", + selector: 'imx-list-setting', templateUrl: './list-setting.component.html', - styleUrls: ['./list-setting.component.scss'] + styleUrls: ['./list-setting.component.scss'], }) export class ListSettingComponent implements OnInit, OnChanges { - - constructor(private readonly session: imx_SessionService, private readonly configSvc: ConfigService) { - } + constructor( + private readonly session: imx_SessionService, + private readonly configSvc: ConfigService, + ) {} @Input() public setting: KeyData; @@ -60,7 +61,7 @@ export class ListSettingComponent implements OnInit, OnChanges { this.hasLimitedValues = this.setting.Type == ConfigSettingType.LimitedValues; if (this.hasLimitedValues) { this.validvalues = await this.session.Client.admin_apiconfig_values_get(this.configSvc.appId, this.setting.Path); - }; + } } drop(event: CdkDragDrop) { diff --git a/imxweb/projects/qbm/src/lib/admin/log-details-sidesheet.component.html b/imxweb/projects/qbm/src/lib/admin/log-details-sidesheet.component.html index d609b0b72..0616f2a0e 100644 --- a/imxweb/projects/qbm/src/lib/admin/log-details-sidesheet.component.html +++ b/imxweb/projects/qbm/src/lib/admin/log-details-sidesheet.component.html @@ -2,42 +2,37 @@
    - {{'#LDS#Sequence ID' | translate}} - {{data.SequenceID}} + {{ '#LDS#Sequence ID' | translate }} + {{ data.SequenceID }}
    - {{'#LDS#Level' | translate}} - {{data.Level}} + {{ '#LDS#Level' | translate }} + {{ data.Level }}
    - {{'#LDS#Logger name' | translate}} - {{data.LoggerName}} + {{ '#LDS#Logger name' | translate }} + {{ data.LoggerName }}
    - {{'#LDS#Messages' | translate}} + {{ '#LDS#Messages' | translate }} - - + {{ data.Message?.trim() }} +
    -
    - {{'#LDS#Error messages' | translate}} +
    + {{ (data.Level === 'Error' ? '#LDS#Error messages' : '#LDS#Nested messages') | translate }} - + {{ data.Exception?.Messages?.join('\n')?.trim() }}
    -
    - {{'#LDS#Stack trace' | translate}} +
    + {{ '#LDS#Stack trace' | translate }} - +
    - diff --git a/imxweb/projects/qbm/src/lib/admin/log-details-sidesheet.component.scss b/imxweb/projects/qbm/src/lib/admin/log-details-sidesheet.component.scss index 1f100bb6d..44986aaad 100644 --- a/imxweb/projects/qbm/src/lib/admin/log-details-sidesheet.component.scss +++ b/imxweb/projects/qbm/src/lib/admin/log-details-sidesheet.component.scss @@ -17,14 +17,13 @@ float: left; width: 33.33%; } - - + .row:after { - content: ""; + content: ''; display: table; clear: both; } - + textarea { white-space: pre-line; text-align: justify; @@ -37,4 +36,4 @@ textarea { .stacktrace { height: 200px; -} \ No newline at end of file +} diff --git a/imxweb/projects/qbm/src/lib/admin/log-details-sidesheet.component.ts b/imxweb/projects/qbm/src/lib/admin/log-details-sidesheet.component.ts index 9b844cc4b..5d68788f3 100644 --- a/imxweb/projects/qbm/src/lib/admin/log-details-sidesheet.component.ts +++ b/imxweb/projects/qbm/src/lib/admin/log-details-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,18 +26,17 @@ import { Component, Inject, OnInit } from '@angular/core'; import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; -import { ApiLogEntry } from 'imx-api-qbm'; +import { ApiLogEntry } from '@imx-modules/imx-api-qbm'; @Component({ selector: 'imx-log-details-sidesheet', templateUrl: './log-details-sidesheet.component.html', - styleUrls: ['./log-details-sidesheet.component.scss'] + styleUrls: ['./log-details-sidesheet.component.scss'], }) export class LogDetailsSidesheetComponent implements OnInit { - - constructor(@Inject(EUI_SIDESHEET_DATA) public readonly data: ApiLogEntry, - public readonly sidesheetRef: EuiSidesheetRef) { } - ngOnInit(): void { - - } + constructor( + @Inject(EUI_SIDESHEET_DATA) public readonly data: ApiLogEntry, + public readonly sidesheetRef: EuiSidesheetRef, + ) {} + ngOnInit(): void {} } diff --git a/imxweb/projects/qbm/src/lib/admin/logs.component.html b/imxweb/projects/qbm/src/lib/admin/logs.component.html index 6b6bdbc37..5cf59b358 100644 --- a/imxweb/projects/qbm/src/lib/admin/logs.component.html +++ b/imxweb/projects/qbm/src/lib/admin/logs.component.html @@ -1,4 +1,4 @@ - +
    @@ -13,14 +13,11 @@ [alwaysVisible]="true" > - + #LDS#Use regular expressions - -

    {{ '#LDS#You can use regular expressions when searching.' | translate }}

    -
    @@ -29,22 +26,28 @@ #LDS#The regular expression you entered is invalid.
    - + - -
    {{ log.Message }}
    -
    {{ log.TimeStamp | localizedDate }}
    + +
    {{ log.Message }}
    +
    {{ log.TimeStamp | localizedDate }}
    -
    +

    {{ '#LDS#There are currently no log entries.' | translate }}

    -
    +

    {{ '#LDS#There is no data matching your search.' | translate }}

    - - + +
    @@ -62,16 +65,17 @@ [alwaysVisible]="true" > - + #LDS#Use regular expressions

    {{ '#LDS#You can use regular expressions when searching.' | translate }}

    -
    +
    @@ -79,20 +83,26 @@
    - -
    {{ log.Message }}
    -
    {{ log.TimeStamp | localizedDate }}
    + +
    {{ log.Message }}
    +
    {{ log.TimeStamp | localizedDate }}
    -
    +

    {{ '#LDS#There are currently no log entries.' | translate }}

    -
    +

    {{ '#LDS#There is no data matching your search.' | translate }}

    - +
    @@ -100,13 +110,17 @@
    -
    {{ log.File }}
    -
    {{ log.LastModified | localizedDate }}
    +
    {{ log.File }}
    +
    {{ log.LastModified | localizedDate }}
    -
    {{ '#LDS#There are currently no log entries.' | translate }}
    +
    {{ '#LDS#There are currently no log entries.' | translate }}
    + + +

    {{ '#LDS#You can use regular expressions when searching.' | translate }}

    +
    diff --git a/imxweb/projects/qbm/src/lib/admin/logs.component.scss b/imxweb/projects/qbm/src/lib/admin/logs.component.scss index cfe7372df..99e9e4997 100644 --- a/imxweb/projects/qbm/src/lib/admin/logs.component.scss +++ b/imxweb/projects/qbm/src/lib/admin/logs.component.scss @@ -4,45 +4,28 @@ width: 100%; height: 100%; - mat-tab-group { - height: inherit; - } - .imx-log-timeline-container { height: inherit; display: flex; flex-direction: column; overflow: hidden; + .imx-slide-toggle{ + margin:auto; + } } -.flex-container { - display: flex; - align-items: stretch; - margin-top: 2em; -} - -.imx-error-icon { - color: $color-red-60; -} - -.imx-warning-icon { - color: $color-orange-60; -} - -.imx-primary-icon { - color: $color-blue-60; -} - -.no-data, .no-result { - height: 100%; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; + .flex-container { + display: flex; + align-items: stretch; + margin-top: 2em; + } - .eui-icon { - font-size: 100px; - } + .imx-no-results { + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; p { margin: 0; @@ -50,12 +33,6 @@ } } -.mat-spinner { - align-self: center; - margin: auto; - margin-top: 100px; -} - mat-nav-list { height: calc(100vh - 280px); overflow: auto; @@ -67,22 +44,9 @@ transition: 1s; } -.slide-toggle { - margin-left: 10px; - margin-top: 13px; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; -} - -.imx-info-button { - margin-top: 5px; -} - p { color: $color-gray-40; } - } .imx-error-message { @@ -93,7 +57,6 @@ eui-icon { margin-right: 8px; } - } @media only screen and (max-width: 1024px) { diff --git a/imxweb/projects/qbm/src/lib/admin/logs.component.ts b/imxweb/projects/qbm/src/lib/admin/logs.component.ts index 5c26317b7..b5038e1ad 100644 --- a/imxweb/projects/qbm/src/lib/admin/logs.component.ts +++ b/imxweb/projects/qbm/src/lib/admin/logs.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,25 +24,25 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; -import { Component, ViewChild, OnInit, Input } from '@angular/core'; +import { Component, Input, OnInit, ViewChild } from '@angular/core'; +import { MatPaginator, PageEvent } from '@angular/material/paginator'; +import { MatSlideToggleChange } from '@angular/material/slide-toggle'; +import { MatTabChangeEvent } from '@angular/material/tabs'; import { EuiDownloadOptions, EuiLoadingService, EuiSidesheetConfig, EuiSidesheetService } from '@elemental-ui/core'; +import { ApiLogEntry, LogFileInfo, V2ApiClientMethodFactory } from '@imx-modules/imx-api-qbm'; +import { FilterData, MethodDefinition, ValType } from '@imx-modules/imx-qbm-dbts'; import { TranslateService } from '@ngx-translate/core'; -import { ApiLogEntry, LogFileInfo, V2ApiClientMethodFactory } from 'imx-api-qbm'; +import moment from 'moment-timezone'; import { AppConfigService } from '../appConfig/appConfig.service'; -import { LogDetailsSidesheetComponent } from './log-details-sidesheet.component'; +import { calculateSidesheetWidth } from '../base/sidesheet-helper'; import { ClassloggerService } from '../classlogger/classlogger.service'; -import { LocalizedDatePipe } from '../date/localized-date.pipe'; -import { MatTabChangeEvent } from '@angular/material/tabs'; -import { CollectionLoadParameters, FilterData, MethodDefinition, TypedEntity, ValType } from 'imx-qbm-dbts'; -import { MatSlideToggleChange } from '@angular/material/slide-toggle'; -import { DataSourceToolbarComponent } from '../data-source-toolbar/data-source-toolbar.component'; import { ElementalUiConfigService } from '../configuration/elemental-ui-config.service'; -import { DataSourceToolbarSettings } from '../data-source-toolbar/data-source-toolbar-settings'; -import { MatPaginator, PageEvent } from '@angular/material/paginator'; -import moment from 'moment-timezone'; import { DataSourceToolbarFilter } from '../data-source-toolbar/data-source-toolbar-filters.interface'; +import { DataSourceToolbarSettings } from '../data-source-toolbar/data-source-toolbar-settings'; +import { DataSourceToolbarComponent } from '../data-source-toolbar/data-source-toolbar.component'; +import { LocalizedDatePipe } from '../date/localized-date.pipe'; import { SideNavigationComponent } from '../side-navigation-view/side-navigation-view-interfaces'; +import { LogDetailsSidesheetComponent } from './log-details-sidesheet.component'; @Component({ selector: 'imx-logs', @@ -87,16 +87,16 @@ export class LogsComponent implements OnInit, SideNavigationComponent { private datePipe: LocalizedDatePipe, private logger: ClassloggerService, private elementalUiConfigService: ElementalUiConfigService, - private translator: TranslateService + private translator: TranslateService, ) {} public async ngOnInit(): Promise { - const overlayRef = this.busyService.show(); + this.showBusyIndicator(); try { const timeFilter: DataSourceToolbarFilter = { Name: 'TimeFilter', - Description: this.translator.instant('#LDS#Time'), + Description: this.translator.instant('#LDS#Time period'), Options: [ { Display: this.translator.instant('#LDS#Last hour'), @@ -165,23 +165,23 @@ export class LogsComponent implements OnInit, SideNavigationComponent { this.stream.onmessage = (evt) => { this.logger.trace('Logs data', evt.data); this.liveLogs.push(JSON.parse(evt.data)); - this.onLiveLogSearch(this.dstSettingsLive.navigationState.search); + this.onLiveLogSearch(this.dstSettingsLive?.navigationState?.search ?? '', true); }; this.stream.onerror = (err) => { this.logger.error('An error occured in data stream:', err); }; } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } } private async getSessionData(): Promise { - const overlayRef = this.busyService.show(); + this.showBusyIndicator(); try { return await this.appConfigService.client.admin_systeminfo_log_session_get(); } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } } @@ -191,32 +191,33 @@ export class LogsComponent implements OnInit, SideNavigationComponent { this.currentTab = event.index; if (this.currentTab === 2) { - const overlayRef = this.busyService.show(); + this.showBusyIndicator(); try { this.logFiles = await this.appConfigService.client.admin_systeminfo_logs_get(); this.logFiles.forEach((log) => { - var dir = log.Path.split('\\'); - const url = - this.appConfigService.BaseUrl + - new MethodDefinition(new V2ApiClientMethodFactory().admin_systeminfo_log_get(dir[0], dir[1])).path; - this.logDownloads.push({ - ...this.elementalUiConfigService.Config.downloadOptions, - fileMimeType: '', - url, - fileName: log.File, - }); + var dir = log.Path?.split('\\'); + if (dir && dir.length > 1) { + const url = + this.appConfigService.BaseUrl + + new MethodDefinition(new V2ApiClientMethodFactory().admin_systeminfo_log_get(dir[0], dir[1])).path; + this.logDownloads.push({ + ...this.elementalUiConfigService.Config.downloadOptions, + fileMimeType: '', + url, + fileName: log.File, + }); + } }); } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } } } public onSessionLogSearch(keywords: string): void { - const sessionKeywords: ({ IsRegex?: boolean } & FilterData)[] = this.dstSettingsSession.navigationState.filter?.filter( - (filter) => filter.Type === 1 - ); + const sessionKeywords: ({ IsRegex?: boolean } & FilterData)[] = + this.dstSettingsSession.navigationState.filter?.filter((filter) => filter.Type === 1) ?? []; this.sessionLogsFiltered = this.sessionLogs; //Handles keyword filters @@ -224,16 +225,16 @@ export class LogsComponent implements OnInit, SideNavigationComponent { this.sessionLogsFiltered = this.sessionLogsFiltered.filter((log) => sessionKeywords.every((keyword) => keyword.IsRegex - ? this.regexTest(keyword.Value1.toLowerCase(), log.Message?.toLowerCase()) - : log.Message?.toLowerCase().includes(keyword.Value1.toLowerCase()) - ) + ? this.regexTest(keyword.Value1.toLowerCase(), log.Message?.toLowerCase() ?? '') + : log.Message?.toLowerCase().includes(keyword.Value1.toLowerCase()), + ), ); } //Handles time filters if (this.dstSettingsSession.navigationState.TimeFilter) { this.sessionLogsFiltered = this.sessionLogsFiltered.filter((log) => - moment(log.TimeStamp).isAfter(this.dstSettingsSession.navigationState.TimeFilter) + moment(log.TimeStamp).isAfter(this.dstSettingsSession.navigationState.TimeFilter), ); } @@ -254,10 +255,9 @@ export class LogsComponent implements OnInit, SideNavigationComponent { this.setSessionPage(); } - public onLiveLogSearch(keywords: string): void { - const liveKeywords: ({ IsRegex?: boolean } & FilterData)[] = this.dstSettingsLive.navigationState.filter?.filter( - (filter) => filter.Type === 1 - ); + public onLiveLogSearch(keywords: string, stream = false): void { + const liveKeywords: ({ IsRegex?: boolean } & FilterData)[] = + this.dstSettingsLive.navigationState.filter?.filter((filter) => filter.Type === 1) ?? []; this.liveLogsFiltered = this.liveLogs; //Handles keyword filters @@ -265,16 +265,16 @@ export class LogsComponent implements OnInit, SideNavigationComponent { this.liveLogsFiltered = this.liveLogsFiltered.filter((log) => liveKeywords.every((keyword) => keyword.IsRegex - ? this.regexTest(keyword.Value1.toLowerCase(), log.Message?.toLowerCase()) - : log.Message?.toLowerCase().includes(keyword.Value1.toLowerCase()) - ) + ? this.regexTest(keyword.Value1.toLowerCase(), log.Message?.toLowerCase() ?? '') + : log.Message?.toLowerCase().includes(keyword.Value1.toLowerCase()), + ), ); } //Handles time filters if (this.dstSettingsLive.navigationState.TimeFilter) { this.liveLogsFiltered = this.liveLogsFiltered.filter((log) => - moment(log.TimeStamp).isAfter(this.dstSettingsLive.navigationState.TimeFilter) + moment(log.TimeStamp).isAfter(this.dstSettingsLive.navigationState.TimeFilter), ); } @@ -291,7 +291,9 @@ export class LogsComponent implements OnInit, SideNavigationComponent { } this.liveTotalCount = this.liveLogsFiltered.length; - this.livePaginator.pageIndex = 0; + if (!stream) { + this.livePaginator.pageIndex = 0; + } this.setLivePage(); } @@ -303,19 +305,21 @@ export class LogsComponent implements OnInit, SideNavigationComponent { public onRegexSessionLogSearch(keywords: string): void { this.sessionLogsFiltered = keywords - ? this.sessionLogsFiltered.filter((log) => this.regexTest(keywords, log.Message)) + ? this.sessionLogsFiltered.filter((log) => this.regexTest(keywords, log.Message ?? '')) : this.sessionLogsFiltered; } public onRegexLiveLogSearch(keywords: string): void { - this.liveLogsFiltered = keywords ? this.liveLogsFiltered.filter((log) => this.regexTest(keywords, log.Message)) : this.liveLogsFiltered; + this.liveLogsFiltered = keywords + ? this.liveLogsFiltered.filter((log) => this.regexTest(keywords, log.Message ?? '')) + : this.liveLogsFiltered; } public onRegexToggle(event: MatSlideToggleChange): void { this.searchBoxText = event.checked ? '#LDS#Search using regular expressions' : '#LDS#Search'; - if (this.currentTab === 0) this.onSessionLogSearch(this.dstSession.settings.navigationState.search); - if (this.currentTab === 1) this.onLiveLogSearch(this.dstLive.settings.navigationState.search); + if (this.currentTab === 0) this.onSessionLogSearch(this.dstSession.settings.navigationState.search ?? ''); + if (this.currentTab === 1) this.onLiveLogSearch(this.dstLive.settings.navigationState.search ?? ''); } public validateRegex(keywords: string): boolean { @@ -373,33 +377,32 @@ export class LogsComponent implements OnInit, SideNavigationComponent { } public getIconColor(changeType: string): string { - let color = 'imx-primary-icon'; + let color = 'imx-icon-info'; switch (changeType) { case 'Error': - color = 'imx-error-icon'; + color = 'imx-icon-error'; break; case 'Warn': - color = 'imx-warning-icon'; + color = 'imx-icon-warning'; break; } return color; } public async openLogSideSheet(log: ApiLogEntry): Promise { - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.busyService.show())); + this.showBusyIndicator(); let config: EuiSidesheetConfig; try { config = { title: await this.translateService.get('#LDS#Heading View Log Entry Details').toPromise(), subTitle: this.datePipe.transform(log.TimeStamp), padding: '0', - width: 'max(600px, 60%)', + width: calculateSidesheetWidth(), testId: 'log-details-sidesheet', data: log, }; } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } this.sidesheet.open(LogDetailsSidesheetComponent, config); } @@ -407,4 +410,10 @@ export class LogsComponent implements OnInit, SideNavigationComponent { public ngOnDestroy(): void { if (this.stream) this.stream.close(); } + + private showBusyIndicator(): void { + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } + } } diff --git a/imxweb/projects/qbm/src/lib/admin/packages.component.html b/imxweb/projects/qbm/src/lib/admin/packages.component.html index e9c0d564b..4439d4da3 100644 --- a/imxweb/projects/qbm/src/lib/admin/packages.component.html +++ b/imxweb/projects/qbm/src/lib/admin/packages.component.html @@ -1,4 +1,4 @@ -

    #LDS#Packages

    +

    #LDS#Heading Packages

    diff --git a/imxweb/projects/qbm/src/lib/admin/packages.component.scss b/imxweb/projects/qbm/src/lib/admin/packages.component.scss index 8e348ee30..2e5fdde2f 100644 --- a/imxweb/projects/qbm/src/lib/admin/packages.component.scss +++ b/imxweb/projects/qbm/src/lib/admin/packages.component.scss @@ -1,5 +1,4 @@ - -:host{ +:host { display: flex; flex-flow: column; flex: 1; @@ -8,7 +7,7 @@ padding: 0.5em 0.5em 0.5em 0; overflow: auto; - &.loading{ + &.loading { justify-content: center; flex: 1; align-content: center; diff --git a/imxweb/projects/qbm/src/lib/admin/packages.component.ts b/imxweb/projects/qbm/src/lib/admin/packages.component.ts index 99f4cca21..e4f459260 100644 --- a/imxweb/projects/qbm/src/lib/admin/packages.component.ts +++ b/imxweb/projects/qbm/src/lib/admin/packages.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,25 +24,26 @@ * */ -import { Component, OnInit, Input } from "@angular/core"; -import { PackageInfo } from "imx-api-qbm"; -import { AppConfigService } from "../appConfig/appConfig.service"; -import { imx_SessionService } from "../session/imx-session.service"; -import { SideNavigationComponent } from "../side-navigation-view/side-navigation-view-interfaces"; +import { Component, Input, OnInit } from '@angular/core'; +import { PackageInfo } from '@imx-modules/imx-api-qbm'; +import { AppConfigService } from '../appConfig/appConfig.service'; +import { imx_SessionService } from '../session/imx-session.service'; +import { SideNavigationComponent } from '../side-navigation-view/side-navigation-view-interfaces'; type ExtendedPackageInfo = PackageInfo & { App?: string }; @Component({ templateUrl: './packages.component.html', selector: 'imx-packages', - styleUrls: ['./packages.component.scss'] + styleUrls: ['./packages.component.scss'], }) export class PackagesComponent implements OnInit, SideNavigationComponent { @Input() public isAdmin: boolean; - constructor(private readonly session: imx_SessionService, - private readonly appConfigService: AppConfigService) { - } + constructor( + private readonly session: imx_SessionService, + private readonly appConfigService: AppConfigService, + ) {} public busy = true; public displayedColumns: string[] = ['Name', 'App', 'RelativePath', 'LastChangeDate', 'Fingerprint']; @@ -53,13 +54,11 @@ export class PackagesComponent implements OnInit, SideNavigationComponent { this.packages = await this.session.Client.admin_packages_get(); const apps = await this.session.Client.imx_applications_get(); for (var p of this.packages) { - p.Fingerprint = p.Fingerprint.substring(0, 8); + p.Fingerprint = p.Fingerprint?.substring(0, 8); - var app = apps.filter(a => a.Name == p.Name); - if (app.length > 0) - p.App = app[0].DisplayName; + var app = apps.filter((a) => a.Name == p.Name); + if (app.length > 0) p.App = app[0].DisplayName; } - } finally { this.busy = false; } diff --git a/imxweb/projects/qbm/src/lib/admin/plugins.component.html b/imxweb/projects/qbm/src/lib/admin/plugins.component.html index 79abc313c..ee93622cc 100644 --- a/imxweb/projects/qbm/src/lib/admin/plugins.component.html +++ b/imxweb/projects/qbm/src/lib/admin/plugins.component.html @@ -1,11 +1,11 @@
    -

    #LDS#Plugins

    +

    #LDS#Heading Plugins

    - {{p.Plugin}} + {{ p.Plugin }} @@ -14,10 +14,10 @@

    #LDS#Plugins

    check - {{'#LDS#Running' |translate}} + {{ '#LDS#Running' | translate }}
    -
    \ No newline at end of file +
    diff --git a/imxweb/projects/qbm/src/lib/admin/plugins.component.ts b/imxweb/projects/qbm/src/lib/admin/plugins.component.ts index 437da1e93..49d956453 100644 --- a/imxweb/projects/qbm/src/lib/admin/plugins.component.ts +++ b/imxweb/projects/qbm/src/lib/admin/plugins.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { Component, Input, OnInit } from '@angular/core'; -import { LoadedPlugin } from 'imx-api-qbm'; +import { LoadedPlugin } from '@imx-modules/imx-api-qbm'; import { AppConfigService } from '../appConfig/appConfig.service'; import { SideNavigationComponent } from '../side-navigation-view/side-navigation-view-interfaces'; diff --git a/imxweb/projects/qbm/src/lib/admin/select-value.component.html b/imxweb/projects/qbm/src/lib/admin/select-value.component.html index 6864f7546..5a0a9c671 100644 --- a/imxweb/projects/qbm/src/lib/admin/select-value.component.html +++ b/imxweb/projects/qbm/src/lib/admin/select-value.component.html @@ -1,8 +1,8 @@ - {{'#LDS#Value' | translate}} + {{ '#LDS#Value' | translate }} - {{pr.Display}} + {{ pr.Display }} - \ No newline at end of file + diff --git a/imxweb/projects/qbm/src/lib/admin/select-value.component.ts b/imxweb/projects/qbm/src/lib/admin/select-value.component.ts index 2797c5ac4..d7970d803 100644 --- a/imxweb/projects/qbm/src/lib/admin/select-value.component.ts +++ b/imxweb/projects/qbm/src/lib/admin/select-value.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,20 +24,22 @@ * */ -import { Component, Input, OnInit } from "@angular/core"; -import { ConfigSettingValidValue } from "imx-api-qbm"; -import { imx_SessionService } from "../session/imx-session.service"; -import { KeyData } from "./config-section"; -import { ConfigService } from "./config.service"; +import { Component, Input, OnInit } from '@angular/core'; +import { ConfigSettingValidValue } from '@imx-modules/imx-api-qbm'; +import { imx_SessionService } from '../session/imx-session.service'; +import { KeyData } from './config-section'; +import { ConfigService } from './config.service'; @Component({ templateUrl: './select-value.component.html', styles: ['.wide-field {min-width:450px;}'], - selector: 'imx-config-select' + selector: 'imx-config-select', }) export class SelectValueComponent implements OnInit { - - constructor(private readonly session: imx_SessionService, public readonly configSvc: ConfigService) { } + constructor( + private readonly session: imx_SessionService, + public readonly configSvc: ConfigService, + ) {} async ngOnInit(): Promise { this.validvalues = await this.session.Client.admin_apiconfig_values_get(this.configSvc.appId, this.conf.Path); @@ -46,5 +48,4 @@ export class SelectValueComponent implements OnInit { @Input() conf: KeyData; public validvalues: ConfigSettingValidValue[] = []; - -} \ No newline at end of file +} diff --git a/imxweb/projects/qbm/src/lib/admin/shared.scss b/imxweb/projects/qbm/src/lib/admin/shared.scss index 0f6e8da39..75b1baa74 100644 --- a/imxweb/projects/qbm/src/lib/admin/shared.scss +++ b/imxweb/projects/qbm/src/lib/admin/shared.scss @@ -1,15 +1,16 @@ -:host -{ + +@import '../../../../../shared/scss/components/top-navigation.scss'; +:host { min-width: 800px; display: flex; flex: 1; flex-direction: column; - .content-container{ + .content-container { padding: 0.5em 0.5em 0 0; overflow: auto; - &.loading{ + &.loading { display: flex; justify-content: center; flex: 1; @@ -17,52 +18,35 @@ } } - table - { + table { table-layout: fixed; border-collapse: collapse; margin-bottom: 1em; } - h2 - { + h2 { margin-bottom: 1em; } - td - { - padding: .5em; + td { + padding: 0.5em; vertical-align: top; border: solid 1px lightgray; } - table.status-table td:nth-child(1) - { + table.status-table td:nth-child(1) { width: 40%; } - table.plugin-table td:nth-child(2) - { + table.plugin-table td:nth-child(2) { width: 30%; } - .mat-icon - { + .mat-icon { vertical-align: middle; } - pre - { + pre { margin: 0; } - - .button-bar { - display: flex; - gap: 0.5rem; - justify-content: flex-end; - - button:first-child { - margin-right: auto; - } - } } diff --git a/imxweb/projects/qbm/src/lib/admin/status.component.html b/imxweb/projects/qbm/src/lib/admin/status.component.html index e523f93a4..fc7cb2560 100644 --- a/imxweb/projects/qbm/src/lib/admin/status.component.html +++ b/imxweb/projects/qbm/src/lib/admin/status.component.html @@ -1,47 +1,57 @@ -

    {{ '#LDS#An error occurred while connecting to the database. Please try again. If the problem persists, contact your administrator.' | translate }}

    +

    + {{ + '#LDS#An error occurred while connecting to the database. Please try again. If the problem persists, contact your administrator.' + | translate + }} +

    -

    {{ '#LDS#Overview' | translate }}

    +

    {{ '#LDS#Overview' | translate }}

    - {{'#LDS#Sessions' | translate}} + {{ '#LDS#Sessions' | translate }} - + - diff --git a/imxweb/projects/qbm/src/lib/data-table/data-table-generic-column.component.scss b/imxweb/projects/qbm/src/lib/data-table/data-table-generic-column.component.scss deleted file mode 100644 index 1ca156755..000000000 --- a/imxweb/projects/qbm/src/lib/data-table/data-table-generic-column.component.scss +++ /dev/null @@ -1,4 +0,0 @@ -/* You can add styles to this file, and also import other style files */ -td.mat-cell { - margin-right: 10px; -} diff --git a/imxweb/projects/qbm/src/lib/data-table/data-table-generic-column.component.ts b/imxweb/projects/qbm/src/lib/data-table/data-table-generic-column.component.ts index e21d262c8..24cbf17f5 100644 --- a/imxweb/projects/qbm/src/lib/data-table/data-table-generic-column.component.ts +++ b/imxweb/projects/qbm/src/lib/data-table/data-table-generic-column.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -46,8 +46,7 @@ import { MatColumnDef } from '@angular/material/table'; */ @Component({ selector: 'imx-data-table-generic-column', - templateUrl: './data-table-generic-column.component.html', - styleUrls: ['./data-table-generic-column.component.scss'] + templateUrl: './data-table-generic-column.component.html' }) export class DataTableGenericColumnComponent implements OnInit { /** diff --git a/imxweb/projects/qbm/src/lib/data-table/data-table-groups.interface.ts b/imxweb/projects/qbm/src/lib/data-table/data-table-groups.interface.ts index d774d618f..388499266 100644 --- a/imxweb/projects/qbm/src/lib/data-table/data-table-groups.interface.ts +++ b/imxweb/projects/qbm/src/lib/data-table/data-table-groups.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { CollectionLoadParameters, TypedEntity } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { DataSourceToolbarSettings } from '../data-source-toolbar/data-source-toolbar-settings'; export function getParameterSubsetForGrouping(original: CollectionLoadParameters): CollectionLoadParameters { @@ -46,12 +46,12 @@ export interface DataTableGroupedData { /** * The DataSourceToolbarSettings for the grouped data */ - settings: DataSourceToolbarSettings; + settings?: DataSourceToolbarSettings; /** * The navigationState for the grouped data */ - navigationState: CollectionLoadParameters; + navigationState?: CollectionLoadParameters; /** * Keeps track if the nested group data is currently visible (expanded) or not diff --git a/imxweb/projects/qbm/src/lib/data-table/data-table-row-highlight.interface.ts b/imxweb/projects/qbm/src/lib/data-table/data-table-row-highlight.interface.ts index e2a460d3a..2d075f852 100644 --- a/imxweb/projects/qbm/src/lib/data-table/data-table-row-highlight.interface.ts +++ b/imxweb/projects/qbm/src/lib/data-table/data-table-row-highlight.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,5 +29,3 @@ export interface RowHighlight { */ filter: (row: any) => boolean; } - - diff --git a/imxweb/projects/qbm/src/lib/data-table/data-table.component.html b/imxweb/projects/qbm/src/lib/data-table/data-table.component.html index b88c365b2..143d6ac72 100644 --- a/imxweb/projects/qbm/src/lib/data-table/data-table.component.html +++ b/imxweb/projects/qbm/src/lib/data-table/data-table.component.html @@ -6,20 +6,26 @@ #LDS#Group by: {{ dst?.settings?.groupData?.currentGrouping?.display }} - + + @@ -134,11 +144,20 @@ *matRowDef="let row; columns: getNamesOfDisplayedColumns()" (click)="debouncedHighlightRow(row, $event); $event.stopPropagation()" (keydown.enter)="debouncedHighlightRow(row, $event); $event.stopPropagation()" - [ngClass]="{ 'imx-data-table-row-highlighted': highlightedEntity === row, 'imx-data-table-row-conditional': isHighlight(row) && !(highlightedEntity === row) }" + [ngClass]="{ + 'imx-data-table-row-highlighted': highlightedEntity === row, + 'imx-data-table-row-conditional': isHighlight(row) && !(highlightedEntity === row), + }" >
    - +
    {{ '#LDS#API projects' | translate }} - + {{ p.AppId }} - +
    {{ '#LDS#Software update' | translate }}
    {{ '#LDS#Update is currently running.' | translate }}
    -
    {{ '#LDS#Software updates are not enabled for this server.' | translate }}
    +
    + {{ '#LDS#Software updates are not enabled for this server.' | translate }} +
    {{ '#LDS#Updates are available.' | translate }}

    - - {{option}} + {{ option }} - \ No newline at end of file + diff --git a/imxweb/projects/qbm/src/lib/auto-complete/auto-complete.component.scss b/imxweb/projects/qbm/src/lib/auto-complete/auto-complete.component.scss index 20c499adb..963eb99c8 100644 --- a/imxweb/projects/qbm/src/lib/auto-complete/auto-complete.component.scss +++ b/imxweb/projects/qbm/src/lib/auto-complete/auto-complete.component.scss @@ -6,24 +6,9 @@ padding: 0 0 0 10px; } -.mat-input-element { +.mat-mdc-input-element { flex: 1; - height: 100%; - border: 0px solid transparent; - background: transparent; padding-left: 5px; - outline: none; -} - -.mat-input-element:focus { - outline: none; - border: 0px solid transparent; -} - -.mat-icon-button { - width: 24px; - height: 24px; - line-height: 24px; } .imx-search-image { diff --git a/imxweb/projects/qbm/src/lib/auto-complete/auto-complete.component.ts b/imxweb/projects/qbm/src/lib/auto-complete/auto-complete.component.ts index 89e2de604..12811beed 100644 --- a/imxweb/projects/qbm/src/lib/auto-complete/auto-complete.component.ts +++ b/imxweb/projects/qbm/src/lib/auto-complete/auto-complete.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,10 +30,9 @@ import { MatAutocompleteTrigger } from '@angular/material/autocomplete'; @Component({ selector: 'imx-auto-complete', templateUrl: './auto-complete.component.html', - styleUrls: ['./auto-complete.component.scss'] + styleUrls: ['./auto-complete.component.scss'], }) export class AutoCompleteComponent { - public filterText = ''; public filteredOptions: string[]; @@ -49,8 +48,7 @@ export class AutoCompleteComponent { } public filterOptions(): void { - this.filteredOptions = this.availableOptions - .filter(app => app.toLocaleLowerCase().includes(this.filterText.toLocaleLowerCase())); + this.filteredOptions = this.availableOptions.filter((app) => app.toLocaleLowerCase().includes(this.filterText.toLocaleLowerCase())); } public clearText(): void { @@ -58,5 +56,4 @@ export class AutoCompleteComponent { this.filterOptions(); this.EmitValueChangedEvent(); } - } diff --git a/imxweb/projects/qbm/src/lib/auto-complete/auto-complete.module.ts b/imxweb/projects/qbm/src/lib/auto-complete/auto-complete.module.ts index ea49479e5..93d2d73b0 100644 --- a/imxweb/projects/qbm/src/lib/auto-complete/auto-complete.module.ts +++ b/imxweb/projects/qbm/src/lib/auto-complete/auto-complete.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -34,17 +34,9 @@ import { EuiCoreModule } from '@elemental-ui/core'; import { AutoCompleteComponent } from './auto-complete.component'; - @NgModule({ declarations: [AutoCompleteComponent], - imports: [ - CommonModule, - FormsModule, - EuiCoreModule, - MatAutocompleteModule, - MatInputModule, - MatButtonModule - ], - exports: [AutoCompleteComponent] + imports: [CommonModule, FormsModule, EuiCoreModule, MatAutocompleteModule, MatInputModule, MatButtonModule], + exports: [AutoCompleteComponent], }) -export class AutoCompleteModule { } +export class AutoCompleteModule {} diff --git a/imxweb/projects/qbm/src/lib/base/Guid.ts b/imxweb/projects/qbm/src/lib/base/Guid.ts index 554fd2722..0de2fade0 100644 --- a/imxweb/projects/qbm/src/lib/base/Guid.ts +++ b/imxweb/projects/qbm/src/lib/base/Guid.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qbm/src/lib/base/busy.service.ts b/imxweb/projects/qbm/src/lib/base/busy.service.ts index 16559eef5..25ff25dd0 100644 --- a/imxweb/projects/qbm/src/lib/base/busy.service.ts +++ b/imxweb/projects/qbm/src/lib/base/busy.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { EventEmitter, Injectable } from "@angular/core"; +import { EventEmitter, Injectable } from '@angular/core'; export interface Busy { endBusy: (forceEnd?: boolean) => void; @@ -37,9 +37,10 @@ export interface Busy { providedIn: 'root', }) export class BusyService { - /** Returns false if and only if there are no active busy sections. */ - public get isBusy() { return this.busyCounter > 0; } + public get isBusy() { + return this.busyCounter > 0; + } private busyCounter = 0; @@ -48,19 +49,16 @@ export class BusyService { */ beginBusy(): Busy { this.busyCounter++; - if (this.busyCounter >= 1) - this.busyStateChanged.emit(true); + if (this.busyCounter >= 1) this.busyStateChanged.emit(true); var isEnded = false; return { endBusy: (forceEnd?: boolean) => { // section can only be ended once. - if (isEnded) - return; + if (isEnded) return; isEnded = true; - this.busyCounter = forceEnd ? 0 : this.busyCounter-1; - if (this.busyCounter <= 0) - this.busyStateChanged.emit(false); - } + this.busyCounter = forceEnd ? 0 : this.busyCounter - 1; + if (this.busyCounter <= 0) this.busyStateChanged.emit(false); + }, }; } diff --git a/imxweb/projects/qbm/src/lib/temp-billboard/temp-billboard.module.ts b/imxweb/projects/qbm/src/lib/base/elemental-defaults.ts similarity index 58% rename from imxweb/projects/qbm/src/lib/temp-billboard/temp-billboard.module.ts rename to imxweb/projects/qbm/src/lib/base/elemental-defaults.ts index b07ba08d5..9eaa0c60d 100644 --- a/imxweb/projects/qbm/src/lib/temp-billboard/temp-billboard.module.ts +++ b/imxweb/projects/qbm/src/lib/base/elemental-defaults.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,19 +24,22 @@ * */ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { TempBillboardComponent } from './temp-billboard.component'; -import { TempBillboardService } from './temp-billboard.service'; -import { EuiCoreModule } from '@elemental-ui/core'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; +import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE } from '@angular/material/core'; -/** - * @deprecated This will be removed with A15 support - */ -@NgModule({ - declarations: [TempBillboardComponent], - providers: [TempBillboardService], - imports: [CommonModule, EuiCoreModule], - exports: [TempBillboardComponent], -}) -export class TempBillboardModule {} +export const EUI_DATE_FORMATS = { + parse: { + dateInput: ['LL', 'L'], + }, + display: { + dateInput: 'LL', + monthYearLabel: 'MMM YYYY', + dateA11yLabel: 'LL', + monthYearA11yLabel: 'MMMM YYYY', + }, +}; + +export const EuiDateProviders = [ + { provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE] }, + { provide: MAT_DATE_FORMATS, useValue: EUI_DATE_FORMATS }, +]; diff --git a/imxweb/projects/qbm/src/lib/base/error.service.ts b/imxweb/projects/qbm/src/lib/base/error.service.ts index a9d3aa852..fc20fc997 100644 --- a/imxweb/projects/qbm/src/lib/base/error.service.ts +++ b/imxweb/projects/qbm/src/lib/base/error.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,16 +24,18 @@ * */ -import { Injectable } from "@angular/core"; +import { Injectable } from '@angular/core'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ErrorService { private _target: string; /** Returns the current target for generated error messages. */ - public get target() { return this._target; } + public get target() { + return this._target; + } /** Sets a new target and returns a callback function that resets * the target to its previous value. @@ -44,10 +46,9 @@ export class ErrorService { var disposed = false; return () => { // only call once - if (disposed) - return; + if (disposed) return; disposed = true; this._target = previousTarget; }; } -} \ No newline at end of file +} diff --git a/imxweb/projects/qbm/src/lib/base/global-error-handler.ts b/imxweb/projects/qbm/src/lib/base/global-error-handler.ts index 09bf9aa4a..48dd3ceab 100644 --- a/imxweb/projects/qbm/src/lib/base/global-error-handler.ts +++ b/imxweb/projects/qbm/src/lib/base/global-error-handler.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,19 +24,24 @@ * */ +import { HttpErrorResponse } from '@angular/common/http'; import { ErrorHandler, Injectable, Injector } from '@angular/core'; -import { UserMessageService } from '../user-message/user-message.service'; import { ClassloggerService } from '../classlogger/classlogger.service'; -import { HttpErrorResponse } from '@angular/common/http'; +import { ConfirmationService } from '../confirmation/confirmation.service'; +import { UserMessageService } from '../user-message/user-message.service'; import { ErrorService } from './error.service'; @Injectable() export class GlobalErrorHandler implements ErrorHandler { private messageService: UserMessageService; private logger: ClassloggerService; + private confirm: ConfirmationService; - constructor(private injector: Injector, private readonly errorService: ErrorService) {} + constructor( + private injector: Injector, + private readonly errorService: ErrorService, + ) {} private get target() { return this.errorService.target; @@ -46,8 +51,16 @@ export class GlobalErrorHandler implements ErrorHandler { this.checkInjectedServices(); if (error instanceof HttpErrorResponse) { - this.handleHttpErrorResponse(error); + if (error.status !== 419) { + this.handleHttpErrorResponse(error); + } + } else if (error.name === 'AbortError') { + return; } else if (error instanceof Error) { + if (error.message != null && error.message.indexOf('57002006') !== -1) { + this.handleSessionExpired(); + return; + } if (error.message != null && error.message.indexOf('\n') !== -1) { this.messageService.subject.next({ text: error.message.substring(0, error.message.indexOf('\n')).replace('Uncaught (in promise):', ''), @@ -111,5 +124,13 @@ export class GlobalErrorHandler implements ErrorHandler { if (this.logger == null) { this.logger = this.injector.get(ClassloggerService); } + + if (this.confirm == null) { + this.confirm = this.injector.get(ConfirmationService); + } + } + + private handleSessionExpired(): void { + this.confirm.handleExpiredSession(); } } diff --git a/imxweb/projects/qbm/src/lib/base/ie-warning.service.ts b/imxweb/projects/qbm/src/lib/base/ie-warning.service.ts index 8cf40534f..11c342d5f 100644 --- a/imxweb/projects/qbm/src/lib/base/ie-warning.service.ts +++ b/imxweb/projects/qbm/src/lib/base/ie-warning.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -41,7 +41,7 @@ export class IeWarningService { private readonly alertBanner: EuiAlertBannerService, private readonly translate: TranslateService, private readonly ldsReplace: LdsReplacePipe, - private readonly mastHeadService: MastHeadService + private readonly mastHeadService: MastHeadService, ) {} public async showIe11Banner(): Promise { @@ -58,10 +58,10 @@ export class IeWarningService { message: this.ldsReplace.transform( await this.translate .get( - '#LDS#Internet Explorer is no longer supported and the application may not work properly. Please use a browser from the following list: {0}.' + '#LDS#Internet Explorer is no longer supported and the application may not work properly. Please use a browser from the following list: {0}.', ) .toPromise(), - docLink + docLink, ), }); this.alertBanner.userDismissed.subscribe(() => this.storageService.storeHelperAlertDismissal(alertKey)); diff --git a/imxweb/projects/qbm/src/lib/base/metadata.service.ts b/imxweb/projects/qbm/src/lib/base/metadata.service.ts index 4e5cb4a93..dffeaa003 100644 --- a/imxweb/projects/qbm/src/lib/base/metadata.service.ts +++ b/imxweb/projects/qbm/src/lib/base/metadata.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,61 +24,85 @@ * */ -import { Injectable } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; - -import { MetaTableData } from 'imx-api-qbm'; -import { imx_SessionService } from '../session/imx-session.service'; +import { Injectable, OnDestroy } from '@angular/core'; +import { MetaTableData } from '@imx-modules/imx-qbm-dbts'; +/** + * Abstract implementation for getting portal specific metadata. + */ @Injectable({ providedIn: 'root', }) -export class MetadataService { - public readonly tables: { [id: string]: MetaTableData } = {}; +export abstract class MetadataService implements OnDestroy { + public readonly tables: { [id: string]: MetaTableData | undefined } = {}; + protected abortController: AbortController; + + /** + * @deprecated use tables instead + */ + private tableMetadata: { [id: string]: MetaTableData | undefined } = {}; + + constructor() { + this.abortController = new AbortController(); + } + + ngOnDestroy(): void { + this.abortCall(); + } /** - * @deprecated Use tables instead. + * Fetches table metadata. Applications will provide authentication / methods to use. + * @param tableName The name of the table to fetch data for + * @param options Additional api options */ - private tableMetadata: { [id: string]: MetaTableData } = {}; + protected abstract getTable(tableName: string, options?: unknown): Promise; - constructor( - private sessionService: imx_SessionService, - private readonly translateService: TranslateService, - ) {} + /** + * Handles aborting any current requests managed by this service. + */ + public abortCall() { + this.abortController.abort(); + this.abortController = new AbortController(); + } /** - * Updates meta data for the tables of the provided table names that are not already present in the tables map - * @param tableName The names of the tables to update + * Fetches and updates metadata for the tables of the provided table names that are not already present in the tables map + * @param tableNames The names of the tables to update + * @param options Additional api options */ - public async updateNonExisting(tableNames: string[]): Promise { + public async updateNonExisting(tableNames: string[], options?: unknown): Promise { // Use a Set to obtain unique values const uniqueSet = Array.from(new Set(tableNames.filter((tableName) => this.tables[tableName] == null))); - return this.update(uniqueSet); + return this.update(uniqueSet, options); } /** - * Updates meta data for the tables of the provided table names - * @param tableName The names of the tables to update + * Fetches and updates metadata for the tables of the provided table names + * @param tableNames The names of the tables to update + * @param options Additional api options */ - public async update(tableNames: string[]): Promise { + public async update(tableNames: string[], options?: unknown): Promise { for (const tableName of tableNames) { - this.tables[tableName] = await this.sessionService.Client.imx_metadata_table_get(tableName, { - cultureName: this.translateService.currentLang, - }); + const metaTableData = await this.getTable(tableName, options); + if (metaTableData) { + this.tables[tableName] = metaTableData; + } } } /** * @deprecated Use use the method update and the property tables instead. Will be removed. - * @param table The name of the table to update and get metadata for + * @param tableName The name of the table to update and get metadata for + * @param options Additional api options */ - public async GetTableMetadata(table: string): Promise { - if (this.tableMetadata[table] == null) { - this.tableMetadata[table] = await this.sessionService.Client.imx_metadata_table_get(table, { - cultureName: this.translateService.currentLang, - }); + public async GetTableMetadata(tableName: string, options?: unknown): Promise { + if (this.tableMetadata[tableName] == null) { + const metaTableData = await this.getTable(tableName, options); + if (metaTableData) { + this.tableMetadata[tableName] = metaTableData; + } } - return this.tableMetadata[table]; + return this.tableMetadata[tableName]; } } diff --git a/imxweb/projects/qbm/src/lib/base/opsupport-db-object.service.ts b/imxweb/projects/qbm/src/lib/base/opsupport-db-object.service.ts index df0045a0f..b1bffad09 100644 --- a/imxweb/projects/qbm/src/lib/base/opsupport-db-object.service.ts +++ b/imxweb/projects/qbm/src/lib/base/opsupport-db-object.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,7 +26,7 @@ import { Injectable } from '@angular/core'; -import { EntityData } from 'imx-qbm-dbts'; +import { EntityData } from '@imx-modules/imx-qbm-dbts'; import { imx_SessionService } from '../session/imx-session.service'; export interface OpsupportDbObjectParameters { diff --git a/imxweb/projects/qbm/src/lib/base/paginator.spec.ts b/imxweb/projects/qbm/src/lib/base/paginator.spec.ts index 3be77a6ee..73c63b6c6 100644 --- a/imxweb/projects/qbm/src/lib/base/paginator.spec.ts +++ b/imxweb/projects/qbm/src/lib/base/paginator.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -32,9 +32,9 @@ import { LdsReplacePipe } from '../lds-replace/lds-replace.pipe'; import { clearStylesFromDOM } from '../testing/clear-styles.spec'; export class TranslateServiceStub { - public get(key: any): any { - return of(key); - } + public get(key: any): any { + return of(key); + } } describe('Paginator', () => { @@ -51,38 +51,48 @@ describe('Paginator', () => { [ { - page: 0, pageSize: 0, length: 1, + page: 0, + pageSize: 0, + length: 1, expected: [ { index: 0, value: '0' }, - { index: 2, value: '1' } - ] + { index: 2, value: '1' }, + ], }, { - page: 0, pageSize: 1, length: 0, + page: 0, + pageSize: 1, + length: 0, expected: [ { index: 0, value: '0' }, - { index: 2, value: '0' } - ] + { index: 2, value: '0' }, + ], }, { - page: 0, pageSize: 1, length: 2, + page: 0, + pageSize: 1, + length: 2, expected: [ { index: 0, value: '1' }, { index: 2, value: '1' }, - { index: 4, value: '2' } - ] + { index: 4, value: '2' }, + ], }, { - page: 2, pageSize: 1, length: 2, + page: 2, + pageSize: 1, + length: 2, expected: [ { index: 0, value: '3' }, { index: 2, value: '3' }, - { index: 4, value: '2' } - ] - } - ].forEach(testcase => it('displays correct page info', () => { - const paginator = Paginator.Create(translationService, mockLdsReplacePipe); - const rangelabelTokens = paginator.getRangeLabel(testcase.page, testcase.pageSize, testcase.length).replace('#LDS#', '').split(' '); - testcase.expected.forEach(result => expect(rangelabelTokens[result.index]).toEqual(result.value)); - })); + { index: 4, value: '2' }, + ], + }, + ].forEach((testcase) => + it('displays correct page info', () => { + const paginator = Paginator.Create(translationService, mockLdsReplacePipe); + const rangelabelTokens = paginator.getRangeLabel(testcase.page, testcase.pageSize, testcase.length).replace('#LDS#', '').split(' '); + testcase.expected.forEach((result) => expect(rangelabelTokens[result.index]).toEqual(result.value)); + }), + ); }); diff --git a/imxweb/projects/qbm/src/lib/base/paginator.ts b/imxweb/projects/qbm/src/lib/base/paginator.ts index 8a0470726..92c055cd4 100644 --- a/imxweb/projects/qbm/src/lib/base/paginator.ts +++ b/imxweb/projects/qbm/src/lib/base/paginator.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,33 +30,36 @@ import { TranslateService } from '@ngx-translate/core'; import { LdsReplacePipe } from '../lds-replace/lds-replace.pipe'; export class Paginator extends MatPaginatorIntl { - private rangeText = ''; - private rangeTextFallback = ''; - - private constructor(private translateService: TranslateService, private readonly ldsReplace: LdsReplacePipe) { - super(); - this.translateService.get('#LDS#Entries per page').subscribe((text: string) => this.itemsPerPageLabel = text + ':'); - this.translateService.get('#LDS#First page').subscribe((text: string) => this.firstPageLabel = text); - this.translateService.get('#LDS#Previous page').subscribe((text: string) => this.previousPageLabel = text); - this.translateService.get('#LDS#Next page').subscribe((text: string) => this.nextPageLabel = text); - this.translateService.get('#LDS#Last page').subscribe((text: string) => this.lastPageLabel = text); - this.translateService.get('#LDS#{0} - {1} of {2}').subscribe((text: string) => this.rangeText = text); - this.translateService.get('#LDS#0 of {0}').subscribe((text: string) => this.rangeTextFallback = text); - this.getRangeLabel = (page: number, pageSize: number, length: number) => { - length = Math.max(length, 0); - - if (length === 0 || pageSize === 0) { - return this.ldsReplace.transform(this.rangeTextFallback, length); - } - - const startIndex = page * pageSize; - const endIndex = startIndex < length ? Math.min(startIndex + pageSize, length) : startIndex + pageSize; - - return this.ldsReplace.transform(this.rangeText, startIndex + 1, endIndex, length); - }; - } - - public static Create(translateService: TranslateService, ldsReplace: LdsReplacePipe): Paginator { - return new Paginator(translateService, ldsReplace); - } + private rangeText = ''; + private rangeTextFallback = ''; + + private constructor( + private translateService: TranslateService, + private readonly ldsReplace: LdsReplacePipe, + ) { + super(); + this.translateService.get('#LDS#Entries per page').subscribe((text: string) => (this.itemsPerPageLabel = text + ':')); + this.translateService.get('#LDS#First page').subscribe((text: string) => (this.firstPageLabel = text)); + this.translateService.get('#LDS#Previous page').subscribe((text: string) => (this.previousPageLabel = text)); + this.translateService.get('#LDS#Next page').subscribe((text: string) => (this.nextPageLabel = text)); + this.translateService.get('#LDS#Last page').subscribe((text: string) => (this.lastPageLabel = text)); + this.translateService.get('#LDS#{0} - {1} of {2}').subscribe((text: string) => (this.rangeText = text)); + this.translateService.get('#LDS#0 of {0}').subscribe((text: string) => (this.rangeTextFallback = text)); + this.getRangeLabel = (page: number, pageSize: number, length: number) => { + length = Math.max(length, 0); + + if (length === 0 || pageSize === 0) { + return this.ldsReplace.transform(this.rangeTextFallback, length); + } + + const startIndex = page * pageSize; + const endIndex = startIndex < length ? Math.min(startIndex + pageSize, length) : startIndex + pageSize; + + return this.ldsReplace.transform(this.rangeText, startIndex + 1, endIndex, length); + }; + } + + public static Create(translateService: TranslateService, ldsReplace: LdsReplacePipe): Paginator { + return new Paginator(translateService, ldsReplace); + } } diff --git a/imxweb/projects/qbm/src/lib/base/qbm-sqlwizard.service.ts b/imxweb/projects/qbm/src/lib/base/qbm-sqlwizard.service.ts index 6f8015d14..c85787f3f 100644 --- a/imxweb/projects/qbm/src/lib/base/qbm-sqlwizard.service.ts +++ b/imxweb/projects/qbm/src/lib/base/qbm-sqlwizard.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { Injectable } from '@angular/core'; -import { FilterProperty, CollectionLoadParameters, EntityCollectionData } from 'imx-qbm-dbts'; +import { FilterProperty, CollectionLoadParameters, EntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { SqlWizardApiService } from '../sqlwizard/sqlwizard-api.service'; @Injectable({ diff --git a/imxweb/projects/qbm/src/lib/base/query-parameters-handler.spec.ts b/imxweb/projects/qbm/src/lib/base/query-parameters-handler.spec.ts index a23b0343d..636bf6e9e 100644 --- a/imxweb/projects/qbm/src/lib/base/query-parameters-handler.spec.ts +++ b/imxweb/projects/qbm/src/lib/base/query-parameters-handler.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,28 +24,16 @@ * */ +import { ActivatedRouteSnapshot } from '@angular/router'; -import { ActivatedRouteSnapshot, ParamMap } from '@angular/router'; -import * as TypeMoq from 'typemoq'; - -import { QueryParametersHandler } from './query-parameters-handler'; import { clearStylesFromDOM } from '../testing/clear-styles.spec'; +import { QueryParametersHandler } from './query-parameters-handler'; -function CreateActiveRouteSnapshot(queryParams: { [key: string]: any }): ActivatedRouteSnapshot { - const mock = TypeMoq.Mock.ofType(); - mock - .setup(item => item.queryParamMap) - .returns(() => { - const mockParamMap = TypeMoq.Mock.ofType(); - mockParamMap.setup(item => item.keys).returns(() => Object.keys(queryParams)); - mockParamMap.setup(item => item.get(TypeMoq.It.isAnyString())).returns((key: string) => queryParams[key]); - return mockParamMap.object; - }); - return mock.object; +function CreateActiveRouteSnapshot(queryParams: { [key: string]: string }): ActivatedRouteSnapshot { + return { queryParamMap: { keys: Object.keys(queryParams), get: (key: string) => queryParams[key] } } as unknown as ActivatedRouteSnapshot; } describe('QueryParametersHandler', () => { - afterAll(() => { clearStylesFromDOM(); }); @@ -53,39 +41,39 @@ describe('QueryParametersHandler', () => { [ { search: '', - expected: undefined + expected: undefined, }, { search: '?', - expected: undefined + expected: undefined, }, { search: '?a', - expected: { 'a': '' } + expected: { a: '' }, }, { search: '?a=1', - expected: { 'a': '1' } + expected: { a: '1' }, }, { search: '?a=1&b=2', - expected: { 'a': '1', 'b': '2' } + expected: { a: '1', b: '2' }, }, { search: '?a=1&b=2', - route: CreateActiveRouteSnapshot({ 'c': '3' }), - expected: { 'a': '1', 'b': '2', 'c': '3' } + route: CreateActiveRouteSnapshot({ c: '3' }), + expected: { a: '1', b: '2', c: '3' }, }, { search: '?a=1&b=2', - route: CreateActiveRouteSnapshot({ 'c': '3' }), + route: CreateActiveRouteSnapshot({ c: '3' }), filter: (key: string) => key === 'c', - expected: { 'c': '3' } - } - ].forEach(testcase => + expected: { c: '3' }, + }, + ].forEach((testcase) => it('can parse querystrings correctly', () => { const handler = new QueryParametersHandler(testcase.search, testcase.route); expect(handler.GetQueryParameters(testcase.filter)).toEqual(testcase.expected); - }) + }), ); }); diff --git a/imxweb/projects/qbm/src/lib/base/query-parameters-handler.ts b/imxweb/projects/qbm/src/lib/base/query-parameters-handler.ts index e8eb459ec..e3196a198 100644 --- a/imxweb/projects/qbm/src/lib/base/query-parameters-handler.ts +++ b/imxweb/projects/qbm/src/lib/base/query-parameters-handler.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,16 +24,22 @@ * */ -import { ActivatedRouteSnapshot, ParamMap, DefaultUrlSerializer } from '@angular/router'; +import { ActivatedRouteSnapshot, DefaultUrlSerializer, ParamMap } from '@angular/router'; export class QueryParametersHandler { - public get path(): string { return this.lastPath || this.route?.routeConfig?.path; } + public get path(): string { + return this.lastPath || (this.route?.routeConfig?.path ?? ''); + } private readonly urlSerializer = new DefaultUrlSerializer(); private readonly queryParametersCollection: ParamMap[] = []; private readonly lastPath: string; - constructor(search?: string, private readonly route?: ActivatedRouteSnapshot, lastUrl?: string) { + constructor( + search?: string, + private readonly route?: ActivatedRouteSnapshot, + lastUrl?: string, + ) { if (lastUrl) { const lastLocation = lastUrl.split('?'); @@ -50,12 +56,12 @@ export class QueryParametersHandler { } } - public GetQueryParameters(filter: (name: string) => boolean = null): { [key: string]: any } { + public GetQueryParameters(filter: (name: string) => boolean = null as any): { [key: string]: any } | undefined { const outparams: { [id: string]: any } = {}; - this.queryParametersCollection.forEach(params => { + this.queryParametersCollection.forEach((params) => { if (params.keys) { params.keys - .filter(name => filter == null || filter(name)) + .filter((name) => filter == null || filter(name)) .forEach((name: string) => { outparams[name] = params.get(name); }); diff --git a/imxweb/projects/qbm/src/lib/base/registry.service.ts b/imxweb/projects/qbm/src/lib/base/registry.service.ts index 4a4570e68..92709d2a8 100644 --- a/imxweb/projects/qbm/src/lib/base/registry.service.ts +++ b/imxweb/projects/qbm/src/lib/base/registry.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,10 +28,11 @@ import { Injectable } from '@angular/core'; @Injectable() export class RegistryService { + public get Registry(): { [id: string]: T[] } { + return this.registry; + } - public get Registry(): { [id: string]: T[]; } { return this.registry; } - - private registry: { [id: string]: T[]; } = {}; + private registry: { [id: string]: T[] } = {}; public register(key: string, obj: T): void { if (!this.registry[key]) { @@ -39,5 +40,4 @@ export class RegistryService { } this.registry[key].push(obj); } - } diff --git a/imxweb/projects/qbm/src/lib/base/server-error.ts b/imxweb/projects/qbm/src/lib/base/server-error.ts index 06b837e04..ce9c2d6ab 100644 --- a/imxweb/projects/qbm/src/lib/base/server-error.ts +++ b/imxweb/projects/qbm/src/lib/base/server-error.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,15 +24,15 @@ * */ -import { MethodDescriptor } from 'imx-qbm-dbts'; +import { MethodDescriptor } from '@imx-modules/imx-qbm-dbts'; export class ServerError extends Error { - protected messageUserFriendly: string; + protected messageUserFriendly: string; - constructor(message: string, endpoint?: MethodDescriptor) { - super(message + (endpoint ? '\n' + JSON.stringify(endpoint) : '')); - this.messageUserFriendly = message; - } + constructor(message: string, endpoint?: MethodDescriptor) { + super(message + (endpoint ? '\n' + JSON.stringify(endpoint) : '')); + this.messageUserFriendly = message; + } - public toString = (): string => this.messageUserFriendly; + public toString = (): string => this.messageUserFriendly; } diff --git a/imxweb/projects/qbm/src/lib/base/server-exception-error.ts b/imxweb/projects/qbm/src/lib/base/server-exception-error.ts index 7b17ea5b3..fb77b2b6b 100644 --- a/imxweb/projects/qbm/src/lib/base/server-exception-error.ts +++ b/imxweb/projects/qbm/src/lib/base/server-exception-error.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,25 +24,25 @@ * */ -import { ExceptionData } from 'imx-api-qbm'; +import { ExceptionData } from '@imx-modules/imx-api-qbm'; import { ServerError } from './server-error'; export class ServerExceptionError extends ServerError { - constructor(private readonly dataItems: ExceptionData[]) { - super(ServerExceptionError.parse(dataItems)); + constructor(private readonly dataItems: ExceptionData[]) { + super(ServerExceptionError.parse(dataItems)); - this.messageUserFriendly = ServerExceptionError.parse(this.dataItems, true); - } - - private static parse(dataItems: ExceptionData[], userFriendly: boolean = false): string { - if (dataItems && dataItems.length > 0) { - if (userFriendly) { - return dataItems.map(item => `${item.Message}${item.Number ? ` [${item.Number}]` : ''}`).join(', '); - } + this.messageUserFriendly = ServerExceptionError.parse(this.dataItems, true); + } - return JSON.stringify(dataItems); - } + private static parse(dataItems: ExceptionData[], userFriendly: boolean = false): string { + if (dataItems && dataItems.length > 0) { + if (userFriendly) { + return dataItems.map((item) => `${item.Message}${item.Number ? ` [${item.Number}]` : ''}`).join(', '); + } - return 'Unknown error'; + return JSON.stringify(dataItems); } + + return 'Unknown error'; + } } diff --git a/imxweb/projects/qbm/src/lib/base/sidesheet-helper.ts b/imxweb/projects/qbm/src/lib/base/sidesheet-helper.ts new file mode 100644 index 000000000..cc32be51e --- /dev/null +++ b/imxweb/projects/qbm/src/lib/base/sidesheet-helper.ts @@ -0,0 +1,43 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +/** + * Calculates the sidesheet width. + * @param pixelWidth + * @param percentageWidth + * @returns The calculated sidesheet width in pixel. + */ +export function calculateSidesheetWidth(pixelWidth: number = 900, percentageWidth: number = 0.6): string { + const calculatedWidth = document.body.offsetWidth * percentageWidth; + return `${Math.max(pixelWidth, calculatedWidth)}px`; +} + +/** + * Checks the width of the document body is lower than 768px. + */ +export function isMobile(): boolean { + return document.body.offsetWidth <= 768; +} diff --git a/imxweb/projects/qbm/src/lib/base/timezone-info.spec.ts b/imxweb/projects/qbm/src/lib/base/timezone-info.spec.ts index 8634a96f6..79809903b 100644 --- a/imxweb/projects/qbm/src/lib/base/timezone-info.spec.ts +++ b/imxweb/projects/qbm/src/lib/base/timezone-info.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,7 +28,6 @@ import { TimezoneInfo } from './timezone-info'; import { clearStylesFromDOM } from '../testing/clear-styles.spec'; describe('TimezoneInfo', () => { - afterAll(() => { clearStylesFromDOM(); }); @@ -40,10 +39,8 @@ describe('TimezoneInfo', () => { }); it('should be created', () => { - expect(() => { const timezoneInfo = TimezoneInfo.get(); }).not.toThrowError(); - }); }); diff --git a/imxweb/projects/qbm/src/lib/base/timezone-info.ts b/imxweb/projects/qbm/src/lib/base/timezone-info.ts index 277d54514..b0184fac4 100644 --- a/imxweb/projects/qbm/src/lib/base/timezone-info.ts +++ b/imxweb/projects/qbm/src/lib/base/timezone-info.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,49 +25,40 @@ */ export class TimezoneInfo { + public static get(): string { + const year = 2015; // Falls die Uhrzeit auf dem Client mal überhaupt nicht stimmt. + let lastDate = null; + let curTestDate: Date; + let lastUtcOffset = 0; + let curUtcOffset: number; + let dayLightChanges = ''; + let countDayLightChanges = 0; - public static get(): string { - const year = 2015; // Falls die Uhrzeit auf dem Client mal überhaupt nicht stimmt. - let lastDate = null; - let curTestDate: Date; - let lastUtcOffset = 0; - let curUtcOffset: number; - let dayLightChanges = ''; - let countDayLightChanges = 0; + // alle Tage des aktuellen Jahres prüfen, + for (let month = 0; month < 12 && countDayLightChanges < 2; month++) { + for (let day = 1; day <= 31 && countDayLightChanges < 2; day++) { + curTestDate = new Date(Date.UTC(year, month, day, 0, 0, 0, 0)); - // alle Tage des aktuellen Jahres prüfen, - for (let month = 0; month < 12 && countDayLightChanges < 2; month++) { - for (let day = 1; day <= 31 && countDayLightChanges < 2; day++) { - curTestDate = new Date(Date.UTC(year, month, day, 0, 0, 0, 0)); - - // man kann den 31 Februar definieren, dann kommt automatisch der 3 März heraus - // deshalb ignorieren, wenn der Monatstag abweicht - if (curTestDate.getUTCDate() !== day) { - continue; - } - - curUtcOffset = curTestDate.getTimezoneOffset() * -1; + // man kann den 31 Februar definieren, dann kommt automatisch der 3 März heraus + // deshalb ignorieren, wenn der Monatstag abweicht + if (curTestDate.getUTCDate() !== day) { + continue; + } - if (lastDate != null && curUtcOffset !== lastUtcOffset) { - dayLightChanges += year + - ',' + - (lastDate.getUTCMonth() + 1) + - ',' + - lastDate.getUTCDate() + - ',' + - lastUtcOffset + - ',' + - curUtcOffset + - ';'; - countDayLightChanges++; - } + curUtcOffset = curTestDate.getTimezoneOffset() * -1; - lastDate = curTestDate; - lastUtcOffset = curUtcOffset; - } + if (lastDate != null && curUtcOffset !== lastUtcOffset) { + dayLightChanges += + year + ',' + (lastDate.getUTCMonth() + 1) + ',' + lastDate.getUTCDate() + ',' + lastUtcOffset + ',' + curUtcOffset + ';'; + countDayLightChanges++; } - // wenn es keine Sommer-/ Winterzeit gibt, dann nur das UTC Offset merken - return dayLightChanges === '' ? lastUtcOffset + '' : dayLightChanges; + lastDate = curTestDate; + lastUtcOffset = curUtcOffset; + } } + + // wenn es keine Sommer-/ Winterzeit gibt, dann nur das UTC Offset merken + return dayLightChanges === '' ? lastUtcOffset + '' : dayLightChanges; + } } diff --git a/imxweb/projects/qbm/src/lib/base/user-action.service.ts b/imxweb/projects/qbm/src/lib/base/user-action.service.ts index e6814c778..a6ff93561 100644 --- a/imxweb/projects/qbm/src/lib/base/user-action.service.ts +++ b/imxweb/projects/qbm/src/lib/base/user-action.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qbm/src/lib/base/user-agent-helper.ts b/imxweb/projects/qbm/src/lib/base/user-agent-helper.ts index 3fe810041..35eef9b93 100644 --- a/imxweb/projects/qbm/src/lib/base/user-agent-helper.ts +++ b/imxweb/projects/qbm/src/lib/base/user-agent-helper.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-item/bulk-item-icon.ts b/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-item/bulk-item-icon.ts index f9979200c..d402f38b7 100644 --- a/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-item/bulk-item-icon.ts +++ b/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-item/bulk-item-icon.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-item/bulk-item.component.html b/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-item/bulk-item.component.html index a9129f5f4..d4d027998 100644 --- a/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-item/bulk-item.component.html +++ b/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-item/bulk-item.component.html @@ -1,36 +1,53 @@ - + - - + + {{ bulkItem?.customTitle || bulkItem?.entity?.GetEntity()?.GetDisplay() }} - + {{ bulkItem?.additionalInfo }}

    {{ bulkItem?.description }}
    - + [attr.data-imx-identifier]="'bulk-item' + cdr.column.ColumnName + '_' + i" + > - + [attr.data-imx-identifier]="'bulk-item-custom-select-option-' + i" + >
    - - + +
    diff --git a/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-item/bulk-item.component.scss b/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-item/bulk-item.component.scss index 0c9f3f288..518edff47 100644 --- a/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-item/bulk-item.component.scss +++ b/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-item/bulk-item.component.scss @@ -1,36 +1,9 @@ @import '@elemental-ui/core/src/styles/_palette.scss'; -eui-icon { - color: $aspen-green; -} - button { margin-left: 10px; } -.mat-expansion-panel { - margin-bottom: 20px; - margin-top: 20px; -} - -.mat-expansion-panel-header-description { - justify-content: end; -} - -.mat-expansion-panel-header { - min-height: 24px; - height: auto; - padding: 12px 24px; -} - -.mat-expansion-panel-header-title { - flex-basis: auto; -} - -.mat-expansion-panel-header-description, .mat-expansion-panel-header-title { - align-items: center; -} - .imx-custom-property { margin-bottom: 20px; } @@ -44,10 +17,14 @@ button { margin-bottom: 10px; } -.imx-panel-icon { +.imx-icon-new { margin-right: 10px; } .imx-madatory-item { color: $corbin-orange; } + +.imx-bulk-item-description { + flex: initial; +} diff --git a/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-item/bulk-item.component.ts b/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-item/bulk-item.component.ts index 092185e0b..c569e9aa6 100644 --- a/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-item/bulk-item.component.ts +++ b/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-item/bulk-item.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,11 +28,11 @@ import { Component, EventEmitter, Input, OnInit, Output, QueryList, ViewChild, V import { AbstractControl, UntypedFormGroup } from '@angular/forms'; import { MatExpansionPanel } from '@angular/material/expansion'; +import { EntityWriteDataSingle } from '@imx-modules/imx-qbm-dbts'; +import { isEqual } from 'lodash'; +import { CdrEditorComponent } from '../../cdr/cdr-editor/cdr-editor.component'; import { BulkItem, BulkItemStatus } from './bulk-item'; import { BulkItemIcon } from './bulk-item-icon'; -import { CdrEditorComponent } from '../../cdr/cdr-editor/cdr-editor.component'; -import { isEqual } from 'lodash'; -import { EntityWriteDataSingle } from 'imx-qbm-dbts'; @Component({ selector: 'imx-bulk-item', diff --git a/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-item/bulk-item.ts b/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-item/bulk-item.ts index 5695d3551..905993b2c 100644 --- a/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-item/bulk-item.ts +++ b/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-item/bulk-item.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { TypedEntity } from 'imx-qbm-dbts'; +import { TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { ColumnDependentReference } from '../../cdr/column-dependent-reference.interface'; import { EntitySelect } from '../../entity/entity-select/entity-select.interface'; @@ -33,7 +33,7 @@ export enum BulkItemStatus { saved, skipped, error, - valid + valid, } export interface BulkItem { diff --git a/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-property-editor.component.html b/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-property-editor.component.html index 57b8b982e..23ea4cdf3 100644 --- a/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-property-editor.component.html +++ b/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-property-editor.component.html @@ -1,13 +1,15 @@
    - + (controlCreated)="formGroup.addControl('bulk-item' + i, $event)" + >
    diff --git a/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-property-editor.component.scss b/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-property-editor.component.scss index dd3e7eb0a..90d4ee007 100644 --- a/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-property-editor.component.scss +++ b/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-property-editor.component.scss @@ -1,2 +1 @@ /* You can add global styles to this file, and also import other style files */ - diff --git a/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-property-editor.component.ts b/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-property-editor.component.ts index 64031b93f..e523ddcbd 100644 --- a/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-property-editor.component.ts +++ b/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-property-editor.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -33,7 +33,7 @@ import { BulkItemComponent } from './bulk-item/bulk-item.component'; @Component({ selector: 'imx-bulk-editor', templateUrl: './bulk-property-editor.component.html', - styleUrls: ['./bulk-property-editor.component.scss'] + styleUrls: ['./bulk-property-editor.component.scss'], }) export class BulkPropertyEditorComponent implements OnInit { public formGroup = new UntypedFormGroup({}); @@ -50,8 +50,8 @@ export class BulkPropertyEditorComponent implements OnInit { public ngOnInit(): void { this.entities.sort((a, b) => { - let typeA = a.properties.every(p => p.isReadOnly()); - let typeB = b.properties.every(p => p.isReadOnly()); + let typeA = a.properties.every((p) => p.isReadOnly()); + let typeB = b.properties.every((p) => p.isReadOnly()); if (typeA && typeB) { return 0; @@ -61,8 +61,8 @@ export class BulkPropertyEditorComponent implements OnInit { return 1; } - typeA = a.properties.some(p => p.column.GetMetadata().GetMinLength() > 0); - typeB = b.properties.some(p => p.column.GetMetadata().GetMinLength() > 0); + typeA = a.properties.some((p) => p.column.GetMetadata().GetMinLength() > 0); + typeB = b.properties.some((p) => p.column.GetMetadata().GetMinLength() > 0); if (typeA && typeB) { return 0; diff --git a/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-property-editor.module.ts b/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-property-editor.module.ts index 8ce22e233..aaace196b 100644 --- a/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-property-editor.module.ts +++ b/imxweb/projects/qbm/src/lib/bulk-property-editor/bulk-property-editor.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -56,8 +56,8 @@ import { BulkPropertyEditorComponent } from './bulk-property-editor.component'; MatCardModule, ReactiveFormsModule, TranslateModule, - EntityModule + EntityModule, ], - exports: [BulkPropertyEditorComponent] + exports: [BulkPropertyEditorComponent], }) -export class BulkPropertyEditorModule { } +export class BulkPropertyEditorModule {} diff --git a/imxweb/projects/qbm/src/lib/busy-indicator/busy-indicator.component.html b/imxweb/projects/qbm/src/lib/busy-indicator/busy-indicator.component.html index a7c8a3e09..5340bb296 100644 --- a/imxweb/projects/qbm/src/lib/busy-indicator/busy-indicator.component.html +++ b/imxweb/projects/qbm/src/lib/busy-indicator/busy-indicator.component.html @@ -1,4 +1,4 @@
    {{ '#LDS#Loading...' | translate }}
    -
    \ No newline at end of file +
    diff --git a/imxweb/projects/qbm/src/lib/busy-indicator/busy-indicator.component.scss b/imxweb/projects/qbm/src/lib/busy-indicator/busy-indicator.component.scss index 2d008bd32..100fefc0b 100644 --- a/imxweb/projects/qbm/src/lib/busy-indicator/busy-indicator.component.scss +++ b/imxweb/projects/qbm/src/lib/busy-indicator/busy-indicator.component.scss @@ -2,6 +2,7 @@ flex-grow: 1; display: flex; align-items: center; + justify-content: center; margin: 0 auto; > div { @@ -10,8 +11,7 @@ flex-direction: column; } - - .imx-loading-text{ + .imx-loading-text { margin-top: 40px; } -} \ No newline at end of file +} diff --git a/imxweb/projects/qbm/src/lib/busy-indicator/busy-indicator.component.ts b/imxweb/projects/qbm/src/lib/busy-indicator/busy-indicator.component.ts index 1a9d5c36c..70ebc8323 100644 --- a/imxweb/projects/qbm/src/lib/busy-indicator/busy-indicator.component.ts +++ b/imxweb/projects/qbm/src/lib/busy-indicator/busy-indicator.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,8 +29,6 @@ import { Component } from '@angular/core'; @Component({ selector: 'imx-busy-indicator', templateUrl: './busy-indicator.component.html', - styleUrls: ['./busy-indicator.component.scss'] + styleUrls: ['./busy-indicator.component.scss'], }) -export class BusyIndicatorComponent { - -} \ No newline at end of file +export class BusyIndicatorComponent {} diff --git a/imxweb/projects/qbm/src/lib/busy-indicator/busy-indicator.module.ts b/imxweb/projects/qbm/src/lib/busy-indicator/busy-indicator.module.ts index 01e22b893..8993ced3a 100644 --- a/imxweb/projects/qbm/src/lib/busy-indicator/busy-indicator.module.ts +++ b/imxweb/projects/qbm/src/lib/busy-indicator/busy-indicator.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,23 +24,14 @@ * */ -import { NgModule } from "@angular/core"; -import { MatProgressSpinnerModule } from "@angular/material/progress-spinner"; -import { TranslateModule } from "@ngx-translate/core"; -import { BusyIndicatorComponent } from "./busy-indicator.component"; +import { NgModule } from '@angular/core'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { TranslateModule } from '@ngx-translate/core'; +import { BusyIndicatorComponent } from './busy-indicator.component'; @NgModule({ - imports: [ - MatProgressSpinnerModule, - TranslateModule - ], - declarations: [ - BusyIndicatorComponent - ], - exports: [ - BusyIndicatorComponent - ] + imports: [MatProgressSpinnerModule, TranslateModule], + declarations: [BusyIndicatorComponent], + exports: [BusyIndicatorComponent], }) -export class BusyIndicatorModule { - -} \ No newline at end of file +export class BusyIndicatorModule {} diff --git a/imxweb/projects/qbm/src/lib/cache/cache.service.ts b/imxweb/projects/qbm/src/lib/cache/cache.service.ts index 09f26c2d6..b8562831c 100644 --- a/imxweb/projects/qbm/src/lib/cache/cache.service.ts +++ b/imxweb/projects/qbm/src/lib/cache/cache.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,22 +24,20 @@ * */ -import { Injectable } from "@angular/core"; -import { CachedPromise } from "imx-qbm-dbts"; -import { Subscription } from "rxjs"; -import { AuthenticationService } from "../authentication/authentication.service"; +import { Injectable } from '@angular/core'; +import { CachedPromise } from '@imx-modules/imx-qbm-dbts'; +import { Subscription } from 'rxjs'; +import { AuthenticationService } from '../authentication/authentication.service'; /** Provides caching for promises. */ @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class CacheService { - - constructor(private readonly authService: AuthenticationService) { - } + constructor(private readonly authService: AuthenticationService) {} private defaultOptions: CacheOptions = { - flushOnAuthentication: true + flushOnAuthentication: true, }; /** Returns a new cached promise. The cache options can be configured @@ -47,12 +45,13 @@ export class CacheService { * @param func Construction function for a new promise. */ public buildCache(func: () => Promise, opts?: CacheOptions): CachedPromise { - var o = { ...this.defaultOptions, opts }; var subscription: Subscription; - const cachedPromise = new CachedPromise(func, () => { subscription?.unsubscribe(); }); - + const cachedPromise = new CachedPromise(func, () => { + subscription?.unsubscribe(); + }); + if (o.flushOnAuthentication) { subscription = this.authService.onSessionResponse.subscribe(() => cachedPromise.reset()); } @@ -65,5 +64,5 @@ export interface CacheOptions { /** Sets whether the cache will be flushed on authentication events, i.e. * when the user logs on or off. */ - flushOnAuthentication?: boolean + flushOnAuthentication?: boolean; } diff --git a/imxweb/projects/qbm/src/lib/captcha/captcha.component.html b/imxweb/projects/qbm/src/lib/captcha/captcha.component.html index ef3627a4a..a4cc00d9a 100644 --- a/imxweb/projects/qbm/src/lib/captcha/captcha.component.html +++ b/imxweb/projects/qbm/src/lib/captcha/captcha.component.html @@ -1,3 +1,37 @@ - + + {{ LdsCaptchaInfo | translate }} + -

    The CAPTCHA configuration is unsupported.

    \ No newline at end of file +
    + +

    The CAPTCHA configuration is unsupported.

    + + + +
    + +
    + + + +
    diff --git a/imxweb/projects/qbm/src/lib/captcha/captcha.component.scss b/imxweb/projects/qbm/src/lib/captcha/captcha.component.scss new file mode 100644 index 000000000..9b048dfb3 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/captcha/captcha.component.scss @@ -0,0 +1,18 @@ +@import 'common/captcha-login'; + +:host { + display: block; +} + +.captcha-container { + display: flex; + + .mat-mdc-form-field { + flex: 1 1 auto; + } + + img { + padding-right: 1em; + height: 34px; + } +} diff --git a/imxweb/projects/qbm/src/lib/captcha/captcha.component.ts b/imxweb/projects/qbm/src/lib/captcha/captcha.component.ts index fa142b00d..8fb1d6789 100644 --- a/imxweb/projects/qbm/src/lib/captcha/captcha.component.ts +++ b/imxweb/projects/qbm/src/lib/captcha/captcha.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,23 +24,62 @@ * */ -import { Component, Input } from '@angular/core'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; import { AppConfigService } from '../appConfig/appConfig.service'; -import { CaptchaMode, CaptchaService } from './captcha.service'; +import { CaptchaService } from './captcha.service'; @Component({ - selector: 'imx-captcha', - templateUrl: './captcha.component.html', - styles: [`:host { display: block; } img { height: 100%; }`] + selector: 'imx-captcha', + templateUrl: './captcha.component.html', + styleUrls: ['./captcha.component.scss'], }) export class CaptchaComponent { + /** + * Disable next button. + */ + @Input() disableButton: boolean; - constructor(public readonly captchaSvc: CaptchaService, public readonly appConfig: AppConfigService) { - } + /** + * Show back button. + */ + @Input() showBackButton = false; - @Input() builtInCaptchaUrl: string = 'passwordreset/captchaimage'; + /** + * Show all buttons. + */ + @Input() showAllButtons = true; - isBuiltIn(): boolean { - return this.captchaSvc.Mode == CaptchaMode.BuiltIn; - } -} \ No newline at end of file + /** + * Event emitter for the next button, which should take the user to the next function. + */ + @Output() nextClick: EventEmitter = new EventEmitter(); + + @Output() onBackEvent: EventEmitter = new EventEmitter(); + + /** + * Url for One Identity's ReCaptcha image + */ + builtInCaptchaUrl: string; + public LdsCaptchaInfo: string = '#LDS#Enter the characters from the image.'; + + constructor( + public readonly captchaSvc: CaptchaService, + public readonly appConfig: AppConfigService, + ) { + this.builtInCaptchaUrl = this.captchaSvc.captchaImageUrl; + } + + /** + * Emits an event to the parent component, when the Next button was clicked. + */ + public onNext() { + this.nextClick.emit(true); + } + + /** + * Emits an event to the parent component, when the Back button was clicked. + */ + public onBack(): void { + this.onBackEvent.emit(); + } +} diff --git a/imxweb/projects/qbm/src/lib/captcha/captcha.module.ts b/imxweb/projects/qbm/src/lib/captcha/captcha.module.ts index 8ddcbf55b..11ab28e52 100644 --- a/imxweb/projects/qbm/src/lib/captcha/captcha.module.ts +++ b/imxweb/projects/qbm/src/lib/captcha/captcha.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -35,21 +35,8 @@ import { TranslateModule } from '@ngx-translate/core'; import { CaptchaComponent } from './captcha.component'; @NgModule({ - imports: [ - CommonModule, - FormsModule, - EuiCoreModule, - MatButtonModule, - MatFormFieldModule, - MatInputModule, - TranslateModule - ], - declarations: [ - CaptchaComponent - ], - exports: [ - CaptchaComponent - ] + imports: [CommonModule, FormsModule, EuiCoreModule, MatButtonModule, MatFormFieldModule, MatInputModule, TranslateModule], + declarations: [CaptchaComponent], + exports: [CaptchaComponent], }) -export class CaptchaModule { -} \ No newline at end of file +export class CaptchaModule {} diff --git a/imxweb/projects/qbm/src/lib/captcha/captcha.service.ts b/imxweb/projects/qbm/src/lib/captcha/captcha.service.ts index d5389b097..0806185ee 100644 --- a/imxweb/projects/qbm/src/lib/captcha/captcha.service.ts +++ b/imxweb/projects/qbm/src/lib/captcha/captcha.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,40 +24,72 @@ * */ -import { Injectable } from "@angular/core"; +import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class CaptchaService { - - constructor() { - this.ReinitCaptcha(); - } - private _recaptchaPublicKey: string; - public get recaptchaPublicKey() { return this._recaptchaPublicKey; } - /** CAPTCHA response entered by the user. */ - public Response: string = ""; + public Response: string = ''; + /** + * Url parameter for One Identity's ReCaptcha + */ public builtInUrlParameter: string; - enableReCaptcha(publicKey: string) { - throw new Error("not supported"); - this._recaptchaPublicKey = publicKey; + /** + * Url parameter for captcha image. + */ + public captchaImageUrl: string; + + /** + * This variable holds the public key for ReCaptcha V3. + * The ReCaptchaPublicKey can be set in Password Reset Portal config. + */ + public get recaptchaPublicKey(): string { + return this._recaptchaPublicKey; } + /** + * Holds a CaptchaMode based on if the user has recaptchaPublicKey or not. + */ public get Mode(): CaptchaMode { - if (this._recaptchaPublicKey) - return CaptchaMode.RecaptchaV2; + if (this.recaptchaPublicKey) return CaptchaMode.RecaptchaV3; return CaptchaMode.BuiltIn; } + /** + * True if One Identity's ReCaptcha is enabled + */ + public get isBuiltIn(): boolean { + return this.Mode === CaptchaMode.BuiltIn; + } + + /** + * True if ReCaptchaV3 is enabled + */ + public get isReCaptchaV3(): boolean { + return this.Mode === CaptchaMode.RecaptchaV3; + } + + constructor() { + this.ReinitCaptcha(); + } + + /** + * Enables Google's ReCaptcha V3 function. + * @param publicKey Google ReCaptcha's public key, provided by Password Reset Portal's config. + */ + public enableReCaptcha(publicKey: string) { + this._recaptchaPublicKey = publicKey; + } + /** Reinitializes the image to help users who cannot read a particular CAPTCHA, or if an authentication * attempt has failed. */ - ReinitCaptcha() { - this.Response = ""; + public ReinitCaptcha() { + this.Response = ''; // Add a cache-busting parameter this.builtInUrlParameter = '?t=' + new Date().getTime(); @@ -66,5 +98,6 @@ export class CaptchaService { export enum CaptchaMode { BuiltIn, - RecaptchaV2 -} \ No newline at end of file + RecaptchaV2, + RecaptchaV3, +} diff --git a/imxweb/projects/qbm/src/lib/cdr/Readme.md b/imxweb/projects/qbm/src/lib/cdr/Readme.md index 9cdfffead..d960b7a12 100644 --- a/imxweb/projects/qbm/src/lib/cdr/Readme.md +++ b/imxweb/projects/qbm/src/lib/cdr/Readme.md @@ -1,19 +1,22 @@ # CDR: **C**olumn **D**ependent **R**eference + The classes and interfaces in the cdr module supply the possibility to dynamically include UI components that allow editing of entity properties. -The name *CDR* refers to the fact that those editor components are not included -directly in the code but instead *referenced* indirectly. +The name _CDR_ refers to the fact that those editor components are not included +directly in the code but instead _referenced_ indirectly. This indirect reference will be resolved to an editor at runtime. -What editor component this will be exactly in the end *depends* on the -*column* and its meta-data. +What editor component this will be exactly in the end _depends_ on the +_column_ and its meta-data. ## The registry: resolving CDRs to editors + Generally, when you want to include an editor for a column dependent reference, you make use of the [CdrEditorComponent](cdr-editor\cdr-editor.component.ts), which provides a convenient tag that you can include in a template: imx-cdr-editor. E.g. + ```html

    Resulting Editor

    @@ -31,11 +34,13 @@ If none of the registered plugins was able to supply an editor the registry resolves to a EditDefaultComponent. The registry askes the registered plugins in FILO fashion, so that the plugins registered first, -will be the last to be asked. This order was chosen assuming that the more appliation specific a module is -* the later it will be initialized and -* the more likely it can provide a specialized CDR editor. +will be the last to be asked. This order was chosen assuming that the more appliation specific a module is + +- the later it will be initialized and +- the more likely it can provide a specialized CDR editor. ## Out-of-the-box CDR editors + As mentioned above, the CdrRegistryService solely supplies a single fallback editor out of the box: [EditDefaultComponent](edit-default/edit-default.component.ts). All other editors have to be provided by plugins that register at the CdrRegistryService. @@ -43,17 +48,20 @@ All other editors have to be provided by plugins that register at the CdrRegistr Nevertheless within the CDR module there are two plugins that supply some general default editors. [DefaultCdrEditorProvider](default-cdr-editor-provider.ts) provided editors: -* [EditBooleanComponent](edit-boolean/edit-boolean.component.ts): for boolean properties -* [EditLimitedValueComponent](edit-limited-value/edit-limited-value.component.ts): for properties where the value can be one of a limited set of allowed values -* [EditMultiLimitedValueComponent](edit-multi-limited-value/edit-multi-limited-value.component.ts): for properties where the value can be one or many of a limited set of alowed values -* [EditMulitValueComponent](edit-multi-value/edit-multi-value.component.ts) -* [EditMultiLineComponent](edit-multiline/edit-multiline.component.ts): for text properties that allow multiple lines -* [EditNumberComponent](edit-number/edit-number.component.ts): for numeric properties + +- [EditBooleanComponent](edit-boolean/edit-boolean.component.ts): for boolean properties +- [EditLimitedValueComponent](edit-limited-value/edit-limited-value.component.ts): for properties where the value can be one of a limited set of allowed values +- [EditMultiLimitedValueComponent](edit-multi-limited-value/edit-multi-limited-value.component.ts): for properties where the value can be one or many of a limited set of alowed values +- [EditMulitValueComponent](edit-multi-value/edit-multi-value.component.ts) +- [EditMultiLineComponent](edit-multiline/edit-multiline.component.ts): for text properties that allow multiple lines +- [EditNumberComponent](edit-number/edit-number.component.ts): for numeric properties [FkCdrEditorProvider](fk-cdr-editor-provider.ts) provided editors: -* [EditFkComponent](edit-fk/edit-fk.component.ts): for foreign key properties, i.e. properties pointing to other entities, e.g. "primary department" property of a person + +- [EditFkComponent](edit-fk/edit-fk.component.ts): for foreign key properties, i.e. properties pointing to other entities, e.g. "primary department" property of a person If you want to make use of these two provider services you have to register them at the CdrRegistryService, e.g. during your app's initialization like this: + ```typescript export function initApp(registry: CdrRegistryService) { return () => @@ -66,27 +74,32 @@ export function initApp(registry: CdrRegistryService) { ``` ## Providing custom CDR editors + First thing you have to do is to create an editor component. Second thing is to create a provider class that must be registered at the CdrRegistryService. ### Creating a CDR editor component + A CDR editor is just a normal Angular UI component that implements a certain interface: [CdrEditor](cdr-editor.interface.ts) This interface defines a method that should be called by the provider to tell the editor which column dependent reference it should display/edit: + ```typescript bind(cdref: ColumnDependentReference) ``` ### Creating a CDR editor provider + A CDR editor provider is a class implementing the [CdrEditorProvider](cdr-editor-provider.interface.ts) interface. Although not necessary it is a good choice to make this class an Angular service. The interface allows to supply multiple different editor types -but if you want to supply instances of a single editor class only, +but if you want to supply instances of a single editor class only, there is a convenient abstract base class that you can inherit from: [BaseCdrEditorProvider](base-cdr-editor-provider.ts) When you inherit from BaseCdrEditorProvider the only method you have to implement is the one that 'accepts' a given column dependent reference, i.e. whether the provided editor fits for it. + ```typescript protected abstract accept(cdref: ColumnDependentReference): boolean; -``` \ No newline at end of file +``` diff --git a/imxweb/projects/qbm/src/lib/cdr/base-cdr-editor-provider.ts b/imxweb/projects/qbm/src/lib/cdr/base-cdr-editor-provider.ts index 8f8b41674..00ef74206 100644 --- a/imxweb/projects/qbm/src/lib/cdr/base-cdr-editor-provider.ts +++ b/imxweb/projects/qbm/src/lib/cdr/base-cdr-editor-provider.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,9 +24,9 @@ * */ -import { CdrEditor } from './cdr-editor.interface'; +import { ComponentFactoryResolver, ComponentRef, ViewContainerRef } from '@angular/core'; import { CdrEditorProvider } from './cdr-editor-provider.interface'; -import { ViewContainerRef, ComponentRef, ComponentFactoryResolver } from '@angular/core'; +import { CdrEditor } from './cdr-editor.interface'; import { ColumnDependentReference } from './column-dependent-reference.interface'; /** @@ -44,7 +44,7 @@ export abstract class BaseCdrEditorProvider implements CdrE * @param cdref A column dependent reference that contains the data for the editor. * @returns An instance of {@link CdrEditor}, that can be used for editing data. */ - public createEditor(parent: ViewContainerRef, cdref: ColumnDependentReference): ComponentRef { + public createEditor(parent: ViewContainerRef, cdref: ColumnDependentReference): ComponentRef | null { if (!this.accept(cdref)) { return null; } diff --git a/imxweb/projects/qbm/src/lib/cdr/base-cdr.spec.ts b/imxweb/projects/qbm/src/lib/cdr/base-cdr.spec.ts index ea77131aa..a5aca1acd 100644 --- a/imxweb/projects/qbm/src/lib/cdr/base-cdr.spec.ts +++ b/imxweb/projects/qbm/src/lib/cdr/base-cdr.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,17 +25,18 @@ */ import { BaseCdr } from './base-cdr'; -import { IEntityColumn } from 'imx-qbm-dbts'; +import { IEntityColumn } from '@imx-modules/imx-qbm-dbts'; describe('BaseCdr', () => { - [ true, false ].forEach(canEdit => + [true, false].forEach((canEdit) => it('can create', () => { - const cdr = new BaseCdr({ - GetMetadata: () => ({ - CanEdit: () => canEdit - }) - } as IEntityColumn); - expect(cdr.column).toBeDefined(); - expect(cdr.isReadOnly()).toEqual(!canEdit); - })); + const cdr = new BaseCdr({ + GetMetadata: () => ({ + CanEdit: () => canEdit, + }), + } as IEntityColumn); + expect(cdr.column).toBeDefined(); + expect(cdr.isReadOnly()).toEqual(!canEdit); + }), + ); }); diff --git a/imxweb/projects/qbm/src/lib/cdr/base-cdr.ts b/imxweb/projects/qbm/src/lib/cdr/base-cdr.ts index bb7ca0153..464646adb 100644 --- a/imxweb/projects/qbm/src/lib/cdr/base-cdr.ts +++ b/imxweb/projects/qbm/src/lib/cdr/base-cdr.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,10 +24,10 @@ * */ -import { IEntityColumn } from 'imx-qbm-dbts'; +import { IEntityColumn } from '@imx-modules/imx-qbm-dbts'; -import { ColumnDependentReference } from './column-dependent-reference.interface'; import { Subject } from 'rxjs'; +import { ColumnDependentReference } from './column-dependent-reference.interface'; /** * Generic implementation of a {@link ColumnDependentReference | column dependent reference}. @@ -37,7 +37,6 @@ import { Subject } from 'rxjs'; * const value = new BaseCdr(columnToUse, undefined, renderedReadonlyOrNot ); // Build a CDR with a give readOnly state */ export class BaseCdr implements ColumnDependentReference { - /** * A small hint, that is displayed on a hint icon */ @@ -53,18 +52,17 @@ export class BaseCdr implements ColumnDependentReference { */ public minlengthSubject = new Subject(); - constructor(public readonly column: IEntityColumn, public readonly display?: string, public readonly isReadOnlyColumn?: boolean) {} + constructor( + public readonly column: IEntityColumn, + public readonly display?: string, + ) {} /** * Checks, whether a CDR should be rendered as read-only * @returns True, if the CDR needs to be show as 'read-only', otherwise false. */ public isReadOnly(): boolean { - if (this.isReadOnlyColumn !== undefined) { - return this.column == null || this.isReadOnlyColumn || !this.column.GetMetadata().CanEdit(); - } else { - return this.column == null || !this.column.GetMetadata().CanEdit(); - } + return this.column == null || !this.column.GetMetadata().CanEdit(); } /** diff --git a/imxweb/projects/qbm/src/lib/cdr/base-readonly-cdr.ts b/imxweb/projects/qbm/src/lib/cdr/base-readonly-cdr.ts index 83981a169..1bc6d04de 100644 --- a/imxweb/projects/qbm/src/lib/cdr/base-readonly-cdr.ts +++ b/imxweb/projects/qbm/src/lib/cdr/base-readonly-cdr.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,6 @@ * */ - import { ColumnDependentReference } from './column-dependent-reference.interface'; import { BaseCdr } from './base-cdr'; diff --git a/imxweb/projects/qbm/src/lib/cdr/cdr-editor-provider-registry.interface.ts b/imxweb/projects/qbm/src/lib/cdr/cdr-editor-provider-registry.interface.ts index 16382bd15..bac51a110 100644 --- a/imxweb/projects/qbm/src/lib/cdr/cdr-editor-provider-registry.interface.ts +++ b/imxweb/projects/qbm/src/lib/cdr/cdr-editor-provider-registry.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -35,10 +35,9 @@ import { CdrEditorProvider } from './cdr-editor-provider.interface'; * registered editor provider. */ export interface CdrEditorProviderRegistry extends CdrEditorProvider { - - /** - * Registers an editor provider for column dependent references. - * @param provider The editor provider to register. - */ - register(provider: CdrEditorProvider): void; + /** + * Registers an editor provider for column dependent references. + * @param provider The editor provider to register. + */ + register(provider: CdrEditorProvider): void; } diff --git a/imxweb/projects/qbm/src/lib/cdr/cdr-editor-provider.interface.ts b/imxweb/projects/qbm/src/lib/cdr/cdr-editor-provider.interface.ts index a2510e7e6..052a371ff 100644 --- a/imxweb/projects/qbm/src/lib/cdr/cdr-editor-provider.interface.ts +++ b/imxweb/projects/qbm/src/lib/cdr/cdr-editor-provider.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,23 +24,22 @@ * */ -import { ColumnDependentReference } from './column-dependent-reference.interface'; +import { ComponentRef, ViewContainerRef } from '@angular/core'; import { CdrEditor } from './cdr-editor.interface'; -import { ViewContainerRef, ComponentRef } from '@angular/core'; +import { ColumnDependentReference } from './column-dependent-reference.interface'; /** * Defines an interface that can provide an UI component to display and/or edit a column dependent reference, * namely a CDR "editor". */ export interface CdrEditorProvider { - - /** - * Creates an editor for a given column dependent reference. - * - * If the column dependent reference does not suite any of the editors - * the provider can provide, it should return null. - * - * @param cdref The column dependent reference. - */ - createEditor(parent: ViewContainerRef, cdref: ColumnDependentReference): ComponentRef; + /** + * Creates an editor for a given column dependent reference. + * + * If the column dependent reference does not suite any of the editors + * the provider can provide, it should return null. + * + * @param cdref The column dependent reference. + */ + createEditor(parent: ViewContainerRef, cdref: ColumnDependentReference): ComponentRef | null; } diff --git a/imxweb/projects/qbm/src/lib/cdr/cdr-editor.interface.ts b/imxweb/projects/qbm/src/lib/cdr/cdr-editor.interface.ts index 20381aa4a..27b1fded6 100644 --- a/imxweb/projects/qbm/src/lib/cdr/cdr-editor.interface.ts +++ b/imxweb/projects/qbm/src/lib/cdr/cdr-editor.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -33,16 +33,16 @@ import { ColumnDependentReference } from './column-dependent-reference.interface * Interface for the argument, that it emitted in the CDR editor. */ export interface ValueHasChangedEventArg { - /** - * The new value of the editor. - */ - value: any; + /** + * The new value of the editor. + */ + value: any; - /** - * A flag to show whether the emitting of a follow up event should be forced - * (evaluated by {@link CdrEditorComponent|CdrEditorComponent}). - */ - forceEmit?: boolean; + /** + * A flag to show whether the emitting of a follow up event should be forced + * (evaluated by {@link CdrEditorComponent|CdrEditorComponent}). + */ + forceEmit?: boolean; } /** @@ -54,6 +54,11 @@ export interface CdrEditor { */ control: AbstractControl; + /** + * Determines, if the control should only be validated after the value has been changed + */ + validateOnlyOnChange?: boolean; + /** * An event, that is emitted, if the value of the cdr has changed. */ diff --git a/imxweb/projects/qbm/src/lib/cdr/cdr-editor/cdr-editor.component.html b/imxweb/projects/qbm/src/lib/cdr/cdr-editor/cdr-editor.component.html index 56b101a3b..4fbad3415 100644 --- a/imxweb/projects/qbm/src/lib/cdr/cdr-editor/cdr-editor.component.html +++ b/imxweb/projects/qbm/src/lib/cdr/cdr-editor/cdr-editor.component.html @@ -4,6 +4,6 @@

    - {{description}} + {{ description }}

    diff --git a/imxweb/projects/qbm/src/lib/cdr/cdr-editor/cdr-editor.component.scss b/imxweb/projects/qbm/src/lib/cdr/cdr-editor/cdr-editor.component.scss index 075680508..25a05bfda 100644 --- a/imxweb/projects/qbm/src/lib/cdr/cdr-editor/cdr-editor.component.scss +++ b/imxweb/projects/qbm/src/lib/cdr/cdr-editor/cdr-editor.component.scss @@ -4,8 +4,8 @@ /* place for hints and other indicators */ > .hint-container { flex-grow: 0; - margin: 0 0.5em 2em; + margin: 0 0.5em 1.25em; align-items: center; display: flex; } -} \ No newline at end of file +} diff --git a/imxweb/projects/qbm/src/lib/cdr/cdr-editor/cdr-editor.component.ts b/imxweb/projects/qbm/src/lib/cdr/cdr-editor/cdr-editor.component.ts index f6979e0aa..7c13db180 100644 --- a/imxweb/projects/qbm/src/lib/cdr/cdr-editor/cdr-editor.component.ts +++ b/imxweb/projects/qbm/src/lib/cdr/cdr-editor/cdr-editor.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,13 +24,13 @@ * */ -import { Component, Input, ViewChild, ViewContainerRef, OnChanges, SimpleChanges, EventEmitter, Output, ElementRef } from '@angular/core'; +import { Component, ElementRef, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewChild, ViewContainerRef } from '@angular/core'; import { AbstractControl } from '@angular/forms'; -import { ColumnDependentReference } from '../column-dependent-reference.interface'; -import { CdrRegistryService } from '../cdr-registry.service'; import { ClassloggerService } from '../../classlogger/classlogger.service'; import { CdrEditor } from '../cdr-editor.interface'; +import { CdrRegistryService } from '../cdr-registry.service'; +import { ColumnDependentReference } from '../column-dependent-reference.interface'; /** * This component provides an {@link CdrEditor|editor} for a {@link ColumnDependentReference|column dependent reference}. @@ -42,11 +42,11 @@ import { CdrEditor } from '../cdr-editor.interface'; * In the *.ts file: * cdrForColumns: ColumnDependentReferences; * formGroup: FormGroup<{cdrArray: FormArray}>; - * In the *.html file: * + * In the *.html file: * * --> *
    - * { if (value?.forceEmit === true) { this.valueChange.emit(value.value); @@ -122,14 +131,17 @@ export class CdrEditorComponent implements OnChanges { } }); } - if (ref.instance.pendingChanged) { + if (ref?.instance.pendingChanged) { ref.instance.pendingChanged.subscribe((value) => { this.pendingChanged.emit(value); }); } - this.controlCreated.emit(ref.instance.control); + this.controlCreated.emit(ref?.instance.control); this.elementRef.nativeElement.setAttribute('data-imx-identifier', `cdr-editor-${this.cdr.column.ColumnName}`); - this.editor = ref.instance; + this.editor = ref?.instance; + if (this.editor) { + this.editor.validateOnlyOnChange = this.validateOnlyOnChange; + } } catch (e) { this.logger.error(this, 'Failed to create editor for column dependent reference.', e); } diff --git a/imxweb/projects/qbm/src/lib/cdr/cdr-factory.service.ts b/imxweb/projects/qbm/src/lib/cdr/cdr-factory.service.ts index 33a3c6b26..5f0933524 100644 --- a/imxweb/projects/qbm/src/lib/cdr/cdr-factory.service.ts +++ b/imxweb/projects/qbm/src/lib/cdr/cdr-factory.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { Injectable } from '@angular/core'; -import { IEntity, IEntityColumn } from 'imx-qbm-dbts'; +import { IEntity, IEntityColumn } from '@imx-modules/imx-qbm-dbts'; import { BaseCdr } from './base-cdr'; import { BaseReadonlyCdr } from './base-readonly-cdr'; import { ColumnDependentReference } from './column-dependent-reference.interface'; @@ -36,19 +36,19 @@ import { ColumnDependentReference } from './column-dependent-reference.interface export class CdrFactoryService { /** * Builds an array of column dependent references, depending on the columns provided. - * If the column does not exists, it is left out of the array. + * If the column does not exists, it is filtered out via the Boolean constructor. * @param entity The complete entity that provides the columns. * @param columnNames The list of columns, a CDR is needed for. * @param readOnly If true, readonly CDR will be build otherwise normal base CDR. * @returns A list of column dependent references. */ - public buildCdrFromColumnList(entity: IEntity, columnNames: string[], readOnly: boolean = false): ColumnDependentReference[] { - return columnNames.map((column) => this.buildCdr(entity, column, readOnly)).filter((cdr) => cdr != null); + public buildCdrFromColumnList(entity: IEntity | undefined, columnNames: string[], readOnly: boolean = false): ColumnDependentReference[] { + return columnNames.map((column) => this.buildCdr(entity, column, readOnly)).filter(Boolean) as ColumnDependentReference[]; } /** * Builds an array of column dependent references, depending on the columns provided. - * If the column does not exists, it is left out of the array. + * If the column does not exists, it is filtered out via the Boolean constructor. * You are able to add a list of column names, that should be readonly * @param entity The complete entity that provides the columns. * @param columnNames The list of columns, a CDR is needed for. @@ -58,9 +58,11 @@ export class CdrFactoryService { public buildCdrFromColumnListAdvanced( entity: IEntity, columnNames: string[], - readOnlyColumns: string[] = [] + readOnlyColumns: string[] = [], ): ColumnDependentReference[] { - return columnNames.map((column) => this.buildCdr(entity, column, readOnlyColumns.includes(column))).filter((cdr) => cdr != null); + return columnNames + .map((column) => this.buildCdr(entity, column, readOnlyColumns.includes(column))) + .filter(Boolean) as ColumnDependentReference[]; } /** @@ -70,10 +72,15 @@ export class CdrFactoryService { * @param readOnly If true, a read-only CDR will be build otherwise a normal base CDR. * @returns The column dependent reference or null, if the column is not defined in the entity. */ - public buildCdr(entity: IEntity, columnName: string, readOnly: boolean = false, columnDisplay?: string): ColumnDependentReference { + public buildCdr( + entity: IEntity | undefined, + columnName: string, + readOnly: boolean = false, + columnDisplay?: string, + ): ColumnDependentReference | undefined { const column = CdrFactoryService.tryGetColumn(entity, columnName); - return column == null ? null : readOnly ? new BaseReadonlyCdr(column, columnDisplay) : new BaseCdr(column, columnDisplay); + return column == null ? undefined : readOnly ? new BaseReadonlyCdr(column, columnDisplay) : new BaseCdr(column, columnDisplay); } /** @@ -82,7 +89,7 @@ export class CdrFactoryService { * @param columnName The name of the column, that should be provided. * @returns Null, if the entity doesn't have a column with the given name otherwise the column is returned. */ - public static tryGetColumn(entity: IEntity, columnName: string): IEntityColumn { + public static tryGetColumn(entity: IEntity | undefined, columnName: string): IEntityColumn | undefined { try { return entity?.GetColumn(columnName); } catch { diff --git a/imxweb/projects/qbm/src/lib/cdr/cdr-registry.service.ts b/imxweb/projects/qbm/src/lib/cdr/cdr-registry.service.ts index f48a54bf0..362cadeda 100644 --- a/imxweb/projects/qbm/src/lib/cdr/cdr-registry.service.ts +++ b/imxweb/projects/qbm/src/lib/cdr/cdr-registry.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,14 +24,14 @@ * */ -import { Injectable, ErrorHandler, ViewContainerRef, ComponentFactoryResolver, ComponentRef } from '@angular/core'; +import { ComponentFactoryResolver, ComponentRef, ErrorHandler, Injectable, ViewContainerRef } from '@angular/core'; +import { ClassloggerService } from '../classlogger/classlogger.service'; import { CdrEditorProviderRegistry } from './cdr-editor-provider-registry.interface'; import { CdrEditorProvider } from './cdr-editor-provider.interface'; -import { ColumnDependentReference } from './column-dependent-reference.interface'; import { CdrEditor } from './cdr-editor.interface'; +import { ColumnDependentReference } from './column-dependent-reference.interface'; import { EditDefaultComponent } from './edit-default/edit-default.component'; -import { ClassloggerService } from '../classlogger/classlogger.service'; /** * A service that is capable of {@link create|creating} an {@link CdrEditor|editor} @@ -45,10 +45,9 @@ import { ClassloggerService } from '../classlogger/classlogger.service'; * @see CdrEditorProvider */ @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class CdrRegistryService implements CdrEditorProviderRegistry { - /** * This array contains the registered (@see register) providers. */ @@ -61,8 +60,11 @@ export class CdrRegistryService implements CdrEditorProviderRegistry { * @param logger The logger used for logging messages. * @throws {Error} Throws an error if the given error handler is null or undefined. */ - constructor(private componentFactoryResolver: ComponentFactoryResolver, - private errorHandler: ErrorHandler, private logger: ClassloggerService) { } + constructor( + private componentFactoryResolver: ComponentFactoryResolver, + private errorHandler: ErrorHandler, + private logger: ClassloggerService, + ) {} /** * Registers an editor provider for column dependent references. @@ -76,12 +78,15 @@ export class CdrRegistryService implements CdrEditorProviderRegistry { throw new Error('The provider must not be null or undefined.'); } - if (this.registeredProviders.find(p => p === provider)) { + if (this.registeredProviders.find((p) => p === provider)) { throw new Error('This provider has already been registered.'); } - this.logger.debug(this, `Registering '${this.className(provider)}' as - column dependent reference editor provider #${this.registeredProviders.length + 1}.`); + this.logger.debug( + this, + `Registering '${this.className(provider)}' as + column dependent reference editor provider #${this.registeredProviders.length + 1}.`, + ); this.registeredProviders.push(provider); } @@ -100,7 +105,7 @@ export class CdrRegistryService implements CdrEditorProviderRegistry { * @param cdref The column dependent reference for which an editor shall be created * @throws {Error} Throws an error if the given column dependent reference is null or undefined. */ - public createEditor(parent: ViewContainerRef, cdref: ColumnDependentReference): ComponentRef { + public createEditor(parent: ViewContainerRef, cdref: ColumnDependentReference): ComponentRef | null { if (cdref == null) { throw new Error('The cdref must not be null or undefined.'); } @@ -113,16 +118,26 @@ export class CdrRegistryService implements CdrEditorProviderRegistry { const editor = provider.createEditor(parent, cdref); if (!editor) { - this.logger.debug(this, `Provider '${this.className(provider)}' returned '${this.className(editor)}' - for '${this.className(cdref)}' -> skipping it.'`); + this.logger.debug( + this, + `Provider '${this.className(provider)}' returned '${this.className(editor)}' + for '${this.className(cdref)}' -> skipping it.'`, + ); } else { - this.logger.debug(this, `Returning editor '${this.className(editor.instance)}' - for '${this.className(cdref)}' created by '${this.className(provider)}'.`); + this.logger.debug( + this, + `Returning editor '${this.className(editor.instance)}' + for '${this.className(cdref)}' created by '${this.className(provider)}'.`, + ); return editor; } } catch (e) { - this.logger.error(this, `Error during attempt to create editor through provider ${this.className(provider)} - -> skipping the provider this time.`, e); + this.logger.error( + this, + `Error during attempt to create editor through provider ${this.className(provider)} + -> skipping the provider this time.`, + e, + ); this.errorHandler.handleError(e); } } diff --git a/imxweb/projects/qbm/src/lib/cdr/cdr-sidesheet/cdr-sidesheet-config.ts b/imxweb/projects/qbm/src/lib/cdr/cdr-sidesheet/cdr-sidesheet-config.ts index 2d2bd2052..091b9807c 100644 --- a/imxweb/projects/qbm/src/lib/cdr/cdr-sidesheet/cdr-sidesheet-config.ts +++ b/imxweb/projects/qbm/src/lib/cdr/cdr-sidesheet/cdr-sidesheet-config.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qbm/src/lib/cdr/cdr-sidesheet/cdr-sidesheet.component.html b/imxweb/projects/qbm/src/lib/cdr/cdr-sidesheet/cdr-sidesheet.component.html index 5b25aff80..6138ca778 100644 --- a/imxweb/projects/qbm/src/lib/cdr/cdr-sidesheet/cdr-sidesheet.component.html +++ b/imxweb/projects/qbm/src/lib/cdr/cdr-sidesheet/cdr-sidesheet.component.html @@ -1,12 +1,19 @@
    - + - +
    - +
    diff --git a/imxweb/projects/qbm/src/lib/cdr/cdr-sidesheet/cdr-sidesheet.component.ts b/imxweb/projects/qbm/src/lib/cdr/cdr-sidesheet/cdr-sidesheet.component.ts index 8c9ab6c3a..9004b50de 100644 --- a/imxweb/projects/qbm/src/lib/cdr/cdr-sidesheet/cdr-sidesheet.component.ts +++ b/imxweb/projects/qbm/src/lib/cdr/cdr-sidesheet/cdr-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,21 +25,20 @@ */ import { Component, Inject } from '@angular/core'; -import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; -import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { AbstractControl, UntypedFormGroup } from '@angular/forms'; +import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; import { CdrSidesheetConfig } from './cdr-sidesheet-config'; /** * Provides a side sheet, that displays a form with {@link CdrEditor | cdr editors}. - * + * * Writeable properties can be edited. */ @Component({ - templateUrl: './cdr-sidesheet.component.html' + templateUrl: './cdr-sidesheet.component.html', }) export class CdrSidesheetComponent { - /** * The form, that stores the editors. */ @@ -52,7 +51,7 @@ export class CdrSidesheetComponent { */ constructor( @Inject(EUI_SIDESHEET_DATA) public readonly config: CdrSidesheetConfig, - public readonly sidesheetRef: EuiSidesheetRef + public readonly sidesheetRef: EuiSidesheetRef, ) {} /** @@ -60,7 +59,7 @@ export class CdrSidesheetComponent { * @param name The name of the control. * @param control The form control that should be added. */ - public addFormControl(name: string, control: UntypedFormControl): void { + public addFormControl(name: string, control: AbstractControl): void { this.cdrFormGroup.addControl(name, control); } } diff --git a/imxweb/projects/qbm/src/lib/cdr/cdr.module.ts b/imxweb/projects/qbm/src/lib/cdr/cdr.module.ts index 118515868..ae3f3c91b 100644 --- a/imxweb/projects/qbm/src/lib/cdr/cdr.module.ts +++ b/imxweb/projects/qbm/src/lib/cdr/cdr.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,9 +24,9 @@ * */ +import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { CommonModule } from '@angular/common'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; import { MatCheckboxModule } from '@angular/material/checkbox'; @@ -38,47 +38,47 @@ import { MatPaginatorModule } from '@angular/material/paginator'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatRadioModule } from '@angular/material/radio'; import { MatSelectModule } from '@angular/material/select'; -import { MatSliderModule } from '@angular/material/slider'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; +import { MatSliderModule } from '@angular/material/slider'; import { MatTableModule } from '@angular/material/table'; import { TranslateModule } from '@ngx-translate/core'; import { EuiCoreModule } from '@elemental-ui/core'; -import { CdrRegistryService } from './cdr-registry.service'; +import { ScrollingModule } from '@angular/cdk/scrolling'; +import { MatCardModule } from '@angular/material/card'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { DataSourceToolbarModule } from '../data-source-toolbar/data-source-toolbar.module'; +import { DataTableModule } from '../data-table/data-table.module'; +import { DateModule } from '../date/date.module'; +import { DisableControlModule } from '../disable-control/disable-control.module'; +import { FkAdvancedPickerModule } from '../fk-advanced-picker/fk-advanced-picker.module'; +import { FkHierarchicalDialogModule } from '../fk-hierarchical-dialog/fk-hierarchical-dialog.module'; +import { ImageModule } from '../image/image.module'; +import { InfoModalDialogModule } from '../info-modal-dialog/info-modal-dialog.module'; +import { LdsReplaceModule } from '../lds-replace/lds-replace.module'; import { CdrEditorComponent } from './cdr-editor/cdr-editor.component'; -import { EditBooleanComponent } from './edit-boolean/edit-boolean.component'; -import { EditDefaultComponent } from './edit-default/edit-default.component'; -import { EditMultilineComponent } from './edit-multiline/edit-multiline.component'; -import { EditNumberComponent } from './edit-number/edit-number.component'; -import { EditLimitedValueComponent } from './edit-limited-value/edit-limited-value.component'; -import { EditMultiValueComponent } from './edit-multi-value/edit-multi-value.component'; -import { EditMultiLimitedValueComponent } from './edit-multi-limited-value/edit-multi-limited-value.component'; +import { CdrSidesheetComponent } from './cdr-sidesheet/cdr-sidesheet.component'; +import { DateRangeComponent } from './date-range/date-range.component'; import { EditBinaryComponent } from './edit-binary/edit-binary.component'; +import { EditBitmaskComponent } from './edit-bitmask/edit-bitmask.component'; +import { EditBooleanComponent } from './edit-boolean/edit-boolean.component'; import { EditDateComponent } from './edit-date/edit-date.component'; -import { PropertyViewerComponent } from './property-viewer/property-viewer.component'; -import { DisableControlModule } from '../disable-control/disable-control.module'; -import { EditImageComponent } from './edit-image/edit-image.component'; -import { DataSourceToolbarModule } from '../data-source-toolbar/data-source-toolbar.module'; -import { DataTableModule } from '../data-table/data-table.module'; +import { EditDefaultComponent } from './edit-default/edit-default.component'; import { EditFkMultiComponent } from './edit-fk/edit-fk-multi.component'; import { EditFkComponent } from './edit-fk/edit-fk.component'; -import { LdsReplaceModule } from '../lds-replace/lds-replace.module'; -import { FkAdvancedPickerModule } from '../fk-advanced-picker/fk-advanced-picker.module'; +import { EditImageComponent } from './edit-image/edit-image.component'; +import { EditLimitedValueComponent } from './edit-limited-value/edit-limited-value.component'; +import { EditMultiLimitedValueComponent } from './edit-multi-limited-value/edit-multi-limited-value.component'; +import { EditMultiValueComponent } from './edit-multi-value/edit-multi-value.component'; +import { EditMultilineComponent } from './edit-multiline/edit-multiline.component'; +import { EditNumberComponent } from './edit-number/edit-number.component'; import { EditRiskIndexComponent } from './edit-risk-index/edit-risk-index.component'; +import { EditUrlComponent } from './edit-url/edit-url.component'; +import { EntityColumnEditorComponent } from './entity-column-editor/entity-column-editor.component'; +import { PropertyViewerComponent } from './property-viewer/property-viewer.component'; import { ViewPropertyDefaultComponent } from './view-property-default/view-property-default.component'; -import { ScrollingModule } from '@angular/cdk/scrolling'; -import { FkHierarchicalDialogModule } from '../fk-hierarchical-dialog/fk-hierarchical-dialog.module'; import { ViewPropertyComponent } from './view-property/view-property.component'; -import { DateModule } from '../date/date.module'; -import { DateRangeComponent } from './date-range/date-range.component'; -import { ImageModule } from '../image/image.module'; -import { EntityColumnEditorComponent } from './entity-column-editor/entity-column-editor.component'; -import { EditUrlComponent } from './edit-url/edit-url.component'; -import { CdrSidesheetComponent } from './cdr-sidesheet/cdr-sidesheet.component'; -import { MatCardModule } from '@angular/material/card'; -import { MatTooltipModule } from '@angular/material/tooltip'; -import { InfoModalDialogModule } from '../info-modal-dialog/info-modal-dialog.module'; @NgModule({ declarations: [ @@ -91,6 +91,7 @@ import { InfoModalDialogModule } from '../info-modal-dialog/info-modal-dialog.mo EditMultiValueComponent, EditMultiLimitedValueComponent, EditBinaryComponent, + EditBitmaskComponent, EditDateComponent, PropertyViewerComponent, EditImageComponent, diff --git a/imxweb/projects/qbm/src/lib/cdr/column-dependent-reference.interface.ts b/imxweb/projects/qbm/src/lib/cdr/column-dependent-reference.interface.ts index 9717f5e78..ac38d9f73 100644 --- a/imxweb/projects/qbm/src/lib/cdr/column-dependent-reference.interface.ts +++ b/imxweb/projects/qbm/src/lib/cdr/column-dependent-reference.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,44 +24,43 @@ * */ - -import { IEntityColumn, ValueConstraint } from 'imx-qbm-dbts'; +import { IEntityColumn, ValueConstraint } from '@imx-modules/imx-qbm-dbts'; import { Subject } from 'rxjs'; /** * Defines a column dependent reference. */ export interface ColumnDependentReference { - /** - * The column of the entity. - */ - column: IEntityColumn; + /** + * The column of the entity. + */ + column: IEntityColumn; - /** - * Custom display - if it is set it will be used instead of column.GetMetadata().GetDisplay() - */ - display?: string; + /** + * Custom display - if it is set it will be used instead of column.GetMetadata().GetDisplay() + */ + display?: string; - /** - * Custom MinLength - is evaluated in addition to column.GetMetadata().GetMinLength() - */ - minLength?: number; + /** + * Custom MinLength - is evaluated in addition to column.GetMetadata().GetMinLength() + */ + minLength?: number; - minlengthSubject?: Subject + minlengthSubject?: Subject; - /** - * Custom valueConstraint - if it is set it will be used instead of column.GetMetadata().valueConstraint - */ - valueConstraint?: ValueConstraint; + /** + * Custom valueConstraint - if it is set it will be used instead of column.GetMetadata().valueConstraint + */ + valueConstraint?: ValueConstraint; - /** - * Optional hint - */ - hint?: string; + /** + * Optional hint + */ + hint?: string; - /** - * Returns whether the column should be only displayed (true) - * or also allow editing (false). - */ - isReadOnly(): boolean; + /** + * Returns whether the column should be only displayed (true) + * or also allow editing (false). + */ + isReadOnly(): boolean; } diff --git a/imxweb/projects/qbm/src/lib/cdr/date-range/date-range.component.html b/imxweb/projects/qbm/src/lib/cdr/date-range/date-range.component.html index 4fa842234..b81d2622d 100644 --- a/imxweb/projects/qbm/src/lib/cdr/date-range/date-range.component.html +++ b/imxweb/projects/qbm/src/lib/cdr/date-range/date-range.component.html @@ -1,28 +1,31 @@ - - {{ columnContainer?.display | translate }} - - {{'#LDS#Use dynamic time frame' | translate}} - + + {{ columnContainer.display | translate }} + + + {{ '#LDS#Use dynamic time frame' | translate }} + + - {{ '#LDS#Dynamic time frame' | translate }} + {{ '#LDS#Dynamic time frame' | translate }} {{ option.display }} -
    +
    -
    +
    + [imxIdentifierSuffix]="'-from-' + columnContainer?.name" + > + [imxIdentifierSuffix]="'-until-' + columnContainer?.name" + >
    - - + - - - diff --git a/imxweb/projects/qbm/src/lib/cdr/date-range/date-range.component.scss b/imxweb/projects/qbm/src/lib/cdr/date-range/date-range.component.scss index 80bb65375..7588c93b4 100644 --- a/imxweb/projects/qbm/src/lib/cdr/date-range/date-range.component.scss +++ b/imxweb/projects/qbm/src/lib/cdr/date-range/date-range.component.scss @@ -2,13 +2,6 @@ flex-grow: 1; } -.mat-form-field { - width: 100%; -} -.mat-checkbox{ +.mat-mdc-checkbox { padding-bottom: 1.25em; } -.hidden{ - display: none; -} - diff --git a/imxweb/projects/qbm/src/lib/cdr/date-range/date-range.component.ts b/imxweb/projects/qbm/src/lib/cdr/date-range/date-range.component.ts index a8abef75d..fbfbf0e91 100644 --- a/imxweb/projects/qbm/src/lib/cdr/date-range/date-range.component.ts +++ b/imxweb/projects/qbm/src/lib/cdr/date-range/date-range.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,19 +26,19 @@ import { Component, ErrorHandler, EventEmitter, OnDestroy } from '@angular/core'; import { UntypedFormControl, Validators } from '@angular/forms'; -import { Subscription } from 'rxjs'; import moment from 'moment-timezone'; +import { Subscription } from 'rxjs'; -import { DateRangeType, DateRangeTypeLabels, ValType, ValueRange } from 'imx-qbm-dbts'; +import { EuiSelectOption } from '@elemental-ui/core'; +import { DateRangeType, DateRangeTypeLabels, ValType, ValueRange } from '@imx-modules/imx-qbm-dbts'; +import { ImxTranslationProviderService } from '../../translation/imx-translation-provider.service'; import { CdrEditor, ValueHasChangedEventArg } from '../cdr-editor.interface'; import { ColumnDependentReference } from '../column-dependent-reference.interface'; import { EntityColumnContainer } from '../entity-column-container'; -import { EuiSelectOption } from '@elemental-ui/core'; -import { ImxTranslationProviderService } from '../../translation/imx-translation-provider.service'; /** * Provides a {@link CdrEditor | CDR editor} for editing / viewing date range columns. - * + * * The user can choose between these two options: * It displays either two {@link DateComponent | date components} or a dynamic time frame like 'two weeks ago'. * When set to read-only, it uses a {@link ViewPropertyComponent | view property component} to display the content. @@ -104,7 +104,10 @@ export class DateRangeComponent implements CdrEditor, OnDestroy { private required = false; - public constructor(private readonly errorHandler: ErrorHandler, private translateProviderService: ImxTranslationProviderService) { + public constructor( + private readonly errorHandler: ErrorHandler, + private translateProviderService: ImxTranslationProviderService, + ) { this.translateProviderService.GetCultures().then(() => this.updateOptions()); } @@ -138,7 +141,7 @@ export class DateRangeComponent implements CdrEditor, OnDestroy { this.dateFrom.setValidators(null); this.dateUntil.setValidators(null); } - }) + }), ); } @@ -147,7 +150,7 @@ export class DateRangeComponent implements CdrEditor, OnDestroy { if (!!value) { this.writeValue({ from: value?.toDate(), until: this.dateUntil.value?.toDate() }); } - }) + }), ); this.subscribers.push( @@ -155,7 +158,7 @@ export class DateRangeComponent implements CdrEditor, OnDestroy { if (!!value) { this.writeValue({ from: this.dateFrom.value?.toDate(), until: value?.toDate() }); } - }) + }), ); this.subscribers.push( @@ -167,7 +170,7 @@ export class DateRangeComponent implements CdrEditor, OnDestroy { this.updateControlValues(); } this.valueHasChanged.emit({ value: this.control.value }); - }) + }), ); this.subscribers.push( @@ -175,7 +178,7 @@ export class DateRangeComponent implements CdrEditor, OnDestroy { if (!!value) { this.writeValue(value); } - }) + }), ); } } @@ -238,8 +241,8 @@ export class DateRangeComponent implements CdrEditor, OnDestroy { } const valueRange = ValueRange.Parse(value); if (valueRange.success) { - const from = valueRange.result.Start ? moment(valueRange.result.Start) : null; - const until = valueRange.result.End ? moment(valueRange.result.End) : null; + const from = valueRange.result?.Start ? moment(valueRange.result.Start) : null; + const until = valueRange.result?.End ? moment(valueRange.result.End) : null; this.dateFrom.setValue(from, { emitEvent: true }); this.dateUntil.setValue(until, { emitEvent: true }); if (this.required) { diff --git a/imxweb/projects/qbm/src/lib/cdr/default-cdr-editor-provider.spec.ts b/imxweb/projects/qbm/src/lib/cdr/default-cdr-editor-provider.spec.ts index 6fa2dbf68..5cad2f0b8 100644 --- a/imxweb/projects/qbm/src/lib/cdr/default-cdr-editor-provider.spec.ts +++ b/imxweb/projects/qbm/src/lib/cdr/default-cdr-editor-provider.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,27 +24,23 @@ * */ -import { ViewContainerRef, ComponentRef } from '@angular/core'; -import * as TypeMoq from 'typemoq'; - -import { DefaultCdrEditorProvider } from './default-cdr-editor-provider'; +import { ComponentRef, ViewContainerRef } from '@angular/core'; +import { IValueMetadata, LimitedValueData, ValType } from '@imx-modules/imx-qbm-dbts'; +import { clearStylesFromDOM } from '../testing/clear-styles.spec'; +import { CdrEditor } from './cdr-editor.interface'; import { ColumnDependentReference } from './column-dependent-reference.interface'; -import { IValueMetadata, ValType, LimitedValueData, IEntityColumn } from 'imx-qbm-dbts'; +import { DefaultCdrEditorProvider } from './default-cdr-editor-provider'; import { EditBooleanComponent } from './edit-boolean/edit-boolean.component'; -import { CdrEditor } from './cdr-editor.interface'; -import { EditNumberComponent } from './edit-number/edit-number.component'; import { EditLimitedValueComponent } from './edit-limited-value/edit-limited-value.component'; import { EditMultiLimitedValueComponent } from './edit-multi-limited-value/edit-multi-limited-value.component'; import { EditMultiValueComponent } from './edit-multi-value/edit-multi-value.component'; import { EditMultilineComponent } from './edit-multiline/edit-multiline.component'; +import { EditNumberComponent } from './edit-number/edit-number.component'; import { EditRiskIndexComponent } from './edit-risk-index/edit-risk-index.component'; import { ViewPropertyDefaultComponent } from './view-property-default/view-property-default.component'; -import { clearStylesFromDOM } from '../testing/clear-styles.spec'; describe('DefaultCdrEditorProvider', () => { - beforeEach(() => { - - }); + beforeEach(() => {}); afterAll(() => { clearStylesFromDOM(); @@ -147,62 +143,53 @@ describe('DefaultCdrEditorProvider', () => { it('should create ViewPropertyDefaultComponent for readonly RiskIndex cdr', () => { testCreateEditor(ViewPropertyDefaultComponent, ValType.Double, false, false, false, [], '', true); }); - }); -function testCreateEditor(TCtor: new (...args: any[]) => T, type: ValType, - multiLine: boolean = false, multiValue: boolean = false, range: boolean = false, - limitedValues: string[] = [], schemaKey: string = '', isReadOnly: boolean = false) { - // Arrange - const cdrMock = createCdr(multiLine, multiValue, range, type, limitedValues, schemaKey, isReadOnly); - const editorMock = TypeMoq.Mock.ofType(); - const parentMock = TypeMoq.Mock.ofType(); - const childMock = createComponentMock(editorMock.object); - - parentMock.setup( p => p.createComponent(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => childMock.object); - - // Act - const provider = new DefaultCdrEditorProvider(); - const editor = provider.createEditor(parentMock.object, cdrMock.object); - - // Assert - expect(editor === childMock.object).toBeTruthy(); - editorMock.verify(e => e.bind(cdrMock.object), TypeMoq.Times.once()); - editorMock.verify(e => e.bind(TypeMoq.It.isAny()), TypeMoq.Times.once()); +function testCreateEditor( + TCtor: new (...args: any[]) => T, + type: ValType, + multiLine: boolean = false, + multiValue: boolean = false, + range: boolean = false, + limitedValues: string[] = [], + schemaKey: string = '', + isReadOnly: boolean = false, +) { + const cdrMock = createCdr(multiLine, multiValue, range, type, limitedValues, schemaKey, isReadOnly); + const childMock = createComponentMock({} as T); + const parentMock = { createComponent: () => childMock } as unknown as ViewContainerRef; + const provider = new DefaultCdrEditorProvider(); + + const editor = provider.createEditor(parentMock, cdrMock); + expect(editor === childMock).toBeTruthy(); } -export function createComponentMock(instance: T): TypeMoq.IMock> { - const mock = TypeMoq.Mock.ofType>(); - mock.setup(m => m.instance).returns(() => instance); - return mock; +function createCdr( + multiLine: boolean, + multiValue: boolean, + range: boolean, + type: ValType, + limitedValues?: string[], + schemaKey?: string, + isReadOnly?: boolean, +): ColumnDependentReference { + const limited: LimitedValueData[] = limitedValues.map((elem) => ({ Value: elem })); + + const metaData: IValueMetadata = { + IsMultiLine: () => multiLine, + IsMultiValue: () => multiValue, + IsRange: () => range, + GetType: () => type, + GetLimitedValues: () => limited as ReadonlyArray, + GetSchemaKey: () => schemaKey, + CanEdit: () => !isReadOnly, + GetBitMaskCaptions: () => [] as ReadonlyArray, + } as IValueMetadata; + + return { column: { GetMetadata: () => metaData }, isReadOnly: () => isReadOnly } as unknown as ColumnDependentReference; } -function createCdr(multiLine: boolean, multiValue: boolean, range: boolean, - type: ValType, limitedValues?: string[], schemaKey?: string, isReadOnly?: boolean) -: TypeMoq.IMock { - - const metaMock = TypeMoq.Mock.ofType(); - metaMock.setup(m => m.IsMultiLine()).returns(() => multiLine); - metaMock.setup(m => m.IsMultiValue()).returns(() => multiValue); - metaMock.setup(m => m.IsRange()).returns(() => range); - metaMock.setup(m => m.GetType()).returns(() => type); - - const lvArray: LimitedValueData[] = []; - if (limitedValues != null) { - for (const lv of limitedValues) { - const lvMock = TypeMoq.Mock.ofType(); - lvMock.setup(m => m.Value).returns(() => lv); - lvArray.push(lvMock.object); - } - } - - metaMock.setup(m => m.GetLimitedValues()).returns(() => lvArray); - metaMock.setup(m => m.GetSchemaKey()).returns(() => schemaKey) - metaMock.setup(m => m.CanEdit()).returns(() => !isReadOnly) - - const cdrMock = TypeMoq.Mock.ofType(); - cdrMock.setup(m => m.column).returns(() => ({ GetMetadata: () => metaMock.object } as IEntityColumn)); - cdrMock.setup(m => m.isReadOnly()).returns(() => isReadOnly); - - return cdrMock; +export function createComponentMock(instance: T): ComponentRef { + instance.bind = () => {}; + return { instance } as ComponentRef; } diff --git a/imxweb/projects/qbm/src/lib/cdr/default-cdr-editor-provider.ts b/imxweb/projects/qbm/src/lib/cdr/default-cdr-editor-provider.ts index 956f6d2e1..8fe8a6130 100644 --- a/imxweb/projects/qbm/src/lib/cdr/default-cdr-editor-provider.ts +++ b/imxweb/projects/qbm/src/lib/cdr/default-cdr-editor-provider.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,21 +24,22 @@ * */ +import { ComponentRef, Type, ViewContainerRef } from '@angular/core'; +import { IValueMetadata, ValType } from '@imx-modules/imx-qbm-dbts'; import { CdrEditorProvider } from './cdr-editor-provider.interface'; -import { ViewContainerRef, ComponentRef, Type } from '@angular/core'; -import { ColumnDependentReference } from './column-dependent-reference.interface'; import { CdrEditor } from './cdr-editor.interface'; -import { ValType, IValueMetadata } from 'imx-qbm-dbts'; +import { ColumnDependentReference } from './column-dependent-reference.interface'; +import { DateRangeComponent } from './date-range/date-range.component'; import { EditBooleanComponent } from './edit-boolean/edit-boolean.component'; -import { EditNumberComponent } from './edit-number/edit-number.component'; -import { EditMultiLimitedValueComponent } from './edit-multi-limited-value/edit-multi-limited-value.component'; +import { EditDateComponent } from './edit-date/edit-date.component'; +import { EditImageComponent } from './edit-image/edit-image.component'; import { EditLimitedValueComponent } from './edit-limited-value/edit-limited-value.component'; +import { EditMultiLimitedValueComponent } from './edit-multi-limited-value/edit-multi-limited-value.component'; import { EditMultiValueComponent } from './edit-multi-value/edit-multi-value.component'; import { EditMultilineComponent } from './edit-multiline/edit-multiline.component'; -import { EditImageComponent } from './edit-image/edit-image.component'; -import { EditDateComponent } from './edit-date/edit-date.component'; +import { EditNumberComponent } from './edit-number/edit-number.component'; +import { EditBitmaskComponent } from './edit-bitmask/edit-bitmask.component'; import { EditRiskIndexComponent } from './edit-risk-index/edit-risk-index.component'; -import { DateRangeComponent } from './date-range/date-range.component'; import { EditUrlComponent } from './edit-url/edit-url.component'; /** @@ -61,7 +62,7 @@ export class DefaultCdrEditorProvider implements CdrEditorProvider { * @param cdref A column dependent reference that contains the data for the editor. * @returns An instance of {@link CdrEditor}, that can be used or editing data. */ - public createEditor(parent: ViewContainerRef, cdref: ColumnDependentReference): ComponentRef { + public createEditor(parent: ViewContainerRef, cdref: ColumnDependentReference): ComponentRef | null { const meta = cdref.column.GetMetadata(); const multiLine = meta.IsMultiLine(); const multiValue = meta.IsMultiValue(); @@ -76,6 +77,10 @@ export class DefaultCdrEditorProvider implements CdrEditorProvider { return this.createBound(EditImageComponent, parent, cdref); } + if (type === ValType.Int && meta.GetBitMaskCaptions()?.length > 0) { + return this.createBound(EditBitmaskComponent, parent, cdref); + } + if (!multiLine && !multiValue && !range && !limitedValues && !isRiskIndexColumn) { switch (type) { case ValType.Bool: @@ -122,7 +127,7 @@ export class DefaultCdrEditorProvider implements CdrEditorProvider { private createBound( editor: Type, parent: ViewContainerRef, - cdref: ColumnDependentReference + cdref: ColumnDependentReference, ): ComponentRef { const result = parent.createComponent(editor); result.instance.bind(cdref); @@ -130,7 +135,7 @@ export class DefaultCdrEditorProvider implements CdrEditorProvider { } /** - * @ignore only ised internally + * @ignore only used internally * Checks, if there are limited values defined for the column. * @returns True, if there are limited values available, otherwise false. */ diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-binary/edit-binary.component.html b/imxweb/projects/qbm/src/lib/cdr/edit-binary/edit-binary.component.html index da31437a4..64dadf8e1 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-binary/edit-binary.component.html +++ b/imxweb/projects/qbm/src/lib/cdr/edit-binary/edit-binary.component.html @@ -1,13 +1,8 @@ - - {{ columnContainer?.display | translate }} - + + {{ columnContainer.display | translate }} + - - + - diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-binary/edit-binary.component.scss b/imxweb/projects/qbm/src/lib/cdr/edit-binary/edit-binary.component.scss index b03a7d6a4..bfe7e877f 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-binary/edit-binary.component.scss +++ b/imxweb/projects/qbm/src/lib/cdr/edit-binary/edit-binary.component.scss @@ -1,7 +1,3 @@ :host { flex-grow: 1; } - -.mat-form-field { - width: 100%; -} \ No newline at end of file diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-binary/edit-binary.component.ts b/imxweb/projects/qbm/src/lib/cdr/edit-binary/edit-binary.component.ts index 4a33a8754..1e66290b0 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-binary/edit-binary.component.ts +++ b/imxweb/projects/qbm/src/lib/cdr/edit-binary/edit-binary.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-bitmask/edit-bitmask.component.html b/imxweb/projects/qbm/src/lib/cdr/edit-bitmask/edit-bitmask.component.html new file mode 100644 index 000000000..61723f2ad --- /dev/null +++ b/imxweb/projects/qbm/src/lib/cdr/edit-bitmask/edit-bitmask.component.html @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-bitmask/edit-bitmask.component.scss b/imxweb/projects/qbm/src/lib/cdr/edit-bitmask/edit-bitmask.component.scss new file mode 100644 index 000000000..bcd8ac2d3 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/cdr/edit-bitmask/edit-bitmask.component.scss @@ -0,0 +1,3 @@ +:host { + flex-grow: 1; +} \ No newline at end of file diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-bitmask/edit-bitmask.component.ts b/imxweb/projects/qbm/src/lib/cdr/edit-bitmask/edit-bitmask.component.ts new file mode 100644 index 000000000..fd526d951 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/cdr/edit-bitmask/edit-bitmask.component.ts @@ -0,0 +1,179 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Component, EventEmitter } from '@angular/core'; +import { UntypedFormControl, Validators } from '@angular/forms'; +import { EuiSelectOption } from '@elemental-ui/core'; +import { Subject, Subscription } from 'rxjs'; +import { ClassloggerService } from '../../classlogger/classlogger.service'; +import { CdrEditor, ValueHasChangedEventArg } from '../cdr-editor.interface'; +import { ColumnDependentReference } from '../column-dependent-reference.interface'; +import { EntityColumnContainer } from '../entity-column-container'; + +@Component({ + selector: 'imx-edit-bitmask', + templateUrl: './edit-bitmask.component.html', + styleUrls: ['./edit-bitmask.component.scss'], +}) +export class EditBitmaskComponent implements CdrEditor { + /** + * The form control associated with the editor. + */ + public readonly control = new UntypedFormControl(undefined, { updateOn: 'blur' }); + + /** + * The container that wraps the column functionality. + */ + public readonly columnContainer = new EntityColumnContainer(); + + /** + * Event that is emitted, after a value has been changed. + */ + public readonly valueHasChanged = new EventEmitter(); + + /** + * A subject for triggering an update of the editor. + */ + public readonly updateRequested = new Subject(); + + /** + * The list of selectable bitmask options. + */ + public options: EuiSelectOption[] = []; + + private readonly subscribers: Subscription[] = []; + private isWriting = false; + + constructor(private readonly logger: ClassloggerService) {} + + /** + * Binds a column dependent reference to the component. + * Subscribes to subjects from the column dependent reference and its container. + * @param cdref a column dependent reference + */ + public bind(cdref: ColumnDependentReference): void { + if (cdref && cdref.column) { + this.columnContainer.init(cdref); + const value = this.toArray(this.columnContainer.value); + this.control.setValue(value, { emitEvent: false }); + if (this.columnContainer.isValueRequired && this.columnContainer.canEdit) { + this.logger.debug(this, 'value is required'); + this.control.setValidators(Validators.required); + } + + if (cdref.minlengthSubject) { + this.subscribers.push( + cdref.minlengthSubject.subscribe(() => { + if (this.columnContainer.isValueRequired && this.columnContainer.canEdit) { + this.logger.debug(this, 'value is required'); + this.control.setValidators(Validators.required); + } else { + this.control.setValidators(null); + } + }), + ); + } + + this.subscribers.push( + this.columnContainer.subscribe(() => { + if (!this.isWriting) { + return; + } + if (this.control.value !== this.columnContainer.value) { + this.control.setValue(this.toArray(this.columnContainer.value)); + } + this.valueHasChanged.emit({ value: this.control.value }); + }), + ); + this.subscribers.push(this.control.valueChanges.subscribe(async (value) => this.writeValue(this.fromArray(value)))); + this.initOptions(); + this.logger.trace(this, 'Control initialized'); + } else { + this.logger.error(this, 'The Column Dependent Reference is undefined'); + } + } + + /** + * Updates the value for the CDR. + * @param values The values, that will be used as a new value. + */ + private async writeValue(value: Number): Promise { + this.logger.debug(this, 'writeValue called with value', value); + + if (!this.columnContainer.canEdit || this.columnContainer.value === value) { + return; + } + + try { + this.isWriting = true; + this.logger.debug(this, 'writeValue - PutValue...'); + await this.columnContainer.updateValue(value); + } catch (e) { + this.logger.error(this, e); + } finally { + this.isWriting = false; + const valueAfterWrite = this.toArray(this.columnContainer.value); + if (this.control.value !== valueAfterWrite) { + this.control.setValue(valueAfterWrite, { emitEvent: false }); + } + } + + this.valueHasChanged.emit({ value, forceEmit: true }); + } + + private toArray(value: Number): Number[] { + let array: number[] = []; + let binaryRepresentation = parseInt(value.toString(), 10).toString(2); + binaryRepresentation = binaryRepresentation.split('').reverse().join(''); + + // You need to reverse the string to get the power of 2 corresponding + for (let i = binaryRepresentation.length - 1; i >= 0; i--) { + if (Number(binaryRepresentation[i]) === 1) { + array.push(Math.pow(2, i)); + } + } + return array; + } + + private fromArray(values: number[]): Number { + let result = 0; + for (let idx = 0; idx < values.length; idx++) { + result += values[idx]; + } + return result; + } + + protected getBinaryNumber(idx: number): number { + return Math.pow(2, idx); + } + + private async initOptions(): Promise { + const bitMaskCaptions = this.columnContainer?.metaData?.GetBitMaskCaptions(); + const allOptions = bitMaskCaptions?.map((elem, idx): EuiSelectOption => ({ display: elem, value: this.getBinaryNumber(idx) })) || []; + // filter out deactivated captions, these are empty strings + this.options = allOptions?.filter((option: EuiSelectOption) => !!option?.display.length); + } +} diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-boolean/edit-boolean.component.html b/imxweb/projects/qbm/src/lib/cdr/edit-boolean/edit-boolean.component.html index 9080b4daa..044835833 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-boolean/edit-boolean.component.html +++ b/imxweb/projects/qbm/src/lib/cdr/edit-boolean/edit-boolean.component.html @@ -1,12 +1,12 @@ - - {{ columnContainer?.display | translate }} + + {{ columnContainer.display | translate }} - + - - + diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-boolean/edit-boolean.component.scss b/imxweb/projects/qbm/src/lib/cdr/edit-boolean/edit-boolean.component.scss index d1d1a4480..2593e3e7a 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-boolean/edit-boolean.component.scss +++ b/imxweb/projects/qbm/src/lib/cdr/edit-boolean/edit-boolean.component.scss @@ -7,14 +7,8 @@ } mat-checkbox { - display: inline-block; - span { white-space: normal; word-break: break-word; } } - -.imx-checkbox-spinner { - margin-left: 10px; -} diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-boolean/edit-boolean.component.ts b/imxweb/projects/qbm/src/lib/cdr/edit-boolean/edit-boolean.component.ts index 1d6202d9c..69b888ac2 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-boolean/edit-boolean.component.ts +++ b/imxweb/projects/qbm/src/lib/cdr/edit-boolean/edit-boolean.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-date/edit-date.component.html b/imxweb/projects/qbm/src/lib/cdr/edit-date/edit-date.component.html index 482c41131..41ea6de24 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-date/edit-date.component.html +++ b/imxweb/projects/qbm/src/lib/cdr/edit-date/edit-date.component.html @@ -6,8 +6,10 @@ [max]="columnContainer?.valueConstraint?.MaxValue" [isValueRequired]="columnContainer?.isValueRequired" [withTime]="withTime" + [validateOnlyOnChange]="validateOnlyOnChange" [isLoading]="isBusy" - [imxIdentifierSuffix]="'-cdr-' + columnContainer?.name"> + [imxIdentifierSuffix]="'-cdr-' + columnContainer?.name" +> diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-date/edit-date.component.scss b/imxweb/projects/qbm/src/lib/cdr/edit-date/edit-date.component.scss index bcd8ac2d3..bfe7e877f 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-date/edit-date.component.scss +++ b/imxweb/projects/qbm/src/lib/cdr/edit-date/edit-date.component.scss @@ -1,3 +1,3 @@ :host { flex-grow: 1; -} \ No newline at end of file +} diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-date/edit-date.component.ts b/imxweb/projects/qbm/src/lib/cdr/edit-date/edit-date.component.ts index 52fc4d146..82e3706ee 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-date/edit-date.component.ts +++ b/imxweb/projects/qbm/src/lib/cdr/edit-date/edit-date.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,19 +26,18 @@ import { Component, ErrorHandler, EventEmitter, OnDestroy } from '@angular/core'; import { UntypedFormControl } from '@angular/forms'; +import moment, { Moment } from 'moment-timezone'; import { Subject, Subscription } from 'rxjs'; import { CdrEditor, ValueHasChangedEventArg } from '../cdr-editor.interface'; import { ColumnDependentReference } from '../column-dependent-reference.interface'; -import moment from 'moment-timezone'; -import { Moment } from 'moment-timezone'; -import { EntityColumnContainer } from '../entity-column-container'; +import { DateFormat } from '@imx-modules/imx-qbm-dbts'; import { ClassloggerService } from '../../classlogger/classlogger.service'; -import { DateFormat } from 'imx-qbm-dbts'; +import { EntityColumnContainer } from '../entity-column-container'; /** * Provides a {@link CdrEditor | CDR editor} for editing / viewing date value columns - * + * * It uses a {@link DateComponent | date component} for editing the value. * When set to read-only, it uses a {@link ViewPropertyComponent | view property component} to display the content. */ @@ -73,8 +72,11 @@ export class EditDateComponent implements CdrEditor, OnDestroy { */ public isBusy = false; + public validateOnlyOnChange: boolean = false; + private readonly subscribers: Subscription[] = []; private isWriting = false; + private previousValue: Moment | undefined; /** * Determines, if a time control should be added. @@ -86,7 +88,10 @@ export class EditDateComponent implements CdrEditor, OnDestroy { return dateFormat === DateFormat.DateTime || dateFormat === DateFormat.UtcDateTime; } - public constructor(private readonly errorHandler: ErrorHandler, private logger: ClassloggerService) {} + public constructor( + private readonly errorHandler: ErrorHandler, + private logger: ClassloggerService, + ) {} /** * Unsubscribes all events, after the 'OnDestroy' hook is triggered. @@ -110,7 +115,7 @@ export class EditDateComponent implements CdrEditor, OnDestroy { this.subscribers.push( cdref.minlengthSubject.subscribe(() => { this.resetControlValue(); - }) + }), ); } @@ -123,8 +128,9 @@ export class EditDateComponent implements CdrEditor, OnDestroy { this.logger.trace(this, 'Control set to new value'); this.resetControlValue(); this.valueHasChanged.emit({ value: this.control.value }); + this.control.updateValueAndValidity({ onlySelf: true, emitEvent: true }); } - }) + }), ); this.setValidators(); @@ -140,7 +146,7 @@ export class EditDateComponent implements CdrEditor, OnDestroy { } this.valueHasChanged.emit({ value: this.control.value }); }); - }) + }), ); } } @@ -162,7 +168,7 @@ export class EditDateComponent implements CdrEditor, OnDestroy { this.updateControlValue(value); } - private updateControlValue(value: Moment): void { + private updateControlValue(value: Moment | undefined): void { if (this.control.value !== value) { this.control.setValue(value, { emitEvent: false }); } @@ -173,12 +179,13 @@ export class EditDateComponent implements CdrEditor, OnDestroy { * @param value The Moment object, that is used as the new value for the control. */ private async writeValue(value: Moment): Promise { - if (this.control.errors) { + if (this.control.errors || value?.isSame(this.previousValue)) { return; } - + this.previousValue = value; // Beware: the columnContainer used date while the date editor uses moment!! const date = value == null ? undefined : value.toDate(); + const resetValue = this.columnContainer.value; this.logger.debug(this, 'writeValue called with value', date); if (!this.columnContainer.canEdit || this.columnContainer.value === date) { return; @@ -192,6 +199,7 @@ export class EditDateComponent implements CdrEditor, OnDestroy { await this.columnContainer.updateValue(date); } catch (error) { this.errorHandler.handleError(error); + this.control?.setValue(resetValue ? moment(resetValue) : undefined); } finally { this.isBusy = false; this.isWriting = false; diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-default/edit-default.component.html b/imxweb/projects/qbm/src/lib/cdr/edit-default/edit-default.component.html index b21f28bd8..8ce5a02b8 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-default/edit-default.component.html +++ b/imxweb/projects/qbm/src/lib/cdr/edit-default/edit-default.component.html @@ -1,27 +1,28 @@ - - - {{ columnContainer?.display | translate }} - - -
    - -
    - - {{ '#LDS#This field is mandatory.' | translate }} - - - {{validationErrorMessage}} - + + + {{ columnContainer?.display ?? '' | translate }} + + +
    + +
    + + {{ '#LDS#This field is mandatory.' | translate }} + + + {{ validationErrorMessage }} +
    - - + - diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-default/edit-default.component.scss b/imxweb/projects/qbm/src/lib/cdr/edit-default/edit-default.component.scss index 541e13d68..22baa9c3e 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-default/edit-default.component.scss +++ b/imxweb/projects/qbm/src/lib/cdr/edit-default/edit-default.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; :host { display: flex; @@ -6,10 +6,6 @@ align-items: center; } -.mat-form-field { - flex-grow: 1; -} - .imx-spacer { height: 30px; } diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-default/edit-default.component.ts b/imxweb/projects/qbm/src/lib/cdr/edit-default/edit-default.component.ts index 0e5e390d6..658865002 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-default/edit-default.component.ts +++ b/imxweb/projects/qbm/src/lib/cdr/edit-default/edit-default.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -38,7 +38,7 @@ import { EditorBase } from '../editor-base'; @Component({ selector: 'imx-edit-default', templateUrl: './edit-default.component.html', - styleUrls: ['./edit-default.component.scss'] + styleUrls: ['./edit-default.component.scss'], }) export class EditDefaultComponent extends EditorBase { public readonly control = new UntypedFormControl(undefined, { updateOn: 'blur' }); diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk-multi.component.html b/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk-multi.component.html index c4bdba47f..c3a52bf19 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk-multi.component.html +++ b/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk-multi.component.html @@ -1,13 +1,24 @@ - - {{ columnContainer?.display | translate }} - + {{ columnContainer.display | translate }} + + [required]="columnContainer.isValueRequired" + />
    -
    @@ -16,11 +27,6 @@
    - - - + diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk-multi.component.scss b/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk-multi.component.scss index ce02f3513..ed30bbbdc 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk-multi.component.scss +++ b/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk-multi.component.scss @@ -1,41 +1,5 @@ +@import 'common/select'; + :host { - display: flex; flex-grow: 1; - flex-direction: column; - align-items: baseline; - - >* { - margin-right: 10px; - } -} - -input { - flex: 1; -} - -.mat-form-field { - width: 100%; -} - -.imx-spacer { - height: 30px; -} - -.mat-button { - min-width: initial; } - -.mat-error { - margin-top: 5px; - font-size: small; -} - -.imx-suffix-container { - display: flex; - margin-bottom: 5px; - - > *:not(:last-child) { - margin-right: 5px; - margin-top: 0; - } -} \ No newline at end of file diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk-multi.component.spec.ts b/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk-multi.component.spec.ts index 7e3790e7a..6ca7e8d98 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk-multi.component.spec.ts +++ b/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk-multi.component.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,18 +26,18 @@ import { fakeAsync, tick } from '@angular/core/testing'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { of } from 'rxjs'; import { EuiSidesheetService } from '@elemental-ui/core'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; +import { of } from 'rxjs'; -import { IValueMetadata } from 'imx-qbm-dbts'; -import { EntityColumnStub } from '../../testing/entity-column-stub.spec'; -import { clearStylesFromDOM } from '../../testing/clear-styles.spec'; -import { EditFkMultiComponent } from './edit-fk-multi.component'; -import { MultiValueService } from '../../multi-value/multi-value.service'; +import { IValueMetadata } from '@imx-modules/imx-qbm-dbts'; import { MockBuilder, MockedComponentFixture, MockRender } from 'ng-mocks'; -import { CdrModule } from '../cdr.module'; import { QbmDefaultMocks } from '../../../default-mocks.spec'; +import { MultiValueService } from '../../multi-value/multi-value.service'; +import { clearStylesFromDOM } from '../../testing/clear-styles.spec'; +import { EntityColumnStub } from '../../testing/entity-column-stub.spec'; +import { CdrModule } from '../cdr.module'; +import { EditFkMultiComponent } from './edit-fk-multi.component'; describe('EditFkMultiComponent', () => { let component: EditFkMultiComponent; @@ -105,7 +105,7 @@ describe('EditFkMultiComponent', () => { valueStructs: [], expected: { value: undefined, - display: undefined, + display: '', controlValue: undefined, }, canEdit: true, @@ -114,7 +114,7 @@ describe('EditFkMultiComponent', () => { valueStructs: [], expected: { value: undefined, - display: undefined, + display: '', controlValue: undefined, }, canEdit: false, @@ -163,8 +163,8 @@ describe('EditFkMultiComponent', () => { expect(component.control.value).toEqual(start.display); expect(component.columnContainer.value).toEqual(start.value); } - }) - ) + }), + ), ); for (const testcase of [ @@ -194,7 +194,7 @@ describe('EditFkMultiComponent', () => { } else { expect(component.control.errors).toBeNull(); } - } + }, ); } }); diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk-multi.component.ts b/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk-multi.component.ts index afc34ecfd..7d99f9b54 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk-multi.component.ts +++ b/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk-multi.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,24 +26,25 @@ import { Component, EventEmitter, OnDestroy, OnInit } from '@angular/core'; import { UntypedFormControl } from '@angular/forms'; -import { TranslateService } from '@ngx-translate/core'; import { EuiSidesheetService } from '@elemental-ui/core'; -import { Subscription, Subject } from 'rxjs'; +import { TranslateService } from '@ngx-translate/core'; +import { Subject, Subscription } from 'rxjs'; -import { CdrEditor, ValueHasChangedEventArg } from '../cdr-editor.interface'; -import { EntityColumnContainer } from '../entity-column-container'; -import { ColumnDependentReference } from '../column-dependent-reference.interface'; +import { ValueStruct } from '@imx-modules/imx-qbm-dbts'; +import { calculateSidesheetWidth } from '../../base/sidesheet-helper'; import { ClassloggerService } from '../../classlogger/classlogger.service'; -import { FkAdvancedPickerComponent } from '../../fk-advanced-picker/fk-advanced-picker.component'; -import { ValueStruct } from 'imx-qbm-dbts'; import { ForeignKeySelection } from '../../fk-advanced-picker/./foreign-key-selection.interface'; +import { FkAdvancedPickerComponent } from '../../fk-advanced-picker/fk-advanced-picker.component'; +import { FkHierarchicalDialogComponent } from '../../fk-hierarchical-dialog/fk-hierarchical-dialog.component'; import { LdsReplacePipe } from '../../lds-replace/lds-replace.pipe'; import { MultiValueService } from '../../multi-value/multi-value.service'; -import { FkHierarchicalDialogComponent } from '../../fk-hierarchical-dialog/fk-hierarchical-dialog.component'; +import { CdrEditor, ValueHasChangedEventArg } from '../cdr-editor.interface'; +import { ColumnDependentReference } from '../column-dependent-reference.interface'; +import { EntityColumnContainer } from '../entity-column-container'; /** * Provides a {@link CdrEditor | CDR editor} for editing / viewing multi foreign key value columns. - * + * * Its value is changed by clicking on the 'select' / 'change' button. Then a side sheet is opened for selecting multiple values. * When set to read-only, it uses a {@link ViewPropertyComponent | view property component} to display the content. */ @@ -62,7 +63,7 @@ export class EditFkMultiComponent implements CdrEditor, OnInit, OnDestroy { public loading = false; private isWriting = false; - private currentValueStruct: ValueStruct; + private currentValueStruct: ValueStruct; private isHierarchical: boolean; private readonly subscribers: Subscription[] = []; @@ -71,13 +72,13 @@ export class EditFkMultiComponent implements CdrEditor, OnInit, OnDestroy { * Creates a new EditFkMultiComponent. * @param logger Log service. * @param sidesheet Side sheet, that opens the picker dialog for selecting objects. - */ + */ constructor( private readonly logger: ClassloggerService, private readonly sidesheet: EuiSidesheetService, private readonly translateService: TranslateService, private readonly ldsReplace: LdsReplacePipe, - private readonly multiValueProvider: MultiValueService + private readonly multiValueProvider: MultiValueService, ) {} public ngOnDestroy(): void { @@ -117,7 +118,7 @@ export class EditFkMultiComponent implements CdrEditor, OnInit, OnDestroy { this.subscribers.push( cdref.minlengthSubject.subscribe((elem) => { this.setValidators(); - }) + }), ); } @@ -131,7 +132,7 @@ export class EditFkMultiComponent implements CdrEditor, OnInit, OnDestroy { this, `Control (${this.columnContainer.name}) set to new value:`, this.columnContainer.value, - this.control.value + this.control.value, ); this.currentValueStruct = { DataValue: this.columnContainer.value, @@ -140,7 +141,7 @@ export class EditFkMultiComponent implements CdrEditor, OnInit, OnDestroy { this.control.setValue(await this.multiValueToDisplay(this.currentValueStruct), { emitEvent: false }); } this.valueHasChanged.emit({ value: this.currentValueStruct }); - }) + }), ); this.subscribers.push( @@ -152,7 +153,7 @@ export class EditFkMultiComponent implements CdrEditor, OnInit, OnDestroy { DataValue: this.columnContainer.value, DisplayValue: this.columnContainer.displayValue, }; - const candidateCollection = await this.columnContainer.fkRelations[0]?.Get({ PageSize: -1 }); + const candidateCollection = await this.columnContainer.fkRelations?.[0]?.Get({ PageSize: -1 }); this.isHierarchical = candidateCollection?.Hierarchy != null; this.setValidators(); this.control.setValue(await this.multiValueToDisplay(this.currentValueStruct), { emitEvent: false }); @@ -161,7 +162,7 @@ export class EditFkMultiComponent implements CdrEditor, OnInit, OnDestroy { this.loading = false; } }); - }) + }), ); this.logger.trace(this, 'Control initialized'); } else { @@ -191,7 +192,7 @@ export class EditFkMultiComponent implements CdrEditor, OnInit, OnDestroy { subTitle: await this.translateService.get(this.columnContainer?.display).toPromise(), padding: '0', disableClose: true, - width: 'max(600px,60%)', + width: calculateSidesheetWidth(), testId: this.isHierarchical ? 'edit-fk-multi-hierarchy-sidesheet' : 'edit-fk-multi-sidesheet', data: { idList: this.multiValueProvider.getValues(this.columnContainer.value), @@ -211,7 +212,7 @@ export class EditFkMultiComponent implements CdrEditor, OnInit, OnDestroy { selection.candidates && selection.candidates.length > 0 ? { DataValue: this.multiValueProvider.getMultiValue(selection.candidates.map((v) => v.DataValue)), - DisplayValue: this.multiValueProvider.getMultiValue(selection.candidates.map((v) => v.DisplayValue)), + DisplayValue: this.multiValueProvider.getMultiValue(selection.candidates.map((v) => v.DisplayValue ?? '')), } : { DataValue: undefined, @@ -232,7 +233,7 @@ export class EditFkMultiComponent implements CdrEditor, OnInit, OnDestroy { * Updates the value for the CDR. * @param value The new value struct, that is used for the new value of the component. */ - private async writeValue(value: ValueStruct): Promise { + private async writeValue(value: ValueStruct): Promise { this.logger.debug(this, 'writeValue - called with', value); if (!this.columnContainer.canEdit) { @@ -270,7 +271,7 @@ export class EditFkMultiComponent implements CdrEditor, OnInit, OnDestroy { this.valueHasChanged.emit({ value: this.currentValueStruct, forceEmit: true }); } - private async multiValueToDisplay(value: ValueStruct): Promise { + private async multiValueToDisplay(value: ValueStruct): Promise { const values = this.multiValueProvider.getValues(value.DataValue); if (values == null) { diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk.component.html b/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk.component.html index 34dd8394a..c12daca68 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk.component.html +++ b/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk.component.html @@ -1,25 +1,43 @@ - + - {{ columnContainer?.display | translate }} - - ({{ '#LDS#Table: {0}' | translate | ldsReplace : metadataProvider.tables[selectedTable.TableName]?.DisplaySingular - || selectedTable.TableName }}) + {{ columnContainer.display | translate }} + + ({{ + '#LDS#Table: {0}' + | translate + | ldsReplace: metadataProvider.tables[selectedTable.TableName]?.DisplaySingular || selectedTable.TableName + }}) - - + [required]="columnContainer.isValueRequired" + /> + - + [attr.data-imx-identifier]="'cdr-edit-fk-mat-option-assign-candidate-' + columnContainer?.name" + >
    {{ getDisplay(candidate) }}
    @@ -29,35 +47,47 @@
    -
    - {{ '#LDS#Loading...' | translate }} + + {{ '#LDS#Loading...' | translate }}
    - + [attr.data-imx-identifier]="'cdr-edit-fk-button-remove-assignment-' + columnContainer?.name" + >
    -
    - {{ '#LDS#The value entered in the {0} box could not be found. Please select a value from the list.' | translate | - ldsReplace : (columnContainer?.display | translate) }} + {{ + '#LDS#The value entered in the {0} box could not be found. Please select a value from the list.' + | translate + | ldsReplace: (columnContainer.display | translate) + }} {{ '#LDS#This field is mandatory.' | translate }}
    - - - - + + - \ No newline at end of file + diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk.component.scss b/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk.component.scss index 761bc542e..a99329601 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk.component.scss +++ b/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; :host { flex-grow: 1; @@ -6,60 +6,25 @@ align-items: center; } -.mat-form-field { - flex-grow: 1; -} - .imx-spacer { height: 30px; } -.imx-candidate-option { - line-height: 1.5em; - display: flex; - width: 100%; - height: 50px; - - .imx-candidate-item { - width: 100%; - - .imx-candidate-content { - width: 100%; - overflow: hidden; - - .imx-candidate-display { - font-size: 14px; - line-height: 14px; - text-overflow: ellipsis; - overflow: hidden; - width: 100vw; - } - - .imx-candidate-longdisplay { - font-size: 12px; - text-overflow: ellipsis; - overflow: hidden; - } - } - } -} - .imx-viewport { height: 200px; width: 100%; overflow-x: hidden; } -.mat-option { +.mat-mdc-option { height: 50px; } .imx-suffix-container { display: flex; - margin-bottom: 5px; > *:not(:last-child) { - margin-right: 5px; + margin-right: 8px; margin-top: 0; } } @@ -68,16 +33,16 @@ cursor: pointer; margin-top: 0; display: inline; - line-height: 35px; + line-height: 36px !important; } .eui-dark-theme { :host { color: $color-gray-2; - .imx-candidate-option{ - .imx-candidate-item{ - .imx-candidate-content{ - .imx-candidate-display{ + .imx-candidate-option { + .imx-candidate-item { + .imx-candidate-content { + .imx-candidate-display { color: $color-gray-5; } .imx-candidate-longdisplay { @@ -92,10 +57,10 @@ .eui-contrast-theme { :host { color: $color-gray-0; - .imx-candidate-option{ - .imx-candidate-item{ - .imx-candidate-content{ - .imx-candidate-display{ + .imx-candidate-option { + .imx-candidate-item { + .imx-candidate-content { + .imx-candidate-display { color: $color-gray-0; } .imx-candidate-longdisplay { diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk.component.spec.ts b/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk.component.spec.ts index eca50406e..8de399133 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk.component.spec.ts +++ b/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk.component.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,22 +25,22 @@ */ import { discardPeriodicTasks, fakeAsync, tick } from '@angular/core/testing'; -import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; import { EuiSidesheetService } from '@elemental-ui/core'; -import { EditFkComponent } from './edit-fk.component'; -import { IForeignKeyInfo, IValueMetadata, IEntityColumn, ValueStruct, EntityCollectionData } from 'imx-qbm-dbts'; -import { EntityColumnStub } from '../../testing/entity-column-stub.spec'; -import { clearStylesFromDOM } from '../../testing/clear-styles.spec'; -import { MetadataService } from '../../base/metadata.service'; -import { MockBuilder, MockedComponentFixture, MockRender } from 'ng-mocks'; -import { CdrModule } from '../cdr.module'; +import { ChangeDetectorRef } from '@angular/core'; +import { EntityCollectionData, IEntityColumn, IForeignKeyInfo, IValueMetadata, ValueStruct } from '@imx-modules/imx-qbm-dbts'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; +import { MockBuilder, MockRender, MockedComponentFixture } from 'ng-mocks'; import { QbmDefaultMocks } from '../../../default-mocks.spec'; +import { MetadataService } from '../../base/metadata.service'; import { LdsReplacePipe } from '../../lds-replace/lds-replace.pipe'; +import { clearStylesFromDOM } from '../../testing/clear-styles.spec'; +import { EntityColumnStub } from '../../testing/entity-column-stub.spec'; +import { CdrModule } from '../cdr.module'; import { ViewPropertyComponent } from '../view-property/view-property.component'; -import { ChangeDetectorRef } from '@angular/core'; +import { EditFkComponent } from './edit-fk.component'; function createColumnStub( value: ValueStruct, @@ -108,6 +108,7 @@ describe('EditFkComponent', () => { const metadataMinLength = 5; const columnStub = new EntityColumnStub('value', 'display', { CanEdit: () => !testcase.input.isReadonly, + GetDisplay: () => 'display', GetMinLength: () => metadataMinLength, GetFkRelations: () => [{} as IForeignKeyInfo] as ReadonlyArray, GetLimitedValues: () => undefined, @@ -183,7 +184,7 @@ describe('EditFkComponent', () => { expect(component.control.value).toEqual(testcase.valueStructs[0]); expect(component.columnContainer.value).toEqual(testcase.valueStructs[0].DataValue); } else { - expect(component.columnContainer.displayValue).toBeUndefined(); + expect(component.columnContainer.displayValue).toEqual(''); expect(component.control.value).toBeUndefined(); expect(component.columnContainer.value).toBeUndefined(); } @@ -226,7 +227,7 @@ describe('EditFkComponent', () => { await component.removeAssignment(); - expect(component.columnContainer.displayValue).toBeUndefined(); + expect(component.columnContainer.displayValue).toEqual(''); expect(component.control.value).toBeUndefined(); expect(component.columnContainer.value).toBeUndefined(); }); @@ -296,7 +297,6 @@ describe('EditFkComponent', () => { expect(component.candidates[0].DataValue).toEqual(candidateCollection.Entities[0].Columns.XObjectKey.Value); expect(component.candidates[0].DisplayValue).toEqual(candidateCollection.Entities[0].Display); - expect(component.hasCandidatesOrIsLoading).toEqual(true); }); [ diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk.component.ts b/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk.component.ts index 0a027be34..830da52dd 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk.component.ts +++ b/imxweb/projects/qbm/src/lib/cdr/edit-fk/edit-fk.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -42,8 +42,16 @@ import { TranslateService } from '@ngx-translate/core'; import { Subject, Subscription } from 'rxjs'; import { debounceTime } from 'rxjs/operators'; -import { CollectionLoadParameters, DbObjectKey, IForeignKeyInfo, ValueStruct } from 'imx-qbm-dbts'; +import { + CollectionLoadParameters, + DbObjectKey, + EntityColumnData, + EntityData, + IForeignKeyInfo, + ValueStruct, +} from '@imx-modules/imx-qbm-dbts'; import { MetadataService } from '../../base/metadata.service'; +import { calculateSidesheetWidth } from '../../base/sidesheet-helper'; import { ClassloggerService } from '../../classlogger/classlogger.service'; import { Candidate } from '../../fk-advanced-picker/candidate.interface'; import { FkAdvancedPickerComponent } from '../../fk-advanced-picker/fk-advanced-picker.component'; @@ -78,21 +86,6 @@ export class EditFkComponent implements CdrEditor, AfterViewInit, OnDestroy, OnI */ public readonly updateRequested = new Subject(); - /** - * Indicator that the component is loading data from the server, or has a candidate list. - */ - public get hasCandidatesOrIsLoading(): boolean { - return ( - this.candidatesTotalCount > 0 || - // make sure the user can change selectedTable even if there are no available candidates - // in the first candidate table - this.columnContainer?.fkRelations?.length > 1 || - this.parameters?.search?.length > 0 || - this.parameters?.filter != null || - this.loading - ); - } - /** * The form control associated with the editor. */ @@ -111,14 +104,13 @@ export class EditFkComponent implements CdrEditor, AfterViewInit, OnDestroy, OnI /** * A list of possible candidates, that can be selected. */ + // public candidates: Candidate[] ; private _candidates: Candidate[]; - public get candidates(): Candidate[] { return this._candidates; } - - public set candidates(value: Candidate[]) { - this._candidates = value; + public set candidates(value: Candidate[] | undefined | null) { + this._candidates = value || []; } /** @@ -130,7 +122,7 @@ export class EditFkComponent implements CdrEditor, AfterViewInit, OnDestroy, OnI * The table, the user is currently selecting items from. * It is possible to choose elements from different tables at the same time. */ - public selectedTable: IForeignKeyInfo; + public selectedTable: IForeignKeyInfo | undefined; /** * Indicator, whether the candidate data is hierarchical or not. @@ -149,7 +141,7 @@ export class EditFkComponent implements CdrEditor, AfterViewInit, OnDestroy, OnI private parameters: CollectionLoadParameters = { PageSize: this.pageSize, StartIndex: 0 }; private savedParameters: CollectionLoadParameters; - private savedCandidates: Candidate[]; + private savedCandidates: Candidate[] | undefined | null; private readonly subscribers: Subscription[] = []; private isWriting = false; @@ -166,7 +158,7 @@ export class EditFkComponent implements CdrEditor, AfterViewInit, OnDestroy, OnI private readonly changeDetectorRef: ChangeDetectorRef, private readonly translator: TranslateService, public readonly metadataProvider: MetadataService, - private readonly errorHandler: ErrorHandler + private readonly errorHandler: ErrorHandler, ) { this.subscribers.push( this.control.valueChanges.pipe(debounceTime(500)).subscribe(async (keyword) => { @@ -176,7 +168,7 @@ export class EditFkComponent implements CdrEditor, AfterViewInit, OnDestroy, OnI } return this.search(keyword); - }) + }), ); } @@ -233,7 +225,7 @@ export class EditFkComponent implements CdrEditor, AfterViewInit, OnDestroy, OnI * @returns The display of the candidate object. */ public getDisplay(candidate: Candidate): string { - return candidate ? candidate.DisplayValue : undefined; + return candidate?.DisplayValue ?? ''; } /** @@ -300,11 +292,11 @@ export class EditFkComponent implements CdrEditor, AfterViewInit, OnDestroy, OnI subTitle: await this.translator.get(this.columnContainer?.display).toPromise(), padding: '0', disableClose: true, - width: 'max(600px,60%)', + width: calculateSidesheetWidth(), testId: this.isHierarchical ? 'edit-fk-hierarchy-sidesheet' : 'edit-fk-sidesheet', data: { fkRelations: this.columnContainer.fkRelations, - selectedTableName: this.selectedTable.TableName, + selectedTableName: this.selectedTable?.TableName, idList: this.columnContainer.value ? [this.columnContainer.value] : [], }, }); @@ -343,7 +335,7 @@ export class EditFkComponent implements CdrEditor, AfterViewInit, OnDestroy, OnI cdref.minlengthSubject.subscribe((elem) => { this.setControlValue(); this.changeDetectorRef.detectChanges(); - }) + }), ); } @@ -361,7 +353,7 @@ export class EditFkComponent implements CdrEditor, AfterViewInit, OnDestroy, OnI this, `Control (${this.columnContainer.name}) set to new value:`, this.columnContainer.value, - this.control.value + this.control.value, ); this.candidates = []; this.setControlValue(); @@ -371,7 +363,7 @@ export class EditFkComponent implements CdrEditor, AfterViewInit, OnDestroy, OnI } } this.valueHasChanged.emit({ value: this.control.value }); - }) + }), ); this.subscribers.push( @@ -387,7 +379,7 @@ export class EditFkComponent implements CdrEditor, AfterViewInit, OnDestroy, OnI } this.valueHasChanged.emit({ value: this.control.value }); }); - }) + }), ); this.logger.trace(this, 'Control initialized', this.control.value); } else { @@ -402,7 +394,7 @@ export class EditFkComponent implements CdrEditor, AfterViewInit, OnDestroy, OnI private setControlValue(): void { const fkRelations = this.columnContainer.fkRelations; if (fkRelations && fkRelations.length > 0) { - let table: IForeignKeyInfo; + let table: IForeignKeyInfo | undefined; if (fkRelations.length > 1 && this.columnContainer.value) { this.logger.trace(this, 'the column already has a value, and it is a dynamic foreign key'); const dbObjectKey = DbObjectKey.FromXml(this.columnContainer.value); @@ -415,7 +407,7 @@ export class EditFkComponent implements CdrEditor, AfterViewInit, OnDestroy, OnI this.control.setValue(this.getValueStruct(), { emitEvent: false }); const autoCompleteValidator = (control) => - control.value != null || this.parameters.search == null || this.candidates?.length > 0 ? null : { checkAutocomplete: true }; + control.value != null || this.parameters.search == null || !!this.candidates?.length ? null : { checkAutocomplete: true }; if (this.columnContainer.isValueRequired && this.columnContainer.canEdit) { this.control.setValidators([ (control) => (control.value == null || control.value.length === 0 ? { required: true } : null), @@ -445,7 +437,7 @@ export class EditFkComponent implements CdrEditor, AfterViewInit, OnDestroy, OnI * Updates the value for the CDR. * @param value The new value struct, that should be used as the new control value. */ - private async writeValue(value: ValueStruct): Promise { + private async writeValue(value: ValueStruct): Promise { this.logger.debug(this, 'writeValue called with value', value); if (!this.columnContainer.canEdit || this.equal(this.getValueStruct(), value)) { @@ -493,45 +485,50 @@ export class EditFkComponent implements CdrEditor, AfterViewInit, OnDestroy, OnI const multipleFkRelations = this.columnContainer.fkRelations && this.columnContainer.fkRelations.length > 1; const identityRelatedTable = this.selectedTable.TableName === 'Person'; - const newCandidates = candidateCollection.Entities?.map((entityData) => { - let key: string = ''; - let detailValue: string = entityData.LongDisplay ?? ''; - const defaultEmailColumn = entityData.Columns?.['DefaultEmailAddress']; - /** - * If the candidates data relate to identities (fkRelation Person table) - * then we want to use the email address for the detail line (displayLong) - */ - if (defaultEmailColumn && identityRelatedTable) { - detailValue = defaultEmailColumn.Value; - } - if (multipleFkRelations) { - this.logger.trace(this, 'dynamic foreign key'); - const xObjectKeyColumn = entityData.Columns?.['XObjectKey']; - key = xObjectKeyColumn ? xObjectKeyColumn.Value : undefined; - } else { - this.logger.trace(this, 'foreign key'); - - const parentColumn = entityData.Columns ? entityData.Columns[this.columnContainer.fkRelations[0].ColumnName] : undefined; - if (parentColumn != null) { - this.logger.trace(this, 'Use value from explicit parent column'); - key = parentColumn.Value; + const newCandidates: Candidate[] = + candidateCollection.Entities?.map((entityData) => { + let key: string | null | undefined = null; + let detailValue: string = entityData.LongDisplay ?? ''; + const defaultEmailColumn = entityData?.Columns?.['DefaultEmailAddress']; + /** + * If the candidates data relate to identities (fkRelation Person table) + * then we want to use the email address for the detail line (displayLong) + */ + if (defaultEmailColumn && identityRelatedTable) { + detailValue = defaultEmailColumn.Value; + } + if (multipleFkRelations) { + this.logger.trace(this, 'dynamic foreign key'); + const xObjectKeyColumn = entityData.Columns?.['XObjectKey']; + key = xObjectKeyColumn ? xObjectKeyColumn.Value : undefined; } else { - this.logger.trace(this, 'Use the primary key'); - const keys = entityData.Keys; - key = keys && keys.length ? keys[0] : ''; + this.logger.trace(this, 'foreign key'); + const parentColumn = entityData.Columns + ? this.getColumCaseInsensitive( + entityData, + this.columnContainer.fkRelations?.[0].fkColumnName || this.columnContainer.fkRelations?.[0].ColumnName || '', + ) + : undefined; + if (parentColumn != null) { + this.logger.trace(this, 'Use value from explicit parent column'); + key = parentColumn?.Value ?? ''; + } else { + this.logger.trace(this, 'Use the primary key'); + const keys = entityData.Keys; + key = keys && keys.length ? keys[0] : ''; + } } - } - return { - DataValue: key, - DisplayValue: entityData.Display, - displayLong: detailValue, - }; - }); + return { + DataValue: key ?? '', + DisplayValue: entityData.Display ?? '', + displayLong: detailValue, + }; + }) ?? []; if (concatCandidates) { - this.candidates.push(...(newCandidates || [])); + if (newCandidates) this.candidates?.push(...(newCandidates || [])); this.savedCandidates = this.candidates; } else { - this.candidates = newCandidates || []; + this.candidates = newCandidates; this.savedCandidates = this.candidates; } } finally { @@ -541,7 +538,7 @@ export class EditFkComponent implements CdrEditor, AfterViewInit, OnDestroy, OnI } } - private getValueStruct(): ValueStruct { + private getValueStruct(): ValueStruct | undefined { if (this.columnContainer.value) { return { DataValue: this.columnContainer.value, DisplayValue: this.columnContainer.displayValue || '' }; } @@ -549,9 +546,9 @@ export class EditFkComponent implements CdrEditor, AfterViewInit, OnDestroy, OnI return undefined; } - private equal(value: ValueStruct, value2: ValueStruct): boolean { + private equal(value: ValueStruct | undefined, value2: ValueStruct | undefined): boolean { if (value && value2) { - return value.DataValue === value2.DataValue && value.DisplayValue === value.DisplayValue; + return value.DataValue === value2.DataValue && value.DisplayValue === value2.DisplayValue; } return value == null && value2 == null; @@ -575,13 +572,25 @@ export class EditFkComponent implements CdrEditor, AfterViewInit, OnDestroy, OnI }, 0); } + private getColumCaseInsensitive(entityData: EntityData, columnname: string): EntityColumnData | undefined { + const index = Object.keys(entityData.Columns ?? []).find((key) => key.toLowerCase() === columnname.toLowerCase()); + if (index) { + return entityData.Columns?.[index]; + } + return undefined; + } + private async onScroll(event: any): Promise { if ( event.target.offsetHeight + event.target.scrollTop >= event.target.scrollHeight - 10 && !this.loading && - this.candidatesTotalCount > this.pageSize + this.parameters.StartIndex + this.candidatesTotalCount > this.pageSize + (this.parameters.StartIndex ?? 0) ) { this.changeDetectorRef.detectChanges(); + if (!this.parameters.StartIndex) { + this.parameters.StartIndex = 0; + } + this.parameters.StartIndex += this.pageSize; await this.updateCandidates(this.parameters, false, true); this.changeDetectorRef.detectChanges(); diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-image/edit-image.component.html b/imxweb/projects/qbm/src/lib/cdr/edit-image/edit-image.component.html index cbbe32b1a..9c3d38be5 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-image/edit-image.component.html +++ b/imxweb/projects/qbm/src/lib/cdr/edit-image/edit-image.component.html @@ -1,12 +1,14 @@ - + - + (change)="resetFileFormatErrorState(); file.click()" + (remove)="remove()" +> diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-image/edit-image.component.scss b/imxweb/projects/qbm/src/lib/cdr/edit-image/edit-image.component.scss index bcd8ac2d3..bfe7e877f 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-image/edit-image.component.scss +++ b/imxweb/projects/qbm/src/lib/cdr/edit-image/edit-image.component.scss @@ -1,3 +1,3 @@ :host { flex-grow: 1; -} \ No newline at end of file +} diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-image/edit-image.component.ts b/imxweb/projects/qbm/src/lib/cdr/edit-image/edit-image.component.ts index 9d515ffa0..dede468f0 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-image/edit-image.component.ts +++ b/imxweb/projects/qbm/src/lib/cdr/edit-image/edit-image.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,16 +28,16 @@ import { Component, ElementRef, EventEmitter, OnDestroy, ViewChild } from '@angu import { UntypedFormControl, Validators } from '@angular/forms'; import { Subject, Subscription } from 'rxjs'; -import { ColumnDependentReference } from '../column-dependent-reference.interface'; -import { CdrEditor, ValueHasChangedEventArg } from '../cdr-editor.interface'; -import { EntityColumnContainer } from '../entity-column-container'; import { ClassloggerService } from '../../classlogger/classlogger.service'; -import { Base64ImageService } from '../../images/base64-image.service'; import { FileSelectorService } from '../../file-selector/file-selector.service'; +import { Base64ImageService } from '../../images/base64-image.service'; +import { CdrEditor, ValueHasChangedEventArg } from '../cdr-editor.interface'; +import { ColumnDependentReference } from '../column-dependent-reference.interface'; +import { EntityColumnContainer } from '../entity-column-container'; /** * Provides a {@link CdrEditor | CDR editor} for editing / viewing image data columns. - * + * * To change its value, it uses an {@link ImageSelectComponent | image select component}. * When set to read-only, it uses an {@link ImageViewComponent | image view component} to display the content. */ @@ -55,7 +55,7 @@ export class EditImageComponent implements CdrEditor, OnDestroy { /** * Gets a small hint, if the file format is not supported. */ - public get fileFormatHint(): string { + public get fileFormatHint(): string | undefined { return this.fileFormatError ? '#LDS#Please select an image in PNG format.' : undefined; } @@ -92,11 +92,11 @@ export class EditImageComponent implements CdrEditor, OnDestroy { constructor( private readonly logger: ClassloggerService, private readonly imageProvider: Base64ImageService, - private readonly fileSelector: FileSelectorService + private readonly fileSelector: FileSelectorService, ) { this.subscriptions.push( this.fileSelector.fileFormatError.subscribe(() => (this.fileFormatError = true)), - this.fileSelector.fileSelected.subscribe((filepath) => this.writeValue(this.imageProvider.getImageData(filepath))) + this.fileSelector.fileSelected.subscribe((filepath) => this.writeValue(this.imageProvider.getImageData(filepath))), ); } @@ -124,7 +124,7 @@ export class EditImageComponent implements CdrEditor, OnDestroy { this.subscriptions.push( cdref.minlengthSubject.subscribe(() => { this.setValidators(); - }) + }), ); } this.subscriptions.push( @@ -137,7 +137,7 @@ export class EditImageComponent implements CdrEditor, OnDestroy { this.control.setValue(this.columnContainer.value, { emitEvent: false }); } this.valueHasChanged.emit({ value: this.control.value }); - }) + }), ); this.subscriptions.push( @@ -154,7 +154,7 @@ export class EditImageComponent implements CdrEditor, OnDestroy { } finally { } }); - }) + }), ); } } @@ -171,8 +171,8 @@ export class EditImageComponent implements CdrEditor, OnDestroy { * @param files A list of files to emit as *.png. */ // TODO: Check Upgrade - public emitFiles(files: FileList): void { - this.fileSelector.emitFiles(files, 'image/png'); + public emitFiles(files: EventTarget | null): void { + this.fileSelector.emitFiles((files as any).files, 'image/png'); } /** @@ -202,7 +202,7 @@ export class EditImageComponent implements CdrEditor, OnDestroy { * Updates the value for the CDR. * @param value The new image url, that will be used as the new value. */ - private async writeValue(value: string): Promise { + private async writeValue(value: string | undefined): Promise { this.logger.debug(this, 'writeValue called with value', value); if (!this.columnContainer.canEdit || this.columnContainer.value === value) { diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-limited-value/edit-limited-value.component.html b/imxweb/projects/qbm/src/lib/cdr/edit-limited-value/edit-limited-value.component.html index 01fdcf038..87b5f34a6 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-limited-value/edit-limited-value.component.html +++ b/imxweb/projects/qbm/src/lib/cdr/edit-limited-value/edit-limited-value.component.html @@ -1,15 +1,25 @@ - + - {{ columnContainer?.display | translate }} + {{ columnContainer.display | translate }} - - {{ columnContainer?.displayValue }} + + {{ + columnContainer?.displayValue + }} {{ limitedValue.Description }}
    - +
    diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-limited-value/edit-limited-value.component.scss b/imxweb/projects/qbm/src/lib/cdr/edit-limited-value/edit-limited-value.component.scss index fa4f45863..0a4d9f197 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-limited-value/edit-limited-value.component.scss +++ b/imxweb/projects/qbm/src/lib/cdr/edit-limited-value/edit-limited-value.component.scss @@ -5,23 +5,3 @@ display: flex; align-items: center; } - - -mat-form-field { - flex-grow: 1; - - [matsuffix] { - - margin-left: 10px; // set margin to infix - - eui-icon { - margin-top: unset; // correct margin-top: -16px from _material_fixes.scss - display: block; - } - - mat-spinner { - display: inline-flex; - margin-right: 10px; - } - } -} diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-limited-value/edit-limited-value.component.ts b/imxweb/projects/qbm/src/lib/cdr/edit-limited-value/edit-limited-value.component.ts index f99f13e2e..92d59a014 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-limited-value/edit-limited-value.component.ts +++ b/imxweb/projects/qbm/src/lib/cdr/edit-limited-value/edit-limited-value.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,14 +27,14 @@ import { Component } from '@angular/core'; import { UntypedFormControl } from '@angular/forms'; -import { EditorBase } from '../editor-base'; import { ClassloggerService } from '../../classlogger/classlogger.service'; +import { EditorBase } from '../editor-base'; /** * Provides a {@link CdrEditor | CDR editor} for editing / viewing limited value columns. - * + * * To change the value it uses an Angular Material select component. - * When set to read-only, it uses a {@link ViewPropertyComponent | view property component} to display the content. + * When set to read-only, it uses a {@link ViewPropertyComponent | view property component} to display the content. */ @Component({ selector: 'imx-edit-limited-value', diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-multi-limited-value/edit-multi-limited-value.component.html b/imxweb/projects/qbm/src/lib/cdr/edit-multi-limited-value/edit-multi-limited-value.component.html index 8bd8aa2b7..92c47f49a 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-multi-limited-value/edit-multi-limited-value.component.html +++ b/imxweb/projects/qbm/src/lib/cdr/edit-multi-limited-value/edit-multi-limited-value.component.html @@ -1,23 +1,32 @@ - - {{ columnContainer?.display | translate }} - -
    - - {{ item.Description }} - -
    -
    - -
    + + {{ columnContainer.display | translate }} + +
    + + + {{ item.Description }} + + +
    +
    + +
    - {{ '#LDS#This field is mandatory.' | translate }} + {{ '#LDS#This field is mandatory.' | translate }} - - + diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-multi-limited-value/edit-multi-limited-value.component.scss b/imxweb/projects/qbm/src/lib/cdr/edit-multi-limited-value/edit-multi-limited-value.component.scss index 3b9b75c44..2683e6df1 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-multi-limited-value/edit-multi-limited-value.component.scss +++ b/imxweb/projects/qbm/src/lib/cdr/edit-multi-limited-value/edit-multi-limited-value.component.scss @@ -1,13 +1,9 @@ -@import '@elemental-ui/core/src/styles/_palette.scss'; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; :host { flex-grow: 1; } -.mat-form-field { - width: 100%; -} - .imx-error-margin { margin-top: -15px; margin-left: 10px; @@ -18,12 +14,6 @@ flex-flow: column; } -.imx-red-checkbox { - ::ng-deep .mat-checkbox-frame { - border-color: $phoenix-red; - } -} - .imx-suffix-container { display: flex; margin-bottom: 5px; diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-multi-limited-value/edit-multi-limited-value.component.ts b/imxweb/projects/qbm/src/lib/cdr/edit-multi-limited-value/edit-multi-limited-value.component.ts index 111716158..38881f9cf 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-multi-limited-value/edit-multi-limited-value.component.ts +++ b/imxweb/projects/qbm/src/lib/cdr/edit-multi-limited-value/edit-multi-limited-value.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,20 +25,24 @@ */ import { ChangeDetectorRef, Component, EventEmitter, OnDestroy } from '@angular/core'; -import { UntypedFormArray, UntypedFormControl } from '@angular/forms'; +import { FormGroup, UntypedFormArray, UntypedFormControl } from '@angular/forms'; import { Subject, Subscription } from 'rxjs'; import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators'; -import { LimitedValueData } from 'imx-qbm-dbts'; -import { ColumnDependentReference } from '../column-dependent-reference.interface'; +import { LimitedValueData } from '@imx-modules/imx-qbm-dbts'; import { ClassloggerService } from '../../classlogger/classlogger.service'; +import { MultiValueService } from '../../multi-value/multi-value.service'; import { CdrEditor, ValueHasChangedEventArg } from '../cdr-editor.interface'; +import { ColumnDependentReference } from '../column-dependent-reference.interface'; import { EntityColumnContainer } from '../entity-column-container'; -import { MultiValueService } from '../../multi-value/multi-value.service'; + +interface LimitedForm { + array: UntypedFormArray; +} /** * Provides a {@link CdrEditor | CDR editor} for editing / viewing multi limited value columns - * + * * To change the value it uses a list of check boxes with one box per possible value. * When set to read-only, it uses a {@link ViewPropertyComponent | view property component} to display the content. */ @@ -54,7 +58,9 @@ export class EditMultiLimitedValueComponent implements CdrEditor, OnDestroy { * The form control associated with the editor. */ // TODO: Check Upgrade - public control = new UntypedFormArray([]); + public control = new FormGroup({ + array: new UntypedFormArray([]), + }); /** * The container that wraps the column functionality. @@ -68,12 +74,12 @@ export class EditMultiLimitedValueComponent implements CdrEditor, OnDestroy { public readonly pendingChanged = new EventEmitter(); private readonly subscriptions: Subscription[] = []; - private isWriting = false; + public isWriting = false; constructor( private readonly logger: ClassloggerService, private readonly changeDetectorRef: ChangeDetectorRef, - private readonly multiValueProvider: MultiValueService + private readonly multiValueProvider: MultiValueService, ) {} /** @@ -93,22 +99,20 @@ export class EditMultiLimitedValueComponent implements CdrEditor, OnDestroy { this.columnContainer.init(cdref); this.initValues(); this.subscriptions.push( - this.control.valueChanges - .pipe( - tap(() => this.pendingChanged.emit(true)), - debounceTime(1400), - distinctUntilChanged(), - ) - .subscribe( - async (values) => await this.writeValue(values) - ) + this.control.controls.array.valueChanges + .pipe( + tap(() => this.pendingChanged.emit(true)), + debounceTime(1400), + distinctUntilChanged(), + ) + .subscribe(async (values) => await this.writeValue(values)), ); this.subscriptions.push( this.columnContainer.subscribe(() => { if (this.isWriting) { return; } - if (this.getSelectedNamesMultiValue(this.control.value) !== this.columnContainer.value) { + if (this.getSelectedNamesMultiValue(this.control.controls.array.value) !== this.columnContainer.value) { this.initValues(); } @@ -116,7 +120,7 @@ export class EditMultiLimitedValueComponent implements CdrEditor, OnDestroy { this.subscriptions.push( cdref.minlengthSubject.subscribe(() => { this.initValues(); - }) + }), ); } @@ -124,12 +128,12 @@ export class EditMultiLimitedValueComponent implements CdrEditor, OnDestroy { this.updateRequested.subscribe(() => setTimeout(() => { this.initValues(); - this.control.updateValueAndValidity({ onlySelf: true, emitEvent: false }); - }) - ) + this.control.controls.array.updateValueAndValidity({ onlySelf: true, emitEvent: false }); + }), + ), ); this.valueHasChanged.emit({ value: this.columnContainer.value }); - }) + }), ); this.logger.trace(this, 'Control initialized'); } else { @@ -141,16 +145,16 @@ export class EditMultiLimitedValueComponent implements CdrEditor, OnDestroy { * Initializes possible values and marks all selected ones. */ public initValues(): void { - if (this.control.controls?.length > 0) { + if (this.control.controls.array.controls?.length > 0) { return; } const selectedValues = this.multiValueProvider.getValues(this.columnContainer.value); - this.columnContainer.limitedValuesContainer.values.forEach((limitedValueData) => - this.control.push(new UntypedFormControl(this.isSelected(limitedValueData, selectedValues))) + this.columnContainer.limitedValuesContainer?.values?.forEach((limitedValueData) => + this.control.controls.array.push(new UntypedFormControl(this.isSelected(limitedValueData, selectedValues))), ); if (this.columnContainer.isValueRequired && this.columnContainer.canEdit) { - this.control.setValidators((control: UntypedFormArray) => - control.controls.find((checkBox) => checkBox.value) ? null : { required: true } + this.control.controls.array.setValidators((control: UntypedFormArray) => + control.controls.find((checkBox) => checkBox.value) ? null : { required: true }, ); } } @@ -178,33 +182,32 @@ export class EditMultiLimitedValueComponent implements CdrEditor, OnDestroy { try { this.logger.debug(this, 'writeValue - updateCdrValue...'); this.isWriting = true; - this.control.disable({ emitEvent: false }); + this.control.controls.array.disable({ emitEvent: false }); this.changeDetectorRef.detectChanges(); await this.columnContainer.updateValue(value); } catch (e) { this.logger.error(this, e); } finally { this.isWriting = false; - this.control.enable({ emitEvent: false }); + this.control.controls.array.enable({ emitEvent: false }); this.pendingChanged.emit(false); this.changeDetectorRef.detectChanges(); - if (this.getSelectedNamesMultiValue(this.control.value) !== this.columnContainer.value) { + if (this.getSelectedNamesMultiValue(this.control.controls.array.value) !== this.columnContainer.value) { const selectedValues = this.multiValueProvider.getValues(this.columnContainer.value); - this.control.controls.forEach((checkBox, index) => - checkBox.setValue(this.isSelected(this.columnContainer.limitedValuesContainer.values[index], selectedValues), { + this.control.controls.array.controls.forEach((checkBox, index) => + checkBox.setValue(this.isSelected(this.columnContainer.limitedValuesContainer.values?.[index], selectedValues), { emitEvent: false, - }) + }), ); } } } - /** * Gets the MultiValue of the selected values. * @param values The array of booleans provided by the checkboxes */ - private getSelectedNamesMultiValue(values: boolean[]): string { + private getSelectedNamesMultiValue(values: boolean[]): string | undefined { return this.multiValueProvider.getMultiValue(this.getSelectedNames(values)); } @@ -216,13 +219,18 @@ export class EditMultiLimitedValueComponent implements CdrEditor, OnDestroy { const selectedValues: string[] = []; values.forEach((value, index) => { if (value) { - selectedValues.push(this.columnContainer.limitedValuesContainer.values[index]?.Value); + const newValue = this.columnContainer.limitedValuesContainer.values?.[index]?.Value; + if (newValue) { + selectedValues.push(this.columnContainer.limitedValuesContainer.values?.[index]?.Value); + } } }); return selectedValues; } - private isSelected(limitedValueData: LimitedValueData, selectedValues: string[]): boolean { - return selectedValues && selectedValues.indexOf(limitedValueData.Value + '') > -1; + private isSelected(limitedValueData: LimitedValueData | undefined, selectedValues: string[] | undefined): boolean { + if (!limitedValueData || !selectedValues) return false; + + return selectedValues && selectedValues.indexOf(limitedValueData.Value) > -1; } } diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-multi-value/edit-multi-value.component.html b/imxweb/projects/qbm/src/lib/cdr/edit-multi-value/edit-multi-value.component.html index b3cb3e413..38b7c5361 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-multi-value/edit-multi-value.component.html +++ b/imxweb/projects/qbm/src/lib/cdr/edit-multi-value/edit-multi-value.component.html @@ -1,16 +1,18 @@ - - {{ columnContainer?.display | translate }} - + + {{ columnContainer.display | translate }} + {{ '#LDS#This field is mandatory.' | translate }} - - + - diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-multi-value/edit-multi-value.component.scss b/imxweb/projects/qbm/src/lib/cdr/edit-multi-value/edit-multi-value.component.scss index a2fcf0657..7bd3a17ca 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-multi-value/edit-multi-value.component.scss +++ b/imxweb/projects/qbm/src/lib/cdr/edit-multi-value/edit-multi-value.component.scss @@ -2,11 +2,7 @@ flex-grow: 1; } -.mat-form-field { - width: 100%; -} - .imx-multi-value-container { display: flex; flex-flow: column; -} \ No newline at end of file +} diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-multi-value/edit-multi-value.component.ts b/imxweb/projects/qbm/src/lib/cdr/edit-multi-value/edit-multi-value.component.ts index b9c967d42..a40792e0e 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-multi-value/edit-multi-value.component.ts +++ b/imxweb/projects/qbm/src/lib/cdr/edit-multi-value/edit-multi-value.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,15 +28,15 @@ import { Component, EventEmitter, OnDestroy } from '@angular/core'; import { UntypedFormControl, Validators } from '@angular/forms'; import { Subscription } from 'rxjs'; -import { ColumnDependentReference } from '../column-dependent-reference.interface'; import { ClassloggerService } from '../../classlogger/classlogger.service'; -import { EntityColumnContainer } from '../entity-column-container'; -import { CdrEditor, ValueHasChangedEventArg } from '../cdr-editor.interface'; import { MultiValueService } from '../../multi-value/multi-value.service'; +import { CdrEditor, ValueHasChangedEventArg } from '../cdr-editor.interface'; +import { ColumnDependentReference } from '../column-dependent-reference.interface'; +import { EntityColumnContainer } from '../entity-column-container'; /** * Provides a {@link CdrEditor | CDR editor} for editing / viewing multi valued columns. - * + * * To change its value, it uses a text area. Each line represents a part of the multi value. * When set to read-only, it uses a {@link ViewPropertyComponent | view property component} to display the content. */ @@ -64,7 +64,10 @@ export class EditMultiValueComponent implements CdrEditor, OnDestroy { private readonly subscribers: Subscription[] = []; private isWriting = false; - constructor(private readonly logger: ClassloggerService, private readonly multiValueProvider: MultiValueService) {} + constructor( + private readonly logger: ClassloggerService, + private readonly multiValueProvider: MultiValueService, + ) {} /** * Unsubscribes all events, after the 'OnDestroy' hook is triggered. @@ -96,7 +99,7 @@ export class EditMultiValueComponent implements CdrEditor, OnDestroy { } else { this.control.setValidators(null); } - }) + }), ); } @@ -109,7 +112,7 @@ export class EditMultiValueComponent implements CdrEditor, OnDestroy { this.control.setValue(this.columnContainer.value); } this.valueHasChanged.emit({ value: this.control.value }); - }) + }), ); this.subscribers.push(this.control.valueChanges.subscribe(async (value) => this.writeValue(this.fromTextArea(value)))); this.logger.trace(this, 'Control initialized'); @@ -122,7 +125,7 @@ export class EditMultiValueComponent implements CdrEditor, OnDestroy { * Updates the value for the CDR. * @param values The values, that will be used as a new value. */ - private async writeValue(value: string): Promise { + private async writeValue(value: string | undefined): Promise { this.logger.debug(this, 'writeValue called with value', value); if (!this.columnContainer.canEdit || this.columnContainer.value === value) { @@ -146,12 +149,12 @@ export class EditMultiValueComponent implements CdrEditor, OnDestroy { this.valueHasChanged.emit({ value, forceEmit: true }); } - private toTextArea(value: string): string { + private toTextArea(value: string): string | undefined { const values = this.multiValueProvider.getValues(value); return values && values.length > 0 ? values.join('\r\n') : undefined; } - private fromTextArea(value: string): string { + private fromTextArea(value: string): string | undefined { return value ? this.multiValueProvider.getMultiValue(value.replace(/\r\n/g, '\n').split('\n')) : undefined; } } diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-multiline/edit-multiline.component.html b/imxweb/projects/qbm/src/lib/cdr/edit-multiline/edit-multiline.component.html index 835321c87..bca9076d9 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-multiline/edit-multiline.component.html +++ b/imxweb/projects/qbm/src/lib/cdr/edit-multiline/edit-multiline.component.html @@ -1,22 +1,24 @@ - - {{ columnContainer.display | translate }} - - -
    - -
    - - {{ '#LDS#This field is mandatory.' | translate }} - + + {{ columnContainer.display | translate }} + +
    + +
    + + {{ '#LDS#This field is mandatory.' | translate }} +
    - - + diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-multiline/edit-multiline.component.scss b/imxweb/projects/qbm/src/lib/cdr/edit-multiline/edit-multiline.component.scss index 24645d267..3e9f20bb0 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-multiline/edit-multiline.component.scss +++ b/imxweb/projects/qbm/src/lib/cdr/edit-multiline/edit-multiline.component.scss @@ -1,20 +1,7 @@ -textarea.mat-input-element.cdk-textarea-autosize { - box-sizing: content-box; - } - :host { flex-grow: 1; display: flex; align-items: center; - - ::ng-deep .mat-form-field-appearance-outline .mat-form-field-suffix { - align-self: flex-start; - margin-top: 15px; - } -} - -.mat-form-field { - flex-grow: 1; } .imx-spacer { @@ -24,10 +11,10 @@ textarea.mat-input-element.cdk-textarea-autosize { .imx-suffix-container { display: flex; margin-bottom: 5px; - - > *{ + + > * { justify-self: flex-start; margin-right: 5px; margin-top: 0; } -} \ No newline at end of file +} diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-multiline/edit-multiline.component.ts b/imxweb/projects/qbm/src/lib/cdr/edit-multiline/edit-multiline.component.ts index e5b3d8157..e5d5103b3 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-multiline/edit-multiline.component.ts +++ b/imxweb/projects/qbm/src/lib/cdr/edit-multiline/edit-multiline.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-number/edit-number.component.html b/imxweb/projects/qbm/src/lib/cdr/edit-number/edit-number.component.html index 66a3693fd..0dfb4a758 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-number/edit-number.component.html +++ b/imxweb/projects/qbm/src/lib/cdr/edit-number/edit-number.component.html @@ -1,29 +1,38 @@ - - - {{ columnContainer?.display | translate }} - - -
    - -
    - - {{ '#LDS#WD_InputInvalidInteger' | translate | ldsReplace: control.value }} - - - {{ '#LDS#Please enter a number greater than or equal to {0}.' | translate | ldsReplace: columnContainer.valueConstraint.MinValue }} - - - {{ '#LDS#Please enter a number less than or equal to {0}.' | translate | ldsReplace: columnContainer.valueConstraint.MaxValue }} - + + + {{ columnContainer.display | translate }} + + +
    + +
    + + {{ '#LDS#The value you entered is invalid. Enter an integer number.' | translate }} + + + {{ + '#LDS#The value you entered is invalid. Enter a number greater than or equal to {0}.' + | translate + | ldsReplace: columnContainer.valueConstraint?.MinValue + }} + + + {{ + '#LDS#The value you entered is invalid. Enter a number less than or equal to {0}.' + | translate + | ldsReplace: columnContainer.valueConstraint?.MaxValue + }} +
    - - + diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-number/edit-number.component.scss b/imxweb/projects/qbm/src/lib/cdr/edit-number/edit-number.component.scss index 1901f0a99..58e286ef8 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-number/edit-number.component.scss +++ b/imxweb/projects/qbm/src/lib/cdr/edit-number/edit-number.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; :host { flex-grow: 1; @@ -6,10 +6,6 @@ align-items: center; } -.mat-form-field { - flex-grow: 1; -} - .imx-spacer { height: 30px; } @@ -18,8 +14,4 @@ display: flex; margin-bottom: 5px; - > .imx-suffix-spinner { - margin-right: 5px; - margin-top: 0; - } } diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-number/edit-number.component.ts b/imxweb/projects/qbm/src/lib/cdr/edit-number/edit-number.component.ts index 728721d4d..0d9c1b2ab 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-number/edit-number.component.ts +++ b/imxweb/projects/qbm/src/lib/cdr/edit-number/edit-number.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,7 +27,7 @@ import { AfterViewInit, Component } from '@angular/core'; import { AbstractControl, UntypedFormControl, ValidatorFn } from '@angular/forms'; -import { ValType } from 'imx-qbm-dbts'; +import { ValType } from '@imx-modules/imx-qbm-dbts'; import { ClassloggerService } from '../../classlogger/classlogger.service'; import { EditorBase } from '../editor-base'; import { NumberError } from './number-error.interface'; diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-number/number-error.interface.ts b/imxweb/projects/qbm/src/lib/cdr/edit-number/number-error.interface.ts index 21494121d..8482a4070 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-number/number-error.interface.ts +++ b/imxweb/projects/qbm/src/lib/cdr/edit-number/number-error.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-number/number-validator.service.ts b/imxweb/projects/qbm/src/lib/cdr/edit-number/number-validator.service.ts index 0d1ef64d4..edfc43239 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-number/number-validator.service.ts +++ b/imxweb/projects/qbm/src/lib/cdr/edit-number/number-validator.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,7 +26,7 @@ import { Injectable } from '@angular/core'; -import { ValueConstraint } from 'imx-qbm-dbts'; +import { ValueConstraint } from '@imx-modules/imx-qbm-dbts'; import { NumberError } from './number-error.interface'; /** @@ -48,7 +48,7 @@ export class NumberValidatorService { * @param range The {@link ValueConstraint}, that determines the bounds. * @returns */ - public validate(value: any, range: ValueConstraint): NumberError | null { + public validate(value: any, range: ValueConstraint | undefined): NumberError | null { if (value == null) { return null; } diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-risk-index/edit-risk-index.component.html b/imxweb/projects/qbm/src/lib/cdr/edit-risk-index/edit-risk-index.component.html index 84f5765a0..d0b8af7ff 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-risk-index/edit-risk-index.component.html +++ b/imxweb/projects/qbm/src/lib/cdr/edit-risk-index/edit-risk-index.component.html @@ -1,25 +1,28 @@ - - {{ columnContainer?.display | translate }} - -
    +
    +
    + +
    + +
    +
    +
    #LDS#No risk - + #LDS#Maximum risk
    -
    - -
    - - - +
    - - - \ No newline at end of file + + diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-risk-index/edit-risk-index.component.scss b/imxweb/projects/qbm/src/lib/cdr/edit-risk-index/edit-risk-index.component.scss index 4f9c59b0a..2dc95f434 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-risk-index/edit-risk-index.component.scss +++ b/imxweb/projects/qbm/src/lib/cdr/edit-risk-index/edit-risk-index.component.scss @@ -3,70 +3,27 @@ :host { flex-grow: 1; } - -.slider-container { +.imx-slider-label-container{ display: flex; align-items: center; - - .mat-slider { - width: 300px; - flex-grow: 2; - } - - .slider-label { - &__main { - display: block; - } - &__prefix { - font-size: 14px; - margin-right: 8px; - } - &__suffix { - font-size: 14px; - margin-left: 8px; - } - } - - ::ng-deep .mat-slider-thumb-container { - .mat-slider-thumb-label { - transform: rotate(45deg); - border-radius: 50% 50% 0; - } - .mat-slider-thumb { - transform: scale(0); - } - .mat-slider-thumb-label-text { - opacity: 1; + min-height: 24px; + .imx-suffix-container { + display: flex; + margin-left: 10px; + width: 24px; + height: 24px; + + > * { + margin-right: 5px; + margin-top: 0; } } } - -:host { +.imx-slider-container{ display: flex; align-items: center; -} - -.mat-form-field { - flex-grow: 1; -} - -.imx-suffix-container { - display: flex; - margin-bottom: 5px; - margin-left: 10px; - width: 30px; - height: 30px; - - > * { - margin-right: 5px; - margin-top: 0; - } -} - -@media only screen and (max-width: 320px) { - .slider-container { - .mat-slider-horizontal { - min-width: unset; - } + gap: 12px; + .mat-mdc-slider{ + flex-grow: 1; } } diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-risk-index/edit-risk-index.component.ts b/imxweb/projects/qbm/src/lib/cdr/edit-risk-index/edit-risk-index.component.ts index a542e93ee..cd32ea92c 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-risk-index/edit-risk-index.component.ts +++ b/imxweb/projects/qbm/src/lib/cdr/edit-risk-index/edit-risk-index.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,11 +26,12 @@ import { Component } from '@angular/core'; import { UntypedFormControl } from '@angular/forms'; +import { MatSliderDragEvent } from '@angular/material/slider'; import { EditorBase } from '../editor-base'; /** * Provides a {@link CdrEditor | CDR editor} for editing / viewing risk index columns. - * + * * To change the value, it uses an Angular Material slider, that ranges between 0 and 1. * When set to read-only, it uses a {@link ViewPropertyComponent | view property component} to display the content. */ @@ -43,12 +44,7 @@ export class EditRiskIndexComponent extends EditorBase { /** * The form control associated with the editor. */ - public readonly control = new UntypedFormControl(undefined, { updateOn: 'blur' }); - - /** - * @ignore Only used in template. - */ - public sliderFocused = false; + public readonly control = new UntypedFormControl(undefined, { updateOn: 'submit' }); /** * Converts a number value to a string in the current language. @@ -58,4 +54,9 @@ export class EditRiskIndexComponent extends EditorBase { public formatLabel(value: number): string { return value.toLocaleString(); } + + onDragEnd($event: MatSliderDragEvent): void { + this.control.setValue($event.value); + this.control.markAsDirty(); + } } diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-url/edit-url.component.html b/imxweb/projects/qbm/src/lib/cdr/edit-url/edit-url.component.html index a8b949fbd..24ddbb38f 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-url/edit-url.component.html +++ b/imxweb/projects/qbm/src/lib/cdr/edit-url/edit-url.component.html @@ -1,7 +1,13 @@ - - {{ columnContainer?.display | translate }} - + + {{ columnContainer.display | translate }} + {{ '#LDS#Please enter a valid URL in the format https://www.example.com or http://www.example.com respectively.' | translate }} @@ -12,10 +18,5 @@ - - + - diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-url/edit-url.component.scss b/imxweb/projects/qbm/src/lib/cdr/edit-url/edit-url.component.scss index b03a7d6a4..bfe7e877f 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-url/edit-url.component.scss +++ b/imxweb/projects/qbm/src/lib/cdr/edit-url/edit-url.component.scss @@ -1,7 +1,3 @@ :host { flex-grow: 1; } - -.mat-form-field { - width: 100%; -} \ No newline at end of file diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-url/edit-url.component.spec.ts b/imxweb/projects/qbm/src/lib/cdr/edit-url/edit-url.component.spec.ts index cfb630225..24ecfc5a5 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-url/edit-url.component.spec.ts +++ b/imxweb/projects/qbm/src/lib/cdr/edit-url/edit-url.component.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { EditUrlComponent } from './edit-url.component'; @@ -32,11 +32,10 @@ describe('EditUrlComponent', () => { let component: EditUrlComponent; let fixture: ComponentFixture; - beforeEach(async(() => { + beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - declarations: [ EditUrlComponent ] - }) - .compileComponents(); + declarations: [EditUrlComponent], + }).compileComponents(); })); beforeEach(() => { diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-url/edit-url.component.ts b/imxweb/projects/qbm/src/lib/cdr/edit-url/edit-url.component.ts index 7cecba97a..a381fbf4a 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-url/edit-url.component.ts +++ b/imxweb/projects/qbm/src/lib/cdr/edit-url/edit-url.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -100,7 +100,7 @@ export class EditUrlComponent implements CdrEditor, OnDestroy { validators.push(Validators.required); } this.control.setValidators(validators); - }) + }), ); } @@ -113,7 +113,7 @@ export class EditUrlComponent implements CdrEditor, OnDestroy { this.control.setValue(this.columnContainer.value, { emitEvent: false }); } this.valueHasChanged.emit({ value: this.control.value }); - }) + }), ); this.control.setValidators(validators); diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-url/url-validator.service.spec.ts b/imxweb/projects/qbm/src/lib/cdr/edit-url/url-validator.service.spec.ts index e110a941c..f9ac55795 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-url/url-validator.service.spec.ts +++ b/imxweb/projects/qbm/src/lib/cdr/edit-url/url-validator.service.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qbm/src/lib/cdr/edit-url/url-validator.service.ts b/imxweb/projects/qbm/src/lib/cdr/edit-url/url-validator.service.ts index b57933f4f..5bc689dc2 100644 --- a/imxweb/projects/qbm/src/lib/cdr/edit-url/url-validator.service.ts +++ b/imxweb/projects/qbm/src/lib/cdr/edit-url/url-validator.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,7 +31,7 @@ import { ValidatorFn, Validators } from '@angular/forms'; * A service for providing an url validation. */ @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class UrlValidatorService { @@ -43,6 +43,6 @@ export class UrlValidatorService { * 'http://www.google.com' */ public readonly validators: ReadonlyArray = [ - Validators.pattern(new RegExp('^(http|https)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\/\S*)?')) + Validators.pattern(new RegExp('^(http|https)://[a-zA-Z0-9-.]+.[a-zA-Z]{2,3}(/S*)?')), ]; } diff --git a/imxweb/projects/qbm/src/lib/cdr/editor-base.ts b/imxweb/projects/qbm/src/lib/cdr/editor-base.ts index 47e9483bc..978a01694 100644 --- a/imxweb/projects/qbm/src/lib/cdr/editor-base.ts +++ b/imxweb/projects/qbm/src/lib/cdr/editor-base.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -23,16 +23,16 @@ * THIS SOFTWARE OR ITS DERIVATIVES. * */ -import { OnDestroy, Component, EventEmitter, ErrorHandler } from '@angular/core'; -import { AbstractControl, ValidatorFn, Validators } from '@angular/forms'; +import { ChangeDetectorRef, Component, ErrorHandler, EventEmitter, OnDestroy } from '@angular/core'; +import { AbstractControl, Validators } from '@angular/forms'; import { Subject, Subscription } from 'rxjs'; +import { ValType } from '@imx-modules/imx-qbm-dbts'; +import { ServerError } from '../base/server-error'; +import { ClassloggerService } from '../classlogger/classlogger.service'; import { CdrEditor, ValueHasChangedEventArg } from './cdr-editor.interface'; import { ColumnDependentReference } from './column-dependent-reference.interface'; -import { ClassloggerService } from '../classlogger/classlogger.service'; import { EntityColumnContainer } from './entity-column-container'; -import { ServerError } from '../base/server-error'; -import { ValType } from 'imx-qbm-dbts'; /** * A base class for CDR editors, that handles simple dataTypes like string, boolean or integer. @@ -73,7 +73,7 @@ export abstract class EditorBase implements CdrEditor, OnDestroy { * @ignore * Used for the template and displays the last server error, that occured while loading content. */ - public lastError: ServerError | undefined; + public lastError: ServerError; /** * The maximal length a string could have. @@ -86,7 +86,11 @@ export abstract class EditorBase implements CdrEditor, OnDestroy { private readonly subscribers: Subscription[] = []; private isWriting = false; - public constructor(protected readonly logger: ClassloggerService, protected readonly errorHandler?: ErrorHandler) {} + public constructor( + protected readonly logger: ClassloggerService, + protected readonly errorHandler?: ErrorHandler, + private cdr?: ChangeDetectorRef, + ) {} /** * Unsubscribes all events, as soon as the component is destroyed. @@ -98,8 +102,11 @@ export abstract class EditorBase implements CdrEditor, OnDestroy { /** * If an error occured, it returns its message */ - public get validationErrorMessage(): string { - return this.lastError?.toString() || ''; + public get validationErrorMessage(): string | undefined { + if (this.control.errors?.['generalError']) { + return this.lastError.toString(); + } + return undefined; } /** @@ -111,8 +118,6 @@ export abstract class EditorBase implements CdrEditor, OnDestroy { if (cdref && cdref.column) { this.columnContainer.init(cdref); - this.control.addValidators(EditorBase.hasServerError(this)); - this.setControlValue(); this.subscribers.push(this.control.valueChanges.subscribe(async (value) => this.writeValue(value))); @@ -121,7 +126,7 @@ export abstract class EditorBase implements CdrEditor, OnDestroy { this.subscribers.push( cdref.minlengthSubject.subscribe((elem) => { this.setControlValue(); - }) + }), ); } @@ -132,38 +137,30 @@ export abstract class EditorBase implements CdrEditor, OnDestroy { return; } - if (!this.control.hasError('generalError') && this.control.value !== this.columnContainer.value) { + if (this.control.value !== this.columnContainer.value) { this.logger.trace( this, `Control (${this.columnContainer.name}) set to new value:`, this.columnContainer.value, - this.control.value + this.control.value, ); this.setControlValue(); } this.valueHasChanged.emit({ value: this.control.value }); - }) + }), ); this.subscribers.push( this.updateRequested.subscribe(() => { setTimeout(() => { try { - if (!this.control.hasError('generalError') && this.control.value !== this.columnContainer.value) { - this.logger.trace( - this, - `Control (${this.columnContainer.name}) set to new value:`, - this.columnContainer.value, - this.control.value - ); - this.setControlValue(); - this.control.updateValueAndValidity({ onlySelf: true, emitEvent: false }); - } + this.setControlValue(); + this.control.updateValueAndValidity({ onlySelf: true, emitEvent: false }); } finally { } this.valueHasChanged.emit({ value: this.control.value }); }); - }) + }), ); this.logger.trace(this, 'Control initialized'); @@ -183,9 +180,9 @@ export abstract class EditorBase implements CdrEditor, OnDestroy { this.columnContainer.type !== ValType.Bool // because bool is always valid ) { this.logger.debug(this, `A value for column "${this.columnContainer.name}" is required`); - this.control.setValidators([Validators.required, EditorBase.hasServerError(this)]); + this.control.setValidators(Validators.required); } else { - this.control.setValidators(EditorBase.hasServerError(this)); + this.control.setValidators(null); } } @@ -194,10 +191,11 @@ export abstract class EditorBase implements CdrEditor, OnDestroy { * @param value the new value */ private async writeValue(value: any): Promise { - if (this.control.errors && Object.keys(this.control.errors).some((elem) => elem !== 'generalError')) { - this.logger.debug(this, 'writeValue - client validation failed'); + if (this.control.errors) { + this.logger.debug(this, 'writeValue - validation failed'); return; } + this.logger.debug(this, 'writeValue called with value', value); if (!this.columnContainer.canEdit || this.columnContainer.value === value) { @@ -209,11 +207,10 @@ export abstract class EditorBase implements CdrEditor, OnDestroy { try { this.logger.debug(this, 'writeValue - PutValue...'); await this.columnContainer.updateValue(value); - this.lastError = undefined; } catch (e) { this.lastError = e; this.logger.error(this, e); - this.control.updateValueAndValidity({ emitEvent: true }); + this.control.setErrors({ generalError: true }); } finally { this.isBusy = false; this.isWriting = false; @@ -224,11 +221,6 @@ export abstract class EditorBase implements CdrEditor, OnDestroy { } this.valueHasChanged.emit({ value, forceEmit: true }); - } - - private static hasServerError(base: any): ValidatorFn { - return (_: AbstractControl): { [key: string]: boolean } | null => { - return !base.lastError ? null : { generalError: true }; - }; + this.cdr?.detectChanges(); } } diff --git a/imxweb/projects/qbm/src/lib/cdr/entity-column-container.spec.ts b/imxweb/projects/qbm/src/lib/cdr/entity-column-container.spec.ts index 861738b69..7913d4c71 100644 --- a/imxweb/projects/qbm/src/lib/cdr/entity-column-container.spec.ts +++ b/imxweb/projects/qbm/src/lib/cdr/entity-column-container.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { EntityColumnContainer } from './entity-column-container'; -import { IEntityColumn } from 'imx-qbm-dbts'; +import { IEntityColumn } from '@imx-modules/imx-qbm-dbts'; describe('EntityColumnContainer', () => { [ @@ -33,7 +33,7 @@ describe('EntityColumnContainer', () => { { flags: { isReadOnly: false, canEdit: true }, expectedEdit: true }, { flags: { isReadOnly: true, canEdit: false }, expectedEdit: false }, { flags: { isReadOnly: false, canEdit: false }, expectedEdit: false }, - ].forEach(testcase => { + ].forEach((testcase) => { it('determines canEdit', () => { const columnContainer = new EntityColumnContainer(); columnContainer.init({ @@ -42,15 +42,14 @@ describe('EntityColumnContainer', () => { GetValue: () => undefined, GetMetadata: () => ({ CanEdit: () => testcase.flags.canEdit, - GetLimitedValues: () => undefined - }) + GetLimitedValues: () => undefined, + }), } as IEntityColumn, - isReadOnly: () => testcase.flags.isReadOnly + isReadOnly: () => testcase.flags.isReadOnly, }); expect(columnContainer.canEdit).toEqual(testcase.expectedEdit); - } - ); + }); }); it('provides displayValue', () => { @@ -61,9 +60,9 @@ describe('EntityColumnContainer', () => { column: { GetValue: () => undefined, GetDisplayValue: () => displayValue, - GetMetadata: () => undefined + GetMetadata: () => undefined, } as IEntityColumn, - isReadOnly: () => false + isReadOnly: () => false, }); expect(columnContainer.displayValue).toEqual(displayValue); }); @@ -76,9 +75,9 @@ describe('EntityColumnContainer', () => { column: { GetValue: () => value, GetDisplayValue: () => undefined, - GetMetadata: () => undefined + GetMetadata: () => undefined, } as IEntityColumn, - isReadOnly: () => false + isReadOnly: () => false, }); expect(columnContainer.value).toEqual(value); }); diff --git a/imxweb/projects/qbm/src/lib/cdr/entity-column-container.ts b/imxweb/projects/qbm/src/lib/cdr/entity-column-container.ts index e279c5054..71911ab00 100644 --- a/imxweb/projects/qbm/src/lib/cdr/entity-column-container.ts +++ b/imxweb/projects/qbm/src/lib/cdr/entity-column-container.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { IForeignKeyInfo, IValueMetadata, ValType, ValueConstraint, ValueStruct } from 'imx-qbm-dbts'; +import { IForeignKeyInfo, IValueMetadata, ValType, ValueConstraint, ValueStruct } from '@imx-modules/imx-qbm-dbts'; import { Subscription } from 'rxjs'; import { ValueWrapper } from '../value-wrapper/value-wrapper'; import { ColumnDependentReference } from './column-dependent-reference.interface'; @@ -52,14 +52,14 @@ export class EntityColumnContainer implements ValueWrapper { /** * The display value of the column. */ - public get displayValue(): string { + public get displayValue(): string | undefined { return this.cdr && this.cdr.column ? this.cdr.column.GetDisplayValue() : undefined; } /** * A read-only list of {@link IForeignKeyInfo | FK informations} */ - public get fkRelations(): ReadonlyArray { + public get fkRelations(): ReadonlyArray | undefined { return this.cdr && this.cdr.column ? this.cdr.column.GetMetadata().GetFkRelations() : undefined; } @@ -68,51 +68,53 @@ export class EntityColumnContainer implements ValueWrapper { * If the CDR itself doesn't contain a display, the display given by the column is used. */ public get display(): string { - return this.cdr && (this.cdr.display || (this.cdr.column ? this.cdr.column.GetMetadata().GetDisplay() : undefined)); + return this.cdr && (this.cdr.display || (this.cdr.column ? this.cdr.column.GetMetadata().GetDisplay() : '')); } /** * The information, whether a value is mandatory or not. */ public get isValueRequired(): boolean { - return this.cdr && (this.cdr.minLength > 0 || (this.cdr.column && this.cdr.column.GetMetadata().GetMinLength() > 0)); + return this.cdr && ((this.cdr?.minLength ?? 0) > 0 || (this.cdr.column && this.cdr.column.GetMetadata().GetMinLength() > 0)); } /** * The name of the column. */ - public get name(): string { + public get name(): string | undefined { return this.cdr && this.cdr.column ? this.cdr.column.ColumnName : undefined; } - /** * The value type of the column, like bool, number, string, etc. */ - public get type(): ValType { + public get type(): ValType | undefined { return this.cdr && this.cdr.column ? this.cdr.column.GetType() : undefined; } /** * The information of a min value, a max value or limited values used by the given column. */ - public get valueConstraint(): ValueConstraint { + public get valueConstraint(): ValueConstraint | undefined { return this.cdr && (this.cdr.valueConstraint || (this.cdr.column ? this.cdr.column.GetMetadata().valueConstraint : undefined)); } /** * The meta data provided for the given column, like minLength, display, isMultiLine, etc. */ - public get metaData(): IValueMetadata { + public get metaData(): IValueMetadata | undefined { return this.cdr && this.cdr.column ? this.cdr.column.GetMetadata() : undefined; } /** * An additinal hint provided by the given CDR. */ - public get hint(): string { + public get hint(): string | undefined { return this.cdr?.hint; } + /** + * The visibility of the display value. + */ public get showDisplayValue(): boolean { return !!this.metaData?.CanSee(); } @@ -149,7 +151,7 @@ export class EntityColumnContainer implements ValueWrapper { * Updates the value and puts it into the column * @param value The new value for the column */ - public async updateValue(value: T): Promise { + public async updateValue(value: T | undefined): Promise { if (this.cdr && this.cdr.column) { return this.cdr.column.PutValue(value); } @@ -159,7 +161,7 @@ export class EntityColumnContainer implements ValueWrapper { * Updates column value and column display in one call * @param value The value struct, that should be used */ - public async updateValueStruct(value: ValueStruct): Promise { + public async updateValueStruct(value: ValueStruct): Promise { if (this.cdr && this.cdr.column) { return this.cdr.column.PutValueStruct(value); } diff --git a/imxweb/projects/qbm/src/lib/cdr/entity-column-editor/entity-column-editor.component.html b/imxweb/projects/qbm/src/lib/cdr/entity-column-editor/entity-column-editor.component.html index 7373d1ca8..422a3ab20 100644 --- a/imxweb/projects/qbm/src/lib/cdr/entity-column-editor/entity-column-editor.component.html +++ b/imxweb/projects/qbm/src/lib/cdr/entity-column-editor/entity-column-editor.component.html @@ -1,4 +1,2 @@ - + diff --git a/imxweb/projects/qbm/src/lib/cdr/entity-column-editor/entity-column-editor.component.scss b/imxweb/projects/qbm/src/lib/cdr/entity-column-editor/entity-column-editor.component.scss index bcd8ac2d3..bfe7e877f 100644 --- a/imxweb/projects/qbm/src/lib/cdr/entity-column-editor/entity-column-editor.component.scss +++ b/imxweb/projects/qbm/src/lib/cdr/entity-column-editor/entity-column-editor.component.scss @@ -1,3 +1,3 @@ :host { flex-grow: 1; -} \ No newline at end of file +} diff --git a/imxweb/projects/qbm/src/lib/cdr/entity-column-editor/entity-column-editor.component.ts b/imxweb/projects/qbm/src/lib/cdr/entity-column-editor/entity-column-editor.component.ts index c1c0bcafa..dd2282611 100644 --- a/imxweb/projects/qbm/src/lib/cdr/entity-column-editor/entity-column-editor.component.ts +++ b/imxweb/projects/qbm/src/lib/cdr/entity-column-editor/entity-column-editor.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,10 +27,9 @@ import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewChild } from '@angular/core'; import { AbstractControl } from '@angular/forms'; -import { IEntityColumn } from 'imx-qbm-dbts'; -import { ColumnDependentReference } from '../column-dependent-reference.interface'; +import { IEntityColumn } from '@imx-modules/imx-qbm-dbts'; import { CdrEditorComponent } from '../cdr-editor/cdr-editor.component'; - +import { ColumnDependentReference } from '../column-dependent-reference.interface'; /** * Provides a column editor component, that wraps around a {@link CdrEditor | column dependent reference editor}. @@ -45,7 +44,7 @@ export class EntityColumnEditorComponent implements OnChanges { * @ignore only used in template. * The column dependent reference used by the editor. */ - public cdr: ColumnDependentReference; + public cdr: ColumnDependentReference | undefined; /** * An entity column, that should be edited with a {@link CdrEditor | column dependent reference editor}. @@ -56,6 +55,7 @@ export class EntityColumnEditorComponent implements OnChanges { * Indicator, whether the control should be displayed as read-only. */ @Input() public readonly: boolean; + @Input() public display: string; /** * This is emitted, after the control is created properly. @@ -79,6 +79,7 @@ export class EntityColumnEditorComponent implements OnChanges { ? { column: this.column, isReadOnly: () => this.readonly || !this.column.GetMetadata().CanEdit(), + display: this.display, } : undefined; } diff --git a/imxweb/projects/qbm/src/lib/cdr/fk-cdr-editor-provider.spec.ts b/imxweb/projects/qbm/src/lib/cdr/fk-cdr-editor-provider.spec.ts index ddbbf63b0..2793be4a1 100644 --- a/imxweb/projects/qbm/src/lib/cdr/fk-cdr-editor-provider.spec.ts +++ b/imxweb/projects/qbm/src/lib/cdr/fk-cdr-editor-provider.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,22 +24,19 @@ * */ -import { ViewContainerRef, ComponentRef } from '@angular/core'; -import * as TypeMoq from 'typemoq'; +import { ComponentRef, ViewContainerRef } from '@angular/core'; -import { IValueMetadata, IForeignKeyInfo, IEntityColumn } from 'imx-qbm-dbts'; -import { FkCdrEditorProvider } from './fk-cdr-editor-provider'; -import { ColumnDependentReference } from './column-dependent-reference.interface'; +import { IForeignKeyInfo } from '@imx-modules/imx-qbm-dbts'; +import { clearStylesFromDOM } from '../testing/clear-styles.spec'; import { CdrEditor } from './cdr-editor.interface'; +import { ColumnDependentReference } from './column-dependent-reference.interface'; import { createComponentMock } from './default-cdr-editor-provider.spec'; import { EditFkComponent } from './edit-fk/edit-fk.component'; -import { clearStylesFromDOM } from '../testing/clear-styles.spec'; +import { FkCdrEditorProvider } from './fk-cdr-editor-provider'; import { ViewPropertyDefaultComponent } from './view-property-default/view-property-default.component'; describe('FkCdrEditorProvider', () => { - - beforeEach(() => { - }); + beforeEach(() => {}); afterAll(() => { clearStylesFromDOM(); @@ -51,7 +48,7 @@ describe('FkCdrEditorProvider', () => { it('should return null, if non fk column', () => { const editor = testCreateEditor(null, null, true); - expect( editor ).toBeNull(); + expect(editor).toBeNull(); }); it('should create ViewPropertyDefaultComponent for a Fk property that is set as readonly', () => { @@ -63,55 +60,45 @@ describe('FkCdrEditorProvider', () => { }); it('should work with empty fk', () => { - expect(() => {testCreateEditor(EditFkComponent, [createMockForeignKey('', '')], true)}).not.toThrowError(); + expect(() => { + testCreateEditor(EditFkComponent, [createMockForeignKey('', '')], true); + }).not.toThrowError(); }); - }); -function testCreateEditor(TCtor: new (...args: any[]) => T, fkRelation?: IForeignKeyInfo[], - editorShouldBeNull: boolean = false, isReadOnly: boolean = false): ComponentRef { - const cdrMock = createCdr(fkRelation, isReadOnly); - const editorMock = TypeMoq.Mock.ofType(); - const parentMock = TypeMoq.Mock.ofType(); - const childMock = createComponentMock(editorMock.object); - - parentMock.setup( p => p.createComponent(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => childMock.object); - - // Act - const provider = new FkCdrEditorProvider(); - const editor = provider.createEditor(parentMock.object, cdrMock.object); - - // Assert - if (editorShouldBeNull) { - expect(editor).toBeNull(); - } else { - expect(editor === childMock.object).toBeTruthy(); - } - editorMock.verify(e => e.bind(cdrMock.object), TypeMoq.Times.atMostOnce()); - editorMock.verify(e => e.bind(TypeMoq.It.isAny()), TypeMoq.Times.atMostOnce()); - - return editor; +function testCreateEditor( + TCtor: new (...args: any[]) => T, + fkRelation?: IForeignKeyInfo[], + editorShouldBeNull: boolean = false, + isReadOnly: boolean = false, +): ComponentRef { + const cdrMock = createCdr(fkRelation, isReadOnly); + const childMock = createComponentMock({} as T); + const parentMock = { createComponent: () => childMock } as unknown as ViewContainerRef; + // Act + const provider = new FkCdrEditorProvider(); + const editor = provider.createEditor(parentMock, cdrMock); + + // Assert + if (editorShouldBeNull) { + expect(editor).toBeNull(); + } else { + expect(editor === childMock).toBeTruthy(); + } + + return editor; } function createMockForeignKey(tablename: string, column: string): IForeignKeyInfo { - const mockKey = TypeMoq.Mock.ofType(); - mockKey.setup(m => m.TableName).returns(() => tablename); - mockKey.setup(m => m.ColumnName).returns(() => column); - - return mockKey.object; + const mockKey: IForeignKeyInfo = { TableName: tablename, ColumnName: column } as unknown as IForeignKeyInfo; + return mockKey; } - -function createCdr( fkRelation?: IForeignKeyInfo[], isReadOnly?: boolean) -: TypeMoq.IMock { - - const metaMock = TypeMoq.Mock.ofType(); - metaMock.setup(m => m.GetFkRelations()).returns(() => fkRelation); - metaMock.setup(m => m.CanEdit()).returns(() => !isReadOnly) - - const cdrMock = TypeMoq.Mock.ofType(); - cdrMock.setup(m => m.column).returns(() => ({ GetMetadata: () => metaMock.object } as IEntityColumn)); - cdrMock.setup(m => m.isReadOnly()).returns(() => isReadOnly); - - return cdrMock; +function createCdr(fkRelation?: IForeignKeyInfo[], isReadOnly?: boolean): ColumnDependentReference { + const metaData = { + GetFkRelations: () => fkRelation, + CanEdit: () => !isReadOnly, + IsMultiValue: () => false, + }; + return { column: { GetMetadata: () => metaData }, isReadOnly: () => isReadOnly } as unknown as ColumnDependentReference; } diff --git a/imxweb/projects/qbm/src/lib/cdr/fk-cdr-editor-provider.ts b/imxweb/projects/qbm/src/lib/cdr/fk-cdr-editor-provider.ts index 0d5659432..e58e10bc0 100644 --- a/imxweb/projects/qbm/src/lib/cdr/fk-cdr-editor-provider.ts +++ b/imxweb/projects/qbm/src/lib/cdr/fk-cdr-editor-provider.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,13 +24,13 @@ * */ -import { ViewContainerRef, ComponentRef, Type } from '@angular/core'; +import { ComponentRef, Type, ViewContainerRef } from '@angular/core'; import { CdrEditorProvider } from './cdr-editor-provider.interface'; -import { ColumnDependentReference } from './column-dependent-reference.interface'; import { CdrEditor } from './cdr-editor.interface'; -import { EditFkComponent } from './edit-fk/edit-fk.component'; +import { ColumnDependentReference } from './column-dependent-reference.interface'; import { EditFkMultiComponent } from './edit-fk/edit-fk-multi.component'; +import { EditFkComponent } from './edit-fk/edit-fk.component'; /** * A special provider for foreign key columns. @@ -47,7 +47,7 @@ export class FkCdrEditorProvider implements CdrEditorProvider { * @param cdref A column dependent reference that contains the data for the editor. * @returns An instance of {@link CdrEditor}, that can be used or editing data, or null, if no foreign key information is given. */ - public createEditor(parent: ViewContainerRef, cdref: ColumnDependentReference): ComponentRef { + public createEditor(parent: ViewContainerRef, cdref: ColumnDependentReference): ComponentRef | null { if (this.hasFkRelations(cdref)) { return cdref.column.GetMetadata().IsMultiValue() ? this.createBound(EditFkMultiComponent, parent, cdref) @@ -64,7 +64,7 @@ export class FkCdrEditorProvider implements CdrEditorProvider { private createBound( type: Type, parent: ViewContainerRef, - cdref: ColumnDependentReference + cdref: ColumnDependentReference, ): ComponentRef { const result = parent.createComponent(type); result.instance.bind(cdref); @@ -74,7 +74,7 @@ export class FkCdrEditorProvider implements CdrEditorProvider { /** * Determines, if there are fk relations present or not. * @param cdref The column dependent reference, that needs to be checked - * @returns + * @returns */ private hasFkRelations(cdref: ColumnDependentReference): boolean { const fkRelations = cdref.column.GetMetadata().GetFkRelations(); diff --git a/imxweb/projects/qbm/src/lib/cdr/limited-values-container.spec.ts b/imxweb/projects/qbm/src/lib/cdr/limited-values-container.spec.ts index 641645331..fe5baedf4 100644 --- a/imxweb/projects/qbm/src/lib/cdr/limited-values-container.spec.ts +++ b/imxweb/projects/qbm/src/lib/cdr/limited-values-container.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,31 +24,34 @@ * */ - -import { ValType, IValueMetadata, LimitedValueData } from 'imx-qbm-dbts'; +import { ValType, IValueMetadata, LimitedValueData } from '@imx-modules/imx-qbm-dbts'; import { LimitedValuesContainer } from './limited-values-container'; describe('LimitedValuesContainer', () => { - function buildLimitedValues(limited: any[]): ReadonlyArray { - return limited.map(element => ({ Value: element, Description: `${element}` })); - } + function buildLimitedValues(limited: any[]): ReadonlyArray { + return limited.map((element) => ({ Value: element, Description: `${element}` })); + } [ { value: { limited: ['a', 'b', 'c'], minLength: 0, type: ValType.String }, description: 'with string allowing null', expected: true }, { value: { limited: [null, 'b', 'c'], minLength: 1, type: ValType.String }, description: 'with string allowing null', expected: false }, - { value: { limited: ['a', 'b', 'c'], minLength: 1, type: ValType.String }, description: 'with string not allowing null', expected: false }, + { + value: { limited: ['a', 'b', 'c'], minLength: 1, type: ValType.String }, + description: 'with string not allowing null', + expected: false, + }, { value: { limited: [1, 2, 3], minLength: 0, type: ValType.Int }, description: 'with number allowing null', expected: true }, { value: { limited: [0, 2, 3], minLength: 0, type: ValType.Int }, description: 'with number allowing null', expected: false }, { value: { limited: [1, 2, 3], minLength: 1, type: ValType.Int }, description: 'with number not allowing null', expected: false }, - ].forEach(testcase => { + ].forEach((testcase) => { it(`should have a "null" option ${testcase.description}`, () => { - const container = new LimitedValuesContainer({ - GetLimitedValues: () => buildLimitedValues(testcase.value.limited), - GetMinLength: () => testcase.value.minLength, - GetType: () => testcase.value.type - } as IValueMetadata); - - expect(container.hasNullOption()).toEqual(testcase.expected); + const container = new LimitedValuesContainer({ + GetLimitedValues: () => buildLimitedValues(testcase.value.limited), + GetMinLength: () => testcase.value.minLength, + GetType: () => testcase.value.type, + } as IValueMetadata); + + expect(container.hasNullOption()).toEqual(testcase.expected); }); }); @@ -57,45 +60,45 @@ describe('LimitedValuesContainer', () => { metadata: { limited: ['a', 'b', 'c'], type: ValType.String }, value: 'd', description: 'for string not in limited values', - expected: true + expected: true, }, { metadata: { limited: ['a', 'b', 'c'], type: ValType.String }, value: 'a', description: 'for string in limited values', - expected: false + expected: false, }, { metadata: { limited: ['a', 'b', 'c'], type: ValType.String }, description: 'for string null', - expected: false + expected: false, }, { - metadata: { limited: [1, 2, 3], type: ValType.Int }, + metadata: { limited: [1, 2, 3], type: ValType.Int }, description: 'for number not in limited values', value: 4, - expected: true + expected: true, }, { - metadata: { limited: [1, 2, 3], type: ValType.Int }, + metadata: { limited: [1, 2, 3], type: ValType.Int }, description: 'for number in limited value', value: 1, - expected: false + expected: false, }, { - metadata: { limited: [1, 2, 3], type: ValType.Int }, + metadata: { limited: [1, 2, 3], type: ValType.Int }, description: 'for number 0', value: 0, - expected: true + expected: true, }, - ].forEach(testcase => { + ].forEach((testcase) => { it(`isNotInLimitedValueRange ${testcase.description}`, () => { - const container = new LimitedValuesContainer({ - GetLimitedValues: () => buildLimitedValues(testcase.metadata.limited), - GetType: () => testcase.metadata.type - } as IValueMetadata); + const container = new LimitedValuesContainer({ + GetLimitedValues: () => buildLimitedValues(testcase.metadata.limited), + GetType: () => testcase.metadata.type, + } as IValueMetadata); - expect(container.isNotInLimitedValueRange(testcase.value)).toEqual(testcase.expected); + expect(container.isNotInLimitedValueRange(testcase.value)).toEqual(testcase.expected); }); }); }); diff --git a/imxweb/projects/qbm/src/lib/cdr/limited-values-container.ts b/imxweb/projects/qbm/src/lib/cdr/limited-values-container.ts index d15b22501..fdf749a45 100644 --- a/imxweb/projects/qbm/src/lib/cdr/limited-values-container.ts +++ b/imxweb/projects/qbm/src/lib/cdr/limited-values-container.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { LimitedValueData, IValueMetadata, ValType } from 'imx-qbm-dbts'; +import { IValueMetadata, LimitedValueData, ValType } from '@imx-modules/imx-qbm-dbts'; /** * A wrapper, that encapsules limited value property functions. @@ -33,7 +33,7 @@ export class LimitedValuesContainer { /** * A read-only list of a possible limited values. */ - public get values(): ReadonlyArray { + public get values(): ReadonlyArray | undefined { return this.metadata ? this.metadata.GetLimitedValues() : undefined; } @@ -42,12 +42,15 @@ export class LimitedValuesContainer { /** * Determines, whether the limited value collection allows a null option. */ + /** + * Determines, whether the limited value collection allows a null option + */ public hasNullOption(): boolean { return this.metadata.GetMinLength() === 0 && !this.contains(this.getNullValue()); } /** - * Determines, whether the value is part of the limited value range or not. + * Determines, whether the value is part of the limited value range or not */ public isNotInLimitedValueRange(value: string | number): boolean { return !((value || '') === (this.getNullValue() || '')) && !this.contains(value); @@ -57,7 +60,7 @@ export class LimitedValuesContainer { * Gets the value representing 'null'. * @returns the value that is used as 'null'. */ - private getNullValue(): string { + private getNullValue(): string | null { return this.metadata.GetType() === ValType.String ? null : '0'; } @@ -66,7 +69,7 @@ export class LimitedValuesContainer { * @param value The value to be checked. * @returns */ - private contains(value: string | number): boolean { - return this.values && this.values.filter((v) => `${v.Value}` === `${value}`).length > 0; + private contains(value: string | number | null): boolean { + return (this.values && this.values.filter((v) => `${v.Value}` === `${value}`).length > 0) ?? false; } } diff --git a/imxweb/projects/qbm/src/lib/cdr/property-viewer/property-viewer.component.ts b/imxweb/projects/qbm/src/lib/cdr/property-viewer/property-viewer.component.ts index aaa74b1fa..cf7d611f1 100644 --- a/imxweb/projects/qbm/src/lib/cdr/property-viewer/property-viewer.component.ts +++ b/imxweb/projects/qbm/src/lib/cdr/property-viewer/property-viewer.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,38 +26,32 @@ import { Component, Input, OnChanges } from '@angular/core'; -import { IEntityColumn } from 'imx-qbm-dbts'; +import { IEntityColumn } from '@imx-modules/imx-qbm-dbts'; import { BaseReadonlyCdr } from '../base-readonly-cdr'; import { ColumnDependentReference } from '../column-dependent-reference.interface'; @Component({ - selector: 'imx-property-viewer', - templateUrl: './property-viewer.component.html', - styleUrls: ['./property-viewer.component.scss'] + selector: 'imx-property-viewer', + templateUrl: './property-viewer.component.html', + styleUrls: ['./property-viewer.component.scss'], }) export class PropertyViewerComponent implements OnChanges { - public cdrList: ColumnDependentReference[]; - @Input() public showHiddenProperties = true; - @Input() public properties: IEntityColumn[] = []; + public cdrList: ColumnDependentReference[]; + @Input() public showHiddenProperties = true; + @Input() public properties: IEntityColumn[] = []; - public ngOnChanges(): void { - if (this.properties) { - this.cdrList = this.properties - .filter(column => this.showProperty(column)) - .map(column => new BaseReadonlyCdr(column)); - } + public ngOnChanges(): void { + if (this.properties) { + this.cdrList = this.properties.filter((column) => this.showProperty(column)).map((column) => new BaseReadonlyCdr(column)); } + } - private showProperty(column: IEntityColumn): boolean { - // show hidden properties? - return (column.GetMetadata().CanSee() || this.showHiddenProperties) - - && - - true; - /* TODO + private showProperty(column: IEntityColumn): boolean { + // show hidden properties? + return (column.GetMetadata().CanSee() || this.showHiddenProperties) && true; + /* TODO // show empty properties? (from edittable select current not(isnullorempty(currentcolumn)) or getconfig("VI_Common_DisplayEmptyProperties") = "true");*/ - } + } } diff --git a/imxweb/projects/qbm/src/lib/cdr/view-property-default/view-property-default.component.html b/imxweb/projects/qbm/src/lib/cdr/view-property-default/view-property-default.component.html index 8416faeb7..bbb9d7c58 100644 --- a/imxweb/projects/qbm/src/lib/cdr/view-property-default/view-property-default.component.html +++ b/imxweb/projects/qbm/src/lib/cdr/view-property-default/view-property-default.component.html @@ -1,5 +1,6 @@ + [attr.data-imx-identifier]="'cdr-readonly-' + columnContainer?.name" +> diff --git a/imxweb/projects/qbm/src/lib/cdr/view-property-default/view-property-default.component.scss b/imxweb/projects/qbm/src/lib/cdr/view-property-default/view-property-default.component.scss index bcd8ac2d3..bfe7e877f 100644 --- a/imxweb/projects/qbm/src/lib/cdr/view-property-default/view-property-default.component.scss +++ b/imxweb/projects/qbm/src/lib/cdr/view-property-default/view-property-default.component.scss @@ -1,3 +1,3 @@ :host { flex-grow: 1; -} \ No newline at end of file +} diff --git a/imxweb/projects/qbm/src/lib/cdr/view-property-default/view-property-default.component.ts b/imxweb/projects/qbm/src/lib/cdr/view-property-default/view-property-default.component.ts index b7cd76a99..1330c0dfb 100644 --- a/imxweb/projects/qbm/src/lib/cdr/view-property-default/view-property-default.component.ts +++ b/imxweb/projects/qbm/src/lib/cdr/view-property-default/view-property-default.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,7 +31,7 @@ import { EditorBase } from '../editor-base'; @Component({ selector: 'imx-view-property-default', templateUrl: './view-property-default.component.html', - styleUrls: ['./view-property-default.component.scss'] + styleUrls: ['./view-property-default.component.scss'], }) export class ViewPropertyDefaultComponent extends EditorBase { public readonly control = new UntypedFormControl(undefined); diff --git a/imxweb/projects/qbm/src/lib/cdr/view-property/view-property.component.html b/imxweb/projects/qbm/src/lib/cdr/view-property/view-property.component.html index c287e3d5a..ad6f75b98 100644 --- a/imxweb/projects/qbm/src/lib/cdr/view-property/view-property.component.html +++ b/imxweb/projects/qbm/src/lib/cdr/view-property/view-property.component.html @@ -1,4 +1,4 @@
    - {{ (columnContainer?.display | translate) + (columnContainer.isValueRequired ? '*' : '') }} + {{ (columnContainer.display | translate) + (columnContainer.isValueRequired ? '*' : '') }} {{ columnContainer.showDisplayValue ? (displayedValue | translate) : '' }}
    diff --git a/imxweb/projects/qbm/src/lib/cdr/view-property/view-property.component.ts b/imxweb/projects/qbm/src/lib/cdr/view-property/view-property.component.ts index 22a53673e..9e40eaa56 100644 --- a/imxweb/projects/qbm/src/lib/cdr/view-property/view-property.component.ts +++ b/imxweb/projects/qbm/src/lib/cdr/view-property/view-property.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,7 +27,7 @@ import { Component, Input } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; -import { ValType } from 'imx-qbm-dbts'; +import { ValType } from '@imx-modules/imx-qbm-dbts'; import { EntityColumnContainer } from '../entity-column-container'; import { ImxTranslationProviderService } from '../../translation/imx-translation-provider.service'; diff --git a/imxweb/projects/qbm/src/lib/chart-options/line-chart-options.spec.ts b/imxweb/projects/qbm/src/lib/chart-options/line-chart-options.spec.ts index f5d220643..a588adbd8 100644 --- a/imxweb/projects/qbm/src/lib/chart-options/line-chart-options.spec.ts +++ b/imxweb/projects/qbm/src/lib/chart-options/line-chart-options.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,7 +31,6 @@ import { SeriesInformation } from './series-information'; import { clearStylesFromDOM } from '../testing/clear-styles.spec'; describe('LineChartOptions', () => { - afterAll(() => { clearStylesFromDOM(); }); @@ -44,8 +43,8 @@ describe('LineChartOptions', () => { { isSmooth: true, isArea: true, expect: 'area-spline' }, { isSmooth: true, isArea: false, expect: 'spline' }, { isSmooth: false, isArea: true, expect: 'area' }, - { isSmooth: false, isArea: false, expect: 'line' } - ].forEach(element => { + { isSmooth: false, isArea: false, expect: 'line' }, + ].forEach((element) => { it(`gets line type with smooth lines = ${element.isSmooth} and colored area = ${element.isArea}`, () => { const xAxisInformation = new XAxisInformation('number', [1, 2, 3]); const yAxisInformation = new YAxisInformation([new SeriesInformation('dummy', [1, 2, 3])]); @@ -54,7 +53,6 @@ describe('LineChartOptions', () => { chart.colorArea = element.isArea; expect(chart.options.data.type).toBe(element.expect); }); - }); it('should create an instance', () => { const xAxisInformation = new XAxisInformation('number', [1, 2, 3]); @@ -62,6 +60,4 @@ describe('LineChartOptions', () => { const chart = new LineChartOptions(xAxisInformation, yAxisInformation); expect(chart.options.data.type).toBe('area-spline'); }); - - }); diff --git a/imxweb/projects/qbm/src/lib/chart-options/line-chart-options.ts b/imxweb/projects/qbm/src/lib/chart-options/line-chart-options.ts index 908fa64e4..3512e3f09 100644 --- a/imxweb/projects/qbm/src/lib/chart-options/line-chart-options.ts +++ b/imxweb/projects/qbm/src/lib/chart-options/line-chart-options.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -72,17 +72,11 @@ export class LineChartOptions { */ public tooltip: (data: any) => string; // TODO: hier müsste noch eine bessere Lösung gefunden werden - /** - * Gets the {@link XAxisInformation|x axis informations} for the chart - */ - public readonly xAxisInformation: XAxisInformation; - - /** - * Gets the {@link YAxisInformation|y axis informations} for the chart - */ - public readonly yAxisInformation: YAxisInformation; - - public constructor(xAxisInformation: XAxisInformation, yAxisInformation: YAxisInformation) { + public constructor( + public readonly xAxisInformation: XAxisInformation, + public readonly yAxisInformation: YAxisInformation, + public readonly emptyText?: string, + ) { this.xAxisInformation = xAxisInformation; this.yAxisInformation = yAxisInformation; } @@ -103,6 +97,11 @@ export class LineChartOptions { columns: col, names: this.yAxisInformation.getNames(), type: this.getType(), + empty: { + label: { + text: this.emptyText ?? '', + }, + }, colors: this.yAxisInformation.getColors(), }, tooltip: { @@ -121,6 +120,7 @@ export class LineChartOptions { lines: this.additionalLines, }, }, + padding: this.padding, }; } diff --git a/imxweb/projects/qbm/src/lib/chart-options/series-information.spec.ts b/imxweb/projects/qbm/src/lib/chart-options/series-information.spec.ts index 5c5e8ccbc..5ba981e53 100644 --- a/imxweb/projects/qbm/src/lib/chart-options/series-information.spec.ts +++ b/imxweb/projects/qbm/src/lib/chart-options/series-information.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,7 +28,6 @@ import { SeriesInformation } from './series-information'; import { clearStylesFromDOM } from '../testing/clear-styles.spec'; describe('YAxisInformation', () => { - afterAll(() => { clearStylesFromDOM(); }); diff --git a/imxweb/projects/qbm/src/lib/chart-options/series-information.ts b/imxweb/projects/qbm/src/lib/chart-options/series-information.ts index 11667ed40..c2479951f 100644 --- a/imxweb/projects/qbm/src/lib/chart-options/series-information.ts +++ b/imxweb/projects/qbm/src/lib/chart-options/series-information.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,6 @@ */ export class SeriesInformation { - /** * Gets the name of the serie */ @@ -34,7 +33,7 @@ export class SeriesInformation { /** * Gets the color of the serie */ - public readonly color: string; + public readonly color?: string; /** * Gets the values of the serie @@ -51,6 +50,6 @@ export class SeriesInformation { * Combines the axis data with its title, so that it's useable by the billboard.js ChartOptions */ public getSerie(): any[] { - return ([this.name.replace(' ', '') as any]).concat(this.values); + return [this.name.replace(' ', '') as any].concat(this.values); } } diff --git a/imxweb/projects/qbm/src/lib/chart-options/x-axis-information.spec.ts b/imxweb/projects/qbm/src/lib/chart-options/x-axis-information.spec.ts index 16d87a122..c3a3825fa 100644 --- a/imxweb/projects/qbm/src/lib/chart-options/x-axis-information.spec.ts +++ b/imxweb/projects/qbm/src/lib/chart-options/x-axis-information.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,7 +28,6 @@ import { XAxisInformation } from './x-axis-information'; import { clearStylesFromDOM } from '../testing/clear-styles.spec'; describe('XAxisInformation', () => { - afterAll(() => { clearStylesFromDOM(); }); @@ -47,5 +46,4 @@ describe('XAxisInformation', () => { const info = new XAxisInformation('number', [1, 2, 3], {}); expect(info.getAxisData()).toEqual(['x', 1, 2, 3]); }); - }); diff --git a/imxweb/projects/qbm/src/lib/chart-options/x-axis-information.ts b/imxweb/projects/qbm/src/lib/chart-options/x-axis-information.ts index c83d8ae86..9970aae8c 100644 --- a/imxweb/projects/qbm/src/lib/chart-options/x-axis-information.ts +++ b/imxweb/projects/qbm/src/lib/chart-options/x-axis-information.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -40,7 +40,7 @@ export class XAxisInformation { /** * Gets the tick configuration for the axis */ - public readonly tickConfiguration: XTickConfiguration; + public readonly tickConfiguration: XTickConfiguration | undefined; public constructor(dataType: 'number' | 'date' | 'string', values: (number | Date | string)[], xTickConfiguration?: XTickConfiguration) { this.dataType = dataType; diff --git a/imxweb/projects/qbm/src/lib/chart-options/y-axis-information.spec.ts b/imxweb/projects/qbm/src/lib/chart-options/y-axis-information.spec.ts index c68be4fd2..8bca5e883 100644 --- a/imxweb/projects/qbm/src/lib/chart-options/y-axis-information.spec.ts +++ b/imxweb/projects/qbm/src/lib/chart-options/y-axis-information.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,7 +28,6 @@ import { YAxisInformation } from './y-axis-information'; import { clearStylesFromDOM } from '../testing/clear-styles.spec'; describe('YAxisInformationGroup', () => { - afterAll(() => { clearStylesFromDOM(); }); diff --git a/imxweb/projects/qbm/src/lib/chart-options/y-axis-information.ts b/imxweb/projects/qbm/src/lib/chart-options/y-axis-information.ts index b07824c06..23abe5ef9 100644 --- a/imxweb/projects/qbm/src/lib/chart-options/y-axis-information.ts +++ b/imxweb/projects/qbm/src/lib/chart-options/y-axis-information.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,11 +24,10 @@ * */ -import { SeriesInformation } from './series-information'; import { YTickConfiguration, yAxisConfiguration } from 'billboard.js'; +import { SeriesInformation } from './series-information'; export class YAxisInformation { - /** * Gets the series, that are displayed on the chart */ @@ -60,7 +59,7 @@ export class YAxisInformation { return { max: this.max, min: this.min, - tick: this.tickConfiguration + tick: this.tickConfiguration, }; } @@ -69,7 +68,7 @@ export class YAxisInformation { */ public getNames(): { [key: string]: string } { const names: { [id: string]: string } = {}; - this.series.forEach(element => { + this.series.forEach((element) => { names[element.name.replace(' ', '')] = element.name; }); @@ -81,8 +80,11 @@ export class YAxisInformation { */ public getColors(): { [key: string]: string } { const colors: { [id: string]: string } = {}; - this.series.forEach(element => { - colors[element.name.replace(' ', '')] = element.color; + this.series.forEach((element) => { + const index = element.name.replace(' ', ''); + if (index && element.color) { + colors[index] = element.color; + } }); return colors; diff --git a/imxweb/projects/qbm/src/lib/chart-tile/chart-tile.component.html b/imxweb/projects/qbm/src/lib/chart-tile/chart-tile.component.html index e9b936f77..1b74c861f 100644 --- a/imxweb/projects/qbm/src/lib/chart-tile/chart-tile.component.html +++ b/imxweb/projects/qbm/src/lib/chart-tile/chart-tile.component.html @@ -1,8 +1,14 @@ - - -
    - -
    -
    - + -->
    +
    diff --git a/imxweb/projects/qbm/src/lib/chart-tile/chart-tile.component.ts b/imxweb/projects/qbm/src/lib/chart-tile/chart-tile.component.ts index b033e0328..ef13f9be4 100644 --- a/imxweb/projects/qbm/src/lib/chart-tile/chart-tile.component.ts +++ b/imxweb/projects/qbm/src/lib/chart-tile/chart-tile.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,14 +24,13 @@ * */ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; @Component({ templateUrl: './chart-tile.component.html', - selector: 'imx-chart-tile' + selector: 'imx-chart-tile', }) export class ChartTileComponent { - @Input() public displayNameDialogDashboardDef: string; @Input() public chartType: string; @Input() public useHistogramStyle: boolean; diff --git a/imxweb/projects/qbm/src/lib/classlogger/classlogger.module.ts b/imxweb/projects/qbm/src/lib/classlogger/classlogger.module.ts index c7997d965..d96b03e16 100644 --- a/imxweb/projects/qbm/src/lib/classlogger/classlogger.module.ts +++ b/imxweb/projects/qbm/src/lib/classlogger/classlogger.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -32,12 +32,7 @@ import { LoggerModule } from 'ngx-logger'; @NgModule({ declarations: [], - imports: [ - CommonModule, - LoggerModule - ], - providers: [ - ClassloggerService - ] + imports: [CommonModule, LoggerModule], + providers: [ClassloggerService], }) -export class ClassloggerModule { } +export class ClassloggerModule {} diff --git a/imxweb/projects/qbm/src/lib/classlogger/classlogger.service.ts b/imxweb/projects/qbm/src/lib/classlogger/classlogger.service.ts index 7994e38e4..37d6e34ba 100644 --- a/imxweb/projects/qbm/src/lib/classlogger/classlogger.service.ts +++ b/imxweb/projects/qbm/src/lib/classlogger/classlogger.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -61,10 +61,9 @@ import { NGXLogger } from 'ngx-logger'; * */ @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ClassloggerService { - constructor(private nativLogger: NGXLogger) {} /** diff --git a/imxweb/projects/qbm/src/lib/configuration/elemental-ui-config.interface.ts b/imxweb/projects/qbm/src/lib/configuration/elemental-ui-config.interface.ts index 38fa37463..c33b7379b 100644 --- a/imxweb/projects/qbm/src/lib/configuration/elemental-ui-config.interface.ts +++ b/imxweb/projects/qbm/src/lib/configuration/elemental-ui-config.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,7 +27,5 @@ import { EuiDownloadOptions } from '@elemental-ui/core'; export interface ElementalUiConfig { - downloadOptions?: EuiDownloadOptions; - } diff --git a/imxweb/projects/qbm/src/lib/configuration/elemental-ui-config.service.ts b/imxweb/projects/qbm/src/lib/configuration/elemental-ui-config.service.ts index 40be47939..7fa2a0584 100644 --- a/imxweb/projects/qbm/src/lib/configuration/elemental-ui-config.service.ts +++ b/imxweb/projects/qbm/src/lib/configuration/elemental-ui-config.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,6 @@ * */ - import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { AuthenticationService } from '../authentication/authentication.service'; @@ -35,10 +34,9 @@ import { ElementalUiConfig } from './elemental-ui-config.interface'; * A service that helps configure Element UI */ @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ElementalUiConfigService { - private readonly config: ElementalUiConfig = { downloadOptions: { url: '', @@ -49,9 +47,9 @@ export class ElementalUiConfigService { loaderConfig: { helperText: '', buttonText: '', - spinnerAriaLabel: '' - } - } + spinnerAriaLabel: '', + }, + }, }; /** @@ -63,15 +61,24 @@ export class ElementalUiConfigService { constructor( private readonly translate: TranslateService, - authentication: AuthenticationService + authentication: AuthenticationService, ) { authentication.onSessionResponse.subscribe(() => { - this.translate.get('#LDS#File download in progress'). - subscribe((trans: string) => this.config.downloadOptions.loaderConfig.helperText = trans); - this.translate.get('#LDS#Cancel download'). - subscribe((trans: string) => this.config.downloadOptions.loaderConfig.buttonText = trans); - this.translate.get('#LDS#Loading...'). - subscribe((trans: string) => this.config.downloadOptions.loaderConfig.spinnerAriaLabel = trans); + this.translate.get('#LDS#File download in progress').subscribe((trans: string) => { + if (this.config?.downloadOptions?.loaderConfig) { + this.config.downloadOptions.loaderConfig.helperText = trans; + } + }); + this.translate.get('#LDS#Cancel download').subscribe((trans: string) => { + if (this.config?.downloadOptions?.loaderConfig) { + this.config.downloadOptions.loaderConfig.buttonText = trans; + } + }); + this.translate.get('#LDS#Loading...').subscribe((trans: string) => { + if (this.config?.downloadOptions?.loaderConfig) { + this.config.downloadOptions.loaderConfig.spinnerAriaLabel = trans; + } + }); }); } } diff --git a/imxweb/projects/qbm/src/lib/confirmation/confirmation.module.ts b/imxweb/projects/qbm/src/lib/confirmation/confirmation.module.ts index 94e595ce7..6a6e8ef40 100644 --- a/imxweb/projects/qbm/src/lib/confirmation/confirmation.module.ts +++ b/imxweb/projects/qbm/src/lib/confirmation/confirmation.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,8 +31,6 @@ import { ConfirmationService } from './confirmation.service'; @NgModule({ providers: [ConfirmationService], - imports: [ - CommonModule - ] + imports: [CommonModule], }) -export class ConfirmationModule { } +export class ConfirmationModule {} diff --git a/imxweb/projects/qbm/src/lib/confirmation/confirmation.service.ts b/imxweb/projects/qbm/src/lib/confirmation/confirmation.service.ts index 507219f75..8f7098c8e 100644 --- a/imxweb/projects/qbm/src/lib/confirmation/confirmation.service.ts +++ b/imxweb/projects/qbm/src/lib/confirmation/confirmation.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,13 +26,17 @@ import { Injectable, NgZone } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; +import { EuiLoadingService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; import { MessageDialogResult } from '../message-dialog/message-dialog-result.enum'; import { MessageDialogComponent } from '../message-dialog/message-dialog.component'; +import { MessageDialogService } from '../message-dialog/message-dialog.service'; import { MessageParameter } from '../message-dialog/message-parameter.interface'; +import { AuthenticationService } from '../authentication/authentication.service'; import { LdsReplacePipe } from '../lds-replace/lds-replace.pipe'; +import { ISessionState } from '../session/session-state'; @Injectable({ providedIn: 'root', @@ -43,7 +47,15 @@ export class ConfirmationService { private readonly translate: TranslateService, private readonly pipe: LdsReplacePipe, private readonly zone: NgZone, - ) {} + private readonly messageDialogService: MessageDialogService, + private readonly authentication: AuthenticationService, + private readonly busyService: EuiLoadingService, + ) { + authentication.onSessionResponse.subscribe((session) => (this.currentSession = session)); + } + + private showErrorDialogState = true; + private currentSession: ISessionState; public async confirmLeaveWithUnsavedChanges(title?: string, message?: string, disableClose?: boolean): Promise { const dialogRef = this.dialogService.open(MessageDialogComponent, { @@ -93,7 +105,8 @@ export class ConfirmationService { }, panelClass: 'imx-messageDialog', }); - return (await dialogRef.afterClosed().toPromise()) === MessageDialogResult.YesResult; + const result =await dialogRef.afterClosed().toPromise(); + return result === MessageDialogResult.YesResult || result === MessageDialogResult.OkResult; } // Damit es bis "Pull Request 38432: 299557-imxweb-confirmdialogs-with-yes-no-buttons" funktioniert @@ -103,4 +116,53 @@ export class ConfirmationService { Message: message || '#LDS#Are you sure you want to delete the object?', }); } + + public async showErrorMessage(data: MessageParameter): Promise { + let message = data?.Message ? this.translate.instant(data.Message) : ''; + message = data?.Parameter ? this.pipe.transform(message, ...data.Parameter) : message; + const title = this.translate.instant('#LDS#Error'); + if (this.showErrorDialogState) { + this.showErrorDialogState = false; + this.messageDialogService.errorMessages$.next([message]); + await this.showMessageBox(title, message, 'error', async () => { + this.showErrorDialogState = true; + this.messageDialogService.errorMessages$.next([]); + }); + } else { + this.messageDialogService.errorMessages$.next([...this.messageDialogService.errorMessages$.value, message]); + } + } + + public async handleExpiredSession(): Promise { + await this.showMessageBox( + this.translate.instant('#LDS#Heading Session Expired'), + this.translate.instant('#LDS#Your session has expired. You will now be redirected to the login page where you can log in again.'), + 'clock', + async () => { + const ref = this.busyService.show(); + try { + await this.authentication.update(true); + } finally { + this.busyService.hide(ref); + } + }, + ); + } + + public async showMessageBox(translatedTitle: string, translatedText, icon: string, callback: () => Promise): Promise { + this.zone.run(() => { + this.dialogService + .open(MessageDialogComponent, { + data: { + Title: translatedTitle, + Message: translatedText, + ShowOk: true, + icon: icon, + }, + panelClass: 'imx-messageDialog-error', + }) + .afterClosed() + .subscribe(callback); + }); + } } diff --git a/imxweb/projects/qbm/src/lib/connection/connection.component.html b/imxweb/projects/qbm/src/lib/connection/connection.component.html index a50ff6044..15e75f41b 100644 --- a/imxweb/projects/qbm/src/lib/connection/connection.component.html +++ b/imxweb/projects/qbm/src/lib/connection/connection.component.html @@ -1,4 +1,4 @@ - +
    @@ -6,7 +6,7 @@
    @@ -20,21 +20,22 @@ class="permission-search" data-imx-identifier="permission-eui-search" width="100%" - [(searchControl)]="search" - [placeholder]="'#LDS#Search' | translate"> + [searchControl]="search" + [placeholder]="'#LDS#Search' | translate" + >
    - - + + - - + + - +
    {{'#LDS#Display name' | translate}}{{entity.Display}}{{ '#LDS#Display name' | translate }}{{ entity.Display }} {{'#LDS#Description' | translate}}{{entity.Description}}{{ '#LDS#Description' | translate }}{{ entity.Description }}
    {{ '#LDS#There is no data matching your search.' | translate }} @@ -51,22 +52,22 @@ - {{group.Display}} + {{ group.Display }}
    - - + + - - + + - +
    {{'#LDS#Display name' | translate}}{{entity.Display}}{{ '#LDS#Display name' | translate }}{{ entity.Display }} {{'#LDS#Description' | translate}}{{entity.Description}}{{ '#LDS#Description' | translate }}{{ entity.Description }}
    @@ -78,7 +79,7 @@
    -
    diff --git a/imxweb/projects/qbm/src/lib/connection/connection.component.scss b/imxweb/projects/qbm/src/lib/connection/connection.component.scss index 5d4182f39..8a504d236 100644 --- a/imxweb/projects/qbm/src/lib/connection/connection.component.scss +++ b/imxweb/projects/qbm/src/lib/connection/connection.component.scss @@ -1,37 +1,6 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; :host { - .mat-tab-group, ::ng-deep .mat-tab-body-wrapper { - height: 100%; - overflow: auto; - } - - ::ng-deep .mat-tab-body-content { - display: flex; - flex-direction: column; - justify-content: space-between; - } - - .imx-mat-tab-container { - padding: 20px; - display: flex; - flex-direction: column; - overflow: auto; - } - - .imx-table-container { - margin-top: 1rem; - height: calc(100% - 57px); - - &.overflow-auto { - overflow: auto; - } - - th, td { - width: 50%; - } - } - .no-results { display: flex; flex-direction: column; @@ -47,15 +16,8 @@ mat-panel-title { font-weight: bold; } -} - -.eui-sidesheet-actions { - ::ng-deep .mat-button-wrapper { + .eui-sidesheet-content { display: flex; - gap: 0.25rem; - } - - .justify-start { - margin-right: auto; + flex-direction: column; } } diff --git a/imxweb/projects/qbm/src/lib/connection/connection.component.ts b/imxweb/projects/qbm/src/lib/connection/connection.component.ts index 672b8e233..ad36b7255 100644 --- a/imxweb/projects/qbm/src/lib/connection/connection.component.ts +++ b/imxweb/projects/qbm/src/lib/connection/connection.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,26 +26,26 @@ import { Clipboard } from '@angular/cdk/clipboard'; import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; +import { FormControl } from '@angular/forms'; import { EUI_SIDESHEET_DATA } from '@elemental-ui/core'; -import { PermissionInfo, SessionInfoData } from 'imx-api-qbm'; -import { DataSourceToolbarSettings } from '../data-source-toolbar/data-source-toolbar-settings'; +import { PermissionInfo } from '@imx-modules/imx-api-qbm'; +import { ValType } from '@imx-modules/imx-qbm-dbts'; +import { TranslateService } from '@ngx-translate/core'; +import { Subscription } from 'rxjs'; +import { distinctUntilChanged } from 'rxjs/operators'; +import { AuthenticationService } from '../authentication/authentication.service'; import { BusyService } from '../base/busy.service'; -import { SnackBarService } from '../snackbar/snack-bar.service'; import { BaseReadonlyCdr } from '../cdr/base-readonly-cdr'; -import { FormControl } from '@angular/forms'; -import { distinctUntilChanged } from 'rxjs/operators'; import { ColumnDependentReference } from '../cdr/column-dependent-reference.interface'; +import { DataSourceToolbarSettings } from '../data-source-toolbar/data-source-toolbar-settings'; import { EntityService } from '../entity/entity.service'; -import { ValType } from 'imx-qbm-dbts'; import { ISessionState } from '../session/session-state'; -import { AuthenticationService } from '../authentication/authentication.service'; -import { Subscription } from 'rxjs'; +import { SnackBarService } from '../snackbar/snack-bar.service'; import { ConnectionSessionInfoData, SystemUsers } from './connection'; -import { TranslateService } from '@ngx-translate/core'; @Component({ templateUrl: './connection.component.html', - styleUrls: ['./connection.component.scss'] + styleUrls: ['./connection.component.scss'], }) /** Shows connection data and can copy data for support */ @@ -53,11 +53,11 @@ export class ConnectionComponent implements OnInit, OnDestroy { public busyService = new BusyService(); public systemUsers: SystemUsers; public dstSettings: DataSourceToolbarSettings; - public displayedColumns = ['Display','Description']; + public displayedColumns = ['Display', 'Description']; public search: FormControl = new FormControl(''); public searchValue: string; public permissionGroups: PermissionInfo[] = []; - public cdrList:ColumnDependentReference[] = []; + public cdrList: ColumnDependentReference[] = []; public sessionState: ISessionState; private readonly subscriptions: Subscription[] = []; @@ -68,24 +68,28 @@ export class ConnectionComponent implements OnInit, OnDestroy { private readonly snackbar: SnackBarService, private readonly entityService: EntityService, private readonly authentication: AuthenticationService, - private readonly translate: TranslateService + private readonly translate: TranslateService, ) { - this.subscriptions.push(this.authentication.onSessionResponse.subscribe((sessionState: ISessionState) => (this.sessionState = sessionState))); + this.subscriptions.push( + this.authentication.onSessionResponse.subscribe((sessionState: ISessionState) => (this.sessionState = sessionState)), + ); } ngOnInit() { - const { FeatureGroups, PermissionGroups, ...systemUsers} = this.data; - this.systemUsers = {...systemUsers}; - this.systemUsers.UserUid = this.data.UserUid = this.sessionState?.UserUid; + const { FeatureGroups, PermissionGroups, ...systemUsers } = this.data; + this.systemUsers = { ...systemUsers }; + this.systemUsers.UserUid = this.data.UserUid = this.sessionState?.UserUid ?? ''; this.cdrList = this.createCdrList(); - this.permissionGroups = this.data.PermissionGroups; - this.subscriptions.push(this.search.valueChanges.pipe(distinctUntilChanged()).subscribe(() => { - const searchValue = this.search.value.toLowerCase(); - this.permissionGroups = this.data.PermissionGroups.filter(permission => { - return permission.Display.toLowerCase().includes(searchValue) - || permission.Description.toLowerCase().includes(searchValue); - }); - })); + this.permissionGroups = this.data.PermissionGroups ?? []; + this.subscriptions.push( + this.search.valueChanges.pipe(distinctUntilChanged()).subscribe(() => { + const searchValue = this.search.value.toLowerCase(); + this.permissionGroups = + this.data.PermissionGroups?.filter((permission) => { + return permission.Display?.toLowerCase().includes(searchValue) || permission.Description?.toLowerCase().includes(searchValue); + }) ?? []; + }), + ); } /** @@ -96,23 +100,31 @@ export class ConnectionComponent implements OnInit, OnDestroy { const cdrList: BaseReadonlyCdr[] = []; const columnNames: string[] = Object.keys(this.systemUsers); const ldsKeys = { - AuthenticatedBy: "#LDS#Authentication used", - CultureFormat: "#LDS#Language for value formatting", - CultureUi: "#LDS#Language", - DialogUserUid: "#LDS#System user UID", - IsReadOnly: "#LDS#Read-only", - TimeZone: "#LDS#Time zone", - UserUid: "#LDS#User UID", - } + AuthenticatedBy: '#LDS#Authentication used', + CultureFormat: '#LDS#Language for value formatting', + CultureUi: '#LDS#Language', + DialogUserUid: '#LDS#System user UID', + IsReadOnly: '#LDS#Read-only', + TimeZone: '#LDS#Time zone', + UserUid: '#LDS#User UID', + }; columnNames?.forEach((name) => { try { - cdrList.push(new BaseReadonlyCdr(this.entityService.createLocalEntityColumn( - { Type: typeof(this.systemUsers[name]) === "boolean" ? ValType.Bool : ValType.String, ColumnName: name, Display: this.translate.instant(ldsKeys[name] ?? "") }, - undefined, - { Value: this.systemUsers[name] } - ))); - } catch(e) {} + cdrList.push( + new BaseReadonlyCdr( + this.entityService.createLocalEntityColumn( + { + Type: typeof this.systemUsers[name] === 'boolean' ? ValType.Bool : ValType.String, + ColumnName: name, + Display: this.translate.instant(ldsKeys[name] ?? ''), + }, + undefined, + { Value: this.systemUsers[name] }, + ), + ), + ); + } catch (e) {} }); //Sort cdrs in ascending order @@ -131,7 +143,7 @@ export class ConnectionComponent implements OnInit, OnDestroy { */ public copyConnectionData(): void { this.clipboard.copy(JSON.stringify(this.data)); - this.snackbar.open({ key: '#LDS#The connection information has been successfully copied to the clipboard.'}); + this.snackbar.open({ key: '#LDS#The connection information has been successfully copied to the clipboard.' }); } ngOnDestroy(): void { diff --git a/imxweb/projects/qbm/src/lib/connection/connection.ts b/imxweb/projects/qbm/src/lib/connection/connection.ts index dc831805b..3ed8eaeb1 100644 --- a/imxweb/projects/qbm/src/lib/connection/connection.ts +++ b/imxweb/projects/qbm/src/lib/connection/connection.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { SessionInfoData } from 'imx-api-qbm'; +import { SessionInfoData } from '@imx-modules/imx-api-qbm'; export interface SystemUsers { AuthenticatedBy?: string; diff --git a/imxweb/projects/qbm/src/lib/custom-theme/custom-theme.module.ts b/imxweb/projects/qbm/src/lib/custom-theme/custom-theme.module.ts index 2b5b8103f..7ca9b2772 100644 --- a/imxweb/projects/qbm/src/lib/custom-theme/custom-theme.module.ts +++ b/imxweb/projects/qbm/src/lib/custom-theme/custom-theme.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,12 +31,8 @@ import { CustomThemeService } from './custom-theme.service'; /** Loads and initializes custom themes provided by the API. */ @NgModule({ - imports: [ - CommonModule - ], - providers: [ - AppConfigService - ] + imports: [CommonModule], + providers: [AppConfigService], }) export class CustomThemeModule { constructor(config: CustomThemeService) { diff --git a/imxweb/projects/qbm/src/lib/custom-theme/custom-theme.service.ts b/imxweb/projects/qbm/src/lib/custom-theme/custom-theme.service.ts index 20380713c..6e38d9391 100644 --- a/imxweb/projects/qbm/src/lib/custom-theme/custom-theme.service.ts +++ b/imxweb/projects/qbm/src/lib/custom-theme/custom-theme.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,15 +26,14 @@ import { DOCUMENT } from '@angular/common'; import { Inject, Injectable } from '@angular/core'; -import { CustomThemeInfo } from 'imx-api-qbm'; import { AppConfigService } from '../appConfig/appConfig.service'; @Injectable({ providedIn: 'root' }) export class CustomThemeService { constructor( @Inject(DOCUMENT) private document: Document, - private readonly config: AppConfigService - ) { } + private readonly config: AppConfigService, + ) {} public initialize(): void { this.config.initializedSubject.subscribe(async () => { @@ -50,6 +49,9 @@ export class CustomThemeService { const head = this.document.getElementsByTagName('head')[0]; for (var theme of customThemes) { + if (!theme.Urls) { + continue; + } for (var url of theme.Urls) { const style = this.document.createElement('link'); style.rel = 'stylesheet'; @@ -58,16 +60,16 @@ export class CustomThemeService { } } - this._customThemes = customThemes.map(m => { + this._customThemes = customThemes.map((m) => { // map .NET types to the expected type for the theme switcher return { - name: m.DisplayName, - class: m.Class + name: m.DisplayName ?? '', + class: m.Class ?? '', }; }); } - private _customThemes: { name: string, class: string }[] = []; + private _customThemes: { name: string; class: string }[] = []; public get customThemes() { return this._customThemes; diff --git a/imxweb/projects/qbm/src/lib/data-export/data-export.component.html b/imxweb/projects/qbm/src/lib/data-export/data-export.component.html index cb37ca351..e2b348e3c 100644 --- a/imxweb/projects/qbm/src/lib/data-export/data-export.component.html +++ b/imxweb/projects/qbm/src/lib/data-export/data-export.component.html @@ -1,80 +1,89 @@
    -
    - {{'#LDS#Data to be exported' | translate}}: - - - {{'#LDS#Displayed data' | translate}} - {{'#LDS#{0} items' | translate | ldsReplace:currentDataCount}} + {{ '#LDS#Data to be exported' | translate }}: + + + {{ '#LDS#Displayed data' | translate }} + ({{ '#LDS#{0} items' | translate | ldsReplace: currentDataCount }}) - {{'#LDS#All data' | translate}} - {{'#LDS#{0} items' | translate | ldsReplace:allDataCount}} + {{ '#LDS#All data' | translate }} + ({{ '#LDS#{0} items' | translate | ldsReplace: allDataCount }})
    - - #LDS#Here you can export data. Select all columns whose contents you want to export. Additionally, you can change the order using drag and drop. Move the mouse pointer over the corresponding area on the left and drag the element to the desired location. + + {{ LdsKey }}
    -
    +
    - - {{'#LDS#Select a column to be exported.' | translate}} + {{ '#LDS#Select a column to be exported.' | translate }} -
    @@ -83,7 +92,14 @@
    -
    diff --git a/imxweb/projects/qbm/src/lib/data-export/data-export.component.scss b/imxweb/projects/qbm/src/lib/data-export/data-export.component.scss index a33312ef3..1a78f090f 100644 --- a/imxweb/projects/qbm/src/lib/data-export/data-export.component.scss +++ b/imxweb/projects/qbm/src/lib/data-export/data-export.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; :host { .tool-bar { @@ -12,30 +12,11 @@ align-items: center; gap: 15px; } - - .mat-radio-group { - display: inherit; - gap: 25px; - } - .eui-select{ - width: 110px; - } - - ::ng-deep .mat-radio-label { - padding: 0; - - .mat-radio-label-content { - display: flex; - flex-direction: column; - } + &-left { + align-items: baseline !important; } } - .eui-sidesheet-content { - display: flex; - flex-direction: column; - } - .card-container { margin-top: 15px; display: flex; @@ -63,12 +44,6 @@ justify-content: space-between; box-sizing: border-box; } - - - .export-column--field { - width: 100%; - margin: 0 10px; - } } // CDK drag styling @@ -94,7 +69,6 @@ transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); } - // Theming .eui-light-theme { :host { diff --git a/imxweb/projects/qbm/src/lib/data-export/data-export.component.ts b/imxweb/projects/qbm/src/lib/data-export/data-export.component.ts index 553ffd261..bd4f1dcdf 100644 --- a/imxweb/projects/qbm/src/lib/data-export/data-export.component.ts +++ b/imxweb/projects/qbm/src/lib/data-export/data-export.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,15 +24,16 @@ * */ -import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; +import { HttpHeaders } from '@angular/common/http'; +import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; import { FormControl } from '@angular/forms'; -import { EuiDownloadOptions, EuiSelectOption, EuiTheme, EUI_SIDESHEET_DATA, EuiSelectFeedbackMessages } from '@elemental-ui/core'; -import { DataSourceToolbarSettings } from '../data-source-toolbar/data-source-toolbar-settings'; -import { ElementalUiConfigService } from '../configuration/elemental-ui-config.service'; +import { EUI_SIDESHEET_DATA, EuiDownloadOptions, EuiSelectFeedbackMessages, EuiSelectOption, EuiTheme } from '@elemental-ui/core'; +import { TranslateService } from '@ngx-translate/core'; import { AppConfigService } from '../appConfig/appConfig.service'; +import { ElementalUiConfigService } from '../configuration/elemental-ui-config.service'; +import { DataSourceToolbarSettings } from '../data-source-toolbar/data-source-toolbar-settings'; import { DSTExportState, ExportColumnsService, FilteredColumnOption } from './export-columns.service'; -import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'imx-data-export', @@ -50,44 +51,52 @@ export class DataExportComponent implements OnInit, OnDestroy { @Inject(EUI_SIDESHEET_DATA) public readonly settings: DataSourceToolbarSettings, private readonly config: AppConfigService, private readonly elementalUiConfigService: ElementalUiConfigService, - private readonly translateService: TranslateService - ) { - this.feedbackMessages = { - ...this.feedbackMessages, - clear: this.translateService.instant('#LDS#Clear'), - search :this.translateService.instant('#LDS#Search'), - } - } + private readonly translateService: TranslateService, + ) { + this.feedbackMessages = { + ...this.feedbackMessages, + clear: this.translateService.instant('#LDS#Clear'), + search: this.translateService.instant('#LDS#Search'), + }; + } public get controlsCount(): number { - return this.state?.columns.length; + return this.state?.columns?.length ?? 0; } public get uniqueControlsCount(): number { - return new Set(this.state?.columns.map(column => column.value)).size; + return new Set(this.state?.columns?.map((column) => column.value)).size; } public get canAddMore(): boolean { - return this.controlsCount < this.state?.columnOptions.length; + return this.controlsCount < (this.state?.columnOptions?.length ?? 0); } public get canExport(): boolean { - return this.controlsCount > 0 && - this.state?.columns.map(control => control.valid).every(value => value) && - this.uniqueControlsCount === this.controlsCount; + return ( + this.controlsCount > 0 && + (this.state?.columns?.map((control) => control.valid)?.every((value) => value) ?? false) && + this.uniqueControlsCount === this.controlsCount + ); } public get currentDataCount(): number { - return this.settings.dataSource.Data.length; + return this.settings.dataSource?.Data.length ?? 0; } public get allDataCount(): number { - return this.settings.dataSource.totalCount; + return this.settings.dataSource?.totalCount ?? 0; } public get theme(): string { const bodyClasses = document.body.classList; - return bodyClasses.contains(EuiTheme.LIGHT) ? EuiTheme.LIGHT : (bodyClasses.contains(EuiTheme.DARK) ? EuiTheme.DARK : (bodyClasses.contains(EuiTheme.CONTRAST) ? EuiTheme.CONTRAST : '')); + return bodyClasses.contains(EuiTheme.LIGHT) + ? EuiTheme.LIGHT + : bodyClasses.contains(EuiTheme.DARK) + ? EuiTheme.DARK + : bodyClasses.contains(EuiTheme.CONTRAST) + ? EuiTheme.CONTRAST + : ''; } public ngOnInit(): void { @@ -105,61 +114,65 @@ export class DataExportComponent implements OnInit, OnDestroy { public setDownloadOptions(): void { this.downloadOptions = { ...this.elementalUiConfigService.Config.downloadOptions, - fileMimeType: this.state.selectedExport.value, + fileMimeType: this.state?.selectedExport?.value, requestOptions: { - headers: { - 'Accept': this.state.selectedExport.value - }, - withCredentials: true + headers: new HttpHeaders({ Accept: this.state.selectedExport?.value }), + withCredentials: true, }, - } + url: '', + }; } // Update the request url public setUrl(): void { - const withProperties = '-' + this.state.columns.map(column => column.value).join(','); - const method = this.settings.exportMethod.getMethod(withProperties, this.state.isAllData ? this.allDataCount : null); + const withProperties = '-' + this.state.columns?.map((column) => column.value).join(','); + const method = this.settings?.exportMethod?.getMethod(withProperties, this.state.isAllData ? this.allDataCount : 0); this.downloadOptions = { ...this.downloadOptions, - url: this.config.BaseUrl + method.path + url: this.config.BaseUrl + (method?.path ?? ''), }; this.updateFilteredColumnOptions(); } // Calculate all valid options for all select (remove selected ones) - public updateFilteredColumnOptions():void{ - this.filteredColumnOptions = this.state.columns.map( - form => ( - { - value: form.value, - options: this.state.columnOptions.filter(option => !this.state.columns.filter(columnForm => columnForm.value !== form.value).map(columnForm => columnForm.value).includes(option.value)) - } - ) - ); + public updateFilteredColumnOptions(): void { + this.filteredColumnOptions = + this.state.columns?.map((form) => ({ + value: form.value, + options: + this.state.columnOptions?.filter( + (option) => + !this.state.columns + ?.filter((columnForm) => columnForm.value !== form.value) + .map((columnForm) => columnForm.value) + .includes(option.value), + ) ?? [], + })) ?? []; } // Return all available options - public getFilteredOptions(value:string): EuiSelectOption[]{ - return this.filteredColumnOptions.filter(columnOption => columnOption.value === value)[0]?.options; + public getFilteredOptions(value: string): EuiSelectOption[] { + return this.filteredColumnOptions.filter((columnOption) => columnOption.value === value)[0]?.options; } // Drag & drop order of columns public drop(event: CdkDragDrop): void { + if (!this.state.columns) { + return; + } moveItemInArray(this.state.columns, event.previousIndex, event.currentIndex); this.setUrl(); } // Remove a column from the export list public deleteColumn(ind: number): void { - this.state.columns.splice(ind, 1); + this.state.columns?.splice(ind, 1); this.setUrl(); } // Add a column to the export list public addNewColumn(value?: EuiSelectOption): void { - this.state.columns.push( - this.columnExportService.createColumn(value) - ); + this.state.columns?.push(this.columnExportService.createColumn(value)); this.updateFilteredColumnOptions(); } @@ -169,11 +182,12 @@ export class DataExportComponent implements OnInit, OnDestroy { ...this.downloadOptions, fileMimeType: type.value, requestOptions: { - headers: { - accept: type.value - }, - withCredentials: true - } + headers: new HttpHeaders({ Accept: type.value }), + withCredentials: true, + }, }; } + + public LdsKey = + '#LDS#Here you can export data. Select all columns whose contents you want to export. Additionally, you can change the order using drag and drop. Move the mouse pointer over the corresponding area on the left and drag the element to the desired location.'; } diff --git a/imxweb/projects/qbm/src/lib/data-export/data-export.module.ts b/imxweb/projects/qbm/src/lib/data-export/data-export.module.ts index 8e6c891e5..585f64cfa 100644 --- a/imxweb/projects/qbm/src/lib/data-export/data-export.module.ts +++ b/imxweb/projects/qbm/src/lib/data-export/data-export.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -38,7 +38,6 @@ import { DataExportComponent } from './data-export.component'; import { LdsReplaceModule } from '../lds-replace/lds-replace.module'; import { MatRadioModule } from '@angular/material/radio'; - @NgModule({ imports: [ CommonModule, @@ -53,7 +52,7 @@ import { MatRadioModule } from '@angular/material/radio'; DragDropModule, CdkScrollableModule, MatRadioModule, - FormsModule + FormsModule, ], declarations: [DataExportComponent], }) diff --git a/imxweb/projects/qbm/src/lib/data-export/export-columns.service.spec.ts b/imxweb/projects/qbm/src/lib/data-export/export-columns.service.spec.ts index 343d4aecb..ff2389755 100644 --- a/imxweb/projects/qbm/src/lib/data-export/export-columns.service.spec.ts +++ b/imxweb/projects/qbm/src/lib/data-export/export-columns.service.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qbm/src/lib/data-export/export-columns.service.ts b/imxweb/projects/qbm/src/lib/data-export/export-columns.service.ts index 7ea4d4c9f..486e05cc7 100644 --- a/imxweb/projects/qbm/src/lib/data-export/export-columns.service.ts +++ b/imxweb/projects/qbm/src/lib/data-export/export-columns.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,24 +27,25 @@ import { Injectable } from '@angular/core'; import { FormControl, Validators } from '@angular/forms'; import { EuiSelectOption } from '@elemental-ui/core'; -import { DataModel, DataModelProperty } from 'imx-qbm-dbts'; +import { DataModel, DataModelProperty } from '@imx-modules/imx-qbm-dbts'; import _ from 'lodash'; import { DataSourceToolbarSettings } from '../data-source-toolbar/data-source-toolbar-settings'; export interface DSTExportState { - dataModel?: DataModel, - selectedExport?: FormControl, - exportOptions?: EuiSelectOption[], - isAllData?: boolean, - columns?: FormControl[], - columnOptions?: EuiSelectOption[] + dataModel?: DataModel; + selectedExport?: FormControl; + exportOptions?: EuiSelectOption[]; + isAllData?: boolean; + columns?: FormControl[]; + columnOptions?: EuiSelectOption[]; } -export interface FilteredColumnOption{ - value: string; options: EuiSelectOption[]; +export interface FilteredColumnOption { + value: string; + options: EuiSelectOption[]; } @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ExportColumnsService { public columnOptions: EuiSelectOption[]; @@ -63,18 +64,21 @@ export class ExportColumnsService { { display: 'PDF', value: 'application/pdf', - } + }, ]; - public exportOptionsFilter = (option: EuiSelectOption, searchInputValue: string) => option.display.toLowerCase().includes(searchInputValue.toLowerCase()); + public exportOptionsFilter = (option: EuiSelectOption, searchInputValue: string) => + option.display.toLowerCase().includes(searchInputValue.toLowerCase()); public columnOptionsFilter = (option: EuiSelectOption, searchInputValue: string) => { const sanitizedInput = searchInputValue.toLowerCase(); - return option.display.toLowerCase().includes(sanitizedInput) || option?.displayDetail?.toLowerCase().includes(sanitizedInput); - } + return ( + (option.display.toLowerCase().includes(sanitizedInput) || option?.displayDetail?.toLowerCase().includes(sanitizedInput)) ?? false + ); + }; // This function will check if the incoming data model is different from what exists. public setupExport(settings?: DataSourceToolbarSettings): void { - if (this.stashedState && this.checkDataModel(settings.dataModel)) { + if (this.stashedState && this.checkDataModel(settings?.dataModel)) { // This is the same data model, don't need to do anything return; } @@ -83,33 +87,33 @@ export class ExportColumnsService { this.createInitialState(settings); } - public checkDataModel(dataModel: DataModel): boolean { - const stashedProperties = this.stashedState.dataModel.Properties.map(column => column.Property.ColumnName); - const properties = dataModel.Properties.map(column => column.Property.ColumnName); + public checkDataModel(dataModel: DataModel | undefined): boolean { + const stashedProperties = this.stashedState.dataModel?.Properties?.map((column) => column?.Property?.ColumnName); + const properties = dataModel?.Properties?.map((column) => column?.Property?.ColumnName); return _.isEqual(stashedProperties, properties); } // Saves the column options internally - public createInitialState(settings: DataSourceToolbarSettings): void { + public createInitialState(settings: DataSourceToolbarSettings | undefined): void { // Column Options sorted alphebetically, not filtering by IsAdditional - this leads to empty exports - const columnOptions = settings.dataModel.Properties.map(prop => { + const columnOptions = settings?.dataModel?.Properties?.map((prop) => { return this.makeOption(prop); }); - columnOptions.sort((a, b) => a.display >= b.display ? 1: -1); + columnOptions?.sort((a, b) => (a.display >= b.display ? 1 : -1)); const selectedExport = new FormControl('text/csv'); // Check for initial columns, or try to use displayed columns const columns: FormControl[] = []; - if (settings.exportMethod?.initialColumns) { - settings.exportMethod.initialColumns.forEach(column => { - const option = columnOptions.find(prop => prop.value === column); + if (settings?.exportMethod?.initialColumns) { + settings.exportMethod.initialColumns.forEach((column) => { + const option = columnOptions?.find((prop) => prop.value === column); if (option) { columns.push(this.createColumn(option)); } - }) + }); } else if (settings?.displayedColumns) { - settings.displayedColumns.forEach(column => { - const option = columnOptions.find(prop => prop.value === column.ColumnName); + settings.displayedColumns.forEach((column) => { + const option = columnOptions?.find((prop) => prop.value === column.ColumnName); if (option) { columns.push(this.createColumn(option)); } @@ -122,23 +126,23 @@ export class ExportColumnsService { } this.stashedState = { - dataModel: settings.dataModel, + dataModel: settings?.dataModel, columnOptions, columns, isAllData: this.isAllData, selectedExport, - exportOptions: this.exportOptions - } + exportOptions: this.exportOptions, + }; } // Setup an option for column export public makeOption(property: DataModelProperty): EuiSelectOption { const display = property.Property?.Display ?? property.Property?.ColumnName; return { - display, - value: property.Property.ColumnName, - displayDetail: property.Property?.Description - } + display: display ?? '', + value: property?.Property?.ColumnName, + displayDetail: property.Property?.Description, + }; } // Create a new column for export @@ -149,10 +153,10 @@ export class ExportColumnsService { // Stash the state of the sidesheet for later use public stashState(): void { // Filter out all invalid columns, leave one if all are invalid - if (this.stashedState.columns.every(column => column.invalid)) { + if (this.stashedState?.columns?.every((column) => column.invalid)) { this.stashedState.columns = [this.createColumn()]; } else { - this.stashedState.columns = this.stashedState.columns.filter(column => column.valid); + this.stashedState.columns = this.stashedState?.columns?.filter((column) => column.valid); } } } diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/additional-infos/additional-infos.component.html b/imxweb/projects/qbm/src/lib/data-source-toolbar/additional-infos/additional-infos.component.html index e7e23a773..c32d8131e 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/additional-infos/additional-infos.component.html +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/additional-infos/additional-infos.component.html @@ -1,39 +1,47 @@ -

    {{(data.type === 'list' ? '#LDS#Heading Additional Information per Entry' : '#LDS#Heading Additional Columns') | translate}}

    +

    + {{ (data.type === 'list' ? '#LDS#Heading Additional Information per Entry' : '#LDS#Heading Additional Columns') | translate }} +

    - + - {{infoText}} + {{ infoText }} - {{infoTextLong}} + {{ infoTextLong }} - {{'#LDS#Columns that can be added' | translate}} + {{ '#LDS#Columns that can be added' | translate }} - - {{property?.Display ?? (property?.untranslatedDisplay | translate) ?? property?.ColumnName}} + *ngFor="let property of possibleProperties" + > + {{ property?.Display ?? (property?.untranslatedDisplay ?? '' | translate) ?? property?.ColumnName }} - {{'#LDS#Columns that are displayed' | translate}} + {{ '#LDS#Columns that are displayed' | translate }}
    - {{property?.Display ?? (property?.untranslatedDisplay | translate) ?? property?.ColumnName}} -
    - + @@ -44,10 +52,9 @@

    {{(data.type === 'list' ? '#LDS#Heading Additional Informat

    - - - - \ No newline at end of file + + + + diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/additional-infos/additional-infos.component.scss b/imxweb/projects/qbm/src/lib/data-source-toolbar/additional-infos/additional-infos.component.scss index bbce90c22..5516d8dfc 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/additional-infos/additional-infos.component.scss +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/additional-infos/additional-infos.component.scss @@ -2,19 +2,19 @@ @import '@elemental-ui/core/src/styles/_palette.scss'; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; -:host{ - display:flex; +:host { + display: flex; flex-direction: column; - height: 100% + height: 100%; } -.imx-dialog-content{ +.imx-dialog-content { flex: 1 1 auto; display: grid; grid-template-columns: 1fr 1fr; } -.imx-element-chooser{ +.imx-element-chooser { margin-right: 15px; overflow: hidden; display: flex; @@ -27,17 +27,6 @@ } } -.imx-helper-alert { - grid-row: 1; - grid-column-start: 1; - grid-column-end: 3; - margin-bottom: 10px; - - .imx-info-content { - font-size: 14px; - } -} - .disabled-list { background: transparent; } @@ -48,7 +37,6 @@ flex-direction: column; background: $white; border-radius: 4px; - overflow: hidden; } .example-box { @@ -61,7 +49,7 @@ align-items: center; justify-content: space-between; box-sizing: border-box; - background: $white; + background: $white; flex-grow: 1; flex-basis: 0; @@ -70,7 +58,7 @@ } } -.dragDropContainer{ +.dragDropContainer { grid-column: 2; padding: 10px; overflow-y: auto; @@ -86,9 +74,9 @@ .cdk-drag-preview { box-sizing: border-box; border-radius: 4px; - box-shadow: 0 5px 5px -3px $black-c - 0 8px 10px 1px mat.get-color-from-palette($asher-gray-palette, 900), - 0 3px 14px 2px mat.get-color-from-palette($asher-gray-palette, 700); + box-shadow: + 0 5px 5px -3px $black-c 0 8px 10px 1px mat.m2-get-color-from-palette($asher-gray-palette, 900), + 0 3px 14px 2px mat.m2-get-color-from-palette($asher-gray-palette, 700); } .cdk-drag-placeholder { @@ -112,7 +100,7 @@ .example-box { border: solid 1px $color-gray-60; color: $color-gray-10; - background: $color-gray-70; + background: $color-gray-70; } } } @@ -126,7 +114,7 @@ .example-box { border: solid 1px $color-gray-0; color: $color-gray-10; - background: $color-gray-100; + background: $color-gray-100; } .imx-drag-handle { diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/additional-infos/additional-infos.component.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/additional-infos/additional-infos.component.ts index e658f8673..0771f61a8 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/additional-infos/additional-infos.component.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/additional-infos/additional-infos.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,9 +26,9 @@ import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; import { Component, Inject, OnInit } from '@angular/core'; -import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MatSelectionListChange } from '@angular/material/list'; -import { DataModel, EntitySchema, IClientProperty } from 'imx-qbm-dbts'; +import { DataModel, EntitySchema, IClientProperty } from '@imx-modules/imx-qbm-dbts'; import { ClientPropertyForTableColumns } from '../client-property-for-table-columns'; @Component({ @@ -37,13 +37,13 @@ import { ClientPropertyForTableColumns } from '../client-property-for-table-colu styleUrls: ['./additional-infos.component.scss'], }) export class AdditionalInfosComponent implements OnInit { - public possibleProperties: IClientProperty[]; + public possibleProperties: ClientPropertyForTableColumns[]; public infoText = '#LDS#Select the columns you want to add.'; public infoTextLong = '#LDS#Here you can add additional columns to your table. Additionally, you can change the order using drag and drop. Move the mouse pointer over the shaded area and drag the element to the desired location.'; - public get result(): any { + public get result(): { all: IClientProperty[]; optionals: IClientProperty[] } { return { all: this.data.preselectedProperties, optionals: this.optionals }; } @@ -62,7 +62,7 @@ export class AdditionalInfosComponent implements OnInit { additionalColumns: ClientPropertyForTableColumns[]; type: 'list' | 'columns'; }, - public dialogRef: MatDialogRef + public dialogRef: MatDialogRef, ) {} public ngOnInit(): void { @@ -112,8 +112,8 @@ export class AdditionalInfosComponent implements OnInit { private static compareNames(column1: IClientProperty, column2: IClientProperty): number { if (column1.Display == null || column2?.Display == null) { - return column1.ColumnName?.localeCompare(column2.ColumnName); + return column1.ColumnName?.localeCompare(column2.ColumnName ?? '') ?? 0; } - return column1.ColumnName?.localeCompare(column2.ColumnName); + return column1.ColumnName?.localeCompare(column2.ColumnName ?? '') ?? 0; } } diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/client-property-for-table-columns.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/client-property-for-table-columns.ts index ccb3fe2b1..737dd37cf 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/client-property-for-table-columns.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/client-property-for-table-columns.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,20 +24,18 @@ * */ -import { IClientProperty } from 'imx-qbm-dbts'; +import { IClientProperty } from '@imx-modules/imx-qbm-dbts'; /** * Extends the IClientProperty interface, by adding a property for marking columns, * that have to be displayed on the far right side */ -export interface ClientPropertyForTableColumns extends IClientProperty{ - +export interface ClientPropertyForTableColumns extends IClientProperty { /** * Marks, whether the property should be displayed on the far right side of a table or not */ afterAdditionals?: boolean; - /** * Gets/Sets an untranslated display, that can be displayed, if the property 'Display' is not set */ diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/column-options.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/column-options.ts index 7d30c019a..961e69a66 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/column-options.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/column-options.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,13 +26,13 @@ import { EventEmitter, Injector } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; -import { DataModel, DataModelViewConfig, EntitySchema, IClientProperty, ValType } from 'imx-qbm-dbts'; +import { DataModel, DataModelViewConfig, EntitySchema, IClientProperty, ValType } from '@imx-modules/imx-qbm-dbts'; +import _ from 'lodash'; import { ClassloggerService } from '../classlogger/classlogger.service'; import { StorageService } from '../storage/storage.service'; import { AdditionalInfosComponent } from './additional-infos/additional-infos.component'; -import { DataSourceToolbarSettings } from './data-source-toolbar-settings'; import { ClientPropertyForTableColumns } from './client-property-for-table-columns'; -import _ from 'lodash'; +import { DataSourceToolbarSettings } from './data-source-toolbar-settings'; import { DSTViewConfig } from './data-source-toolbar-view-config.interface'; export interface ShownClientPropertiesArg { @@ -48,7 +48,7 @@ export class ColumnOptions { /** * List of possible addable columns */ - public optionalColumns: IClientProperty[] = []; + public optionalColumns: (IClientProperty | undefined)[] = []; /** * A list of client properties, that should be shown in the main column @@ -60,7 +60,7 @@ export class ColumnOptions { /** * currently used view settings */ - public currentViewSettings: DataModelViewConfig | DSTViewConfig; + public currentViewSettings: DataModelViewConfig | DSTViewConfig | undefined; /** * Event, that emits, when the shownClientProperies Property changes @@ -76,14 +76,14 @@ export class ColumnOptions { * Indicates whether there are optional columns or not */ public get hasOptionalColumns(): boolean { - return this.currentViewSettings && this.optionalColumns?.length > 0; + return (this.currentViewSettings && this.optionalColumns?.length > 0) ?? false; } // Additional columns are set to null if we are using a config so that we can still edit the columns public get additionalColumns(): IClientProperty[] { return ( this.currentViewSettings?.AdditionalTableColumns?.map((elem) => - ColumnOptions.getClientProperty(ColumnOptions.findKey(elem, this.entitySchema), this.dataModel, this.entitySchema) + ColumnOptions.getClientProperty(ColumnOptions.findKey(elem, this.entitySchema), this.dataModel, this.entitySchema), ) ?? [] ); } @@ -99,19 +99,23 @@ export class ColumnOptions { private logger: ClassloggerService; // getter for settings - private get dataModel(): DataModel { + private get dataModel(): DataModel | undefined { return this.settings.dataModel; } private entitySchema: EntitySchema; private get displayedColumns(): IClientProperty[] { - return this.settings.displayedColumns; + return this.settings?.displayedColumns ?? []; } private originalEntitySchema; - constructor(public settings: DataSourceToolbarSettings, injector: Injector, public viewConfig?: DSTViewConfig) { + constructor( + public settings: DataSourceToolbarSettings, + injector: Injector, + public viewConfig?: DSTViewConfig, + ) { // Use the injected viewConfig if available - this.currentViewSettings = viewConfig ?? this.dataModel.Configurations?.find((elem) => elem.Id === this.dataModel.DefaultConfigId); + this.currentViewSettings = viewConfig ?? this.dataModel?.Configurations?.find((elem) => elem.Id === this.dataModel?.DefaultConfigId); if (this.currentViewSettings) { // Clean up settings, if there are null or empty columnsnames attached @@ -120,7 +124,7 @@ export class ColumnOptions { this.currentViewSettings.AdditionalListColumns.some((elem) => elem == null || elem === '') ) { (this.currentViewSettings.AdditionalListColumns as any) = this.currentViewSettings.AdditionalListColumns.filter( - (elem) => elem != null && elem !== '' + (elem) => elem != null && elem !== '', ); } @@ -129,7 +133,7 @@ export class ColumnOptions { this.currentViewSettings.AdditionalTableColumns.some((elem) => elem == null || elem === '') ) { (this.currentViewSettings.AdditionalTableColumns as any) = this.currentViewSettings.AdditionalTableColumns.filter( - (elem) => elem != null && elem !== '' + (elem) => elem != null && elem !== '', ); } } @@ -145,7 +149,7 @@ export class ColumnOptions { public getPropertiesForNavigation(): string[] { return this.shownClientProperties .filter((elem) => this.displayedColumns.findIndex((disp) => disp.ColumnName === elem.ColumnName) === -1) - .map((elem) => elem.ColumnName); + .map((elem) => elem.ColumnName ?? ''); } /** @@ -199,9 +203,9 @@ export class ColumnOptions { } // We will reset by grabbing the default Id - this.currentViewSettings = this.dataModel.Configurations?.find((elem) => elem.Id === 'Default'); + this.currentViewSettings = this.dataModel?.Configurations?.find((elem) => elem.Id === 'Default'); - const addition = this.additionalColumns; + const addition: IClientProperty[] = this.additionalColumns ?? []; this.selectedOptionals = []; this.shownClientProperties = [...this.displayedColumns]; @@ -232,7 +236,9 @@ export class ColumnOptions { // hack for adding the new columns to to entitySchema elements.forEach((element) => { const key = ColumnOptions.findKey(element, this.entitySchema); - (this.entitySchema.Columns[key] as any) = ColumnOptions.getClientProperty(key, this.dataModel, this.entitySchema); + if (key) { + (this.entitySchema.Columns[key] as any) = ColumnOptions.getClientProperty(key, this.dataModel, this.entitySchema); + } }); } @@ -256,16 +262,17 @@ export class ColumnOptions { } private initOptionalColumns(): void { - const optional = this.dataModel.Properties?.filter((elem) => elem.IsAdditionalColumn).map((elem) => elem.Property); + const optional = this.dataModel?.Properties?.filter((elem) => elem.IsAdditionalColumn).map((elem) => elem.Property); // Check if this isAdditional or if its already in the additionalColumns, both are needed to not lose the option from config selection - this.optionalColumns = optional?.filter((value, index, categoryArray) => { - const isAdditional = - this.isAdditional(value.ColumnName) || - this.additionalColumns.find((ele) => ele.ColumnName.toLocaleLowerCase() == value.ColumnName.toLocaleLowerCase()) != null; - const indexMatch = categoryArray.indexOf(value) === index; - return isAdditional && indexMatch; - }); + this.optionalColumns = + optional?.filter((value, index, categoryArray) => { + const isAdditional = + this.isAdditional(value?.ColumnName) || + this.additionalColumns.find((ele) => ele.ColumnName?.toLocaleLowerCase() == value?.ColumnName?.toLocaleLowerCase()) != null; + const indexMatch = categoryArray.indexOf(value) === index; + return isAdditional && indexMatch; + }) ?? []; this.logger.trace(this, 'optional columns', this.optionalColumns); } @@ -273,7 +280,7 @@ export class ColumnOptions { private initShownClientProperties(): void { const current = this.currentViewSettings?.AdditionalTableColumns?.filter((element) => - this.displayedColumns.every((elem) => elem.ColumnName !== element) + this.displayedColumns.every((elem) => elem.ColumnName !== element), ).map((elem) => ColumnOptions.getClientProperty(elem, this.dataModel)) ?? []; this.shownClientProperties = [...this.displayedColumns]; const index = this.shownClientProperties.findIndex((elem) => elem.afterAdditionals); @@ -284,32 +291,37 @@ export class ColumnOptions { private initAdditionalListElements(): void { const lists = this.currentViewSettings?.AdditionalListColumns; - if (lists?.length > 0) { - this.additionalListElements = lists.map((elem) => - ColumnOptions.getClientProperty(ColumnOptions.findKey(elem, this.entitySchema), this.dataModel, this.entitySchema) - ); - this.additionalListElementsChanged.emit(this.additionalListElements); + if (!!lists?.length) { + this.additionalListElements = + lists?.map((elem) => + ColumnOptions.getClientProperty(ColumnOptions.findKey(elem, this.entitySchema), this.dataModel, this.entitySchema), + ) ?? []; + this.additionalListElementsChanged.emit(this.additionalListElements ?? []); this.logger.trace(this, 'additional list elements from viewSettings', this.additionalListElements); } } - private isAdditional(key: string): boolean { + private isAdditional(key: string | undefined): boolean { return ( - this.displayedColumns.find((elem) => elem.ColumnName.toLocaleLowerCase() === key.toLocaleLowerCase()) == null && - this.currentViewSettings?.AdditionalListColumns?.find((elem) => elem.toLocaleLowerCase() === key.toLocaleLowerCase()) == null && - this.currentViewSettings?.AdditionalTableColumns?.find((elem) => elem.toLocaleLowerCase() === key.toLocaleLowerCase()) == null + this.displayedColumns.find((elem) => elem.ColumnName?.toLocaleLowerCase() === key?.toLocaleLowerCase()) == null && + this.currentViewSettings?.AdditionalListColumns?.find((elem) => elem.toLocaleLowerCase() === key?.toLocaleLowerCase()) == null && + this.currentViewSettings?.AdditionalTableColumns?.find((elem) => elem.toLocaleLowerCase() === key?.toLocaleLowerCase()) == null ); } - public static getClientProperty(name: string, dataModel: DataModel, entitySchema?: EntitySchema): IClientProperty { - let property: IClientProperty; + public static getClientProperty( + name: string | undefined, + dataModel: DataModel | undefined, + entitySchema?: EntitySchema, + ): IClientProperty { + let property: IClientProperty | null | undefined = null; if (entitySchema) { const key = ColumnOptions.findKey(name, entitySchema); property = key != null ? entitySchema.Columns[key] : null; } if (property == null) { property = dataModel?.Properties?.find( - (elem) => elem?.Property?.ColumnName?.toLocaleLowerCase() === name?.toLocaleLowerCase() + (elem) => elem?.Property?.ColumnName?.toLocaleLowerCase() === name?.toLocaleLowerCase(), )?.Property; } if (property == null) { @@ -318,8 +330,8 @@ export class ColumnOptions { return property; } - public static findKey(key: string, schema: EntitySchema): string { - const keyVariant = Object.keys(schema.Columns).find((elem) => elem.toLocaleLowerCase() === key.toLocaleLowerCase()); + public static findKey(key: string | undefined, schema: EntitySchema): string | undefined { + const keyVariant = Object.keys(schema.Columns).find((elem) => elem.toLocaleLowerCase() === key?.toLocaleLowerCase()); return keyVariant ?? key; } } diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-model/data-model-helper.spec.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-model/data-model-helper.spec.ts index 72106ba64..a48747cc7 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-model/data-model-helper.spec.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-model/data-model-helper.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,15 +24,12 @@ * */ -import { DataModelFilterOption, DataModelProperty, ValType } from 'imx-qbm-dbts'; +import { DataModelFilterOption, DataModelProperty, ValType } from '@imx-modules/imx-qbm-dbts'; import { createGroupData } from './data-model-helper'; describe('DataModelHelper', () => { it('should createGroupData - undefined if no data', () => { - const groupData = createGroupData( - { }, - undefined - ); + const groupData = createGroupData({}, undefined); expect(groupData).toBeUndefined(); }); @@ -43,11 +40,11 @@ describe('DataModelHelper', () => { Properties: [ { IsGroupable: false, Property: { Type: ValType.String, ColumnName: 'some columnName not groupable' } }, { IsGroupable: true, Property: { Type: ValType.String, ColumnName: 'some columnName excluded' } }, - { IsGroupable: true, Property: { Type: ValType.String, ColumnName: 'some columnName' } } - ] as DataModelProperty[] + { IsGroupable: true, Property: { Type: ValType.String, ColumnName: 'some columnName' } }, + ] as DataModelProperty[], }, - __ => Promise.resolve({ TotalCount: 0 }), - ['some columnName excluded'] + (__) => Promise.resolve({ TotalCount: 0 }), + ['some columnName excluded'], ); expect(groupData.groups.length).toEqual(1); @@ -58,11 +55,9 @@ describe('DataModelHelper', () => { it('should createGroupData groups based on GroupInfo with length === 1', () => { const groupData = createGroupData( { - GroupInfo: [ - { Options: [{ Value: 'option1' }] } - ] + GroupInfo: [{ Options: [{ Value: 'option1' }] }], }, - __ => Promise.resolve({ TotalCount: 0 }) + (__) => Promise.resolve({ TotalCount: 0 }), ); expect(groupData.groups.length).toEqual(1); @@ -73,12 +68,9 @@ describe('DataModelHelper', () => { it('should createGroupData groupingCategories based on GroupInfo with length > 1', () => { const groupData = createGroupData( { - GroupInfo: [ - { Options: [{ Value: 'option1' }] }, - { Options: [{ Value: 'option2' }] } - ] + GroupInfo: [{ Options: [{ Value: 'option1' }] }, { Options: [{ Value: 'option2' }] }], }, - __ => Promise.resolve({ TotalCount: 0 }) + (__) => Promise.resolve({ TotalCount: 0 }), ); expect(groupData.groups.length).toEqual(0); @@ -92,14 +84,10 @@ describe('DataModelHelper', () => { it('should createGroupData groups based on Properties and GroupInfo length === 1', () => { const groupData = createGroupData( { - Properties: [ - { IsGroupable: true, Property: { Type: ValType.String, ColumnName: 'some columnName' } } - ] as DataModelProperty[], - GroupInfo: [ - { Options: [{ Value: 'option1' }] } - ] + Properties: [{ IsGroupable: true, Property: { Type: ValType.String, ColumnName: 'some columnName' } }] as DataModelProperty[], + GroupInfo: [{ Options: [{ Value: 'option1' }] }], }, - __ => Promise.resolve({ TotalCount: 0 }) + (__) => Promise.resolve({ TotalCount: 0 }), ); expect(groupData.groups.length).toEqual(2); @@ -111,15 +99,10 @@ describe('DataModelHelper', () => { it('should createGroupData groups and groupingCategories based on Properties and GroupInfo with length > 1', () => { const groupData = createGroupData( { - Properties: [ - { IsGroupable: true, Property: { Type: ValType.String, ColumnName: 'some columnName' } } - ] as DataModelProperty[], - GroupInfo: [ - { Options: [{ Value: 'option1' }] }, - { Options: [{ Value: 'option2' }] } - ] + Properties: [{ IsGroupable: true, Property: { Type: ValType.String, ColumnName: 'some columnName' } }] as DataModelProperty[], + GroupInfo: [{ Options: [{ Value: 'option1' }] }, { Options: [{ Value: 'option2' }] }], }, - __ => Promise.resolve({ TotalCount: 0 }) + (__) => Promise.resolve({ TotalCount: 0 }), ); expect(groupData.groups.length).toEqual(1); diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-model/data-model-helper.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-model/data-model-helper.ts index fb38ca44f..de5d5c112 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-model/data-model-helper.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-model/data-model-helper.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,38 +24,42 @@ * */ -import { CollectionLoadParameters, DataModel, DataModelFilterOption, GroupInfo, GroupInfoData } from 'imx-qbm-dbts'; -import { DataSourceToolBarGroup, DataSourceToolbarGroupData } from '../data-source-toolbar-groups.interface'; +import { CollectionLoadParameters, DataModel, DataModelFilterOption, GroupInfo, GroupInfoData } from '@imx-modules/imx-qbm-dbts'; +import { + DataSourceToolBarGroup, + DataSourceToolbarGroupData, + DataSourceToolBarGroupingCategory, +} from '../data-source-toolbar-groups.interface'; import { GroupInfoLoadParameters } from './group-info-load-parameters.interface'; export function createGroupData( dataModel: DataModel, - getGroupInfo: (parameters: GroupInfoLoadParameters) => Promise, - excludedColumns?: string[] -): DataSourceToolbarGroupData { - const groups = []; - const groupingCategories = []; + getGroupInfo: (parameters: GroupInfoLoadParameters) => Promise | undefined, + excludedColumns?: string[], +): DataSourceToolbarGroupData | undefined { + const groups: DataSourceToolBarGroup[] = []; + const groupingCategories: DataSourceToolBarGroupingCategory[] = []; if (dataModel.Properties) { - dataModel.Properties.filter((p) => p.IsGroupable && p.Property && !excludedColumns?.includes(p.Property.ColumnName)).forEach( + dataModel.Properties.filter((p) => p.IsGroupable && p.Property && !excludedColumns?.includes(p.Property.ColumnName ?? '')).forEach( (property) => groups.push({ property, getData: async (parameter: CollectionLoadParameters) => { - return getGroupInfo({ ...parameter, by: property.Property.ColumnName }); + return getGroupInfo({ ...parameter, by: property.Property?.ColumnName ?? '' }); }, - }) + }), ); } if (dataModel.GroupInfo?.length === 1) { - dataModel.GroupInfo[0].Options.forEach((option) => groups.push(getDataSourceToolBarGroup(option, getGroupInfo))); + dataModel.GroupInfo?.[0].Options?.forEach((option) => groups.push(getDataSourceToolBarGroup(option, getGroupInfo))); } else { dataModel.GroupInfo?.forEach((property) => groupingCategories.push({ property, - groups: property.Options.map((option) => getDataSourceToolBarGroup(option, getGroupInfo)), - }) + groups: property.Options?.map((option) => getDataSourceToolBarGroup(option, getGroupInfo)) ?? [], + }), ); } @@ -68,27 +72,29 @@ export function createGroupData( function getDataSourceToolBarGroup( option: DataModelFilterOption, - getGroupInfo: (parameters: GroupInfoLoadParameters) => Promise + getGroupInfo: (parameters: GroupInfoLoadParameters) => Promise | undefined, ): DataSourceToolBarGroup { return { property: option, getData: async (param: CollectionLoadParameters) => { const data = await getGroupInfo({ ...param, ...{ def: option.Value } }); - data.Groups = data.Groups?.map((item) => { - setFilterDisplay(item); - return item; - }); + if (data) { + data.Groups = data.Groups?.map((item) => { + setFilterDisplay(item); + return item; + }); + } return data; }, }; } function setFilterDisplay(item: GroupInfo): void { - item.Display.forEach((display) => - item.Filters.forEach((filter) => { + item.Display?.forEach((display) => + item.Filters?.forEach((filter) => { if (filter.Value1 != null) { - display.Display = display.Display.replace(`%${filter.ColumnName}%`, filter.Value1); + display.Display = display.Display?.replace(`%${filter.ColumnName}%`, filter.Value1); } - }) + }), ); } diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-model/data-model-wrapper.interface.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-model/data-model-wrapper.interface.ts index db6545ef2..343fe3546 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-model/data-model-wrapper.interface.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-model/data-model-wrapper.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,12 +24,12 @@ * */ -import { DataModel, GroupInfoData } from 'imx-qbm-dbts'; +import { DataModel, GroupInfoData } from '@imx-modules/imx-qbm-dbts'; import { GroupInfoLoadParameters } from './group-info-load-parameters.interface'; export interface DataModelWrapper { dataModel: DataModel; - getGroupInfo?: (parameters: GroupInfoLoadParameters) => Promise; + getGroupInfo?: (parameters: GroupInfoLoadParameters) => Promise; groupingFilterOptions?: string[]; groupingExcludedColumns?: string[]; } diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-model/filter-tree-parameter.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-model/filter-tree-parameter.ts index 4f021b94a..93e23d099 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-model/filter-tree-parameter.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-model/filter-tree-parameter.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { FilterTreeData } from 'imx-qbm-dbts'; +import { FilterTreeData } from '@imx-modules/imx-qbm-dbts'; export interface FilterTreeParameter { filterMethode: (parentkey: string) => Promise; diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-model/group-info-load-parameters.interface.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-model/group-info-load-parameters.interface.ts index 81fbd5d5e..27816210a 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-model/group-info-load-parameters.interface.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-model/group-info-load-parameters.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { CollectionLoadParameters } from 'imx-qbm-dbts'; +import { CollectionLoadParameters } from '@imx-modules/imx-qbm-dbts'; export interface GroupInfoLoadParameters extends CollectionLoadParameters { by?: string; diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-item-status.interface.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-item-status.interface.ts index cfadc3064..28e6a1694 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-item-status.interface.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-item-status.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,11 +24,29 @@ * */ -import { TypedEntity } from 'imx-qbm-dbts'; +import { TypedEntity } from '@imx-modules/imx-qbm-dbts'; +import { QueuedActionState } from '../processing-queue/processing-queue.interface'; import { DataTileBadge } from './data-tile-badge.interface'; export interface DataSourceItemStatus { - enabled: (item: TypedEntity) => boolean; - getBadges?: (input: TypedEntity) => DataTileBadge[]; + /** + * Function of the row to determine if the checkbox is clickable. + * @param item row entity + * @returns if the checkbox is clickable + */ + enabled: (item?: TypedEntity) => boolean; + /** + * Function of the row to determine if the row action should be allowed. We may want to prevent an entity from being interacted with while it is being processed. + * @param item row entity + * @returns if the row should be clickable + */ + rowEnabled?: (item?: TypedEntity) => boolean; + /** + * Function to return the status of a entity in the queue. + * @param item row entity + * @returns the state of this item in the queue + */ + status?: (item: TypedEntity) => QueuedActionState; + getBadges?: (input: TypedEntity) => DataTileBadge[]; getImagePath?: (item: TypedEntity) => Promise; } diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-paginator.component.html b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-paginator.component.html index 2cd56911f..5ddd8aab2 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-paginator.component.html +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-paginator.component.html @@ -1 +1,7 @@ - + diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-paginator.component.scss b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-paginator.component.scss deleted file mode 100644 index 23e57191f..000000000 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-paginator.component.scss +++ /dev/null @@ -1,8 +0,0 @@ -/* You can add styles to this file, and also import other style files */ -mat-paginator { - box-shadow: none; - - &.hide-paginator { - display: none; - } -} diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-paginator.component.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-paginator.component.ts index 2aa892d84..e9703c7af 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-paginator.component.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-paginator.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,14 +24,13 @@ * */ -import { Component, Input, Output, EventEmitter, SimpleChanges, OnChanges, ViewChild, OnDestroy, ChangeDetectorRef } from '@angular/core'; -import { PageEvent, MatPaginator } from '@angular/material/paginator'; +import { ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnDestroy, Output, SimpleChanges, ViewChild } from '@angular/core'; +import { MatPaginator, PageEvent } from '@angular/material/paginator'; import { Subscription } from 'rxjs'; -import { CollectionLoadParameters } from 'imx-qbm-dbts'; -import { DataSourceToolbarComponent } from './data-source-toolbar.component'; +import { CollectionLoadParameters } from '@imx-modules/imx-qbm-dbts'; import { DataSourceToolbarSettings } from './data-source-toolbar-settings'; - +import { DataSourceToolbarComponent } from './data-source-toolbar.component'; /** * Paginator for navigating the datasource of the {@link DataSourceToolbarComponent| datasource toolbar component}. @@ -47,7 +46,6 @@ import { DataSourceToolbarSettings } from './data-source-toolbar-settings'; @Component({ selector: 'imx-data-source-paginator', templateUrl: './data-source-paginator.component.html', - styleUrls: ['./data-source-paginator.component.scss'] }) export class DataSourcePaginatorComponent implements OnChanges, OnDestroy { /** @@ -58,11 +56,11 @@ export class DataSourcePaginatorComponent implements OnChanges, OnDestroy { /** * Add in first/last buttons */ - @Input() public showFirstLastButtons: boolean = false; + @Input() public showFirstLastButtons: boolean = true; /** - * List of options for the page size. - */ + * List of options for the page size. + */ @Input() public pageSizeOptions: number[] = [20, 50, 100]; /** @@ -81,11 +79,11 @@ export class DataSourcePaginatorComponent implements OnChanges, OnDestroy { * Hides the paginator, if there is not data, a group is applied or the control is loading new content */ public get hidePaginator() { - return !this.dst?.dataSourceHasData || this.dst.settings?.groupData?.currentGrouping != null || this.isLoading; + return !this.dst?.dataSourceHasData || this.dst.settings?.groupData?.currentGrouping != null || this.isLoading; } public isLoading = true; - constructor (private readonly changeDetector: ChangeDetectorRef){} + constructor(private readonly changeDetector: ChangeDetectorRef) {} /** * @ignore @@ -101,16 +99,18 @@ export class DataSourcePaginatorComponent implements OnChanges, OnDestroy { if (changes['dst'] && changes['dst'].currentValue) { this.setPaginator(); - this.subscriptions.push(this.dst.settingsChanged.subscribe((value: DataSourceToolbarSettings) => { - this.dst.settings = value; - this.setPaginator(); - })); + this.subscriptions.push( + this.dst.settingsChanged.subscribe((value: DataSourceToolbarSettings) => { + this.dst.settings = value; + this.setPaginator(); + }), + ); - if(this.dst.busyService){ - this.dst.busyService.busyStateChanged.subscribe((value:boolean) =>{ + if (this.dst.busyService) { + this.dst.busyService.busyStateChanged.subscribe((value: boolean) => { this.isLoading = value; this.changeDetector.detectChanges(); - }) + }); } else { this.isLoading = false; } @@ -122,7 +122,7 @@ export class DataSourcePaginatorComponent implements OnChanges, OnDestroy { * Unsubscribes all listeners. */ public ngOnDestroy(): void { - this.subscriptions.forEach(subscription => subscription.unsubscribe()); + this.subscriptions.forEach((subscription) => subscription.unsubscribe()); } /** @@ -146,8 +146,9 @@ export class DataSourcePaginatorComponent implements OnChanges, OnDestroy { } if (this.dst.settings.navigationState) { - this.paginator.pageSize = this.dst.settings.navigationState.PageSize; - this.paginator.pageIndex = this.dst.settings.navigationState.StartIndex / this.dst.settings.navigationState.PageSize; + this.paginator.pageSize = this.dst.settings.navigationState.PageSize ?? 0; + if (this.paginator.pageSize > 0) + this.paginator.pageIndex = (this.dst.settings.navigationState.StartIndex ?? 0) / this.paginator.pageSize; } } } diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-custom.component.html b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-custom.component.html index ec0cab1e5..7611b7c50 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-custom.component.html +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-custom.component.html @@ -1,2 +1,2 @@ - + diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-custom.component.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-custom.component.ts index c28712c34..f3dd5523e 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-custom.component.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-custom.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -45,10 +45,8 @@ import { Component, Input, TemplateRef } from '@angular/core'; @Component({ selector: 'imx-data-source-toolbar-custom', templateUrl: './data-source-toolbar-custom.component.html', - styleUrls: ['./data-source-toolbar-custom.component.scss'] + styleUrls: ['./data-source-toolbar-custom.component.scss'], }) - - export class DataSourceToolbarCustomComponent { /** * Reference to a custom template. diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-export-method.interface.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-export-method.interface.ts index 61ba35573..4c5238b44 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-export-method.interface.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-export-method.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,12 +24,11 @@ * */ -import { DataModelProperty, MethodDefinition } from 'imx-qbm-dbts'; - +import { DataModelProperty, MethodDefinition } from '@imx-modules/imx-qbm-dbts'; export interface DataSourceToolbarExportMethod { // This will define an api method that the dst can then call to make a download to the client getMethod: (withProperties: string, PageSize?: number) => MethodDefinition; // inititalColumns will set the initial export columns, if not present then we will try to figure it out from the displayed columns - initialColumns?: string[] + initialColumns?: string[]; } diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-filters.interface.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-filters.interface.ts index ee9a9e03e..f51e41b6d 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-filters.interface.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-filters.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { DataModelFilter, DataModelFilterOption } from 'imx-qbm-dbts'; +import { DataModelFilter, DataModelFilterOption } from '@imx-modules/imx-qbm-dbts'; export interface DataSourceToolbarFilter extends DataModelFilter { /** @@ -51,11 +51,11 @@ export interface DataSourceToolbarSelectedFilter { /** * The option that was selected */ - selectedOption: DataModelFilterOptionExtended; + selectedOption?: DataModelFilterOptionExtended; /** * The filter the value was selected for */ - filter: DataSourceToolbarFilter; + filter?: DataSourceToolbarFilter; /** * Indicates if the selected filter is not part of the standard datamodel filters diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-groups.interface.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-groups.interface.ts index f842b125d..c1ac1de58 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-groups.interface.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-groups.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,13 @@ * */ -import { CollectionLoadParameters, DataModelFilterOption, DataModelGroupInfo, GroupInfoData, IClientProperty } from 'imx-qbm-dbts'; +import { + CollectionLoadParameters, + DataModelFilterOption, + DataModelGroupInfo, + GroupInfoData, + IClientProperty, +} from '@imx-modules/imx-qbm-dbts'; export interface DataSourceToolbarGroupData { /** @@ -42,7 +48,7 @@ export interface DataSourceToolbarGroupData { */ currentGrouping?: { display: string; - getData: (parameter?: CollectionLoadParameters) => Promise; + getData: (parameter?: CollectionLoadParameters) => Promise; navigationState?: CollectionLoadParameters; }; } @@ -56,7 +62,7 @@ export interface DataSourceToolBarGroup { /** * Callback for getting the corresponding GroupInfo data for the property */ - getData: (parameter?: CollectionLoadParameters) => Promise; + getData: (parameter?: CollectionLoadParameters) => Promise; /** * The navigation state used, when loading the group elements for a grouping type diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-menu-item.interface.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-menu-item.interface.ts index a4c4cd2af..fd4bd0535 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-menu-item.interface.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-menu-item.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { IClientProperty } from 'imx-qbm-dbts'; +import { IClientProperty } from '@imx-modules/imx-qbm-dbts'; /** * Internal repesentation of a menu item in a datasource toolbar menu. diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-settings.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-settings.ts index 8e1367277..1eebba275 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-settings.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-settings.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { TypedEntity, TypedEntityCollectionData, CollectionLoadParameters, EntitySchema, DataModel } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, DataModel, EntitySchema, TypedEntity, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { ClientPropertyForTableColumns } from './client-property-for-table-columns'; import { FilterTreeParameter } from './data-model/filter-tree-parameter'; import { DataSourceToolbarExportMethod } from './data-source-toolbar-export-method.interface'; @@ -40,7 +40,7 @@ export interface DataSourceToolbarSettings { * The datasource of the toolbar. * Basically this is a collection of typed entities. */ - dataSource: TypedEntityCollectionData; + dataSource: TypedEntityCollectionData | undefined; /** * Describes which chunk of data is currently loaded. @@ -103,9 +103,4 @@ export interface DataSourceToolbarSettings { } */ exportMethod?: DataSourceToolbarExportMethod; - - /** - * List of filtered columns, that can not be edited in the UI. - */ - staticFilterColumns?: string[]; } diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-view-config-helper.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-view-config-helper.ts index 26aefc859..83eb63281 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-view-config-helper.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-view-config-helper.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { DSTViewConfig } from "./data-source-toolbar-view-config.interface"; +import { DSTViewConfig } from './data-source-toolbar-view-config.interface'; /** * Looks up if the config is set to be default @@ -41,5 +41,5 @@ export function isConfigDefault(config: DSTViewConfig): boolean { * @returns whether the config should be used */ export function isDefaultId(config: DSTViewConfig): boolean { - return config?.Id == 'Default' + return config?.Id == 'Default'; } diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-view-config.interface.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-view-config.interface.ts index 8e00ff92a..a90e1d473 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-view-config.interface.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar-view-config.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { DataModelViewConfig } from 'imx-qbm-dbts'; +import { DataModelViewConfig } from '@imx-modules/imx-qbm-dbts'; export interface DSTViewConfig extends DataModelViewConfig { /** @@ -51,7 +51,7 @@ export interface DSTViewConfig extends DataModelViewConfig { /** Storage for arbitrary URL filter parameter values. */ AdditionalParameters?: { - [key: string]: string; + [key: string]: string; }; } @@ -65,5 +65,4 @@ export interface DataSourceToolbarViewConfig { * The list of available configs */ viewId: string; - } diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar.component.html b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar.component.html index a4b343e77..d804413bd 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar.component.html +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar.component.html @@ -10,7 +10,6 @@ data-imx-identifier="dst-eui-search" [placeholder]="searchTerms.length ? ('#LDS#Search' | translate) : searchBoxText ? searchBoxText : ('#LDS#Search' | translate)" [searchControl]="searchControl" - size="medium" (keydown.enter)="addSearchFilter()" > @@ -18,8 +17,7 @@
    - - + @@ -95,140 +86,106 @@
    - + {{ (isDescending ? '#LDS#Descending' : '#LDS#Ascending') | translate }}
    -
    - - - -
    - - {{ filter.Options[0].Display }} - -

    {{ filter.Description || filter.Name }}

    - - - {{ fopt.Display }} - - - - - {{ fopt.Display }} - - - - - {{ foption.Display }} - - - - - {{ foption.Display }} - - -
    -
    - - - - -
    - -
    +
    -
    +
    -
    +
    -
    -
    - - -
    - - {{'#LDS#Search by keywords' | translate}}: - + + {{ '#LDS#Search by keywords' | translate }}: + - {{ st.selectedOption.Display }} - - - - {{ st.selectedOption?.Display }} + + + + - #LDS#Filter on: - - {{ "#LDS#Custom filter" | translate }} ({{ filterWizardExpression?.Expression?.Expressions?.length}}) - - - {{ '#LDS#Filter on' | translate }}: + + {{ '#LDS#Custom filter' | translate }} ({{ filterWizardExpression?.Expression?.Expressions?.length }}) + + + - {{ sf.selectedOption.Display }} - - - - {{ filterType }}: {{ currentFilterData[0].display }} + {{ sf.selectedOption?.Display ?? '' }} + + + + + {{ filterType }}: {{ currentFilterData?.display }} - - + + - + #LDS#Sort by: - + {{ currentSortColumn }} - - - - +
    diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar.component.scss b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar.component.scss index 68c6e0af5..3367afd41 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar.component.scss +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar.component.scss @@ -1,17 +1,18 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/mixins'; .imx-data-source-toolbar-container { display: flex; flex-direction: column; - + .imx-data-source-toolbar { display: flex; - overflow: hidden; justify-content: space-between; &.no-toolbar-options-visible { height: 0; + visibility: hidden; } } @@ -34,10 +35,6 @@ .imx-data-source-toolbar-search { width: 100%; - - .eui-search { - width: 100%; - } } .imx-chip-container { @@ -48,47 +45,23 @@ .imx-data-source-selected-filters { display: block; - margin: 10px 5px; + margin: 10px 10px; - .mat-chip-list-wrapper > span { - margin-right: 5px; - } - - .mat-chip { + .mat-mdc-chip { cursor: pointer; - .eui-icon { - &.remove { - font-size: 18px; - margin-right: -10px; - } - } - span.tag-name { font-size: 13px; white-space: nowrap; } - - &.isRegex { - background-color: $color-blue-60; - - .eui-icon { - &.remove { - color: $color-gray-0; - } - } - span.tag-name { - color: $color-gray-0; - } - } } } } -.mat-menu-panel { - +.mat-mdc-menu-panel { &.imx-sort-menu { max-width: 40vw; + padding: 12px; .imx-sort-options { max-height: 30vh; @@ -102,6 +75,7 @@ padding: 0 5px; gap: 5px; } + .imx-sort-buttons { display: flex; justify-content: flex-end; @@ -109,7 +83,6 @@ } } - &.imx-filters-menu { padding: 0 8px; } @@ -121,73 +94,39 @@ } } - - -.imx-filter-tree-dialog -> .mat-dialog-container { - display: flex; - flex-direction: column; +.imx-filter-tree-dialog>.mat-mdc-dialog-container { + @include flex-column-container(); } -.mat-menu-content { +.mat-mdc-menu-content { max-width: none; - .imx-data-source-filter-menu { - margin: 2px 15px 20px; - - p { - font-weight: 600; - margin-bottom: 8px; - } - - .mat-checkbox { - display: block; - } - - .mat-radio-button { - display: block; - margin-right: 5px; - - label.mat-radio-label { - white-space: normal; - } - } - .mat-select .mat-select-trigger { - margin-top: 5px; - } - - .mat-select .mat-select-value { - max-width: none; - white-space: normal; - .mat-select-value-text { - white-space: normal; - } - } - - .mat-checkbox-layout { - white-space: normal; - } - } - - .link-button { - color: $iris-blue; - } - - .mat-menu-item:disabled > .link-button { + .mat-mdc-menu-item:disabled>.imx-info { color: inherit; } - button.mat-menu-item { + button.mat-mdc-menu-item { white-space: break-spaces; min-height: 48px; height: auto; + + .mat-mdc-menu-item-text { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + } } } .dst-saved-config-menu { .dst-saved-config-menu-item { - display: flex; - justify-content: space-between; + .mdc-list-item__primary-text { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + } } .dst-saved-config-menu-item-action-buttons { @@ -195,24 +134,6 @@ } } -.cdk-overlay-pane .mat-select-panel .mat-option { - line-height: normal; - height: 100%; - - .mat-option-text { - white-space: normal; - margin: 5px 16px; - } -} - -.imx-data-source-filter-select { - background-color: $asher-gray; -} - -.imx-warning-icon { - color: $corbin-orange; -} - // Theming imx-data-source-toolbar { .themed-style .imx-data-source-toolbar-middle { @@ -220,10 +141,6 @@ imx-data-source-toolbar { border-radius: 5px; } - .mat-form-field-outline-thick { - opacity: 0.2; - } - .eui-search-icon { color: $color-blue-60; } @@ -231,32 +148,6 @@ imx-data-source-toolbar { .imx-data-source-filter-select { background-color: $color-gray-10; } - - .imx-warning-icon { - color: $color-orange-60; - } - - .imx-data-source-selected-filters { - .mat-chip { - .eui-icon.remove { - color: $color-gray-60; - } - - span.tag-name { - color: $color-gray-80; - } - } - } -} - -.mat-menu-content { - .link-button { - color: $color-blue-60; - } - - .dst-saved-config-delete-button { - color: $color-red-60; - } } .eui-dark-theme { @@ -266,10 +157,6 @@ imx-data-source-toolbar { border-radius: 5px; } - .mat-form-field-outline-thick { - opacity: 0.2; - } - .eui-search-icon { color: $color-blue-40; } @@ -277,41 +164,6 @@ imx-data-source-toolbar { .imx-data-source-filter-select { background-color: $color-gray-10; } - - .imx-warning-icon { - color: $color-orange-40; - } - - .imx-data-source-selected-filters { - .mat-chip { - .eui-icon.remove { - opacity: unset; - color: $color-gray-0; - } - span.tag-name { - color: $color-gray-0; - } - &.isRegex { - background-color: $color-blue-40; - - .eui-icon { - &.remove { - color: $color-gray-10; - } - } - } - } - } - } - - .mat-menu-content { - .link-button { - color: $color-blue-40; - } - - .dst-saved-config-delete-button { - color: $color-red-40; - } } } @@ -322,10 +174,6 @@ imx-data-source-toolbar { border-radius: 4px; } - .mat-form-field-outline-thick { - opacity: 0.2; - } - .eui-search-icon { color: $color-blue-40; } @@ -333,44 +181,15 @@ imx-data-source-toolbar { .imx-data-source-filter-select { background-color: $color-gray-10; } - - .imx-warning-icon { - color: $color-orange-40; - } - - .mat-menu-content { - .link-button { - color: $color-blue-40; - } - - .dst-saved-config-delete-button { - color: $color-red-40; - } - } - - .imx-data-source-selected-filters { - .mat-chip { - border: 1px solid $color-gray-0; - - .eui-icon.remove { - opacity: unset; - color: $color-gray-0; - } - span.tag-name { - color: $color-gray-0; - } - } - } } } - @media only screen and (max-width: 768px) { .imx-data-source-toolbar-container { .imx-data-source-toolbar-middle { - .eui-search.mat-form-field.mat-form-field-appearance-outline { + .eui-search.mat-mdc-form-field.mat-form-field-appearance-outline { min-width: unset; } } } -} +} \ No newline at end of file diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar.component.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar.component.ts index bb8c9f943..986ab210d 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar.component.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -42,50 +42,47 @@ import { FormControl, FormGroup, Validators } from '@angular/forms'; import { MatButtonToggleChange } from '@angular/material/button-toggle'; import { MatCheckboxChange } from '@angular/material/checkbox'; import { MatDialog } from '@angular/material/dialog'; +import { MatMenuTrigger } from '@angular/material/menu'; import { MatSelectChange } from '@angular/material/select'; import { MatTableDataSource } from '@angular/material/table'; -import { EuiSelectOption } from '@elemental-ui/core'; -import { Observable, Subject, Subscription } from 'rxjs'; -import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators'; +import { EuiSelectFeedbackMessages, EuiSelectOption, EuiSidesheetService } from '@elemental-ui/core'; import { CollectionLoadParameters, - SqlWizardExpression, - CompareOperator, DataModelFilterOption, EntitySchema, FilterData, + FilterTreeData, FilterType, IClientProperty, - IEntity, + SqlWizardExpression, TypedEntity, TypedEntityCollectionData, ValType, - FilterTreeData, -} from 'imx-qbm-dbts'; -import { v4 as uuid } from 'uuid'; -import { DataSourceToolbarFilter, DataSourceToolbarSelectedFilter } from './data-source-toolbar-filters.interface'; -import { DataSourceToolBarGroup, DataSourceToolBarGroupingCategory } from './data-source-toolbar-groups.interface'; -import { SelectionModelWrapper } from './selection-model-wrapper'; -import { DataSourceItemStatus } from './data-source-item-status.interface'; -import { FilterTreeComponent } from './filter-tree/filter-tree.component'; -import { FilterTreeSelectionArg } from './filter-tree/filter-tree-selection-arg.interface'; -import { ColumnOptions } from './column-options'; -import { EuiSidesheetService } from '@elemental-ui/core'; -import { FilterWizardComponent } from './filter-wizard/filter-wizard.component'; -import { DataExportComponent } from '../data-export/data-export.component'; +} from '@imx-modules/imx-qbm-dbts'; import { TranslateService } from '@ngx-translate/core'; -import { MatMenuTrigger } from '@angular/material/menu'; -import { SaveConfigDialogComponent } from './save-config-dialog/save-config-dialog.component'; +import { Observable, Subject, Subscription } from 'rxjs'; +import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators'; +import { v4 as uuid } from 'uuid'; +import { AppConfigService } from '../appConfig/appConfig.service'; +import { BusyService } from '../base/busy.service'; +import { calculateSidesheetWidth } from '../base/sidesheet-helper'; import { ConfirmationService } from '../confirmation/confirmation.service'; -import { DSTViewConfig } from './data-source-toolbar-view-config.interface'; +import { DataExportComponent } from '../data-export/data-export.component'; import { SnackBarService } from '../snackbar/snack-bar.service'; -import { FilterWizardService, selectedFiltersParams } from './filter-wizard/filter-wizard.service'; -import { BusyService } from '../base/busy.service'; import { SystemInfoService } from '../system-info/system-info.service'; -import { AppConfigService } from '../appConfig/appConfig.service'; -import { FilterTypeIdentifier } from './filter-wizard/filter-wizard.interfaces'; +import { ColumnOptions } from './column-options'; +import { DataSourceItemStatus } from './data-source-item-status.interface'; +import { DataSourceToolbarFilter, DataSourceToolbarSelectedFilter } from './data-source-toolbar-filters.interface'; +import { DataSourceToolBarGroup, DataSourceToolBarGroupingCategory } from './data-source-toolbar-groups.interface'; import { DataSourceToolbarSettings } from './data-source-toolbar-settings'; import { isConfigDefault, isDefaultId } from './data-source-toolbar-view-config-helper'; +import { DSTViewConfig } from './data-source-toolbar-view-config.interface'; +import { FilterTreeSelectionParameter } from './filter-wizard/filter-tree-sidesheet/filter-tree-sidesheet.model'; +import { FilterWizardComponent } from './filter-wizard/filter-wizard.component'; +import { FilterTypeIdentifier, FilterWizardResult } from './filter-wizard/filter-wizard.interfaces'; +import { FilterWizardService, selectedFiltersParams } from './filter-wizard/filter-wizard.service'; +import { SaveConfigDialogComponent } from './save-config-dialog/save-config-dialog.component'; +import { SelectionModelWrapper } from './selection-model-wrapper'; /** * The Datasource toolbar (DST) consist internally of a datasource and a toolbar view, @@ -125,7 +122,7 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy public get additionalColumns(): IClientProperty[] { const additionalColumns = this.columnOptions?.additionalColumns.concat(this.columnOptions.selectedOptionals) ?? []; return additionalColumns.filter(function (column) { - const key = column.ColumnName.toLocaleLowerCase(); + const key = column.ColumnName?.toLocaleLowerCase(); return !this.has(key) && this.add(key); }, new Set()); } @@ -158,14 +155,12 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy public isDescending = false; public ascendingSortControl = new FormControl(this.isDescending); - public selectedSortControl = new FormControl(null, Validators.required); + public selectedSortControl = new FormControl('', { nonNullable: true, validators: Validators.required }); public sortControl = new FormGroup({ selectedSortControl: this.selectedSortControl, ascendingSortControl: this.ascendingSortControl, }); - public sortFeedbackMessages = { - search: this.translate.instant('#LDS#Search'), - }; + public sortFeedbackMessages: EuiSelectFeedbackMessages; public sortOptions: EuiSelectOption[] = []; public sortOptionsFilter = (option: EuiSelectOption, searchInputValue: string) => option.display.toLowerCase().includes(searchInputValue.toLowerCase()); @@ -187,15 +182,22 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy } public get isSortApplied(): boolean { - return this.hasSortFunction && this.settings?.navigationState?.OrderBy && this.settings.navigationState.OrderBy.length > 0; + return this.hasSortFunction && this.settings?.navigationState?.OrderBy != null && this.settings.navigationState.OrderBy.length > 0; } public get isSortDesc(): boolean { - return this.hasSortFunction && this.settings?.navigationState?.OrderBy && this.settings.navigationState.OrderBy.includes(this.descArg); + return ( + this.hasSortFunction && + this.settings?.navigationState?.OrderBy != null && + this.settings.navigationState.OrderBy.toLocaleLowerCase().includes(this.descArg) + ); } + /** + * @ignore Used internally to set the filter column name currently being used + */ public get currentSortColumn(): string { - return this.selectedSortControl.value?.Display ?? this.selectedSortControl.value?.ColumnName; + return this.sortOptions?.find((option) => option.value == this.settings.navigationState?.OrderBy?.split(' ')[0])?.display ?? ''; } /** @@ -293,7 +295,7 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy /** * an optional search api to handle search cancelling when necessary, results will be wihin the observable searchResults$ */ - @Input() searchApi?: () => Observable; + @Input() searchApi?: (keywords?: string) => Observable | undefined; /** * If 'true', it gives the div.imx-chip-container a min-height: 15px, otherwise this component's styling stays the same @@ -314,7 +316,7 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy /** * Emits a collection of typed entities when the datasource changes. */ - @Output() public dataSourceChanged = new EventEmitter>(); + @Output() public dataSourceChanged = new EventEmitter | undefined | null>(); /** * Occurs when user presses next/previous page button or changes the page size. @@ -379,13 +381,13 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy /** * the columnOptions used by the toolbar */ - public columnOptions: ColumnOptions; + public columnOptions: ColumnOptions | undefined; public get additionalListElements(): IClientProperty[] { return this.columnOptions?.additionalListElements ?? []; } - public get optionalColumns(): IClientProperty[] { + public get optionalColumns(): (IClientProperty | undefined)[] { return this.columnOptions?.optionalColumns ?? []; } @@ -411,7 +413,7 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy * Checks if we have any saved configs at all in settings.viewConfig.viewConfigs */ public get hasSavedConfigs(): boolean { - return this.settings?.viewConfig?.viewConfigs && this.settings.viewConfig.viewConfigs.length > 0; + return !!this.settings?.viewConfig?.viewConfigs?.length; } /** @@ -469,10 +471,7 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy // Handle sort if (config?.OrderBy) { - this.isDescending = config.OrderBy.endsWith(this.descArg); - this.ascendingSortControl.reset(this.isDescending); - const columnName = config.OrderBy.slice().replace(this.descArg, ''); - this.findAndSelectSortColumn(columnName); + this.applyOrderBy(config.OrderBy); } // Handle search terms from Filter @@ -500,14 +499,25 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy this.updateNavigateStateWithFilters(); } + /** + * Applys the sortby column along with its direction + * @param config the DSTViewConfig + */ + public applyOrderBy(orderBy: string): void { + // look for a space - if so then we have a direction + const [columnName, dir] = orderBy.split(' '); + this.findAndSelectSortColumn(columnName); + this.isDescending = dir?.trim()?.toLocaleLowerCase() == this.descArg.trim(); + this.ascendingSortControl.reset(this.isDescending); + this.ascendingSortControl.markAsPristine(); + } + /** * Finds and applies the group from the config via onGroupSelected * @param config the DSTViewConfig */ public applyGroupBy(config: DSTViewConfig): void { - const group = this.settings?.groupData?.groups?.find( - (group) => this.getGroupColumnDisplay(group) === config.GroupBy || group.property.Property.ColumnName === config.GroupBy - ); + const group = this.settings?.groupData?.groups?.find((group) => this.getGroupColumnDisplay(group) === config.GroupBy); if (group) { this.onGroupSelected(group); } @@ -519,6 +529,9 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy */ public applyDynamicPropsAsSelectedFilters(config: DSTViewConfig): void { // Handle filters from dynamic properties + if (!config.AdditionalParameters) { + return; + } Object.entries(config.AdditionalParameters).forEach(([filterName, value]) => { const filter = this.getSelectedFilterFromName(filterName, value); if (filter) { @@ -537,13 +550,16 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy const filter = this.settings.filters?.find((filter) => filter.Name === filterName); if (filter) { filter.CurrentValue = value; - return filter.Delimiter - ? filter.CurrentValue.split(filter.Delimiter).map((elem) => ({ - filter, - selectedOption: this.findFilterOptionFromValue(elem, filter), - })) - : [{ filter, selectedOption: this.findFilterOptionFromValue(value, filter) }]; + if (filter.Delimiter) { + return filter.CurrentValue.split(filter.Delimiter).map((elem) => ({ + filter, + selectedOption: this.findFilterOptionFromValue(elem, filter), + })); + } + + return [{ filter, selectedOption: this.findFilterOptionFromValue(value, filter) }]; } + return []; } /** @@ -554,34 +570,34 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy if (!displayName) { return; } - const existingConfig = this.settings.viewConfig.viewConfigs.find((config) => config.DisplayName === displayName); + const existingConfig = this.settings?.viewConfig?.viewConfigs?.find((config) => config.DisplayName === displayName); if ( existingConfig && !(await this.confirm.confirmDelete( '#LDS#Heading Overwrite View', - '#LDS#A view with the entered name already exists. Do you want to overwrite the already existing view with the new view?' + '#LDS#A view with the entered name already exists. Do you want to overwrite the already existing view with the new view?', )) ) { return; } const config: DSTViewConfig = { Id: existingConfig?.Id, - ViewId: this.settings.viewConfig.viewId, + ViewId: this.settings?.viewConfig?.viewId, DisplayName: displayName, Filter: this.settings?.navigationState?.filter, GroupBy: this.settings?.groupData?.currentGrouping?.display, OrderBy: this.settings?.navigationState?.OrderBy, - AdditionalListColumns: this.columnOptions?.additionalListElements?.map((ele) => ele.ColumnName), + AdditionalListColumns: this.columnOptions?.additionalListElements?.map((ele) => ele.ColumnName ?? '') ?? [], AdditionalTableColumns: this.columnOptions?.selectedOptionals - ?.map((column) => column.ColumnName) - .concat(this.columnOptions?.additionalColumns?.map((column) => column.ColumnName)), + ?.map((column) => column.ColumnName ?? '') + .concat(this.columnOptions?.additionalColumns?.map((column) => column.ColumnName ?? '')), UseAsDefault: false, }; if (this.filtersCurrentlyApplied) { config.AdditionalParameters = {}; this.settings?.filters?.forEach((filter) => { - if (filter.CurrentValue) { - config.AdditionalParameters[filter.Name] = filter.CurrentValue; + if (filter.CurrentValue && config.AdditionalParameters) { + config.AdditionalParameters[filter.Name ?? ''] = filter.CurrentValue; } }); } @@ -618,10 +634,10 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy if (!(await this.confirm.confirmDelete('#LDS#Heading Delete View', '#LDS#Are you sure you want to delete the view?'))) { return; } - if (this.settings.viewConfig.viewConfigs.length === 1) { + if (this.settings?.viewConfig?.viewConfigs?.length === 1) { this.savedConfigsTrigger.closeMenu(); } - this.settings.viewConfig.viewConfigs.splice(index, 1); + this.settings?.viewConfig?.viewConfigs?.splice(index, 1); this.deleteConfigById.emit(id); } @@ -629,7 +645,9 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy * Used internally to manage the current search term * This will be triggered by a value change listner that fires just after a user stops typing */ - public searchControl = new FormControl(''); + public searchControl: FormControl = new FormControl('', { + nonNullable: true, + }); /** * Internal subscription used to subscribe to and watch for changes on the `searchControl` @@ -678,11 +696,6 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy */ public isInitialLoad = true; - /** - * An indicator used to determine, if there are any filter tree informations available - */ - public hasFilterTree = false; - /** * An indicator used to determine, if viewSettings could be applied */ @@ -702,7 +715,12 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy /** * The currently selected filter data */ - public currentFilterData: FilterTreeSelectionArg[] = []; + public currentFilterData: FilterTreeSelectionParameter | undefined; + + /** + * The column, that is used for the filter tree. + */ + private columnForTree: string; /** * The filter tree data, that is provided by the settings filter method. @@ -712,7 +730,12 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy /** * Filter wizard SQL expressions */ - public filterWizardExpression: SqlWizardExpression; + public filterWizardExpression: SqlWizardExpression | undefined; + + /** + * An indicator used to determine, if there are any filter tree informations available + */ + private hasFilterTree = false; /** * @ignore Used internally. @@ -773,7 +796,7 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy private readonly config: AppConfigService, private readonly snackbar: SnackBarService, private readonly filterService: FilterWizardService, - private readonly systemInfoService: SystemInfoService + private readonly systemInfoService: SystemInfoService, ) { if (!this.id) this.id = uuid(); @@ -782,21 +805,33 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy if (!this.isUpdatingPreselection) { this.selectionChanged.next(event); } - }) + }), ); this.subscriptions.push( this.filterService.navigationStateChanged.subscribe((event: selectedFiltersParams) => { if (event.id !== this.id) return; this.selectedFilters = event.selectedFilters; - }) + }), ); this.subscriptions.push( this.filterService.filterTabChangedEvent.subscribe((filterType: FilterTypeIdentifier) => { this.selectedFilterType = filterType as FilterTypeIdentifier; - }) + }), ); + this.sortFeedbackMessages = { + search: this.translate.instant('#LDS#Search'), + selected: '', + clear: '', + plusOther: '', + plusOtherPlural: '', + unsupportedCharacter: '', + noResults: '', + clearAll: '', + ok: '', + keyboardOptionsListAria: '', + }; } /** @@ -812,19 +847,19 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy } public get dataSourceHasData(): boolean { - return this.settings?.dataSource?.totalCount > 0; + return (this.settings?.dataSource?.totalCount ?? 0) > 0; } public get dataSourceIsLimitReached(): boolean { - return this.settings?.dataSource?.IsLimitReached; + return this.settings?.dataSource?.IsLimitReached ?? false; } public get searchCurrenltyApplied(): boolean { - return this.settings?.navigationState?.search?.length > 0 || this.searchTerms.length > 0; + return !!this.settings?.navigationState?.search?.length || this.searchTerms.length > 0; } public get filtersCurrentlyApplied(): boolean { - return this.selectedFilters?.length > 0 || this.currentFilterData?.length > 0; + return this.selectedFilters?.length > 0 || this.currentFilterData != null; } /** @@ -864,10 +899,10 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy setTimeout(() => this.navigateLocalDataSource(this.settings)); } + // We only want to do this if this is the initial load... const defaultSavedConfig = this.settings?.viewConfig?.viewConfigs?.find( - (config) => this.isConfigDefault(config) && !this.isDefaultId(config) + (config) => this.isConfigDefault(config) && !this.isDefaultId(config), ); - if (this.isInitialLoad) { await this.setInitialSortOptions(); defaultSavedConfig ? this.applyConfig(defaultSavedConfig) : this.setInitialFilterValues(); @@ -877,18 +912,19 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy this.filterTreeItems = this.settings?.filterTree?.filterMethode ? await this.settings.filterTree?.filterMethode('') : { Elements: [] }; - this.hasFilterTree = this.settings.filterTree && this.filterTreeItems?.Elements?.length > 0; + this.hasFilterTree = this.settings.filterTree != null && !!this.filterTreeItems?.Elements?.length; if (this.settings?.dataModel) { this.initColumnOptions(defaultSavedConfig); - this.hasViewSettings = this.columnOptions?.hasOptionalColumns; + this.hasViewSettings = this.columnOptions?.hasOptionalColumns ?? false; this.updateEntitySchema(); this.settings.dataSource?.Data?.forEach((elem) => elem.GetEntity().ApplySchema(this.settings.entitySchema)); } else { this.hasViewSettings = false; } - this.filterType = this.filterTreeItems?.Description; + this.filterType = this.filterTreeItems?.Description ?? ''; + this.columnForTree = !!this.filterTreeItems?.Elements?.length ? this.filterTreeItems?.Elements?.[0]?.Filter?.ColumnName ?? '' : ''; this.internalDataSource = new MatTableDataSource(this.settings.dataSource?.Data); if (this.isDataSourceLocal && (this.searchCurrenltyApplied || this.filtersCurrentlyApplied)) { // We need to apply a filter still over the local data since it was skipped earlier. Do so now. @@ -904,6 +940,7 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy this.isUpdatingPreselection = true; setTimeout(() => { + this.selection.clear(); this.preSelection.forEach((item) => this.selection.checked(item)); this.isUpdatingPreselection = false; }); @@ -914,12 +951,12 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy this.searchResults$ = this.searchControl.valueChanges.pipe( distinctUntilChanged(), debounceTime(300), - switchMap(() => this.searchApi()) + switchMap((value) => (this.searchApi ? this.searchApi(value) : Observable.create(undefined))), ); } if (changes['disableSearch']) { - this.searchControl = new FormControl({ value: '', disabled: this.disableSearch }); + this.searchControl = new FormControl({ value: '', disabled: this.disableSearch }, { nonNullable: true }); } } @@ -1040,12 +1077,14 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy * Is called internally when a filter is removed from selected filters. * Updates and emits the new navigationState to include any filter query params. */ - public onSelectedFilterRemoved(selectedFilter: DataSourceToolbarSelectedFilter, optionValue: string): void { - let settingsFilter = this.settings.filters?.filter((f) => f.Name === selectedFilter.filter.Name)[0]; - if (selectedFilter && selectedFilter.isCustom) { + public onSelectedFilterRemoved(selectedFilter: DataSourceToolbarSelectedFilter, optionValue: string | undefined): void { + let settingsFilter = this.settings.filters?.filter((f) => f.Name === selectedFilter?.filter?.Name)[0]; + if (selectedFilter?.isCustom ?? false) { settingsFilter = selectedFilter.filter; } - this.removeSelectedFilter(settingsFilter, true, optionValue, selectedFilter); + if (settingsFilter) { + this.removeSelectedFilter(settingsFilter, true, optionValue, selectedFilter); + } } /** @@ -1057,7 +1096,7 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy let selectedFilterData: DataSourceToolbarSelectedFilter; filter.CurrentValue = option ? option.Value : undefined; selectedFilterData = { selectedOption: option, filter }; - const index = this.findSelectedFilterIndex(filter.Name); + const index = this.findSelectedFilterIndex(filter.Name ?? ''); if (index >= 0) { this.selectedFilters[index] = selectedFilterData; } else { @@ -1073,7 +1112,9 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy */ public selectFilterValueChanged(filter: DataSourceToolbarFilter, event: MatSelectChange): void { const option = this.findFilterOptionFromValue(event.value, filter); - this.onRadioFilterChanged(filter, option); + if (option) { + this.onRadioFilterChanged(filter, option); + } } /** @@ -1084,13 +1125,13 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy */ public multiSelectFilterValueChange(filter: DataSourceToolbarFilter, event: MatSelectChange): void { filter.CurrentValue = undefined; - const relevantSelectedItems = this.selectedFilters.filter((sfilter) => sfilter.filter.Name === filter.Name); + const relevantSelectedItems = this.selectedFilters.filter((sfilter) => sfilter.filter?.Name === filter.Name); relevantSelectedItems.forEach((rsi) => { - this.removeSelectedFilter(filter, false, rsi.selectedOption.Value); + this.removeSelectedFilter(filter, false, rsi.selectedOption?.Value); }); event.value.forEach((value) => { const option = this.findFilterOptionFromValue(value, filter); - this.selectedFilters.push({ selectedOption: option, filter }); + if (option) this.selectedFilters.push({ selectedOption: option, filter }); }); this.rebuildSelectedDelimitedValue(filter); this.updateNavigateStateWithFilters(); @@ -1102,9 +1143,9 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy * for the multi select list */ public getMultiSelectCurrentValue(filter: DataSourceToolbarFilter): string[] { - let display = []; + let display: string[] = []; if (filter.Delimiter && filter.CurrentValue) { - display = filter.CurrentValue.split(filter.Delimiter); + display = filter.CurrentValue?.split(filter.Delimiter) ?? []; } return display; } @@ -1115,7 +1156,10 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy */ public toggleSort(): void { this.ascendingSortControl.setValue(this.isDescending); - if (!this.settings.navigationState.OrderBy || this.settings.navigationState.OrderBy.includes(this.descArg) !== this.isDescending) { + if ( + this.settings.navigationState.OrderBy == null || + this.settings.navigationState.OrderBy?.toLocaleLowerCase()?.includes(this.descArg) !== this.isDescending + ) { this.ascendingSortControl.markAsDirty(); return; } @@ -1127,7 +1171,11 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy * @param columnName - IClientProperty.ColumnName */ public findAndSelectSortColumn(columnName: string): void { - const selected = this.sortOptions.find((option) => option.value.ColumnName === columnName); + const selected = this.sortOptions.find((option) => option.value === columnName); + if (!selected) { + // The column isn't in the data model, nothing we can do :( + return; + } this.selectSort(selected); } @@ -1137,8 +1185,11 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy */ public selectSort(option: EuiSelectOption): void { this.selectedSortControl.setValue(option.value); - const column = (option.value as IClientProperty).ColumnName; - if (!this.settings.navigationState.OrderBy || !this.settings.navigationState.OrderBy.includes(column)) { + if ( + this.settings.navigationState.OrderBy == null || + option.value == null || + !(this.settings.navigationState.OrderBy?.includes(option.value) ?? false) + ) { this.selectedSortControl.markAsDirty(); return; } @@ -1151,7 +1202,7 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy */ public clearSort(emit = true): void { this.isDescending = false; - this.selectedSortControl.reset(null); + this.selectedSortControl.reset(''); this.sortControl.markAsPristine(); // If the nav state has sort values, we will clear those out and emit if (this.settings.navigationState.OrderBy && emit) { @@ -1165,12 +1216,12 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy * Pops a warning for large data, otherwise applies the sort to the nav state and emits a navigationStateChanged signal */ public async applySort(toggleAndApply?: boolean): Promise { - const isBigData = this.settings.dataSource.totalCount > this.sortWarningThreshold; + const isBigData = (this.settings.dataSource?.totalCount ?? 0) > this.sortWarningThreshold; if ( isBigData && !(await this.confirm.confirmLeaveWithUnsavedChanges( '#LDS#Heading Sort Data', - '#LDS#Sorting the data may take some time. Are you sure you want to sort the data?' + '#LDS#Sorting the data may take some time. Are you sure you want to sort the data?', )) ) { // Exit early - user doesn't want to continue. @@ -1183,7 +1234,8 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy } // Add sort and ascending to nav state - this.settings.navigationState.OrderBy = this.selectedSortControl.value.ColumnName; + this.settings.navigationState.OrderBy = this.selectedSortControl?.value ?? ''; + if (this.isDescending) { this.settings.navigationState.OrderBy += this.descArg; } @@ -1217,13 +1269,16 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy * unless emitChange is false */ public removeSelectedFilter( - filter: DataSourceToolbarFilter, + filter: DataSourceToolbarFilter | undefined, emitChange: boolean = true, optionValue?: string, - selectedFilter?: DataSourceToolbarSelectedFilter + selectedFilter?: DataSourceToolbarSelectedFilter, ): void { + if (!filter) { + return; + } filter.CurrentValue = undefined; - const index = this.findSelectedFilterIndex(filter.Name, optionValue); + const index = this.findSelectedFilterIndex(filter.Name ?? '', optionValue); if (index >= 0) { this.selectedFilters.splice(index, 1); @@ -1247,35 +1302,6 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy } } - /** - * @ignore Used internally. - * Shows a filter tree dialog and updates the filter set - */ - public async showFilterTree(): Promise { - const filterdata = await this.dialog - .open(FilterTreeComponent, { - width: 'min(600px,60%)', - autoFocus: false, - height: 'min(600px,60%)', - data: { - filterTreeParameter: this.settings.filterTree, - preselection: this.currentFilterData.map((elem) => elem), - type: this.filterType, - }, - panelClass: 'imx-toolbar-dialog', - }) - .afterClosed() - .toPromise(); - if (filterdata) { - //Get all filter, that were not associaed with the tree filter - const otherFilter = (this.settings.navigationState.filter ?? []).filter( - (elem) => elem.ColumnName !== filterdata[0].filter.ColumnName && elem.ColumnName !== this.currentFilterData[0].filter.ColumnName - ); - this.currentFilterData = filterdata; - this.filterTreeSelectionChanged.emit(this.currentFilterData.map((filter) => filter.filter).concat(otherFilter)); // combine the two filter again - } - } - /** * @ignore Used internally. * Shows a dialog for adding/removing additional informations @@ -1292,8 +1318,6 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy if (await this.resetView()) { this.clearTreeFilter(); } - - this.applyConfig(this.settings?.viewConfig?.viewConfigs?.find((config) => this.isDefaultId(config))); } /** @@ -1305,7 +1329,7 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy emit && !(await this.confirm.confirmDelete( '#LDS#Heading Reset View', - '#LDS#If you reset the view, the search, sorting, filters and additional columns will be reset. Are you sure you want to reset the view?' + '#LDS#If you reset the view, the search, sorting, filters and additional columns will be reset. Are you sure you want to reset the view?', )) ) { return false; @@ -1313,23 +1337,23 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy this.columnOptions?.resetView(); this.searchTerms = []; - if (this.settings.navigationState?.search) { - this.searchControl.reset(null); + if (this.settings.navigationState.search) { + this.searchControl.reset(''); delete this.settings.navigationState.search; } - if (this.settings.navigationState?.OrderBy) { + if (this.settings.navigationState.OrderBy) { delete this.settings.navigationState.OrderBy; this.clearSort(false); } if (this.settings?.groupData?.currentGrouping) { this.clearGroupedBy(false); } - delete this.settings.navigationState?.filter; + delete this.settings.navigationState.filter; if (this.filtersCurrentlyApplied) { this.clearFilters(false); } - this.filterWizardExpression = null; + this.filterWizardExpression = undefined; if (emit) { this.navigationStateChanged.emit(this.settings.navigationState); } @@ -1340,11 +1364,11 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy * clears the tree filter and emits the filterTreeSelectionChanged event */ public clearTreeFilter(emit = true): void { - const currentTree: FilterData = this.currentFilterData[0]?.filter; - this.currentFilterData = []; + const currentTree: FilterData | undefined = this.currentFilterData?.filter; + this.currentFilterData = undefined; if (emit) { this.filterTreeSelectionChanged.emit( - this.settings.navigationState.filter?.filter((elem) => elem.ColumnName != currentTree.ColumnName) + this.settings.navigationState.filter?.filter((elem) => elem.ColumnName != currentTree?.ColumnName), ); } } @@ -1355,17 +1379,17 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy * * If isDataSourceLocal is true then a 'settingsChanged signal is emitted isntead of a search signal. */ - public onSearch(keywords: string): void { + public onSearch(keywords: string | null | undefined): void { if (this.settings && this.settings.navigationState) { this.settings.navigationState.StartIndex = 0; - this.settings.navigationState.search = keywords; + this.settings.navigationState.search = keywords ?? ''; } if (this.isDataSourceLocal) { // Do search locally - this.localFilterState.keywords = keywords; + this.localFilterState.keywords = keywords ?? ''; this.localFilter(); } else { - this.search.emit(keywords); + this.search.emit(keywords ?? ''); } } @@ -1374,6 +1398,9 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy * Will be called when a group by option is selected */ public onGroupSelected(group: DataSourceToolBarGroup, groupCategory?: DataSourceToolBarGroupingCategory): void { + if (!this.settings.groupData) { + return; + } this.settings.groupData.currentGrouping = { display: (groupCategory?.property.Display ? groupCategory.property.Display + ' - ' : '') + this.getGroupColumnDisplay(group), getData: group.getData, @@ -1388,6 +1415,9 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy * Removes any grouping currently applied and emits the settings changed event */ public clearGroupedBy(emit = true): void { + if (!this.settings.groupData) { + return; + } this.settings.groupData.currentGrouping = undefined; if (emit) { this.settingsChanged.emit(this.settings); @@ -1399,9 +1429,18 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy * Used to convert the groupBy column api value into a display friendly format */ public getGroupColumnDisplay(group: DataSourceToolBarGroup): string { - return ( - group.property.Display ?? this.entitySchema.Columns[group.property.Property.ColumnName]?.Display ?? group.property.Property.Display - ); + if (group.property.Display) { + return group.property.Display; + } + + if (group.property.Property?.ColumnName) { + return this.entitySchema.Columns[group.property.Property?.ColumnName]?.Display ?? ''; + } + + if (group.property.Property?.Display) { + return group.property.Property?.Display; + } + return group.property.Property?.ColumnName ?? ''; } public canShowFilterWizard(): boolean { @@ -1410,7 +1449,8 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy this.filterService.isSqlWizardImplemented && !this.isDataSourceLocal && !this.disableFilterWizard) || - this.settings?.filters?.length > 0; + !!this.settings?.filters?.length || + this.hasFilterTree; return result; } @@ -1418,7 +1458,7 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy const sidesheetRef = this.sidesheet.open(FilterWizardComponent, { title: await this.translate.get('#LDS#Heading Filter Data').toPromise(), icon: 'filter', - width: '800px', + width: calculateSidesheetWidth(800, 0.5), padding: '0px', testId: 'filter-wizard-sidesheet', disableClose: true, @@ -1427,28 +1467,51 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy settings: this.settings, filterExpression: this.filterWizardExpression, selectedFilters: this.selectedFilters, + filterTreeParameter: { + filterTreeParameter: this.settings.filterTree, + preSelection: this.currentFilterData, + type: this.filterType, + }, isDataSourceLocal: this.isDataSourceLocal, }, }); - sidesheetRef.afterClosed().subscribe((result: SqlWizardExpression) => { - if (!result && !this.filterWizardExpression) { - return; - } - if (result?.Expression?.Expressions.length === 0) { - this.removeFilterWizard(); + sidesheetRef.afterClosed().subscribe((result: FilterWizardResult) => { + if (!result) { return; } - this.settings.navigationState.filter = this.settings.navigationState.filter?.filter((x) => x.Type != FilterType.Expression); - if (result) { - this.filterWizardExpression = result; + this.clearTreeFilter(false); + this.removeFilterWizard(false); + this.currentFilterData = result?.treeFilter; + + const hasNoFilters = (result?.expression?.Expression?.Expressions?.length || 0) === 0; + const hasNewTree = !!result?.treeFilter && this.currentFilterData !== result?.treeFilter; + + if (!hasNoFilters) { + this.filterWizardExpression = result.expression; } - this.settings.navigationState.filter - ? this.settings.navigationState.filter.push({ Type: FilterType.Expression, Expression: this.filterWizardExpression.Expression }) - : (this.settings.navigationState.filter = [{ Type: FilterType.Expression, Expression: this.filterWizardExpression.Expression }]); - if (result?.Expression?.Expressions.length > 0) { + if (hasNewTree && hasNoFilters) { + // has new tree and no other filters, only emit tree state + const otherFilter = (this.settings.navigationState.filter ?? []).filter( + (elem) => elem.ColumnName !== result?.treeFilter?.filter?.ColumnName, + ); + this.filterTreeSelectionChanged.emit( + this.currentFilterData?.filter ? [this.currentFilterData.filter].concat(otherFilter) : otherFilter, + ); + } else { + // tree or filters, do both but only emit nav state + this.settings.navigationState.filter = this.settings.navigationState.filter?.filter((x) => x.Type != FilterType.Expression) ?? []; + if (this.filterWizardExpression?.Expression) { + this.settings.navigationState.filter.push({ + Type: FilterType.Expression, + Expression: this.filterWizardExpression?.Expression, + }); + } + if (result.treeFilter?.filter) { + this.settings.navigationState.filter.push(result.treeFilter.filter); + } this.updateNavigateStateWithFilters(); } }); @@ -1456,7 +1519,7 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy public removeFilterWizard(reload: boolean = true): void { this.settings.navigationState.filter = this.settings.navigationState.filter?.filter((x) => x.Expression == null); - this.filterWizardExpression = null; + this.filterWizardExpression = undefined; if (reload) { this.navigationStateChanged.next(this.settings.navigationState); } @@ -1481,7 +1544,7 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy this.columnSubscriptions.push( this.columnOptions.shownColumnsSelectionChanged.subscribe((elem) => { this.shownColumnsSelectionChanged.emit(elem.properties); - const optionals = this.columnOptions.getPropertiesForNavigation(); + const optionals = this.columnOptions?.getPropertiesForNavigation() ?? []; this.additionalPropertiesForNavigation.forEach((prop) => { if (!optionals.includes(prop)) { optionals.push(prop); @@ -1494,17 +1557,20 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy this.navigationStateChanged.emit(this.settings.navigationState); } } - }) + }), ); this.columnSubscriptions.push( - this.columnOptions.additionalListElementsChanged.subscribe((elem) => this.additionalListElementsChanged.emit(elem)) + this.columnOptions.additionalListElementsChanged.subscribe((elem) => this.additionalListElementsChanged.emit(elem)), ); this.columnOptions.initColumnsAndAdditionalInformation(); } private updateEntitySchema(): void { - const newSchema = this.columnOptions.updateEntitySchema(); + const newSchema = this.columnOptions?.updateEntitySchema(); + if (!newSchema) { + return; + } this.settings.entitySchema = newSchema; this.entitySchema = newSchema; if (this.isDataSourceLocal) { @@ -1521,7 +1587,7 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy } else { // Grab string-based columns to filter over const columns = this.entitySchema.Columns; - const stringColumns = []; + const stringColumns: string[] = []; for (const [key, value] of Object.entries(columns)) { if (value.Type === ValType.String) { stringColumns.push(key); @@ -1550,8 +1616,8 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy private rebuildSelectedDelimitedValue(filter: DataSourceToolbarFilter): void { let val = ''; this.selectedFilters.forEach((sfilter) => { - if (sfilter.filter.Name === filter.Name) { - val += `${sfilter.selectedOption.Value}${filter.Delimiter}`; + if (sfilter.filter?.Name === filter.Name) { + val += `${sfilter.selectedOption?.Value}${filter.Delimiter}`; } }); filter.CurrentValue = val.length ? val.slice(0, -1) : undefined; @@ -1573,19 +1639,20 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy */ private navigateLocalDataSource(settings: DataSourceToolbarSettings): void { const tmpDataSource = this.settings.dataSource; - if (this.internalDataSource.filter.length > 0) { - // This data has a search filter, so apply the pagination over the filtered data - tmpDataSource.Data = this.internalDataSource.filteredData.slice( - settings.navigationState.StartIndex, - settings.navigationState.StartIndex + settings.navigationState.PageSize - ); - } else { - tmpDataSource.Data = this.localDataSource.Data.slice( - settings.navigationState.StartIndex, - settings.navigationState.StartIndex + settings.navigationState.PageSize - ); + if (tmpDataSource) { + if (this.internalDataSource.filter.length > 0) { + // This data has a search filter, so apply the pagination over the filtered data + tmpDataSource.Data = this.internalDataSource.filteredData.slice( + settings.navigationState.StartIndex, + (settings.navigationState.StartIndex ?? 0) + (settings.navigationState.PageSize ?? 0), + ); + } else { + tmpDataSource.Data = this.localDataSource.Data.slice( + settings.navigationState.StartIndex, + (settings.navigationState.StartIndex ?? 0) + (settings.navigationState.PageSize ?? 0), + ); + } } - this.settings = { ...{ dataSource: tmpDataSource, @@ -1603,16 +1670,10 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy * @param config the configuration, that should be loaded. */ private addTreeFilterFromConfig(config: DSTViewConfig): void { - const treeIndex = config?.Filter?.findIndex( - (elem) => - elem.Type === FilterType.Compare && // filter is a compare filter - !this.settings.staticFilterColumns?.some((column) => column.toLocaleUpperCase() === elem.ColumnName.toLocaleUpperCase()) - ); // filter is not static - - if (treeIndex > -1) { - const tree = config.Filter[treeIndex]; - const display = config.FilterDescriptions[treeIndex].LongDisplay ?? tree.Value1; - this.currentFilterData = [{ display, filter: tree }]; + const tree = config?.Filter?.find((elem) => elem.ColumnName === this.columnForTree); + if (tree != null) { + const display = this.filterTreeItems?.Elements?.find((elem) => elem.Filter?.Value1 === tree.Value1)?.Display ?? ''; + this.currentFilterData = { display, filter: tree }; if (this.settings.navigationState.filter) { this.settings.navigationState.filter.push(tree); } else { @@ -1643,6 +1704,8 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy * @ignore Used internally * Sets any initial values for the supplied filters and makes a call to update the navigation state * + * Also marks the 'isInitialLoad' property to false to ensure this only happens on the initial load + * of the component */ private setInitialFilterValues(): void { // Here we prefer the saved default config over emiting the init @@ -1656,8 +1719,10 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy } } }); - - this.applyConfig(this.settings?.viewConfig?.viewConfigs?.find((config) => this.isDefaultId(config))); + // We only need to update the state if there were filters applied + if (this.selectedFilters.length > 0) { + this.updateNavigateStateWithFilters(); + } } private async setInitialSortOptions(): Promise { @@ -1665,30 +1730,15 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy return; } this.sortWarningThreshold = (await this.systemInfoService.getImxConfig()).ThresholdSlowSortingWarning; - this.sortOptions = this.settings.dataModel.Properties.filter((property) => property.IsSortable) + this.sortOptions = (this.settings?.dataModel?.Properties?.filter((property) => property.IsSortable) ?? []) .map((property) => { return { - display: property.Property?.Display ?? property.Property.ColumnName, - displayDetail: property.Property?.Description, - value: property.Property, + display: property.Property?.Display ?? property.Property?.ColumnName ?? '', + displayDetail: property.Property?.Description ?? '', + value: property.Property?.ColumnName ?? '', }; }) .sort((a, b) => (a?.display < b?.display ? -1 : 1)); - - if (this.isSortApplied) { - const [column, dir] = this.settings.navigationState.OrderBy.split(' '); - const option = this.sortOptions.find((opt) => opt.value.ColumnName == column); - if (!option) { - // The initial column isn't in the data model, nothing we can do :( - return; - } - this.selectedSortControl.reset(option.value); - if (dir) { - // There was a dir option, we will make sure to keep consistent here - this.isDescending = dir.trim().toLocaleLowerCase() === this.descArg.trim(); - this.ascendingSortControl.reset(this.isDescending); - } - } } /** @@ -1733,7 +1783,7 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy this.searchResults$ = this.searchControl.valueChanges.pipe( distinctUntilChanged(), debounceTime(300), - switchMap(() => this.searchApi()) + switchMap((value) => (this.searchApi ? this.searchApi(value) : Observable.create(undefined))), ); } @@ -1742,57 +1792,60 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy } } - public isRedundant(value: string): boolean { + public isRedundant(value: string | null | undefined): boolean { // Prevent redundant api call from adding search to searchterm list return ( - !value && - this.searchTerms.length && - this.settings.navigationState?.search === this.searchTerms[this.searchTerms.length - 1].selectedOption.Value + (!value && + this.searchTerms.length > 0 && + this.settings.navigationState?.search === this.searchTerms[this.searchTerms.length - 1].selectedOption?.Value) ?? + false ); } public addSearchFilter(filter?: FilterData): void { - if ( - this.isEnterDisabled || - filter?.Type !== FilterType.Search || - filter?.Expression != null || - (!filter && (!this.searchControl.value || (!!this.searchControl.value && this.searchControl.value?.length === 0))) - ) { - // Here we return early if there is nothing to search over - return; - } - - const searchFilter = filter ?? { - Type: FilterType.Search, - Value1: this.searchControl.value.slice(), - IsRegex: this.isRegex, - }; - this.searchControl.reset(null); + setTimeout(() => { + if ( + this.isEnterDisabled || + filter?.ColumnName === this.columnForTree || + filter?.Expression != null || + (!filter && (!this.searchControl.value || (!!this.searchControl.value && this.searchControl.value?.length === 0))) + ) { + // Here we return early if there is nothing to search over + return; + } - this.searchTerms.push({ - selectedOption: { - Display: searchFilter.Value1, - Value: searchFilter.Value1, + const searchFilter = filter ?? { + Type: FilterType.Search, + Value1: this.searchControl?.value?.slice(), IsRegex: this.isRegex, - }, - filter: { - CurrentValue: searchFilter.Value1, - }, - }); - if (this.isDataSourceLocal) { - this.localFilterState.keywords = ''; - return; - } + }; + this.searchControl.reset(''); + + this.searchTerms.push({ + selectedOption: { + Display: searchFilter.Value1, + Value: searchFilter.Value1, + IsRegex: this.isRegex, + }, + filter: { + CurrentValue: searchFilter.Value1, + }, + }); + if (this.isDataSourceLocal) { + this.localFilterState.keywords = ''; + return; + } - if (this.settings.navigationState.filter) { - this.settings.navigationState.filter.push(searchFilter); - } else { - this.settings.navigationState.filter = [searchFilter]; - } + if (this.settings.navigationState.filter) { + this.settings.navigationState.filter.push(searchFilter); + } else { + this.settings.navigationState.filter = [searchFilter]; + } + }); } public removeSearchTerm(searchTerm: DataSourceToolbarSelectedFilter): void { - let index = this.searchTerms.findIndex((st) => st.selectedOption.Display === searchTerm.selectedOption.Display); + let index = this.searchTerms.findIndex((st) => st.selectedOption?.Display === searchTerm.selectedOption?.Display); this.searchTerms.splice(index, 1); if (this.isDataSourceLocal) { this.localFilter(); @@ -1800,7 +1853,7 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy } if (!!this.settings.navigationState.filter && this.searchTerms.length - 1 !== this.settings.navigationState.filter.length) { // Here we have to check over the filters since the lengths didn't match, we subtract 1 to account for the splicing - index = this.settings.navigationState.filter.findIndex((filter) => filter.Value1 === searchTerm.selectedOption.Display); + index = this.settings.navigationState.filter.findIndex((filter) => filter.Value1 === searchTerm.selectedOption?.Display); } if (!!this.settings.navigationState.filter) { this.settings.navigationState.filter.splice(index, 1); @@ -1816,9 +1869,9 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy private findSelectedFilterIndex(filterName: string, optionValue?: string): number { let index: number; if (optionValue) { - index = this.selectedFilters.map((f) => f.filter.Name + f.selectedOption.Value).indexOf(filterName + optionValue); + index = this.selectedFilters.map((f) => (f.filter?.Name ?? '') + (f.selectedOption?.Value ?? '')).indexOf(filterName + optionValue); } else { - index = this.selectedFilters.map((f) => f.filter.Name).indexOf(filterName); + index = this.selectedFilters.map((f) => f.filter?.Name).indexOf(filterName); } return index; } @@ -1839,9 +1892,9 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy * @ignore Used internally * Finds the relevant DataModelFilterOption from the supplied option value and filter */ - private findFilterOptionFromValue(optionValue: string, filter: DataSourceToolbarFilter): DataModelFilterOption { - const index = filter.Options.map((opt) => opt.Value).indexOf(optionValue); - return filter.Options[index]; + private findFilterOptionFromValue(optionValue: string, filter: DataSourceToolbarFilter): DataModelFilterOption | undefined { + const index = filter.Options?.map((opt) => opt.Value).indexOf(optionValue) ?? -1; + return filter.Options && index > -1 ? filter.Options[index] : undefined; } /** @@ -1854,13 +1907,17 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy private updateNavigateStateWithFilters(emit = true): void { this.settings.filters?.forEach((filter) => { if (filter.CurrentValue) { - this.settings.navigationState[filter.Name] = filter.CurrentValue; + if (filter.Name) { + this.settings.navigationState[filter.Name] = filter.CurrentValue; + } if (filter?.Column) { // This is a local filter and we must filter over this column this.localFilterState.filterColumns[filter.Column] = filter.CurrentValue; } } else { - delete this.settings.navigationState[filter.Name]; + if (filter.Name) { + delete this.settings.navigationState[filter.Name]; + } if (filter?.Column) { delete this.localFilterState.filterColumns[filter.Column]; } @@ -1894,7 +1951,7 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy // First check keyword, '' is default and will return true searchResult = val.includes(this.localFilterState?.keywords?.toLocaleLowerCase()); // Second check terms, allow for short circuiting - searchResult &&= this.searchTerms.every((term) => val.includes(term.selectedOption.Display)); + searchResult &&= this.searchTerms.every((term) => val.includes(term.selectedOption?.Display ?? '')); if (searchResult) { // Exit for loop if we have a positive hit break; @@ -1932,7 +1989,7 @@ export class DataSourceToolbarComponent implements OnChanges, OnInit, OnDestroy this.sidesheet.open(DataExportComponent, { title: await this.translate.get('#LDS#Heading Export Data').toPromise(), padding: '0px', - width: 'max(730px,65%)', + width: calculateSidesheetWidth(1000), icon: 'export', data: this.settings, }); diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar.module.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar.module.ts index 4cad9065f..ae5ab7528 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar.module.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-toolbar.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,71 +24,63 @@ * */ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; import { MatButtonToggleModule } from '@angular/material/button-toggle'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; +import { MatListModule } from '@angular/material/list'; import { MatMenuModule } from '@angular/material/menu'; import { MatPaginatorModule } from '@angular/material/paginator'; import { MatTooltipModule } from '@angular/material/tooltip'; import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; import { TranslateModule } from '@ngx-translate/core'; -import { MatListModule } from '@angular/material/list'; -import { DataSourceToolbarComponent } from './data-source-toolbar.component'; -import { DataSourcePaginatorComponent } from './data-source-paginator.component'; -import { DataSourceToolbarCustomComponent } from './data-source-toolbar-custom.component'; -import { DataTreeModule } from '../data-tree/data-tree.module'; -import { FilterTreeComponent } from './filter-tree/filter-tree.component'; -import { LdsReplaceModule } from '../lds-replace/lds-replace.module'; -import { AdditionalInfosComponent } from './additional-infos/additional-infos.component'; import { DragDropModule } from '@angular/cdk/drag-drop'; +import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatCardModule } from '@angular/material/card'; import { MatChipsModule } from '@angular/material/chips'; +import { MatDividerModule } from '@angular/material/divider'; +import { LoggerModule } from 'ngx-logger'; +import { AppConfigService } from '../appConfig/appConfig.service'; import { ClassloggerModule } from '../classlogger/classlogger.module'; -import { MatCardModule } from '@angular/material/card'; +import { DataTreeModule } from '../data-tree/data-tree.module'; +import { LdsReplaceModule } from '../lds-replace/lds-replace.module'; +import { AdditionalInfosComponent } from './additional-infos/additional-infos.component'; +import { DataSourcePaginatorComponent } from './data-source-paginator.component'; +import { DataSourceToolbarCustomComponent } from './data-source-toolbar-custom.component'; +import { DataSourceToolbarComponent } from './data-source-toolbar.component'; +import { FilterTreeComponent } from './filter-tree/filter-tree.component'; import { SaveConfigDialogComponent } from './save-config-dialog/save-config-dialog.component'; -import { ReactiveFormsModule } from '@angular/forms'; -import { FormsModule } from '@angular/forms'; -import { AppConfigService } from '../appConfig/appConfig.service'; -import { HttpClientModule } from '@angular/common/http'; -import { LoggerConfig, LoggerModule } from 'ngx-logger'; -import { MatDividerModule } from '@angular/material/divider'; -@NgModule({ - declarations: [ - DataSourceToolbarComponent, - DataSourcePaginatorComponent, - DataSourceToolbarCustomComponent, - FilterTreeComponent, - AdditionalInfosComponent, - SaveConfigDialogComponent, - ], - imports: [ - CommonModule, - EuiCoreModule, - EuiMaterialModule, - MatFormFieldModule, - MatMenuModule, - MatIconModule, - MatTooltipModule, - MatPaginatorModule, - MatButtonToggleModule, - MatChipsModule, - MatListModule, - MatCardModule, - MatDividerModule, - DragDropModule, - TranslateModule, - DataTreeModule, - LdsReplaceModule, - ClassloggerModule, - ReactiveFormsModule, - FormsModule, - HttpClientModule, - LoggerModule, - ], - providers: [AppConfigService, LoggerConfig], - exports: [DataSourceToolbarComponent, DataSourcePaginatorComponent, DataSourceToolbarCustomComponent, FilterTreeComponent], -}) +@NgModule({ declarations: [ + DataSourceToolbarComponent, + DataSourcePaginatorComponent, + DataSourceToolbarCustomComponent, + FilterTreeComponent, + AdditionalInfosComponent, + SaveConfigDialogComponent, + ], + exports: [DataSourceToolbarComponent, DataSourcePaginatorComponent, DataSourceToolbarCustomComponent, FilterTreeComponent], imports: [CommonModule, + EuiCoreModule, + EuiMaterialModule, + MatFormFieldModule, + MatMenuModule, + MatIconModule, + MatTooltipModule, + MatPaginatorModule, + MatButtonToggleModule, + MatChipsModule, + MatListModule, + MatCardModule, + MatDividerModule, + DragDropModule, + TranslateModule, + DataTreeModule, + LdsReplaceModule, + ClassloggerModule, + ReactiveFormsModule, + FormsModule, + LoggerModule], providers: [AppConfigService, provideHttpClient(withInterceptorsFromDi())] }) export class DataSourceToolbarModule {} diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-wrapper.spec.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-wrapper.spec.ts index 7364444b9..a0f2dbd66 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-wrapper.spec.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-wrapper.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,46 +24,36 @@ * */ -import { EntitySchema, IClientProperty, TypedEntity } from 'imx-qbm-dbts'; +import { EntitySchema, IClientProperty, TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { DataSourceWrapper } from './data-source-wrapper'; describe('DataSourceWrapper', () => { it('propertyDisplay', () => { const propertyDisplay = { ColumnName: '__Display' } as IClientProperty; - const dstWrapper = new DataSourceWrapper( - undefined, - undefined, - { Columns: { __Display: propertyDisplay } } as EntitySchema - ); - + const dstWrapper = new DataSourceWrapper(undefined, undefined, { Columns: { __Display: propertyDisplay } } as EntitySchema); + expect(dstWrapper.propertyDisplay.ColumnName).toEqual(propertyDisplay.ColumnName); }); it('getDstSettings', async () => { - type SomeEntity = { someProperty: string; } & TypedEntity; + type SomeEntity = { someProperty: string } & TypedEntity; const data = [{ someProperty: 'some value' }] as SomeEntity[]; const collection = { totalCount: data.length, - Data: data + Data: data, }; const someColumn = { ColumnName: 'SomeColumnName' } as IClientProperty; const entitySchema = { Columns: { someColumn } } as EntitySchema; - const displayedColumns = [ - entitySchema.Columns.someColumn - ]; + const displayedColumns = [entitySchema.Columns.someColumn]; - const dstWrapper = new DataSourceWrapper( - __ => Promise.resolve(collection), - displayedColumns, - entitySchema - ); + const dstWrapper = new DataSourceWrapper((__) => Promise.resolve(collection), displayedColumns, entitySchema); const parameters = { StartIndex: 23 }; - + const dstSettings = await dstWrapper.getDstSettings(parameters); expect((dstSettings.dataSource.Data[0] as SomeEntity).someProperty).toEqual(data[0].someProperty); @@ -71,4 +61,4 @@ describe('DataSourceWrapper', () => { expect(dstSettings.entitySchema.Columns.someColumn.ColumnName).toEqual(someColumn.ColumnName); expect(dstSettings.navigationState.StartIndex).toEqual(parameters.StartIndex); }); -}); \ No newline at end of file +}); diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-wrapper.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-wrapper.ts index b002032fd..b33cdb53a 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-wrapper.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-source-wrapper.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,45 +26,57 @@ import { ApiRequestOptions, - CollectionLoadParameters, DataModel, DisplayColumns, EntitySchema, ExtendedTypedEntityCollection, IClientProperty, TypedEntity -} from 'imx-qbm-dbts'; -import { DataModelWrapper } from './data-model/data-model-wrapper.interface'; + CollectionLoadParameters, + DataModel, + DisplayColumns, + EntitySchema, + ExtendedTypedEntityCollection, + IClientProperty, + TypedEntity, +} from '@imx-modules/imx-qbm-dbts'; +import { ClientPropertyForTableColumns } from './client-property-for-table-columns'; import { createGroupData } from './data-model/data-model-helper'; +import { DataModelWrapper } from './data-model/data-model-wrapper.interface'; import { DataSourceToolbarFilter } from './data-source-toolbar-filters.interface'; import { DataSourceToolbarGroupData } from './data-source-toolbar-groups.interface'; import { DataSourceToolbarSettings } from './data-source-toolbar-settings'; -import { ClientPropertyForTableColumns } from './client-property-for-table-columns'; export class DataSourceWrapper { public readonly propertyDisplay: IClientProperty; - public extendedData: TExtendedData; + public extendedData: TExtendedData | undefined; private parameters: CollectionLoadParameters; private readonly filterOptions: DataSourceToolbarFilter[]; private dataModel: DataModel; - private readonly groupData: DataSourceToolbarGroupData; + private readonly groupData: DataSourceToolbarGroupData | undefined; constructor( - private readonly getData: (parameters: CollectionLoadParameters, requestOpts?: ApiRequestOptions) => Promise>, + private readonly getData: ( + parameters: CollectionLoadParameters, + requestOpts?: ApiRequestOptions, + ) => Promise>, private readonly displayedColumns: ClientPropertyForTableColumns[], private readonly entitySchema: EntitySchema, dataModelWrapper?: DataModelWrapper, - private readonly identifier?: string + private readonly identifier?: string, ) { this.propertyDisplay = this.entitySchema.Columns[DisplayColumns.DISPLAY_PROPERTYNAME]; if (dataModelWrapper) { this.dataModel = dataModelWrapper.dataModel; - this.filterOptions = dataModelWrapper.dataModel.Filters; + this.filterOptions = dataModelWrapper.dataModel.Filters ?? []; this.groupData = this.createGroupData(dataModelWrapper); } } - public async getDstSettings(parameters?: CollectionLoadParameters, requestOpts?: ApiRequestOptions ): Promise { + public async getDstSettings( + parameters?: CollectionLoadParameters, + requestOpts?: ApiRequestOptions, + ): Promise { this.parameters = { ...this.parameters, - ...parameters + ...parameters, }; const dataSource = await this.getData(this.parameters, requestOpts); @@ -79,39 +91,48 @@ export class DataSourceWrapper { + public async getGroupDstSettings( + parameters: CollectionLoadParameters, + requestOpts?: ApiRequestOptions, + ): Promise { return { displayedColumns: this.displayedColumns, dataModel: this.dataModel, dataSource: await this.getData(parameters, requestOpts), entitySchema: this.entitySchema, - navigationState: parameters + navigationState: parameters, }; } - private createGroupData(dataModelWrapper: DataModelWrapper): DataSourceToolbarGroupData { + private createGroupData(dataModelWrapper: DataModelWrapper): DataSourceToolbarGroupData | undefined { return createGroupData( dataModelWrapper.dataModel, - parameters => dataModelWrapper.getGroupInfo({ - ...parameters, - ...this.getGroupingFilterOptionParameters(dataModelWrapper.groupingFilterOptions), - }), - dataModelWrapper.groupingExcludedColumns + (parameters) => { + if (dataModelWrapper.getGroupInfo) { + return dataModelWrapper.getGroupInfo({ + ...parameters, + ...this.getGroupingFilterOptionParameters(dataModelWrapper.groupingFilterOptions ?? []), + }); + } + return undefined; + }, + dataModelWrapper.groupingExcludedColumns, ); } private getGroupingFilterOptionParameters(groupingFilterOptions: string[]): { [parameterName: string]: string } { const parameters = {}; - groupingFilterOptions?.forEach(filterOptionName => - parameters[filterOptionName] = this.filterOptions.find(item => item.Name === filterOptionName)?.CurrentValue + groupingFilterOptions?.forEach( + (filterOptionName) => + (parameters[filterOptionName] = this.filterOptions.find((item) => item.Name === filterOptionName)?.CurrentValue), ); return parameters; diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-tile-badge.interface.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-tile-badge.interface.ts index 6a2b27d53..df4a033d0 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/data-tile-badge.interface.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/data-tile-badge.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,7 +28,6 @@ * Defines badge properties, that can be used to add a badge to a {@link DataTileComponent | data tile} */ export interface DataTileBadge { - /** * The text of a badge */ diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-tree/filter-tree-database.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-tree/filter-tree-database.ts index 4d58c6a71..fe845421d 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-tree/filter-tree-database.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-tree/filter-tree-database.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,34 +26,32 @@ import { EuiLoadingService } from '@elemental-ui/core'; -import { CollectionLoadParameters, FilterTreeData, IEntity } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, FilterTreeData, IEntity } from '@imx-modules/imx-qbm-dbts'; import { TreeDatabase } from '../../data-tree/tree-database'; import { TreeNode } from '../../data-tree/tree-node'; import { TreeNodeResultParameter } from '../../data-tree/tree-node-result-parameter.interface'; import { FilterTreeEntityWrapperService } from './filter-tree-entity-wrapper.service'; export class FilterTreeDatabase extends TreeDatabase { - - constructor( private readonly entityWrapper: FilterTreeEntityWrapperService, private readonly getFilterTree: (parentkey: string) => Promise, - private readonly busyLoadingService: EuiLoadingService + private readonly busyLoadingService: EuiLoadingService, ) { super(); } public async getData(showLoading: boolean, parameters: CollectionLoadParameters = {}): Promise { let entities: IEntity[]; - if (showLoading) { - setTimeout(() => this.busyLoadingService.show()); + if (showLoading && this.busyLoadingService.overlayRefs.length === 0) { + this.busyLoadingService.show(); } try { - entities = this.entityWrapper.convertToEntities(await this.getFilterTree(parameters.ParentKey), parameters['parentDisplay']); + entities = this.entityWrapper.convertToEntities(await this.getFilterTree(parameters.ParentKey ?? ''), parameters['parentDisplay']); } finally { if (showLoading) { - setTimeout(() => this.busyLoadingService.hide()); + this.busyLoadingService.hide(); } } @@ -61,19 +59,23 @@ export class FilterTreeDatabase extends TreeDatabase { } /** return children for a given tree node including the information, if more elements are available on the server */ - public async getChildren(node: TreeNode, startIndex: number) - : Promise<{ nodes: TreeNode[], canLoadMore: boolean }> { - - const entities = await this.getData(false, { ParentKey: node.name, StartIndex: startIndex, parentDisplay: node.item.GetColumn('LongDisplay').GetValue() }); + public async getChildren(node: TreeNode, startIndex: number): Promise<{ nodes: TreeNode[]; canLoadMore: boolean }> { + const entities = await this.getData(false, { + ParentKey: node.name, + StartIndex: startIndex, + parentDisplay: node.item?.GetColumn('LongDisplay').GetValue(), + }); const nodes = this.createSortedNodes(entities.entities, node.level + 1); return { - nodes: entities.entities.map(entity => nodes.find(x => this.getId(x.item) === this.getId(entity))), - canLoadMore: entities.canLoadMore + nodes: entities.entities + .map((entity) => nodes.find((x) => this.getId(x?.item) === this.getId(entity)) ?? new TreeNode()) + .filter((elem) => elem?.identifier != null), + canLoadMore: entities.canLoadMore, }; } - public getId(entity: IEntity): string { - return entity.GetColumn('ObjectKey').GetValue(); + public getId(entity: IEntity | undefined): string { + return entity?.GetColumn('ObjectKey')?.GetValue() ?? ''; } } diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-tree/filter-tree-entity-wrapper.service.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-tree/filter-tree-entity-wrapper.service.ts index 1bda0be53..b716d8882 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-tree/filter-tree-entity-wrapper.service.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-tree/filter-tree-entity-wrapper.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,16 +25,26 @@ */ import { Injectable } from '@angular/core'; -import { DisplayBuilder, DisplayPattern, EntityColumnData, EntityData, EntitySchema, FilterTreeData, FilterTreeElement, IClientProperty, IEntity, ReadWriteEntity, ValType } from 'imx-qbm-dbts'; +import { + DisplayBuilder, + DisplayPattern, + EntityColumnData, + EntityData, + EntitySchema, + FilterTreeData, + FilterTreeElement, + IClientProperty, + IEntity, + ReadWriteEntity, + ValType, +} from '@imx-modules/imx-qbm-dbts'; import { ImxTranslationProviderService } from '../../translation/imx-translation-provider.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class FilterTreeEntityWrapperService { - constructor( - private readonly translate: ImxTranslationProviderService - ) { } + constructor(private readonly translate: ImxTranslationProviderService) {} /** * Converts a list of FilterTreeData objects into a list or correlating entities @@ -42,7 +52,7 @@ export class FilterTreeEntityWrapperService { * @returns a list of entity objects */ public convertToEntities(data: FilterTreeData, parentDisplay: string): IEntity[] { - return data.Elements.map(filter => this.buildTreeFilterDataEntity(filter, parentDisplay)); + return data?.Elements?.map((filter) => this.buildTreeFilterDataEntity(filter, parentDisplay)) ?? []; } private buildTreeFilterDataEntity(data: FilterTreeElement, parentDisplay: string): IEntity { @@ -51,7 +61,7 @@ export class FilterTreeEntityWrapperService { this.buildEntityData(data, parentDisplay), undefined, undefined, - new DisplayBuilder(this.translate) + new DisplayBuilder(this.translate), ); } @@ -60,32 +70,32 @@ export class FilterTreeEntityWrapperService { ret['Display'] = { Type: ValType.String, - ColumnName: 'Display' + ColumnName: 'Display', }; ret['Filter'] = { Type: ValType.String, - ColumnName: 'Filter' + ColumnName: 'Filter', }; ret['HasChildren'] = { Type: ValType.Bool, - ColumnName: 'HasChildren' + ColumnName: 'HasChildren', }; ret['ObjectKey'] = { Type: ValType.String, - ColumnName: 'ObjectKey' + ColumnName: 'ObjectKey', }; ret['LongDisplay'] = { Type: ValType.String, - ColumnName: 'LongDisplay' + ColumnName: 'LongDisplay', }; return { DisplayPattern: new DisplayPattern('%Display%'), - Columns: ret + Columns: ret, }; } @@ -102,10 +112,9 @@ export class FilterTreeEntityWrapperService { ret['LongDisplay'] = { Value: (parentDisplay || '') === '' ? data.Display : `${parentDisplay}\\${data.Display}`, - IsReadOnly: true + IsReadOnly: true, }; return { Columns: ret, Keys: [JSON.stringify(data.Filter)] }; } } - diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-tree/filter-tree-selection-arg.interface.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-tree/filter-tree-selection-arg.interface.ts index 587151377..ef0bfca11 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-tree/filter-tree-selection-arg.interface.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-tree/filter-tree-selection-arg.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,22 +24,14 @@ * */ -import { FilterData, IEntity } from 'imx-qbm-dbts'; +import { FilterData, IEntity } from '@imx-modules/imx-qbm-dbts'; +import { FilterTreeSelectionParameter } from '../filter-wizard/filter-tree-sidesheet/filter-tree-sidesheet.model'; /** * Provides information for the selection of a filter tree. * It is used to unify the information, that is provided by the tree and a loaded configuration */ -export interface FilterTreeSelectionArg { - /** - * The text, that is displayed on chips. - */ - display: string; - /** - * The real filter information. - */ - filter: FilterData; - +export interface FilterTreeSelectionArg extends FilterTreeSelectionParameter { /** * An optional entity, that is used for data */ diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-tree/filter-tree.component.html b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-tree/filter-tree.component.html index ebc6c4145..5e53055ff 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-tree/filter-tree.component.html +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-tree/filter-tree.component.html @@ -1,38 +1,72 @@ -

    {{'#LDS#Narrow the selection further down by: {0}' | translate |ldsReplace:data.type}}

    +

    {{ '#LDS#Narrow the selection further down by: {0}' | translate | ldsReplace: data.type }}

    - + - -
    - {{'#LDS#Selected item' |translate}}: - {{currentlySelectedFilter[0].display}} + +
    + {{ '#LDS#Selected item' | translate }}: + {{ currentlySelectedFilter[0]?.display }}
    - -
    -
    - +
    - {{filterElement.display}} + {{ filterElement?.display }}
    diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-tree/filter-tree.component.scss b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-tree/filter-tree.component.scss index 3f0f1c3c7..5d9688d4a 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-tree/filter-tree.component.scss +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-tree/filter-tree.component.scss @@ -1,29 +1,20 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/mixins'; :host { - display: flex; - flex-direction: column; - flex: 1 1 auto; - overflow: hidden; + @include flex-column-container-fill; height: 100%; - ::ng-deep .mat-list-base { - .mat-list-item.imx-list-item { - height: auto; - } - } - - ::ng-deep .mat-menu-panel { + ::ng-deep .mat-mdc-menu-panel { padding: 10px; max-width: 500px; } } .imx-dialog-content { + @include flex-column-container(); flex: 1 1 auto; - display: flex; - flex-direction: column; } .imx-filtertree-buttons { @@ -38,7 +29,6 @@ } } - .imx-badge { background-color: $white; } @@ -50,7 +40,7 @@ .imx-selected-item-element { margin: 5px 0; } -.mat-dialog-actions{ +.mat-mdc-dialog-actions { padding-bottom: 16px; } diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-tree/filter-tree.component.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-tree/filter-tree.component.ts index d8fdaa096..5b4acea51 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-tree/filter-tree.component.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-tree/filter-tree.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,10 +25,10 @@ */ import { ChangeDetectorRef, Component, Inject, OnInit, ViewChild } from '@angular/core'; -import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { EuiLoadingService } from '@elemental-ui/core'; -import { FilterTreeData, IEntity } from 'imx-qbm-dbts'; +import { IEntity } from '@imx-modules/imx-qbm-dbts'; import { DataTreeComponent } from '../../data-tree/data-tree.component'; import { TreeDatabase } from '../../data-tree/tree-database'; import { FilterTreeParameter } from '../data-model/filter-tree-parameter'; @@ -42,9 +42,9 @@ import { FilterTreeDialogResultArg, FilterTreeSelectionArg } from './filter-tree styleUrls: ['./filter-tree.component.scss'], }) export class FilterTreeComponent implements OnInit { - public database: TreeDatabase; - public currentlySelectedFilter: FilterTreeSelectionArg[]; - public currentlySelectedFilterEntities: IEntity[]; + public database: TreeDatabase | undefined; + public currentlySelectedFilter: (FilterTreeSelectionArg | undefined)[]; + public currentlySelectedFilterEntities: (IEntity | undefined)[]; @ViewChild('tree') private tree: DataTreeComponent; constructor( @@ -53,7 +53,7 @@ export class FilterTreeComponent implements OnInit { public readonly data: { filterTreeParameter: FilterTreeParameter; preselection: FilterTreeSelectionArg[]; type: string }, public dialogRef: MatDialogRef, public changeDetector: ChangeDetectorRef, - private readonly entityWrapper: FilterTreeEntityWrapperService + private readonly entityWrapper: FilterTreeEntityWrapperService, ) {} public async ngOnInit(): Promise { @@ -64,7 +64,7 @@ export class FilterTreeComponent implements OnInit { } if (this.data?.preselection) { this.currentlySelectedFilter = this.data.preselection; - this.currentlySelectedFilterEntities = this.currentlySelectedFilter.map(elem=> elem.entity).filter(elem=>elem != null); + this.currentlySelectedFilterEntities = this.currentlySelectedFilter.map((elem) => elem?.entity).filter((elem) => elem != null); this.changeDetector.detectChanges(); } } @@ -78,7 +78,7 @@ export class FilterTreeComponent implements OnInit { if (!this.data.filterTreeParameter.multiSelect) { return; } - this.currentlySelectedFilter = this.tree.selectedEntities.map((elem) => new FilterTreeDialogResultArg(elem)); + this.currentlySelectedFilter = this.tree.selectedEntities.map((elem) => (elem ? new FilterTreeDialogResultArg(elem) : undefined)); } public onNodeSelected(entity: IEntity): void { diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-tree-sidesheet/filter-tree-sidesheet.component.html b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-tree-sidesheet/filter-tree-sidesheet.component.html new file mode 100644 index 000000000..2597b6e66 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-tree-sidesheet/filter-tree-sidesheet.component.html @@ -0,0 +1,36 @@ +
    +
    +

    + {{ '#LDS#Selected item' | translate }}: + {{ currentlySelectedFilter?.display ?? '-' }} +

    + +
    + +
    + + +
    + + {{ node.nodeDisplay }} +
    +
    diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-tree-sidesheet/filter-tree-sidesheet.component.scss b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-tree-sidesheet/filter-tree-sidesheet.component.scss new file mode 100644 index 000000000..9514d2314 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-tree-sidesheet/filter-tree-sidesheet.component.scss @@ -0,0 +1,90 @@ +@import '@elemental-ui/core/src/styles/_palette.scss'; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/mixins'; + +:host { + @include flex-column-container-fill(); + height: 100%; + + .imx-bold-text { + font-weight: 900; + vertical-align: sub; + } + + .imx-filter-sheet { + display: flex; + flex-direction: column; + overflow: hidden; + height: inherit; + } + + .imx-tree-control { + @include flex-row-container-fill(); + } + + .imx-top-line { + @include flex-row-container-fill(); + overflow: hidden; + align-items: center; + p { + flex: 1 1 auto; + overflow: hidden; + + @include flex-row-container-fill(); + span { + vertical-align: middle; + text-wrap: nowrap; + } + + span[boldtext] { + font-weight: 700; + margin-right: 10px; + } + + span[content] { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + } + } +} + +:host { + .imx-filter-tree-node.imx-selected-text { + color: $color-orange-60; + .eui-icon { + color: $color-orange-60; + } + } +} + +.eui-dark-theme { + :host { + .imx-filter-tree-node { + color: $color-gray-0; + } + + .imx-filter-tree-node.imx-selected-text { + color: $color-orange-40; + .eui-icon { + color: $color-orange-40; + } + } + } +} + +.eui-contrast-theme { + :host { + .imx-filter-tree-node { + color: $color-gray-0; + } + + .imx-filter-tree-node.imx-selected-text { + color: $color-orange-80; + .eui-icon { + color: $color-orange-80; + } + } + } +} diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-tree-sidesheet/filter-tree-sidesheet.component.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-tree-sidesheet/filter-tree-sidesheet.component.ts new file mode 100644 index 000000000..0a92a2a35 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-tree-sidesheet/filter-tree-sidesheet.component.ts @@ -0,0 +1,124 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; + +import { FlatTreeControl } from '@angular/cdk/tree'; +import _ from 'lodash'; +import { DynamicDataApiControls, DynamicDataSource } from '../../../sidenav-tree/sidenav-tree-dynamic-extension'; +import { FilterTreeNode, FilterTreeParameterData, FilterTreeSelectionParameter } from './filter-tree-sidesheet.model'; +@Component({ + selector: 'imx-filter-tree-sidesheet', + templateUrl: './filter-tree-sidesheet.component.html', + styleUrls: ['./filter-tree-sidesheet.component.scss'], +}) +export class FilterTreeSidesheetComponent implements OnInit { + public currentlySelectedFilter: FilterTreeSelectionParameter; + @Input() public data: FilterTreeParameterData; + @Output() public filterTreeSelectionChanged = new EventEmitter(); + + public hasChild = (_: number, node: FilterTreeNode) => node.hasChildren; + private isInitial = true; + + public apiControls: DynamicDataApiControls; + public dynamicDataSource: DynamicDataSource; + public treeControl: FlatTreeControl; + public selectFn = (node: FilterTreeNode) => + node.isSelected || + (this.data?.preSelection?.display === node.nodeDisplay && + _.isEqual(this.data?.preSelection?.filter, node?.filterData) && + this.isInitial); + + public isLoading = (node: FilterTreeNode) => node.isLoading || false; + + constructor() { + this.treeControl = new FlatTreeControl( + (leaf) => leaf.level, + (leaf) => leaf.hasChildren, + ); + + this.apiControls = { + setup: async () => { + const items = await this.data.filterTreeParameter.filterMethode(''); + const rootNode: FilterTreeNode = { + level: 0, + nodeDisplay: items.Description ?? '', + hasChildren: true, + objectKey: '', + children: + items.Elements?.map((elem) => ({ + filterData: elem.Filter, + nodeDisplay: elem.Display ?? '', + hasChildren: elem.HasHierarchy ?? false, + objectKey: elem.ObjectKey, + level: 1, + })) ?? [], + }; + return { rootNode }; + }, + getChildren: async (node) => { + node.isLoading = true; + const items = await this.data.filterTreeParameter.filterMethode(node.objectKey ?? ''); + node.isLoading = false; + return ( + items.Elements?.map((elem) => ({ + filterData: elem.Filter, + nodeDisplay: elem.Display ?? '', + hasChildren: elem.HasHierarchy ?? false, + objectKey: elem.ObjectKey, + level: node.level + 1, + })) ?? [] + ); + }, + changeSelection: (data, selectedNode) => { + data.forEach((elem) => (elem.isSelected = false)); + selectedNode.isSelected = true; + this.isInitial = false; + return data; + }, + }; + + this.dynamicDataSource = new DynamicDataSource(this.treeControl, this.apiControls); + } + + public async ngOnInit(): Promise { + this.currentlySelectedFilter = this.data.preSelection; + await this.dynamicDataSource.setup(true); + const current = this.dynamicDataSource.data.find((node) => node.nodeDisplay === this.data.preSelection?.display); + if (current) { + this.dynamicDataSource.setSelection(current); + } + } + + public onSelectedNodeChanged(node: FilterTreeNode) { + this.currentlySelectedFilter = { display: node.nodeDisplay, filter: node.filterData }; + this.filterTreeSelectionChanged.emit({ display: node.nodeDisplay, filter: node.filterData }); + } + + public closeAllNodes(): void { + this.treeControl.collapseAll(); + } +} diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-tree-sidesheet/filter-tree-sidesheet.model.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-tree-sidesheet/filter-tree-sidesheet.model.ts new file mode 100644 index 000000000..da0753e50 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-tree-sidesheet/filter-tree-sidesheet.model.ts @@ -0,0 +1,109 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { FilterData } from '@imx-modules/imx-qbm-dbts'; +import { FilterTreeParameter } from '../../data-model/filter-tree-parameter'; + +/** + * Provides information for the selection of a filter tree. + * It is used to unify the information, that is provided by the tree and a loaded configuration + */ +export interface FilterTreeSelectionParameter { + /** + * The text, that is displayed on chips. + */ + display?: string; + /** + * The real filter information. + */ + filter?: FilterData; +} + +/** + * Provides the information, that are used to describe a tree node in a + * {@link FilterTreeSidesheetComponent | filter tree side sheet component } + */ +export interface FilterTreeNode { + /** + * the level of the node + */ + level: number; + + /** + * Represents, whether the node has children or not. + */ + hasChildren: boolean; + + /** + * Represents the display of the node + */ + nodeDisplay: string; + + /** + * Represents the filter data, associated with the node. + */ + filterData?: FilterData; + + /** + * Represents the children of the tree node + */ + children?: FilterTreeNode[]; + + /** + * Represents an object key, that can be used, to identify the node. + */ + objectKey?: string; + + /** + * Represents, whether the node is loading or not + */ + isLoading?: boolean; + + /** + * Represents, whether the node is shown as selected or not + */ + isSelected?: boolean; +} + +/** + * Provides information for the filter tree sidesheet component + */ +export interface FilterTreeParameterData { + /** + * The parameter, that contains the filter method and other options. + */ + filterTreeParameter: FilterTreeParameter; + + /** + * A list of preselected elements + */ + preSelection: FilterTreeSelectionParameter; + + /** + * the type of the tree filter + */ + type: string; +} diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-wizard.component.html b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-wizard.component.html index 5a46cf50f..fac7d844c 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-wizard.component.html +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-wizard.component.html @@ -1,42 +1,77 @@
    - + - - + + +
    -
    - -
    - - - - - + + + + + + + + + +
    + + {{ '#LDS#You do not have permission to show filters.' | translate }} + +
    +
    + + +
    - + diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-wizard.component.scss b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-wizard.component.scss index a5eaa7cdd..3bf390496 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-wizard.component.scss +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-wizard.component.scss @@ -1,6 +1,9 @@ :host { .imx-sidesheet-content { overflow: hidden; + padding: 0px; + display: flex; + flex-direction: column; .imx-content-container { display: flex; @@ -8,6 +11,8 @@ row-gap: 10px; overflow: hidden; height: 100%; + padding:20px; + margin: 20px; } } @@ -24,19 +29,21 @@ align-items: center; } - .mat-tab-group { - height: 100%; - } - - ::ng-deep .mat-tab-body-wrapper { - padding: 0 10px; - height: 100%; + .imx-no-items-container { + flex: 1 1 auto; } } + imx-sqlwizard { overflow: hidden; flex-grow: 1; display: flex; flex-direction: column; } + +imx-busy-indicator { + flex: 1 1 auto; + justify-content: center; + align-items: center; +} diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-wizard.component.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-wizard.component.ts index 8882d6f2c..9deb18441 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-wizard.component.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-wizard.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,28 +24,30 @@ * */ -import { Component, Inject, OnDestroy } from '@angular/core'; -import { EuiSidesheetRef, EuiSidesheetService, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; +import { MatTabChangeEvent } from '@angular/material/tabs'; +import { EUI_SIDESHEET_DATA, EuiSidesheetRef, EuiSidesheetService } from '@elemental-ui/core'; +import { LogOp, SqlExpression, SqlWizardExpression, isExpressionInvalid } from '@imx-modules/imx-qbm-dbts'; import { TranslateService } from '@ngx-translate/core'; -import { isExpressionInvalid, LogOp, SqlExpression, SqlWizardExpression } from 'imx-qbm-dbts'; import _ from 'lodash'; import { Subscription } from 'rxjs/internal/Subscription'; +import { BusyService } from '../../base/busy.service'; import { ConfirmationService } from '../../confirmation/confirmation.service'; -import { FilterWizardService } from './filter-wizard.service'; -import { FilterFormState, FilterTypeIdentifier, FilterWizardSidesheetData } from './filter-wizard.interfaces'; -import { MatTabChangeEvent } from '@angular/material/tabs'; import { SqlWizardApiService } from '../../sqlwizard/sqlwizard-api.service'; - +import { FilterTreeSelectionParameter } from './filter-tree-sidesheet/filter-tree-sidesheet.model'; +import { FilterFormState, FilterTypeIdentifier, FilterWizardSidesheetData } from './filter-wizard.interfaces'; +import { FilterWizardService } from './filter-wizard.service'; @Component({ selector: 'imx-filter-wizard', templateUrl: './filter-wizard.component.html', styleUrls: ['./filter-wizard.component.scss'], }) -export class FilterWizardComponent implements OnDestroy { +export class FilterWizardComponent implements OnInit, OnDestroy { public sqlExpression: SqlWizardExpression; - public lastGoodExpression: SqlExpression; + public lastGoodExpression: SqlExpression | undefined; public expressionDirty = false; public expressionInvalid = true; + public treeFilterUpdated = false; public selectedTabIndex = 0; public formState: FilterFormState = { canClearFilters: false, dirty: false, filterIdentifier: FilterTypeIdentifier.Predefined }; public readonly FilterTypeIdentifier: FilterTypeIdentifier; @@ -53,18 +55,28 @@ export class FilterWizardComponent implements OnDestroy { public readonly FTICustom = FilterTypeIdentifier.Custom; public readonly FTITargetSystem = FilterTypeIdentifier.TargetSystem; + private busyService = new BusyService(); + private readonly subscriptions: Subscription[] = []; private confirmLeaveTitle = ''; private confirmLeaveMessage = ''; + private hasProperties: boolean = false; + public initialized = false; + public isLoading = false; + public hasTreeFilter = false; + private treeFilterArgs: FilterTreeSelectionParameter | undefined; + private readonly emptyExpression = { Expression: { Expressions: [ { Expressions: [], LogOperator: LogOp.AND, + Negate: false, }, ], LogOperator: LogOp.AND, + Negate: false, }, }; @@ -75,7 +87,7 @@ export class FilterWizardComponent implements OnDestroy { private readonly filterService: FilterWizardService, public readonly sqlWizardSvc: SqlWizardApiService, readonly translation: TranslateService, - @Inject(EUI_SIDESHEET_DATA) public data?: FilterWizardSidesheetData + @Inject(EUI_SIDESHEET_DATA) public data?: FilterWizardSidesheetData, ) { translation.get('#LDS#Heading Cancel Filtering').subscribe((value: string) => (this.confirmLeaveTitle = value)); translation @@ -88,32 +100,51 @@ export class FilterWizardComponent implements OnDestroy { this.lastGoodExpression = _.cloneDeep(this.sqlExpression?.Expression); this.sidesheetRef.closeClicked().subscribe(() => this.close()); - this.expressionInvalid = data?.filterExpression && isExpressionInvalid(this.sqlExpression); + this.expressionInvalid = (data?.filterExpression && isExpressionInvalid(this.sqlExpression)) == true; + + this.treeFilterArgs = data?.filterTreeParameter?.preSelection; this.subscriptions.push( this.filterService.filterFormStateEvent.subscribe((formState: FilterFormState) => { setTimeout(() => (this.formState = formState)); - }) + }), ); + + this.subscriptions.push(this.busyService.busyStateChanged.subscribe((state) => (this.isLoading = state))); + } + + public async ngOnInit(): Promise { + const busy = this.busyService.beginBusy(); + try { + const columns = await this.sqlWizardSvc.getFilterProperties(this.data?.settings?.entitySchema?.TypeName ?? ''); + this.hasProperties = columns?.length > 0; + this.initialized = true; + + this.hasTreeFilter = + this.data?.filterTreeParameter?.filterTreeParameter != null && + !!(await this.data.filterTreeParameter.filterTreeParameter.filterMethode(''))?.Elements?.length; + } finally { + busy.endBusy(); + } } public get hasPredefinedFilters(): boolean { - return this.data.settings?.filters?.length > 0; + return !!this.data?.settings?.filters?.length; } public get canUseCustomFilters(): boolean { - return !this.data.isDataSourceLocal && this.showSqlWizard; + return !this.data?.isDataSourceLocal && this.hasProperties && this.showSqlWizard; } /** * Counts if we have at least 2 tabs to show */ public get useTabs(): boolean { - return [this.hasPredefinedFilters, this.canUseCustomFilters].reduce((a, b) => a + (b ? 1 : 0), 0) > 1; + return [this.hasPredefinedFilters, this.canUseCustomFilters, this.hasTreeFilter].reduce((a, b) => a + (b ? 1 : 0), 0) > 1; } public ngOnDestroy(): void { - if (isExpressionInvalid(this.sqlExpression)) { + if (isExpressionInvalid(this.sqlExpression) && this.sqlExpression.Expression) { this.sqlExpression.Expression.Expressions = []; } @@ -128,35 +159,42 @@ export class FilterWizardComponent implements OnDestroy { public onApplyFilters(): void { this.filterService.applyFilters(); - this.sidesheetService.close(this.sqlExpression); + this.sidesheetService.close({ expression: this.sqlExpression, treeFilter: this.treeFilterArgs }); } public onClearFilters(): void { - this.lastGoodExpression = null; - this.sqlExpression.Expression.Expressions = []; + this.lastGoodExpression = undefined; + if (this.sqlExpression.Expression) { + this.sqlExpression.Expression.Expressions = []; + } this.filterService.clearFilters(); - this.sidesheetService.close(this.sqlExpression); + this.sidesheetService.close({ expression: this.sqlExpression, treeFilter: undefined }); } public onSelectedTabChange(event: MatTabChangeEvent): void { - this.filterService.filterTabChanged(event.tab.content.templateRef.elementRef.nativeElement.parentElement.id as FilterTypeIdentifier); + this.filterService.filterTabChanged(event.tab.content?.templateRef.elementRef.nativeElement.parentElement.id as FilterTypeIdentifier); + } + + public onFilterTreeSelectionChanged(event: FilterTreeSelectionParameter) { + this.treeFilterUpdated = true; + this.treeFilterArgs = event; } public canApplyCustomFilters(): boolean { - return (this.expressionDirty || this.formState?.dirty) && !this.expressionInvalid; + return (this.expressionDirty || this.formState?.dirty || this.treeFilterUpdated) && !this.expressionInvalid; } public canRemoveCustomFilter(): boolean { - return this.lastGoodExpression?.Expressions?.length > 0 || this.formState?.canClearFilters; + return !!this.lastGoodExpression?.Expressions?.length || this.formState?.canClearFilters; } public containsTargetSystemFilter(): boolean { let filters = this.data?.settings.filters; - return filters && filters.find((item) => item.Name === 'namespace') != null; + return (filters && filters.find((item) => item.Name === 'namespace') != null) ?? false; } public get showSqlWizard(): boolean { - return this.sqlWizardSvc.implemented; + return this.sqlWizardSvc.implemented ?? false; } private async close(): Promise { diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-wizard.interfaces.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-wizard.interfaces.ts index 30029d387..2224163b7 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-wizard.interfaces.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-wizard.interfaces.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,9 +24,10 @@ * */ +import { SqlWizardExpression } from '@imx-modules/imx-qbm-dbts'; import { DataSourceToolbarSelectedFilter } from '../data-source-toolbar-filters.interface'; import { DataSourceToolbarSettings } from '../data-source-toolbar-settings'; -import { SqlWizardExpression } from 'imx-qbm-dbts'; +import { FilterTreeParameterData, FilterTreeSelectionParameter } from './filter-tree-sidesheet/filter-tree-sidesheet.model'; export interface FilterWizardSidesheetData { id: string; @@ -34,6 +35,7 @@ export interface FilterWizardSidesheetData { settings: DataSourceToolbarSettings; selectedFilters: DataSourceToolbarSelectedFilter[]; isDataSourceLocal: boolean; + filterTreeParameter: FilterTreeParameterData; } export interface FilterFormState { @@ -47,3 +49,8 @@ export enum FilterTypeIdentifier { Custom = 'Custom', TargetSystem = 'TargetSystem', } + +export interface FilterWizardResult { + expression: SqlWizardExpression; + treeFilter?: FilterTreeSelectionParameter; +} diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-wizard.module.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-wizard.module.ts index ebb71bb5d..7e079d139 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-wizard.module.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-wizard.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,28 +24,30 @@ * */ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatCardModule } from '@angular/material/card'; import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatListModule } from '@angular/material/list'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatTooltipModule } from '@angular/material/tooltip'; import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; import { TranslateModule } from '@ngx-translate/core'; +import { BusyIndicatorModule } from '../../busy-indicator/busy-indicator.module'; import { ClassloggerModule } from '../../classlogger/classlogger.module'; import { LdsReplaceModule } from '../../lds-replace/lds-replace.module'; -import { FilterWizardComponent } from './filter-wizard.component'; +import { CustomTreeControlComponent } from '../../sidenav-tree/custom-tree-control.component'; import { SqlWizardModule } from '../../sqlwizard/sqlwizard.module'; -import { MatCardModule } from '@angular/material/card'; -import { MatListModule } from '@angular/material/list'; -import { MatMenuModule } from '@angular/material/menu'; -import { MatTooltipModule } from '@angular/material/tooltip'; -import { MatTabsModule } from '@angular/material/tabs'; -import { PredefinedFilterComponent } from './predefined-filter/predefined-filter.component'; -import { PredefinedFilterTreeComponent } from './predefined-filter-tree/predefined-filter-tree.component'; -import { SidenavTreeModule } from '../../sidenav-tree/sidenav-tree.module'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { DataSourceToolbarModule } from '../data-source-toolbar.module'; +import { FilterTreeSidesheetComponent } from './filter-tree-sidesheet/filter-tree-sidesheet.component'; +import { FilterWizardComponent } from './filter-wizard.component'; +import { PredefinedFilterTreeComponent } from './predefined-filter-tree/predefined-filter-tree.component'; +import { PredefinedFilterComponent } from './predefined-filter/predefined-filter.component'; @NgModule({ - declarations: [FilterWizardComponent, PredefinedFilterComponent, PredefinedFilterTreeComponent], + declarations: [FilterWizardComponent, PredefinedFilterComponent, PredefinedFilterTreeComponent, FilterTreeSidesheetComponent], imports: [ CommonModule, EuiCoreModule, @@ -62,8 +64,9 @@ import { DataSourceToolbarModule } from '../data-source-toolbar.module'; LdsReplaceModule, ClassloggerModule, SqlWizardModule, - SidenavTreeModule, - DataSourceToolbarModule + DataSourceToolbarModule, + BusyIndicatorModule, + CustomTreeControlComponent, ], exports: [FilterWizardComponent], }) diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-wizard.service.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-wizard.service.ts index 91c92eb3a..8fa37b743 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-wizard.service.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/filter-wizard.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,13 +25,13 @@ */ import { EventEmitter, Injectable } from '@angular/core'; -import { CollectionLoadParameters } from 'imx-qbm-dbts'; +import { CollectionLoadParameters } from '@imx-modules/imx-qbm-dbts'; +import { SqlWizardApiService } from '../../sqlwizard/sqlwizard-api.service'; import { DataSourceToolbarSelectedFilter } from '../data-source-toolbar-filters.interface'; import { FilterFormState, FilterTypeIdentifier } from './filter-wizard.interfaces'; -import { SqlWizardApiService } from '../../sqlwizard/sqlwizard-api.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class FilterWizardService { public navigationStateChanged = new EventEmitter(); @@ -40,10 +40,10 @@ export class FilterWizardService { public filterFormStateEvent = new EventEmitter(); public filterTabChangedEvent = new EventEmitter(); - constructor(public readonly sqlWizardSvc: SqlWizardApiService) { } + constructor(public readonly sqlWizardSvc: SqlWizardApiService) {} public get isSqlWizardImplemented(): boolean { - return this.sqlWizardSvc.implemented; + return this.sqlWizardSvc.implemented ?? false; } public updateNavigation(id: string, params: CollectionLoadParameters, selectedFilters: DataSourceToolbarSelectedFilter[]): void { @@ -68,7 +68,7 @@ export class FilterWizardService { } export interface selectedFiltersParams { - id: string; + id: string; params: CollectionLoadParameters; selectedFilters: DataSourceToolbarSelectedFilter[]; } diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/predefined-filter-tree/predefined-filter-tree.component.scss b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/predefined-filter-tree/predefined-filter-tree.component.scss index 7cf3dc515..53c91a73f 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/predefined-filter-tree/predefined-filter-tree.component.scss +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/predefined-filter-tree/predefined-filter-tree.component.scss @@ -15,4 +15,3 @@ color: $color-orange-60; } } - diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/predefined-filter-tree/predefined-filter-tree.component.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/predefined-filter-tree/predefined-filter-tree.component.ts index 84bf1a778..243cc850e 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/predefined-filter-tree/predefined-filter-tree.component.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/predefined-filter-tree/predefined-filter-tree.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { Component, Inject,OnInit } from '@angular/core'; +import { Component, Inject, OnInit } from '@angular/core'; import { FilterWizardSidesheetData } from '../filter-wizard.interfaces'; import { EUI_SIDESHEET_DATA } from '@elemental-ui/core'; @@ -34,10 +34,7 @@ import { EUI_SIDESHEET_DATA } from '@elemental-ui/core'; styleUrls: ['./predefined-filter-tree.component.scss'], }) export class PredefinedFilterTreeComponent implements OnInit { - constructor(@Inject(EUI_SIDESHEET_DATA) public data?: FilterWizardSidesheetData) {} - public async ngOnInit(): Promise { - } - + public async ngOnInit(): Promise {} } diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/predefined-filter/predefined-filter.component.html b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/predefined-filter/predefined-filter.component.html index 2f9606b86..2ff6ad8c8 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/predefined-filter/predefined-filter.component.html +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/predefined-filter/predefined-filter.component.html @@ -1,35 +1,83 @@ - - - -

    {{ filter.Description || filter.Name }}

    - - {{ filter.Options[0].Display }} - - - + +
    + + + + {{ filter.Description || filter.Name }} - {{ fopt.Display }} - - - - - {{ foption.Display }} - - - - + + + +
    + + + + + + {{ filter.Options[0].Display }} + + + + {{ fopt.Display }} + + + + + + + {{ foption.Display }} + + + + diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/predefined-filter/predefined-filter.component.scss b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/predefined-filter/predefined-filter.component.scss index 82b5c7389..715211ba5 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/predefined-filter/predefined-filter.component.scss +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/predefined-filter/predefined-filter.component.scss @@ -4,23 +4,27 @@ display: flex; flex-direction: column; row-gap: 10px; - padding: 10px 0 10px 0; + padding: 20px; + flex: 1 1 auto; - .mat-checkbox { + .mat-mdc-checkbox { display: block; } - .mat-radio-button { - display: block; - margin-right: 5px; + .imx-filter-title, + // Need to ng deep to overwrite elemental values, specificity wins still + .imx-filter-title ::ng-deep label.eui-label { + font-size: 16px; + font-weight: 600; + } - label.mat-radio-label { - white-space: normal; - } + .hidden { + display: none; } - .imx-filter-title { - font-size: 16px; - font-weight: 600; + imx-busy-indicator { + flex: 1 1 auto; + justify-content: center; + align-items: center; } } diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/predefined-filter/predefined-filter.component.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/predefined-filter/predefined-filter.component.ts index db9b22506..63a8b8967 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/predefined-filter/predefined-filter.component.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/filter-wizard/predefined-filter/predefined-filter.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,19 +24,28 @@ * */ -import { AfterViewInit, Component, EventEmitter, Inject, Input, NgZone, OnDestroy, OnInit, Output, QueryList, ViewChildren } from '@angular/core'; +import { AfterViewInit, Component, EventEmitter, Inject, Input, OnDestroy, OnInit, Output } from '@angular/core'; +import { FormControl } from '@angular/forms'; import { MatCheckboxChange } from '@angular/material/checkbox'; -import { MatSelectChange } from '@angular/material/select'; import { MatTableDataSource } from '@angular/material/table'; -import { EUI_SIDESHEET_DATA } from '@elemental-ui/core'; -import { CollectionLoadParameters, DataModelFilterOption, TypedEntity } from 'imx-qbm-dbts'; +import { EUI_SIDESHEET_DATA, EuiSelectFeedbackMessages, EuiSelectOption } from '@elemental-ui/core'; +import { CollectionLoadParameters, DataModelFilterOption, TypedEntity } from '@imx-modules/imx-qbm-dbts'; +import { TranslateService } from '@ngx-translate/core'; +import * as _ from 'lodash'; import { Subscription } from 'rxjs/internal/Subscription'; import { DataSourceToolbarFilter, DataSourceToolbarSelectedFilter } from '../../data-source-toolbar-filters.interface'; import { DataSourceToolbarSettings } from '../../data-source-toolbar-settings'; import { DSTViewConfig } from '../../data-source-toolbar-view-config.interface'; import { FilterFormState, FilterTypeIdentifier, FilterWizardSidesheetData } from '../filter-wizard.interfaces'; import { FilterWizardService } from '../filter-wizard.service'; -import * as _ from 'lodash'; + +enum FilterTypes { + SingleCheck, + MultiCheck, + SelectCheck, + MultiRadio, + SelectRadio, +} @Component({ selector: 'imx-predefined-filter', templateUrl: './predefined-filter.component.html', @@ -45,10 +54,10 @@ import * as _ from 'lodash'; export class PredefinedFilterComponent implements OnInit, AfterViewInit, OnDestroy { @Input() public settings: DataSourceToolbarSettings; - /** + /** * The DataSourceToolbar's ID generated in data-source-toolbar.component.ts */ - @Input() public id: string; + @Input() public id: string | undefined; /** * List of filter names that should be hidden @@ -103,6 +112,30 @@ export class PredefinedFilterComponent implements OnInit, AfterViewInit, OnDestr public filters: DataSourceToolbarFilter[] = []; + /** + * Holds a reference to the filter type enum for use in html + */ + public filterTypeEnum = FilterTypes; + + /** + * Mapping of the filters to their html element, currentValue control, and EUI options + */ + public filterTypes: { + type: FilterTypes | undefined; + currentValue: FormControl; + options: EuiSelectOption[]; + }[] = []; + + public feedbackMessages: EuiSelectFeedbackMessages; + + /** + * Method to handle searching over options + */ + public searchFunction = (option: EuiSelectOption, search: string): boolean => + option.display.toLocaleLowerCase().includes(search.toLocaleLowerCase()); + + public isLoading: boolean = true; + /** * This is the mat table datasource. */ @@ -112,6 +145,11 @@ export class PredefinedFilterComponent implements OnInit, AfterViewInit, OnDestr private formState: FilterFormState; private filterTypeIndentifier: FilterTypeIdentifier = FilterTypeIdentifier.Predefined; + /** + * Value to specify the cutoff between a multi-x to a select-x field + */ + private selectOptionThreshold = 5; + /** * @ignore Used internally. * Filters the data source with these arguments locally. @@ -127,29 +165,51 @@ export class PredefinedFilterComponent implements OnInit, AfterViewInit, OnDestr keywords: '', }; - constructor(private readonly filterService: FilterWizardService, @Inject(EUI_SIDESHEET_DATA) public data?: FilterWizardSidesheetData) { + constructor( + private readonly filterService: FilterWizardService, + private translateService: TranslateService, + @Inject(EUI_SIDESHEET_DATA) public data?: FilterWizardSidesheetData, + ) { // this.hiddenFilters = ['namespace']; - this.id = data.id; - this.settings = Object.create(data.settings); - this.selectedFilters = data.selectedFilters; - this.filters = _.cloneDeep(data.settings.filters); + this.id = data?.id; + this.settings = data?.settings ? Object.create(data.settings) : undefined; + this.selectedFilters = data?.selectedFilters ?? []; + this.filters = _.cloneDeep(data?.settings.filters) ?? []; + this.getFilterTypes(); this.internalSelectedFilters = Object.create(this.selectedFilters); this.formState = { canClearFilters: this.selectedFilters.length > 0, dirty: false, filterIdentifier: FilterTypeIdentifier.Predefined }; this.subscriptions.push( this.filterService.applyFiltersEvent.subscribe(() => { this.applyFilters(); - }) + }), ); this.subscriptions.push( this.filterService.clearFiltersEvent.subscribe(() => { this.clearFilters(); - }) + }), ); + + this.feedbackMessages = { + selected: this.translateService.instant('#LDS#{{value}} selected'), + clear: this.translateService.instant('#LDS#Clear selection'), + search: this.translateService.instant('#LDS#Search'), + plusOther: this.translateService.instant('#LDS#and 1 more'), + plusOtherPlural: this.translateService.instant('#LDS#and {{value}} more'), + unsupportedCharacter: this.translateService.instant('#LDS#You are using unsupported characters.'), + noResults: this.translateService.instant('#LDS#There is no data matching your search.'), + clearAll: this.translateService.instant('#LDS#Clear selection'), + ok: this.translateService.instant('#LDS#OK'), + keyboardOptionsListAria: this.translateService.instant('#LDS#Use the arrow keys to select items.'), + }; } public ngAfterViewInit(): void { + // workaround for UI problem -> panel is drawn as expanded before its got collapsed + setTimeout(() => { + this.isLoading = false; + }, 500); } public ngOnInit(): void { @@ -160,6 +220,44 @@ export class PredefinedFilterComponent implements OnInit, AfterViewInit, OnDestr this.subscriptions.forEach((s) => s.unsubscribe()); } + /** + * Creates a mapping of all the incoming filters, creates form controls and options for select-x fields + */ + public getFilterTypes(): void { + this.filterTypes = this.filters.map((filter) => ({ + type: this.determineFilterType(filter), + currentValue: filter?.Delimiter + ? new FormControl(filter?.CurrentValue?.split(filter.Delimiter) ?? []) + : new FormControl(filter?.CurrentValue), + options: + filter.Options?.map((option: DataModelFilterOption) => ({ + display: option.Display ?? '', + value: option.Value ?? '', + option: option, + })) ?? [], + })); + } + + /** + * Determines which field we want to show on screen + * @param filter current state of a filter + * @returns The specific enum to generate a html element + */ + public determineFilterType(filter: DataSourceToolbarFilter): FilterTypes | undefined { + switch (true) { + case filter?.Options?.length === 1: + return FilterTypes.SingleCheck; + case filter.Delimiter && (filter?.Options?.length ?? 0) <= this.selectOptionThreshold: + return FilterTypes.MultiCheck; + case filter.Delimiter && (filter?.Options?.length ?? 0) > this.selectOptionThreshold: + return FilterTypes.SelectCheck; + case !filter.Delimiter && (filter?.Options?.length ?? 0) <= this.selectOptionThreshold: + return FilterTypes.MultiRadio; + case !filter.Delimiter && (filter?.Options?.length ?? 0) > this.selectOptionThreshold: + return FilterTypes.SelectRadio; + } + } + public onCheckboxFilterChanged(filter: DataSourceToolbarFilter, option: DataModelFilterOption, event: MatCheckboxChange): void { let selectedFilterData: DataSourceToolbarSelectedFilter; if (event.checked) { @@ -177,30 +275,39 @@ export class PredefinedFilterComponent implements OnInit, AfterViewInit, OnDestr this.filterService.formStatusChanged(this.formState); } - public getMultiSelectCurrentValue(filter: DataSourceToolbarFilter): string[] { - let display = []; - if (filter.Delimiter && filter.CurrentValue) { - display = filter.CurrentValue.split(filter.Delimiter); - } - return display; - } - - public multiSelectFilterValueChange(filter: DataSourceToolbarFilter, event: MatSelectChange): void { - filter.CurrentValue = undefined; - const relevantSelectedItems = this.internalSelectedFilters.filter((sfilter) => sfilter.filter.Name === filter.Name); - relevantSelectedItems.forEach((rsi) => { - this.removeSelectedFilter(filter, false, rsi.selectedOption.Value); + /** + * Handles the adding of new and removal of unselected filters. Also sets the current value using the delimiter + * @param filter current filter having options changed + * @param options option set to apply as filters + */ + public onMultiSelectFilterChanged(filter: DataSourceToolbarFilter, options: EuiSelectOption | EuiSelectOption[]): void { + // Add new options + let selectedFilterData: DataSourceToolbarSelectedFilter; + options.forEach((option) => { + selectedFilterData = { selectedOption: option.option, filter }; + if ( + !this.internalSelectedFilters.some( + (internalFilter) => internalFilter.filter?.Name === filter.Name && internalFilter.filter?.CurrentValue?.includes(option.value), + ) + ) { + // If this is a new selection + this.internalSelectedFilters.push(selectedFilterData); + } }); - event.value.forEach((value) => { - const option = this.findFilterOptionFromValue(value, filter); - this.internalSelectedFilters.push({ selectedOption: option, filter }); + // Check all selected filters for values not in the optionset, remove them + const optionValues = options.map((option) => option.value); + this.internalSelectedFilters.forEach((internalFilter) => { + if (internalFilter.filter?.Name === filter.Name && !optionValues.includes(internalFilter.selectedOption?.Value)) { + this.removeSelectedFilter(filter, false, internalFilter.selectedOption?.Value); + } }); - this.rebuildSelectedDelimitedValue(filter); + // Set current value, mark dirty + filter.CurrentValue = optionValues.join(filter.Delimiter); this.formState.dirty = true; this.filterService.formStatusChanged(this.formState); } - public onRadioFilterChanged(filter: DataSourceToolbarFilter, option: DataModelFilterOption): void { + public onRadioFilterChanged(filter: DataSourceToolbarFilter, option: any): void { let selectedFilterData: DataSourceToolbarSelectedFilter; filter.CurrentValue = option ? option.Value : undefined; selectedFilterData = { selectedOption: option, filter }; @@ -214,9 +321,27 @@ export class PredefinedFilterComponent implements OnInit, AfterViewInit, OnDestr this.filterService.formStatusChanged(this.formState); } - public selectFilterValueChanged(filter: DataSourceToolbarFilter, event: MatSelectChange): void { - const option = this.findFilterOptionFromValue(event.value, filter); - this.onRadioFilterChanged(filter, option); + public onSelectFilterChanged(filter: DataSourceToolbarFilter, option: EuiSelectOption | EuiSelectOption[]): void { + let selectedFilterData: DataSourceToolbarSelectedFilter; + const selectedOption: EuiSelectOption = Array.isArray(option) ? option[0] : option; + filter.CurrentValue = selectedOption ? selectedOption.value : undefined; + selectedFilterData = { selectedOption: { Value: selectedOption.value, Display: selectedOption.display }, filter }; + const index = this.findSelectedFilterIndex(filter.Name); + if (index >= 0) { + this.internalSelectedFilters[index] = selectedFilterData; + } else { + this.internalSelectedFilters.push(selectedFilterData); + } + this.formState.dirty = true; + this.filterService.formStatusChanged(this.formState); + } + + /** + * Use clear from select-x to remove any options applied to it + * @param filter current state of filter + */ + public onClearFilter(filter: DataSourceToolbarFilter): void { + this.removeSelectedFilter(filter, false); this.formState.dirty = true; this.filterService.formStatusChanged(this.formState); } @@ -227,6 +352,9 @@ export class PredefinedFilterComponent implements OnInit, AfterViewInit, OnDestr */ public applyDynamicPropsAsSelectedFilters(config: DSTViewConfig): void { // Handle filters from dynamic properties + if (!config.AdditionalParameters) { + return; + } Object.entries(config.AdditionalParameters).forEach(([filterName, value]) => { const filter = this.getSelectedFilterFromName(filterName, value); if (filter) { @@ -241,7 +369,7 @@ export class PredefinedFilterComponent implements OnInit, AfterViewInit, OnDestr * @param value value of the filter * @returns the filter with the selected option */ - public getSelectedFilterFromName(filterName: string, value: string): DataSourceToolbarSelectedFilter { + public getSelectedFilterFromName(filterName: string, value: string): DataSourceToolbarSelectedFilter | undefined { const filter = this.filters?.find((filter) => filter.Name === filterName); if (filter) { filter.CurrentValue = value; @@ -254,7 +382,7 @@ export class PredefinedFilterComponent implements OnInit, AfterViewInit, OnDestr filter: DataSourceToolbarFilter, emitChange: boolean = true, optionValue?: string, - selectedFilter?: DataSourceToolbarSelectedFilter + selectedFilter?: DataSourceToolbarSelectedFilter, ): void { filter.CurrentValue = undefined; const index = this.findSelectedFilterIndex(filter.Name, optionValue); @@ -324,15 +452,22 @@ export class PredefinedFilterComponent implements OnInit, AfterViewInit, OnDestr this.selectedFilters = Object.create(this.internalSelectedFilters); this.filters?.forEach((filter) => { if (filter.CurrentValue) { - this.settings.navigationState[filter.Name] = filter.CurrentValue; - this.settings.filters.find(elem=>elem.Name === filter.Name).CurrentValue = filter.CurrentValue; + if (filter.Name) { + this.settings.navigationState[filter.Name] = filter.CurrentValue; + } + if (this.settings.filters?.find((elem) => elem.Name === filter.Name)) { + const test = this.settings.filters?.find((elem) => elem.Name === filter.Name); + if (test) test.CurrentValue = filter.CurrentValue; + } if (filter?.Column) { // This is a local filter and we must filter over this column this.localFilterState.filterColumns[filter.Column] = filter.CurrentValue; } } else { - delete this.settings.navigationState[filter.Name]; - delete this.settings.filters.find((elem) => elem.Name === filter.Name).CurrentValue; + if (filter.Name) { + delete this.settings.navigationState[filter.Name]; + } + delete this.settings.filters?.find((elem) => elem.Name === filter.Name)?.CurrentValue; if (filter?.Column) { delete this.localFilterState.filterColumns[filter.Column]; } @@ -346,7 +481,9 @@ export class PredefinedFilterComponent implements OnInit, AfterViewInit, OnDestr // Do filter locally this.localFilter(); } else { - this.filterService.updateNavigation(this.id, this.settings.navigationState, this.selectedFilters); + if (this.id) { + this.filterService.updateNavigation(this.id, this.settings.navigationState, this.selectedFilters); + } } } @@ -366,9 +503,9 @@ export class PredefinedFilterComponent implements OnInit, AfterViewInit, OnDestr * @ignore Used internally * Finds the relevant DataModelFilterOption from the supplied option value and filter */ - private findFilterOptionFromValue(optionValue: string, filter: DataSourceToolbarFilter): DataModelFilterOption { - const index = filter.Options.map((opt) => opt.Value).indexOf(optionValue); - return filter.Options[index]; + private findFilterOptionFromValue(optionValue: string, filter: DataSourceToolbarFilter): DataModelFilterOption | undefined { + const index = filter.Options?.map((opt) => opt.Value).indexOf(optionValue); + return index ? filter.Options?.[index] : undefined; } /** @@ -376,12 +513,14 @@ export class PredefinedFilterComponent implements OnInit, AfterViewInit, OnDestr * Attempts to find an existing selected filter matching the given name. * Returns the index or -1 if no match was found */ - private findSelectedFilterIndex(filterName: string, optionValue?: string): number { + private findSelectedFilterIndex(filterName: string | undefined, optionValue?: string): number { let index: number; if (optionValue) { - index = this.internalSelectedFilters.map((f) => f.filter.Name + f.selectedOption.Value).indexOf(filterName + optionValue); + index = this.internalSelectedFilters + .map((f) => f.filter?.Name ?? '' + f.selectedOption?.Value ?? '') + .indexOf(filterName + optionValue); } else { - index = this.internalSelectedFilters.map((f) => f.filter.Name).indexOf(filterName); + index = this.internalSelectedFilters.map((f) => f.filter?.Name).indexOf(filterName); } return index; } @@ -393,8 +532,8 @@ export class PredefinedFilterComponent implements OnInit, AfterViewInit, OnDestr private rebuildSelectedDelimitedValue(filter: DataSourceToolbarFilter): void { let val = ''; this.internalSelectedFilters.forEach((sfilter) => { - if (sfilter.filter.Name === filter.Name) { - val += `${sfilter.selectedOption.Value}${filter.Delimiter}`; + if (sfilter.filter?.Name === filter.Name) { + val += `${sfilter.selectedOption?.Value}${filter.Delimiter}`; } }); filter.CurrentValue = val.length ? val.slice(0, -1) : undefined; @@ -415,7 +554,7 @@ export class PredefinedFilterComponent implements OnInit, AfterViewInit, OnDestr const val = (entity.GetColumn(column).GetValue() as string).toLocaleLowerCase(); searchResult = this.localFilterState.keywords ? val.includes(this.localFilterState.keywords.toLocaleLowerCase()) : false; // Allow for short circuiting - searchResult &&= this.searchTerms.every((term) => val.includes(term.selectedOption.Display)); + searchResult &&= this.searchTerms.every((term) => val.includes(term.selectedOption?.Display ?? '')); if (searchResult) { // Exit for loop if we have a positive hit break; diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/save-config-dialog/save-config-dialog.component.html b/imxweb/projects/qbm/src/lib/data-source-toolbar/save-config-dialog/save-config-dialog.component.html index 198d1cf0e..0ea9dfb92 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/save-config-dialog/save-config-dialog.component.html +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/save-config-dialog/save-config-dialog.component.html @@ -1,13 +1,26 @@ -

    {{(this.data?.currentName ? '#LDS#Heading Edit Name of View' : '#LDS#Heading Save Current View') | translate}}

    +

    {{ (this.data?.currentName ? '#LDS#Heading Edit Name of View' : '#LDS#Heading Save Current View') | translate }}

    - + + +
    + {{ '#LDS#Note: The order of the columns is not saved.' | translate }} +
    +
    - {{'#LDS#Name of view' | translate}} - + {{ '#LDS#Name of view' | translate }} +
    - - - + + + diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/save-config-dialog/save-config-dialog.component.scss b/imxweb/projects/qbm/src/lib/data-source-toolbar/save-config-dialog/save-config-dialog.component.scss index 644323825..89c9b6c56 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/save-config-dialog/save-config-dialog.component.scss +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/save-config-dialog/save-config-dialog.component.scss @@ -1,5 +1,5 @@ :host { - .mat-dialog-content { + .mat-mdc-dialog-content { padding-top: 5px; padding-bottom: 5px; } diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/save-config-dialog/save-config-dialog.component.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/save-config-dialog/save-config-dialog.component.ts index 5e5eb9746..12c73013f 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/save-config-dialog/save-config-dialog.component.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/save-config-dialog/save-config-dialog.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,28 +25,27 @@ */ import { Component, Inject } from '@angular/core'; -import {FormControl, Validators} from '@angular/forms'; -import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; - +import { FormControl, Validators } from '@angular/forms'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; @Component({ selector: 'imx-save-config-dialog', templateUrl: './save-config-dialog.component.html', - styleUrls: ['./save-config-dialog.component.scss'] + styleUrls: ['./save-config-dialog.component.scss'], }) export class SaveConfigDialogComponent { - - public formControl = new FormControl('', Validators.required) + public formControl = new FormControl('', Validators.required); public get displayName(): string { - return this.formControl.value; + return this.formControl.value ?? ''; } constructor( private dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data?: { - currentName: string - } + @Inject(MAT_DIALOG_DATA) + public data?: { + currentName: string; + }, ) { if (data?.currentName) { this.formControl.reset(data.currentName); @@ -58,7 +57,6 @@ export class SaveConfigDialogComponent { } public get isNew(): boolean { - return this.data?.currentName ? this.formControl.value !== this.data.currentName: true; + return this.data?.currentName ? this.formControl.value !== this.data.currentName : true; } - } diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/selection-model-wrapper.spec.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/selection-model-wrapper.spec.ts index bbe21af39..12f159dfe 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/selection-model-wrapper.spec.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/selection-model-wrapper.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,17 +24,18 @@ * */ -import { TypedEntity } from 'imx-qbm-dbts'; +import { TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { SelectionModelWrapper } from './selection-model-wrapper'; describe('SelectionModelWrapper select-deselect', () => { - const createSomeTypedEntity = (keys = ['']) => ({ - GetEntity: () => ({ - GetKeys: () => keys - }) - }) as TypedEntity; + const createSomeTypedEntity = (keys = ['']) => + ({ + GetEntity: () => ({ + GetKeys: () => keys, + }), + }) as TypedEntity; - const getKey = entity => entity.GetEntity().GetKeys().join(); + const getKey = (entity) => entity.GetEntity().GetKeys().join(); it('should select an item', () => { const selection = new SelectionModelWrapper(); @@ -45,7 +46,7 @@ describe('SelectionModelWrapper select-deselect', () => { selection.checked(entity); - expect(selection.selected.map(e => getKey(e))).toContain(getKey(entity)); + expect(selection.selected.map((e) => getKey(e))).toContain(getKey(entity)); }); it('should deselect an item', () => { @@ -59,6 +60,6 @@ describe('SelectionModelWrapper select-deselect', () => { selection.unChecked(entity); - expect(selection.selected.map(e => getKey(e))).not.toContain(getKey(entity)); + expect(selection.selected.map((e) => getKey(e))).not.toContain(getKey(entity)); }); -}); \ No newline at end of file +}); diff --git a/imxweb/projects/qbm/src/lib/data-source-toolbar/selection-model-wrapper.ts b/imxweb/projects/qbm/src/lib/data-source-toolbar/selection-model-wrapper.ts index b9ce96777..6f2922f12 100644 --- a/imxweb/projects/qbm/src/lib/data-source-toolbar/selection-model-wrapper.ts +++ b/imxweb/projects/qbm/src/lib/data-source-toolbar/selection-model-wrapper.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,25 +27,25 @@ import { SelectionChange, SelectionModel } from '@angular/cdk/collections'; import { Subject } from 'rxjs'; -import { TypedEntity } from 'imx-qbm-dbts'; +import { TypedEntity } from '@imx-modules/imx-qbm-dbts'; -export class SelectionModelWrapper { - public get changed(): Subject> { +export class SelectionModelWrapper { + public get changed(): Subject> { return this.selection.changed; } - public get selected(): ReadonlyArray { + public get selected(): ReadonlyArray { return this.selection.selected; } public get numOfSelectableItems(): number { - return this.selectableItems.filter(v => v).length; + return this.selectableItems.filter((v) => v).length; } /** * Selection model that handles multiple selection in the data source view. */ - private selection = new SelectionModel(true, []); + private selection = new SelectionModel(true, []); /** * Selection cache dictionary used by the data source view to get the check/uncheck state of an item if mutiselect is enabled. @@ -72,14 +72,14 @@ export class SelectionModelWrapper { /** * Checks if an item is selected. */ - public isSelected(item: TypedEntity): boolean { + public isSelected(item: T): boolean { return this.selectionCache[this.getId(item)]; } /** * Toggles selection state. */ - public toggle(item: TypedEntity): void { + public toggle(item: T): void { if (this.isSelected(item)) { this.unChecked(item); } else { @@ -90,10 +90,10 @@ export class SelectionModelWrapper { /** * Selects an item. */ - public checked(item: TypedEntity): void { + public checked(item: T): void { this.selection.select(item); - const found = this.selection.selected.find(x => this.getId(x) === this.getId(item)); + const found = this.selection.selected.find((x) => this.getId(x) === this.getId(item)); if (found) { this.selectionCache[this.getId(item)] = true; } @@ -102,16 +102,26 @@ export class SelectionModelWrapper { /** * Deselects an item. */ - public unChecked(item: TypedEntity): void { - const found = this.selection.selected.find(x => this.getId(x) === this.getId(item)); - + public unChecked(item: T): void { + const found = this.selection.selected.find((x) => this.getId(x) === this.getId(item)); if (found) { this.selectionCache[this.getId(item)] = false; this.selection.deselect(found); } } - private getId(item: TypedEntity): string { + public select(items: T[]): void { + this.selection.select(...items); + items.forEach((item) => (this.selectionCache[this.getId(item)] = true)); + } + + public setSelection(items: any[]): void { + this.selection.setSelection(...(items as T[])); + this.selectionCache = {}; + items.forEach((item) => (this.selectionCache[this.getId(item as T)] = true)); + } + + private getId(item: T): string { return item.GetEntity().GetKeys().join(','); } } diff --git a/imxweb/projects/qbm/src/lib/data-table/data-table-additional-info.model.ts b/imxweb/projects/qbm/src/lib/data-table/data-table-additional-info.model.ts index be3fe92c6..b7874e18a 100644 --- a/imxweb/projects/qbm/src/lib/data-table/data-table-additional-info.model.ts +++ b/imxweb/projects/qbm/src/lib/data-table/data-table-additional-info.model.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,13 +24,13 @@ * */ -import { IClientProperty, IEntity } from 'imx-qbm-dbts'; +import { IClientProperty, IEntity } from '@imx-modules/imx-qbm-dbts'; export function buildAdditionalElementsString(entity: IEntity, additionals: IClientProperty[], separator: string = '; '): string { return additionals == null ? '' : additionals - .filter((elem) => entity.GetColumn(elem?.ColumnName)?.GetDisplayValue() !== '') - .map((elem) => `${elem?.Display || elem?.ColumnName}: ${entity.GetColumn(elem?.ColumnName)?.GetDisplayValue()}`) + .filter((elem) => entity.GetColumn(elem?.ColumnName ?? '')?.GetDisplayValue() !== '') + .map((elem) => `${elem?.Display || elem?.ColumnName}: ${entity.GetColumn(elem?.ColumnName ?? '')?.GetDisplayValue()}`) .join(separator); } diff --git a/imxweb/projects/qbm/src/lib/data-table/data-table-cell/data-table-cell.component.html b/imxweb/projects/qbm/src/lib/data-table/data-table-cell/data-table-cell.component.html index 5f8a69828..c7b1abdca 100644 --- a/imxweb/projects/qbm/src/lib/data-table/data-table-cell/data-table-cell.component.html +++ b/imxweb/projects/qbm/src/lib/data-table/data-table-cell/data-table-cell.component.html @@ -1,6 +1,3 @@ - {{ property?.Type === ValType.Date - ? (column?.GetValue() | shortDate ) - : column?.GetDisplayValue() - }} + {{ property?.Type === ValType.Date ? (column?.GetValue() | shortDate) : column?.GetDisplayValue() }} diff --git a/imxweb/projects/qbm/src/lib/data-table/data-table-cell/data-table-cell.component.scss b/imxweb/projects/qbm/src/lib/data-table/data-table-cell/data-table-cell.component.scss index c9b9348a2..7d74d47cd 100644 --- a/imxweb/projects/qbm/src/lib/data-table/data-table-cell/data-table-cell.component.scss +++ b/imxweb/projects/qbm/src/lib/data-table/data-table-cell/data-table-cell.component.scss @@ -1 +1 @@ -/* for some styles */ \ No newline at end of file +/* for some styles */ diff --git a/imxweb/projects/qbm/src/lib/data-table/data-table-cell/data-table-cell.component.ts b/imxweb/projects/qbm/src/lib/data-table/data-table-cell/data-table-cell.component.ts index 19f6bb133..953a5e13a 100644 --- a/imxweb/projects/qbm/src/lib/data-table/data-table-cell/data-table-cell.component.ts +++ b/imxweb/projects/qbm/src/lib/data-table/data-table-cell/data-table-cell.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,25 +26,23 @@ import { Component, Input } from '@angular/core'; -import { IClientProperty, IEntityColumn, TypedEntity, ValType } from 'imx-qbm-dbts'; +import { IClientProperty, IEntityColumn, TypedEntity, ValType } from '@imx-modules/imx-qbm-dbts'; @Component({ selector: 'imx-data-table-cell', templateUrl: './data-table-cell.component.html', - styleUrls: ['./data-table-cell.component.scss'] + styleUrls: ['./data-table-cell.component.scss'], }) export class DataTableCellComponent { - public readonly ValType = ValType; @Input() public entity: TypedEntity; @Input() public property: IClientProperty; - - public get column(): IEntityColumn{ - try{ - return this.entity?.GetEntity()?.GetColumn(this.property?.ColumnName); - }catch { + public get column(): IEntityColumn | undefined { + try { + return this.entity?.GetEntity()?.GetColumn(this.property?.ColumnName ?? ''); + } catch { return undefined; } } diff --git a/imxweb/projects/qbm/src/lib/data-table/data-table-column.component.html b/imxweb/projects/qbm/src/lib/data-table/data-table-column.component.html index 80c0a93a1..a70809b66 100644 --- a/imxweb/projects/qbm/src/lib/data-table/data-table-column.component.html +++ b/imxweb/projects/qbm/src/lib/data-table/data-table-column.component.html @@ -4,7 +4,7 @@ {{ columnLabel }}
    - {{ translateProvider.GetColumnDisplay(entityColumn?.ColumnName, entitySchema) }} + {{ translateProvider.GetColumnDisplay(entityColumn?.ColumnName ?? '', entitySchema) }}
    diff --git a/imxweb/projects/qbm/src/lib/data-table/data-table-column.component.scss b/imxweb/projects/qbm/src/lib/data-table/data-table-column.component.scss index 091367d96..fe132278e 100644 --- a/imxweb/projects/qbm/src/lib/data-table/data-table-column.component.scss +++ b/imxweb/projects/qbm/src/lib/data-table/data-table-column.component.scss @@ -1,43 +1,3 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; -.imx-table-column { - padding-right: 15px; -} - -td.mat-cell ::ng-deep div[subtitle] { - font-size: smaller; - color: $black-9; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - max-width: 500px; -} - -::ng-deep .eui-sidesheet__content td.mat-cell ::ng-deep div[subtitle] { - max-width: 250px; -} - -@media screen and (max-width: 768px) { - td.mat-cell ::ng-deep div[subtitle] { - max-width: 250px; - } -} - -.eui-dark-theme { - :host { - .mat-header-cell { - color: $color-gray-10; - border-bottom-color: $color-gray-60; - } - } -} - -.eui-contrast-theme { - :host { - .mat-header-cell { - color: $color-gray-0; - border-bottom-color: $color-gray-60; - } - } -} diff --git a/imxweb/projects/qbm/src/lib/data-table/data-table-column.component.ts b/imxweb/projects/qbm/src/lib/data-table/data-table-column.component.ts index 2f0098bd3..47b6c11f7 100644 --- a/imxweb/projects/qbm/src/lib/data-table/data-table-column.component.ts +++ b/imxweb/projects/qbm/src/lib/data-table/data-table-column.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,16 +24,9 @@ * */ -import { - Component, - Input, - ViewChild, - TemplateRef, - ContentChild, - OnInit -} from '@angular/core'; +import { Component, ContentChild, Input, OnInit, TemplateRef, ViewChild } from '@angular/core'; import { MatColumnDef } from '@angular/material/table'; -import { IClientProperty, EntitySchema } from 'imx-qbm-dbts'; +import { EntitySchema, IClientProperty } from '@imx-modules/imx-qbm-dbts'; import { ImxTranslationProviderService } from '../translation/imx-translation-provider.service'; /** @@ -64,10 +57,9 @@ import { ImxTranslationProviderService } from '../translation/imx-translation-pr @Component({ selector: 'imx-data-table-column', templateUrl: './data-table-column.component.html', - styleUrls: ['./data-table-column.component.scss'] + styleUrls: ['./data-table-column.component.scss'], }) export class DataTableColumnComponent implements OnInit { - /** * Set alignment of column header and content */ @@ -94,17 +86,16 @@ export class DataTableColumnComponent implements OnInit { */ @Input() public alignContent: 'left' | 'center' | 'right' = 'left'; - public columnIndex: number; /** * Describes a typed entity property. */ @Input() - public get entityColumn(): IClientProperty { + public get entityColumn(): IClientProperty | undefined { return this.entityColumnField; } - public set entityColumn(value: IClientProperty) { + public set entityColumn(value: IClientProperty | undefined) { this.entityColumnField = value; } @@ -119,24 +110,22 @@ export class DataTableColumnComponent implements OnInit { */ @ViewChild(MatColumnDef, { static: true }) public columnDef: MatColumnDef; - /** + /** * The schema of a typed entity */ - public entitySchema: EntitySchema; + public entitySchema: EntitySchema; /** * @ignore Used internally. */ - private entityColumnField: IClientProperty; + private entityColumnField: IClientProperty | undefined; /** * Inject the 'translateProvider' for use in the template. */ - constructor( - public readonly translateProvider: ImxTranslationProviderService - ) {} + constructor(public readonly translateProvider: ImxTranslationProviderService) {} public ngOnInit(): void { - this.columnDef.name = this.entityColumnField.ColumnName; + this.columnDef.name = this.entityColumnField?.ColumnName || ''; } } diff --git a/imxweb/projects/qbm/src/lib/data-table/data-table-display-cell/data-table-display-cell.component.html b/imxweb/projects/qbm/src/lib/data-table/data-table-display-cell/data-table-display-cell.component.html index b4566cb66..b6e8734e9 100644 --- a/imxweb/projects/qbm/src/lib/data-table/data-table-display-cell/data-table-display-cell.component.html +++ b/imxweb/projects/qbm/src/lib/data-table/data-table-display-cell/data-table-display-cell.component.html @@ -1,7 +1,14 @@
    - {{ property?.Type === ValType.Date - ? (entity?.GetEntity()?.GetColumn(property.ColumnName)?.GetValue() | shortDate ) - : entity?.GetEntity()?.GetColumn(property.ColumnName)?.GetDisplayValue() + {{ + property?.Type === ValType.Date + ? (entity + ?.GetEntity() + ?.GetColumn(property.ColumnName ?? '') + ?.GetValue() | shortDate) + : entity + ?.GetEntity() + ?.GetColumn(property.ColumnName ?? '') + ?.GetDisplayValue() }}
    -
    {{ getSubtitleText()}}
    +
    {{ getSubtitleText() }}
    diff --git a/imxweb/projects/qbm/src/lib/data-table/data-table-display-cell/data-table-display-cell.component.ts b/imxweb/projects/qbm/src/lib/data-table/data-table-display-cell/data-table-display-cell.component.ts index 541cb527c..dba53ded6 100644 --- a/imxweb/projects/qbm/src/lib/data-table/data-table-display-cell/data-table-display-cell.component.ts +++ b/imxweb/projects/qbm/src/lib/data-table/data-table-display-cell/data-table-display-cell.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,16 +26,15 @@ import { Component, Input } from '@angular/core'; -import { IClientProperty, TypedEntity, ValType } from 'imx-qbm-dbts'; +import { IClientProperty, TypedEntity, ValType } from '@imx-modules/imx-qbm-dbts'; import { buildAdditionalElementsString } from '../data-table-additional-info.model'; @Component({ selector: 'imx-data-table-display-cell', templateUrl: './data-table-display-cell.component.html', - styleUrls: ['./data-table-display-cell.component.scss'] + styleUrls: ['./data-table-display-cell.component.scss'], }) export class DataTableDisplayCellComponent { - public readonly ValType = ValType; @Input() public entity: TypedEntity; diff --git a/imxweb/projects/qbm/src/lib/data-table/data-table-generic-column.component.html b/imxweb/projects/qbm/src/lib/data-table/data-table-generic-column.component.html index 71d6a3534..ad5fbe8ca 100644 --- a/imxweb/projects/qbm/src/lib/data-table/data-table-generic-column.component.html +++ b/imxweb/projects/qbm/src/lib/data-table/data-table-generic-column.component.html @@ -1,8 +1,8 @@ - -
    - {{ columnLabel }} + + + {{ columnLabel }} + + {{ grouping?.Display[0].Display || '' }}
    @@ -33,12 +39,12 @@ (selectionChanged)="selectionInGroupChanged($event, grouping?.Display[0]?.Display)" [parentManualColumns]="manualColumns" [parentManualGenericColumns]="manualGenericColumns" - [parentAdditionals]="dst?.additionalColumns" + [parentAdditionals]="dst.additionalColumns" [noDataText]="noDataText" [noDataIcon]="noDataIcon" [noMatchingDataText]="noMatchingDataText" [noMatchingDataIcon]="noMatchingDataIcon" - [groupedTableHasFiltersApplied]="dst?.filtersCurrentlyApplied" + [groupedTableHasFiltersApplied]="dst.filtersCurrentlyApplied" > @@ -66,9 +72,9 @@ matSort data-imx-identifier="datatable-table" [ngClass]="{ - 'imx-data-table__hidden': isGroupingApplied || !dst?.dataSourceHasData || isLoading, + 'imx-data-table__hidden': isGroupingApplied || !dst.dataSourceHasData || isLoading, 'imx-data-table-highlightedActive': ishighlightedEntityChangedUsed, - hidden: isLoading + hidden: isLoading, }" role="grid" > @@ -103,13 +109,15 @@ -
    {{ column.Display }} + {{ translateProvider.GetColumnDisplay(column.ColumnName ?? '', entitySchema) }} + @@ -120,7 +128,9 @@ - {{ column.Display }} + {{ translateProvider.GetColumnDisplay(column.ColumnName ?? '', entitySchema) }} +
    -
    - -

    +
    + +

    diff --git a/imxweb/projects/qbm/src/lib/data-table/data-table.component.scss b/imxweb/projects/qbm/src/lib/data-table/data-table.component.scss index d3c633280..380a54a96 100644 --- a/imxweb/projects/qbm/src/lib/data-table/data-table.component.scss +++ b/imxweb/projects/qbm/src/lib/data-table/data-table.component.scss @@ -13,37 +13,9 @@ display: none; } - .imx-data-table-grouped { - flex: 1 1 auto; - display: flex; - flex-direction: column; - overflow: hidden; - - .spaced-left { - margin-left: 5px; - } - imx-data-table{ - overflow-y: unset; - } - td.mat-cell { - padding-top: 12px; - - div { - overflow: hidden; - - &.group-row-expanded { - margin-bottom: 15px; - } - } - } + - .imx-data-table-grouped-content { - flex: 1 1 auto; - overflow: auto; - } - } - - .imx-data-table-no-results { + .imx-no-results { text-align: center; margin: 20px 0; flex: 1 1 auto; @@ -51,11 +23,6 @@ flex-direction: column; justify-content: center; - .eui-icon { - font-size: 100px; - color: $color-gray-10; - } - p { margin: 0; font-size: 18px; @@ -63,64 +30,6 @@ } } - // Handle background row coloring - .imx-data-table-highlightedActive .mat-row:hover { - background-color: $color-blue-10; - } - - // Handle cursor icons, make sure divs are minimal so we don't have the text icon too much - .imx-data-table-highlightedActive ::ng-deep.mat-cell { - &:hover { - cursor: pointer; - } - - // Make sure to exclude checkboxes as they behave differently with content fitting - > *:not(.mat-checkbox) { - display: flex; - height: fit-content; - - &:hover { - cursor: text; - } - } - - > .mat-button, - .mat-stroked-button, - .mat-raised-button, - .mat-flat-button, - .mat-icon-button { - &:hover { - cursor: pointer; - } - } - } - - .imx-data-table-row-highlighted { - background-color: $color-gray-5; - } - - .imx-data-table-row-conditional { - background-color: $color-red-20; - } - - .mat-column-select { - overflow: initial; - max-width: 50px; - width: 15px; - } - - td.mat-cell { - padding-right: 10px; - } - - table.mat-table { - box-shadow: none; - padding: 1px; - } - .imx-table-column { - padding-right: 15px; - } - .hidden { display: none; } @@ -130,30 +39,8 @@ :host { background-color: $color-gray-70; - .mat-table { - background-color: $color-gray-70; - } - - .imx-data-table-row-highlighted { - background-color: $color-gray-60; - } - .imx-data-table-highlightedActive .mat-row:hover { - background-color: $color-gray-60; - } - - .imx-data-table-row-conditional { - background-color: $color-red-80; - } - - ::ng-deep .custom-row td { - border-bottom-color: $color-gray-60; - } - - .imx-data-table-no-results { - eui-icon { - color: $color-gray-20; - } + .imx-no-results { p { color: $color-gray-10; } @@ -165,57 +52,7 @@ :host { background-color: $color-gray-90; - .mat-table { - background-color: $color-gray-90; - } - - .imx-data-table-row-highlighted, - .imx-data-table-highlightedActive .mat-row:hover { - background-color: $color-gray-0; - - ::ng-deep td.mat-cell { - color: $color-gray-100; - } - - ::ng-deep .mat-checkbox-frame, - ::ng-deep .mat-radio-outer-circle { - border-color: $color-gray-100; - } - } - - table { - border-top: none; - } - - ::ng-deep th:after, - ::ng-deep th:before { - content: ''; - position: absolute; - left: 0; - width: 100%; - } - ::ng-deep th:before { - top: 0; - border-top: 1px solid white; - } - ::ng-deep th:after { - bottom: 0; - border-bottom: 1px solid white; - } - - .imx-data-table-row-conditional { - background-color: $color-red-80; - } - - .imx-data-table-selection-info { - color: $color-blue-40; - } - - .imx-data-table-no-results { - eui-icon { - color: $color-gray-10; - } - + .imx-no-results { p { color: $color-gray-0; } diff --git a/imxweb/projects/qbm/src/lib/data-table/data-table.component.ts b/imxweb/projects/qbm/src/lib/data-table/data-table.component.ts index 384333e04..4c507af80 100644 --- a/imxweb/projects/qbm/src/lib/data-table/data-table.component.ts +++ b/imxweb/projects/qbm/src/lib/data-table/data-table.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,37 +27,36 @@ import { animate, state, style, transition, trigger } from '@angular/animations'; import { SelectionChange } from '@angular/cdk/collections'; import { + AfterViewInit, + ChangeDetectorRef, Component, - ViewChild, + ContentChildren, + EventEmitter, Input, - Output, OnChanges, - SimpleChanges, - EventEmitter, - ContentChildren, - QueryList, OnDestroy, - AfterViewInit, OnInit, - ChangeDetectorRef, + Output, + QueryList, + SimpleChanges, + ViewChild, } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; -import { MatTable, MatColumnDef, MatTableDataSource } from '@angular/material/table'; +import { MatColumnDef, MatTable, MatTableDataSource } from '@angular/material/table'; import { Subscription } from 'rxjs'; -import { TypedEntity, IClientProperty, EntitySchema, CollectionLoadParameters, GroupInfo, GroupInfoData } from 'imx-qbm-dbts'; +import { EuiLoadingService } from '@elemental-ui/core'; +import { CollectionLoadParameters, EntitySchema, GroupInfo, GroupInfoData, IClientProperty, TypedEntity } from '@imx-modules/imx-qbm-dbts'; +import { debounce } from 'lodash'; +import { ColumnOptions } from '../data-source-toolbar/column-options'; +import { DataSourceToolbarSettings } from '../data-source-toolbar/data-source-toolbar-settings'; +import { DataSourceToolbarComponent } from '../data-source-toolbar/data-source-toolbar.component'; import { ImxTranslationProviderService } from '../translation/imx-translation-provider.service'; import { DataTableColumnComponent } from './data-table-column.component'; import { DataTableGenericColumnComponent } from './data-table-generic-column.component'; -import { DataSourceToolbarComponent } from '../data-source-toolbar/data-source-toolbar.component'; -import { DataSourceToolbarSettings } from '../data-source-toolbar/data-source-toolbar-settings'; import { DataTableGroupedData } from './data-table-groups.interface'; import { RowHighlight } from './data-table-row-highlight.interface'; import { GroupPaginatorInformation } from './group-paginator/group-paginator.component'; -import { EuiLoadingService } from '@elemental-ui/core'; -import { OverlayRef } from '@angular/cdk/overlay'; -import { debounce } from 'lodash'; -import { ColumnOptions } from '../data-source-toolbar/column-options'; /** * A data table component with a detail view specialized on typed entities. @@ -103,7 +102,7 @@ export class DataTableComponent implements OnInit, OnChanges, AfterViewInit, * Represents the typed entity that is selected when the users clicks on a row. * Not to be confused with the 'selection' property. */ - public highlightedEntity: TypedEntity; + public highlightedEntity: TypedEntity | null; /** * @ignore Used internally in components template. @@ -322,7 +321,7 @@ export class DataTableComponent implements OnInit, OnChanges, AfterViewInit, public translateProvider: ImxTranslationProviderService, public dialog: MatDialog, private readonly busyService: EuiLoadingService, - private readonly changeDetectorRef: ChangeDetectorRef + private readonly changeDetectorRef: ChangeDetectorRef, ) {} public get isGroupingApplied(): boolean { @@ -375,7 +374,7 @@ export class DataTableComponent implements OnInit, OnChanges, AfterViewInit, this.settings = value; await this.dstHasChanged(); } - }) + }), ); this.subscriptions.push( @@ -383,7 +382,7 @@ export class DataTableComponent implements OnInit, OnChanges, AfterViewInit, if (event && event.source) { this.selectionChanged.emit(event.source.selected); } - }) + }), ); this.subscriptions.push( @@ -391,7 +390,7 @@ export class DataTableComponent implements OnInit, OnChanges, AfterViewInit, if (!!this.settings) { await this.dstHasChanged(); } - }) + }), ); if (this.dst.busyService) { @@ -399,7 +398,7 @@ export class DataTableComponent implements OnInit, OnChanges, AfterViewInit, this.dst.busyService.busyStateChanged.subscribe((busy: boolean) => { this.isLoading = busy; this.changeDetectorRef.detectChanges(); - }) + }), ); } this.isLoading = this.dst?.busyService?.isBusy ?? false; @@ -450,10 +449,10 @@ export class DataTableComponent implements OnInit, OnChanges, AfterViewInit, * Gets the display values of displayed columns */ public getNamesOfDisplayedColumns(): string[] { - let displayedColumnNames = []; + let displayedColumnNames: string[] = []; if (this.displayedColumns && this.displayedColumns.length > 0) { - displayedColumnNames = this.displayedColumns.map((item) => item.ColumnName); + displayedColumnNames = this.displayedColumns.map((item) => item.ColumnName ?? ''); } if (this.selectable) { @@ -475,12 +474,12 @@ export class DataTableComponent implements OnInit, OnChanges, AfterViewInit, // Prevent emission for certain cases if (event) { // Make sure we aren't selecting text - if (event.view.getSelection().type === 'Range') { + if (event.view?.getSelection()?.type === 'Range') { return; } // Prevent button clicks from propogating as row clicks, Walk up node chain until we hit table looking if we are a button - let target = event.target as HTMLElement; + let target: HTMLElement | null = event.target as HTMLElement; while (target) { if (target.tagName === 'BUTTON') { return; @@ -509,7 +508,10 @@ export class DataTableComponent implements OnInit, OnChanges, AfterViewInit, * Gets the display value for a specific column. */ public getDisplayValue(entity: TypedEntity, column: IClientProperty): string { - return entity.GetEntity().GetColumn(column.ColumnName).GetDisplayValue(); + return entity + .GetEntity() + .GetColumn(column.ColumnName ?? '') + .GetDisplayValue(); } /** @@ -542,8 +544,11 @@ export class DataTableComponent implements OnInit, OnChanges, AfterViewInit, * Emits an event to allow data to be retrieved from calling code if no data is present */ public onGroupExpanded(group: GroupInfo): void { - if (group && group.Count > 0) { - const groupingDisplay = group.Display[0].Display; + if (group && (group.Count ?? 0) > 0) { + const groupingDisplay = group.Display?.[0].Display; + if (!groupingDisplay) { + return; + } if (!this.groupData[groupingDisplay]) { this.groupData[groupingDisplay] = { data: undefined, @@ -578,6 +583,10 @@ export class DataTableComponent implements OnInit, OnChanges, AfterViewInit, public selectionInGroupChanged(items: TypedEntity[], groupKey: string): void { const groupingData = this.groupData[groupKey]; + if (!groupingData) { + return; + } + setTimeout(() => { if (groupingData.selected) { groupingData.selected.forEach((selectedItem) => { @@ -590,7 +599,7 @@ export class DataTableComponent implements OnInit, OnChanges, AfterViewInit, groupingData.selected = []; items.forEach((item) => { - groupingData.selected.push(item); + groupingData.selected?.push(item); this.checked(item); }); }); @@ -623,8 +632,9 @@ export class DataTableComponent implements OnInit, OnChanges, AfterViewInit, * updated the navigation state for the current grouping and loads its content */ private async updateGroupingState(currentGrouping: any, newState?: CollectionLoadParameters): Promise { - let busyIndicator: OverlayRef; - setTimeout(() => (busyIndicator = this.busyService.show())); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } try { if (newState) { @@ -638,7 +648,7 @@ export class DataTableComponent implements OnInit, OnChanges, AfterViewInit, this.groupedDataSource = new MatTableDataSource(this.groupPaginatorInformation.currentData.Groups); } finally { - setTimeout(() => this.busyService.hide(busyIndicator)); + this.busyService.hide(); } } @@ -651,10 +661,13 @@ export class DataTableComponent implements OnInit, OnChanges, AfterViewInit, if (this.settings && this.settings.entitySchema) { this.entitySchema = this.settings.entitySchema; //update schema with additionals - this.parentAdditionals.concat(this.additional).forEach((element) => { + (this.parentAdditionals ?? []).concat(this.additional).forEach((element) => { const key = ColumnOptions.findKey(element.ColumnName, this.entitySchema); - (this.entitySchema.Columns[key] as any) = element; + if (key) { + (this.entitySchema.Columns[key] as any) = element; + } }); + this.settings.dataSource?.Data.forEach((elem) => elem.GetEntity().ApplySchema(this.entitySchema)); this.manualColumns?.forEach((item: DataTableColumnComponent) => { item.entitySchema = this.entitySchema; }); @@ -692,10 +705,11 @@ export class DataTableComponent implements OnInit, OnChanges, AfterViewInit, if (this.dst.dataSourceChanged || this.dst.shownColumnsSelectionChanged) { this.displayedColumns = []; - this.additional = this.dst == null || this.dst.additionalColumns?.length === 0 ? this.parentAdditionals : this.dst.additionalColumns; + this.additional = + (this.dst == null || this.dst.additionalColumns?.length === 0 ? this.parentAdditionals : this.dst.additionalColumns) ?? []; // filter additionals for columns, that are already set in the DataSourceToolbarSettings this.additional = this.additional.filter((elem) => - this.settings?.displayedColumns?.every((disp) => disp.ColumnName !== elem.ColumnName) + this.settings?.displayedColumns?.every((disp) => disp.ColumnName !== elem.ColumnName), ); if (this.manualColumns == null && this.manualGenericColumns == null) { return; @@ -767,8 +781,11 @@ export class DataTableComponent implements OnInit, OnChanges, AfterViewInit, } if (grouping.isExpanded || skipNavigationChange) { - const preservedGroupingFilter = grouping.navigationState.filter; + const preservedGroupingFilter = grouping?.navigationState?.filter; grouping.navigationState = JSON.parse(JSON.stringify(this.settings.navigationState)); + if (!grouping.navigationState) { + grouping.navigationState = {}; + } grouping.navigationState.filter = preservedGroupingFilter; grouping.navigationState.StartIndex = 0; grouping.navigationState.withProperties = this.settings.navigationState.withProperties; diff --git a/imxweb/projects/qbm/src/lib/data-table/data-table.module.ts b/imxweb/projects/qbm/src/lib/data-table/data-table.module.ts index 1632bd1a2..7a1d533c1 100644 --- a/imxweb/projects/qbm/src/lib/data-table/data-table.module.ts +++ b/imxweb/projects/qbm/src/lib/data-table/data-table.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,8 +24,8 @@ * */ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatDialogModule } from '@angular/material/dialog'; @@ -42,16 +42,16 @@ import { TranslateModule } from '@ngx-translate/core'; import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; +import { BusyIndicatorModule } from '../busy-indicator/busy-indicator.module'; import { DataSourceToolbarModule } from '../data-source-toolbar/data-source-toolbar.module'; -import { DataTableComponent } from './data-table.component'; -import { DataTableColumnComponent } from './data-table-column.component'; -import { DataTableGenericColumnComponent } from './data-table-generic-column.component'; import { DateModule } from '../date/date.module'; import { DataTableCellComponent } from './data-table-cell/data-table-cell.component'; +import { DataTableColumnComponent } from './data-table-column.component'; import { DataTableDisplayCellComponent } from './data-table-display-cell/data-table-display-cell.component'; -import { GroupPaginatorComponent } from './group-paginator/group-paginator.component'; +import { DataTableGenericColumnComponent } from './data-table-generic-column.component'; +import { DataTableComponent } from './data-table.component'; import { ExcludedColumnsPipe } from './excluded-columns.pipe'; -import { BusyIndicatorModule } from '../busy-indicator/busy-indicator.module'; +import { GroupPaginatorComponent } from './group-paginator/group-paginator.component'; import { TableAccessiblilityDirective } from './table-accessibility.directive'; @NgModule({ @@ -63,7 +63,7 @@ import { TableAccessiblilityDirective } from './table-accessibility.directive'; DataTableGenericColumnComponent, GroupPaginatorComponent, ExcludedColumnsPipe, - TableAccessiblilityDirective + TableAccessiblilityDirective, ], imports: [ CommonModule, @@ -84,8 +84,14 @@ import { TableAccessiblilityDirective } from './table-accessibility.directive'; MatToolbarModule, MatDialogModule, BusyIndicatorModule, - TranslateModule + TranslateModule, + ], + exports: [ + DataTableComponent, + DataTableColumnComponent, + DataTableGenericColumnComponent, + ExcludedColumnsPipe, + TableAccessiblilityDirective, ], - exports: [DataTableComponent, DataTableColumnComponent, DataTableGenericColumnComponent, ExcludedColumnsPipe], }) export class DataTableModule {} diff --git a/imxweb/projects/qbm/src/lib/data-table/excluded-columns.pipe.ts b/imxweb/projects/qbm/src/lib/data-table/excluded-columns.pipe.ts index 7fa9387fb..af28fc35e 100644 --- a/imxweb/projects/qbm/src/lib/data-table/excluded-columns.pipe.ts +++ b/imxweb/projects/qbm/src/lib/data-table/excluded-columns.pipe.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,8 +26,7 @@ import { Pipe, PipeTransform } from '@angular/core'; -import { IClientProperty } from 'imx-qbm-dbts'; - +import { IClientProperty } from '@imx-modules/imx-qbm-dbts'; @Pipe({ name: 'excludedColumns', @@ -41,8 +40,6 @@ export class ExcludedColumnsPipe implements PipeTransform { // filter items array, items which match and return true will be // kept, false will be filtered out - return items.filter(elem=> !filter.includes(elem.ColumnName ?? '')); + return items.filter((elem) => !filter.includes(elem.ColumnName ?? '')); } - - } diff --git a/imxweb/projects/qbm/src/lib/data-table/group-paginator/group-paginator.component.html b/imxweb/projects/qbm/src/lib/data-table/group-paginator/group-paginator.component.html index 37a062841..1cb4a54d9 100644 --- a/imxweb/projects/qbm/src/lib/data-table/group-paginator/group-paginator.component.html +++ b/imxweb/projects/qbm/src/lib/data-table/group-paginator/group-paginator.component.html @@ -1 +1,7 @@ - + diff --git a/imxweb/projects/qbm/src/lib/data-table/group-paginator/group-paginator.component.ts b/imxweb/projects/qbm/src/lib/data-table/group-paginator/group-paginator.component.ts index 1012706e7..14f8b4fe4 100644 --- a/imxweb/projects/qbm/src/lib/data-table/group-paginator/group-paginator.component.ts +++ b/imxweb/projects/qbm/src/lib/data-table/group-paginator/group-paginator.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,11 +24,11 @@ * */ -import { Component, Input, Output, EventEmitter, OnChanges, ViewChild, OnDestroy } from '@angular/core'; -import { PageEvent, MatPaginator } from '@angular/material/paginator'; +import { Component, EventEmitter, Input, OnChanges, OnDestroy, Output, ViewChild } from '@angular/core'; +import { MatPaginator, PageEvent } from '@angular/material/paginator'; import { Subscription } from 'rxjs'; -import { CollectionLoadParameters, GroupInfoData } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, GroupInfoData } from '@imx-modules/imx-qbm-dbts'; export interface GroupPaginatorInformation { currentData: GroupInfoData; @@ -49,7 +49,6 @@ export interface GroupPaginatorInformation { @Component({ selector: 'imx-group-paginator', templateUrl: './group-paginator.component.html', - styleUrls: ['../../data-source-toolbar/data-source-paginator.component.scss'], }) export class GroupPaginatorComponent implements OnChanges, OnDestroy { @Input() public groupPaginatorInformation: GroupPaginatorInformation; @@ -120,8 +119,9 @@ export class GroupPaginatorComponent implements OnChanges, OnDestroy { private setPaginator(): void { this.paginator.length = this.groupPaginatorInformation?.currentData?.TotalCount ?? 0; if (this.navigationState) { - this.paginator.pageSize = this.navigationState.PageSize; - this.paginator.pageIndex = this.navigationState.StartIndex / this.navigationState.PageSize; + this.paginator.pageSize = this.navigationState.PageSize ?? 0; + + this.paginator.pageIndex = this.paginator.pageSize ? (this.navigationState.StartIndex ?? 0) / this.paginator.pageSize : 0; } } } diff --git a/imxweb/projects/qbm/src/lib/data-table/table-accessibility.directive.ts b/imxweb/projects/qbm/src/lib/data-table/table-accessibility.directive.ts index 6493aeade..7a2dc525a 100644 --- a/imxweb/projects/qbm/src/lib/data-table/table-accessibility.directive.ts +++ b/imxweb/projects/qbm/src/lib/data-table/table-accessibility.directive.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -65,7 +65,7 @@ export class TableAccessiblilityDirective implements AfterViewChecked, AfterView }); } // Set tabindex for the first cell on the first row if there is not another tabindex set already - private setTabindex():void{ + private setTabindex(): void { const selectables = this.el.nativeElement.querySelectorAll('tbody td:not(.mat-column-select)'); const tabIndexes = this.el.nativeElement.querySelectorAll('tbody td[tabindex="0"]'); if (tabIndexes.length === 0) { @@ -107,7 +107,7 @@ export class TableAccessiblilityDirective implements AfterViewChecked, AfterView this.moveFocusTo(row - 1, col); break; case 'Home': { - this.moveHome(event, row) + this.moveHome(event, row); break; } case 'End': { @@ -126,19 +126,19 @@ export class TableAccessiblilityDirective implements AfterViewChecked, AfterView }); } // Focus the cell right to the current cell, if its the last selectable cell then move to the next rows last cell - private moveArrowRight(row:number, col: number):void{ + private moveArrowRight(row: number, col: number): void { const newRow = col === this.maxcol ? row + 1 : row; const newcol = col === this.maxcol ? 0 : col + 1; this.moveFocusTo(newRow, newcol); } // Focus the cell left to the current cell, if its the first selectable cell then move to the previous rows last cell - private moveArrowLeft(row:number, col: number):void{ + private moveArrowLeft(row: number, col: number): void { const newRow = col === 0 ? row - 1 : row; const newcol = col === 0 ? this.maxcol : col - 1; this.moveFocusTo(newRow, newcol); } // If you click CTRL + Home than focus the first cell of the first column, else first cell of the current column - private moveHome(event: KeyboardEvent, row:number):void{ + private moveHome(event: KeyboardEvent, row: number): void { if (event.ctrlKey) { let newRow = 0; let result; @@ -156,7 +156,7 @@ export class TableAccessiblilityDirective implements AfterViewChecked, AfterView event.preventDefault(); } // If you click CTRL + End than focus the last cell of the last column, else last cell of the current column - private moveEnd(event: KeyboardEvent, row:number):void{ + private moveEnd(event: KeyboardEvent, row: number): void { if (event.ctrlKey) { let newRow = this.maxrow; let result; @@ -169,15 +169,12 @@ export class TableAccessiblilityDirective implements AfterViewChecked, AfterView newRow--; } while (!result); } else { - this.moveFocusTo( - row, - this.maxcol - ); + this.moveFocusTo(row, this.maxcol); } event.preventDefault(); } // Focus the first cell of the current column - private movePageUp(col: number):void{ + private movePageUp(col: number): void { let newRow = 0; let result; do { @@ -186,7 +183,7 @@ export class TableAccessiblilityDirective implements AfterViewChecked, AfterView } while (!result); } // Focus the last cell of the current column - private movePageDown(col: number):void{ + private movePageDown(col: number): void { let newRow = this.maxrow; let result; do { diff --git a/imxweb/projects/qbm/src/lib/data-tiles/data-tile-menu-item.interface.ts b/imxweb/projects/qbm/src/lib/data-tiles/data-tile-menu-item.interface.ts index 1c3872f90..23a8e5a6f 100644 --- a/imxweb/projects/qbm/src/lib/data-tiles/data-tile-menu-item.interface.ts +++ b/imxweb/projects/qbm/src/lib/data-tiles/data-tile-menu-item.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { TypedEntity } from 'imx-qbm-dbts'; +import { TypedEntity } from '@imx-modules/imx-qbm-dbts'; /** * Interface that represents a menu item and the typed entity attached to the correspondig tile. diff --git a/imxweb/projects/qbm/src/lib/data-tiles/data-tile.component.html b/imxweb/projects/qbm/src/lib/data-tiles/data-tile.component.html index ec52c0bcd..5d12296b1 100644 --- a/imxweb/projects/qbm/src/lib/data-tiles/data-tile.component.html +++ b/imxweb/projects/qbm/src/lib/data-tiles/data-tile.component.html @@ -2,42 +2,60 @@
    -
    - {{badge.content | translate}} +
    + {{ badge.content | translate }}
    - +
    - + -
    - +
    +
    -
    - {{getTitleDisplayValue(titleObject.ColumnName)}} +
    + {{ getTitleDisplayValue(titleObject.ColumnName) }}
    -
    -
    - {{getTitleDisplayValue(subtitleObject.ColumnName)}} +
    +
    + {{ getTitleDisplayValue(subtitleObject.ColumnName) }}
    -
    - {{getAdditionalColumnText()}} +
    + {{ getAdditionalColumnText() }}
    @@ -50,12 +68,17 @@ - diff --git a/imxweb/projects/qbm/src/lib/data-tiles/data-tile.component.scss b/imxweb/projects/qbm/src/lib/data-tiles/data-tile.component.scss index 8a7f6a12a..769f7ebab 100644 --- a/imxweb/projects/qbm/src/lib/data-tiles/data-tile.component.scss +++ b/imxweb/projects/qbm/src/lib/data-tiles/data-tile.component.scss @@ -1,10 +1,6 @@ @import '@elemental-ui/core/src/styles/_palette.scss'; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; -@mixin imx-data-tile-title-mixin { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} +@import 'base/mixins'; :host { margin: 10px 20px 10px 10px; @@ -50,7 +46,7 @@ } } -.mat-card { +.mat-mdc-card { cursor: pointer; width: 340px; height: 140px; @@ -68,7 +64,7 @@ padding: 0 5px 0 5px; } -.mat-mini-fab[disabled] { +.mat-mdc-mini-fab[disabled] { cursor: pointer; } @@ -81,7 +77,7 @@ } .imx-data-tile-title { - @include imx-data-tile-title-mixin(); + @include text-overflow-ellipsis; font-weight: bold; margin: 0; grid-column-start: 2; @@ -91,7 +87,7 @@ } .imx-data-tile-subtitle { - @include imx-data-tile-title-mixin(); + @include text-overflow-ellipsis; font-size: small; color: $black-9; margin: 0 0 10px 0; @@ -100,7 +96,7 @@ grid-row-start: 2; grid-row-end: 3; align-self: end; - display:flex; + display: flex; flex-direction: column; } @@ -134,7 +130,7 @@ .eui-dark-theme { :host { - .imx-data-tile-container{ + .imx-data-tile-container { background-color: $color-gray-70; } } @@ -142,7 +138,7 @@ .eui-contrast-theme { :host { - .imx-data-tile-container{ + .imx-data-tile-container { background-color: $color-gray-90; } } diff --git a/imxweb/projects/qbm/src/lib/data-tiles/data-tile.component.ts b/imxweb/projects/qbm/src/lib/data-tiles/data-tile.component.ts index c17495b1a..fb6d36739 100644 --- a/imxweb/projects/qbm/src/lib/data-tiles/data-tile.component.ts +++ b/imxweb/projects/qbm/src/lib/data-tiles/data-tile.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,17 +24,17 @@ * */ -import { Component, Input, EventEmitter, Output, TemplateRef, ViewChild, OnInit } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output, TemplateRef, ViewChild } from '@angular/core'; import { MatButton } from '@angular/material/button'; import { MatMenuTrigger } from '@angular/material/menu'; -import { TypedEntity, IClientProperty } from 'imx-qbm-dbts'; +import { IClientProperty, TypedEntity } from '@imx-modules/imx-qbm-dbts'; -import { DataTileBadge } from '../data-source-toolbar/data-tile-badge.interface'; -import { DataTileMenuItem } from './data-tile-menu-item.interface'; -import { Base64ImageService } from '../images/base64-image.service'; import { SafeUrl } from '@angular/platform-browser'; import { DataSourceItemStatus } from '../data-source-toolbar/data-source-item-status.interface'; +import { DataTileBadge } from '../data-source-toolbar/data-tile-badge.interface'; +import { Base64ImageService } from '../images/base64-image.service'; +import { DataTileMenuItem } from './data-tile-menu-item.interface'; /** * A single tile component used internally by the tiles components. @@ -43,28 +43,26 @@ import { DataSourceItemStatus } from '../data-source-toolbar/data-source-item-st @Component({ selector: 'imx-data-tile', templateUrl: './data-tile.component.html', - styleUrls: ['./data-tile.component.scss'] + styleUrls: ['./data-tile.component.scss'], }) export class DataTileComponent implements OnInit { /** * If present the badges will be shown in the upper right corner. Can be used e.g. to show different states. */ - public get badges(): DataTileBadge[] { - return this.status?.getBadges ? - this.status.getBadges(this.typedEntity) : undefined; + public get badges(): DataTileBadge[] | undefined { + return this.status?.getBadges ? this.status.getBadges(this.typedEntity) : undefined; } public get enabled(): boolean { - return this.status?.enabled ? - this.status.enabled(this.typedEntity) : true; + return this.status?.enabled ? this.status.enabled(this.typedEntity) : true; } public get hasImage(): boolean { - return (this.image || this.status?.getImagePath || this.fallbackIcon) ? true : false; + return this.image || this.status?.getImagePath || this.fallbackIcon ? true : false; } public get filteredActions(): DataTileMenuItem[] { - return this.enabled ? this.actions : this.actions.filter(elem => elem.useOnDisabledTile); + return this.enabled ? this.actions : this.actions.filter((elem) => elem.useOnDisabledTile); } public isLoadingImage: boolean; @@ -101,7 +99,6 @@ export class DataTileComponent implements OnInit { */ @Input() public additionalSubtitleObjects: IClientProperty[] = []; - /** * The property of the typed entity that will be used as the subtitle of the tile. */ @@ -130,22 +127,22 @@ export class DataTileComponent implements OnInit { @Input() public actions: DataTileMenuItem[]; /** - * The width of the tile. - */ + * The width of the tile. + */ @Input() public width = '340px'; /** - * The height of the tile. - */ + * The height of the tile. + */ @Input() public height = '140px'; @Input() public useActionMenu = true; /** - * Status of this item. If the property enabled is true, the item is selectable. - */ + * Status of this item. If the property enabled is true, the item is selectable. + */ @Input() public status: DataSourceItemStatus = { - enabled: __ => true + enabled: (__) => true, }; /** @@ -163,7 +160,7 @@ export class DataTileComponent implements OnInit { @Output() public selected = new EventEmitter(); // TODO: Check Upgrade - @Output() public badgeClicked = new EventEmitter<{ entity: TypedEntity, badge: DataTileBadge }>(); + @Output() public badgeClicked = new EventEmitter<{ entity: TypedEntity; badge: DataTileBadge }>(); /** * A icon button that indicates if the tile is selected. @@ -177,17 +174,15 @@ export class DataTileComponent implements OnInit { */ @ViewChild(MatMenuTrigger) public menuTrigger: MatMenuTrigger; - constructor(private readonly base64ImageService: Base64ImageService) { } + constructor(private readonly base64ImageService: Base64ImageService) {} public async ngOnInit(): Promise { if (this.status?.getImagePath) { this.isLoadingImage = true; this.imageUrl = await this.status.getImagePath(this.typedEntity); - this.isLoadingImage = false; + this.isLoadingImage = false; } else if (this.image?.ColumnName) { - this.imageUrl = this.base64ImageService.addBase64Prefix( - this.typedEntity.GetEntity().GetColumn(this.image.ColumnName).GetValue() - ); + this.imageUrl = this.base64ImageService.addBase64Prefix(this.typedEntity.GetEntity().GetColumn(this.image.ColumnName).GetValue()); } } @@ -195,14 +190,19 @@ export class DataTileComponent implements OnInit { * Used by the template to show the title or subtitle. * @ignore Used internally in components template. */ - public getTitleDisplayValue(colName: string): string { - return this.typedEntity.GetEntity().GetColumn(colName).GetDisplayValue(); + public getTitleDisplayValue(colName: string | undefined): string { + return colName ? this.typedEntity.GetEntity().GetColumn(colName).GetDisplayValue() : ''; } - public getAdditionalColumnText(): string { - return this.additionalSubtitleObjects.map(elem => - this.typedEntity.GetEntity().GetColumn(elem.ColumnName).GetDisplayValue()).join('; '); + return this.additionalSubtitleObjects + .map((elem) => + this.typedEntity + .GetEntity() + .GetColumn(elem.ColumnName ?? '') + .GetDisplayValue(), + ) + .join('; '); } public getDefaultTypeNameImage() { @@ -222,7 +222,7 @@ export class DataTileComponent implements OnInit { this.isSelected = !this.isSelected; this.selectionChanged.emit(this.typedEntity); //tile selection - if(!this.isSelected){ + if (!this.isSelected) { this.selected.emit(this.isSelected); } } diff --git a/imxweb/projects/qbm/src/lib/data-tiles/data-tiles.component.html b/imxweb/projects/qbm/src/lib/data-tiles/data-tiles.component.html index 91ab89d19..feb11a73d 100644 --- a/imxweb/projects/qbm/src/lib/data-tiles/data-tiles.component.html +++ b/imxweb/projects/qbm/src/lib/data-tiles/data-tiles.component.html @@ -1,5 +1,4 @@ - -
    +
    - - -
    - -

    -
    + +
    + +

    +
    diff --git a/imxweb/projects/qbm/src/lib/data-tiles/data-tiles.component.scss b/imxweb/projects/qbm/src/lib/data-tiles/data-tiles.component.scss index 62ef1c7b4..b2b0e8430 100644 --- a/imxweb/projects/qbm/src/lib/data-tiles/data-tiles.component.scss +++ b/imxweb/projects/qbm/src/lib/data-tiles/data-tiles.component.scss @@ -8,7 +8,7 @@ flex: auto; } -.imx-data-container{ +.imx-data-container { display: flex; flex-wrap: wrap; overflow-y: auto; @@ -16,19 +16,14 @@ } .hidden { - display:none; + display: none; } -.imx-data-tiles-no-results { +.imx-no-results { text-align: center; margin: 20px 0; flex: auto; - .eui-icon { - font-size: 100px; - color: rgba($color-gray-20, 0.55); - } - p { margin: 0; font-size: 18px; diff --git a/imxweb/projects/qbm/src/lib/data-tiles/data-tiles.component.ts b/imxweb/projects/qbm/src/lib/data-tiles/data-tiles.component.ts index 5c3c9c2b5..279c844b2 100644 --- a/imxweb/projects/qbm/src/lib/data-tiles/data-tiles.component.ts +++ b/imxweb/projects/qbm/src/lib/data-tiles/data-tiles.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,14 +24,14 @@ * */ -import { Component, Input, EventEmitter, Output, TemplateRef, OnDestroy, OnChanges, SimpleChanges, ChangeDetectorRef } from '@angular/core'; import { SelectionChange } from '@angular/cdk/collections'; +import { ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnDestroy, Output, SimpleChanges, TemplateRef } from '@angular/core'; import { Subscription } from 'rxjs'; +import { IClientProperty, TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { DataSourceToolbarComponent } from '../data-source-toolbar/data-source-toolbar.component'; -import { TypedEntity, IClientProperty } from 'imx-qbm-dbts'; -import { DataTileMenuItem } from './data-tile-menu-item.interface'; import { DataTileBadge } from '../data-source-toolbar/data-tile-badge.interface'; +import { DataTileMenuItem } from './data-tile-menu-item.interface'; /** * A list component containing {@link SingleTileComponent| tiles}. @@ -130,7 +130,13 @@ export class DataTilesComponent implements OnChanges, OnDestroy { /** * The height of a tile. */ - @Input() public height: '140px'; + @Input() + get height(): string { + return this._height; + } + set height(value: string) { + this._height = value || '140px'; + } @Input() public useActionMenu = true; @@ -151,7 +157,7 @@ export class DataTilesComponent implements OnChanges, OnDestroy { /** * Event, that will fire when the user clicks on the badge. */ - @Output() public badgeClicked = new EventEmitter(); + @Output() public badgeClicked = new EventEmitter<{ entity: TypedEntity; badge: DataTileBadge }>(); /** * @ignore @@ -162,7 +168,7 @@ export class DataTilesComponent implements OnChanges, OnDestroy { /** * Keeps track of the selected item in single select mode */ - private selectedItem: TypedEntity; + private selectedItem: TypedEntity | undefined; /** * @ignore @@ -170,7 +176,9 @@ export class DataTilesComponent implements OnChanges, OnDestroy { */ private subscriptions: Subscription[] = []; - constructor(private readonly changeDetector: ChangeDetectorRef){} + private _height: string; + + constructor(private readonly changeDetector: ChangeDetectorRef) {} /** * @ignore Used internally. @@ -180,19 +188,20 @@ export class DataTilesComponent implements OnChanges, OnDestroy { public ngOnChanges(changes: SimpleChanges): void { if (changes['dst'] && changes['dst'].currentValue) { this.subscriptions.push( - this.dst.selectionChanged.subscribe((event: SelectionChange) => this.selectionChanged.emit(event.source.selected)) + this.dst.selectionChanged.subscribe((event: SelectionChange) => this.selectionChanged.emit(event.source.selected)), ); this.additionalSubtitleObjects = this.dst?.additionalListElements; if (this.dst.busyService) { - this.subscriptions.push(this.dst.busyService.busyStateChanged.subscribe((value:boolean)=>{ - this.isLoading = value; - this.changeDetector.detectChanges() - })); + this.subscriptions.push( + this.dst.busyService.busyStateChanged.subscribe((value: boolean) => { + this.isLoading = value; + this.changeDetector.detectChanges(); + }), + ); } this.isLoading = this.dst?.busyService?.isBusy ?? false; - } } @@ -213,7 +222,7 @@ export class DataTilesComponent implements OnChanges, OnDestroy { this.selectedItem = this.selectedEntity; } - return this.selectedItem && this.selectedItem.GetEntity().GetKeys().join() === item.GetEntity().GetKeys().join(); + return this.selectedItem?.GetEntity().GetKeys().join() === item.GetEntity().GetKeys().join(); } /** @@ -245,7 +254,7 @@ export class DataTilesComponent implements OnChanges, OnDestroy { this.dst.selectAllOnPage(); } - public onBadgeClicked(badge: DataTileBadge): void { + public onBadgeClicked(badge: { entity: TypedEntity; badge: DataTileBadge }): void { this.badgeClicked.emit(badge); } } diff --git a/imxweb/projects/qbm/src/lib/data-tiles/data-tiles.module.ts b/imxweb/projects/qbm/src/lib/data-tiles/data-tiles.module.ts index fd9ca858c..f91626019 100644 --- a/imxweb/projects/qbm/src/lib/data-tiles/data-tiles.module.ts +++ b/imxweb/projects/qbm/src/lib/data-tiles/data-tiles.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -40,10 +40,7 @@ import { DataTileComponent } from './data-tile.component'; import { BusyIndicatorModule } from '../busy-indicator/busy-indicator.module'; @NgModule({ - declarations: [ - DataTilesComponent, - DataTileComponent - ], + declarations: [DataTilesComponent, DataTileComponent], imports: [ CommonModule, EuiCoreModule, @@ -54,8 +51,8 @@ import { BusyIndicatorModule } from '../busy-indicator/busy-indicator.module'; MatMenuModule, MatBadgeModule, TranslateModule, - BusyIndicatorModule + BusyIndicatorModule, ], - exports: [ DataTilesComponent ], + exports: [DataTilesComponent], }) -export class DataTilesModule { } +export class DataTilesModule {} diff --git a/imxweb/projects/qbm/src/lib/data-tree-wrapper/data-tree-wrapper.component.html b/imxweb/projects/qbm/src/lib/data-tree-wrapper/data-tree-wrapper.component.html index a9f4698c9..05adbacda 100644 --- a/imxweb/projects/qbm/src/lib/data-tree-wrapper/data-tree-wrapper.component.html +++ b/imxweb/projects/qbm/src/lib/data-tree-wrapper/data-tree-wrapper.component.html @@ -1,5 +1,6 @@ + + + diff --git a/imxweb/projects/qbm/src/lib/data-tree-wrapper/data-tree-wrapper.component.scss b/imxweb/projects/qbm/src/lib/data-tree-wrapper/data-tree-wrapper.component.scss index 2b502e374..330412829 100644 --- a/imxweb/projects/qbm/src/lib/data-tree-wrapper/data-tree-wrapper.component.scss +++ b/imxweb/projects/qbm/src/lib/data-tree-wrapper/data-tree-wrapper.component.scss @@ -7,4 +7,4 @@ .imx-toolbar-margin { margin-bottom: 16px; } -} \ No newline at end of file +} diff --git a/imxweb/projects/qbm/src/lib/data-tree-wrapper/data-tree-wrapper.component.ts b/imxweb/projects/qbm/src/lib/data-tree-wrapper/data-tree-wrapper.component.ts index ebf979875..56e68cf54 100644 --- a/imxweb/projects/qbm/src/lib/data-tree-wrapper/data-tree-wrapper.component.ts +++ b/imxweb/projects/qbm/src/lib/data-tree-wrapper/data-tree-wrapper.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,7 +26,7 @@ import { Component, ContentChild, EventEmitter, Input, OnChanges, Output, TemplateRef, ViewChild } from '@angular/core'; -import { CollectionLoadParameters, EntitySchema, FilterData, IEntity } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, EntitySchema, FilterData, IEntity } from '@imx-modules/imx-qbm-dbts'; import { FilterTreeParameter } from '../data-source-toolbar/data-model/filter-tree-parameter'; import { DataSourceToolbarFilter } from '../data-source-toolbar/data-source-toolbar-filters.interface'; import { DataSourceToolbarSettings } from '../data-source-toolbar/data-source-toolbar-settings'; @@ -58,6 +58,7 @@ export class DataTreeWrapperComponent implements OnChanges { @ViewChild('tree') public treeControl: DataTreeComponent; @ContentChild(TemplateRef, { static: true }) public templateRef: TemplateRef; + @ContentChild('customDisplay', { static: true }) public customDisplay: TemplateRef; @Output() public nodeSelected = new EventEmitter(); @Output() public checkedNodesChanged = new EventEmitter(); @@ -157,10 +158,10 @@ export class DataTreeWrapperComponent implements OnChanges { * @param entity entity, for identifying the node */ public deleteNode(entity: IEntity, withDescendants: boolean) { - this.treeControl.deleteNode(entity,withDescendants); + this.treeControl.deleteNode(entity, withDescendants); } - public getEntityById(id: string): IEntity { + public getEntityById(id: string): IEntity | undefined { return this.treeControl.getEntityById(id); } } diff --git a/imxweb/projects/qbm/src/lib/data-tree-wrapper/data-tree-wrapper.module.ts b/imxweb/projects/qbm/src/lib/data-tree-wrapper/data-tree-wrapper.module.ts index 20901d55e..d7bde71c1 100644 --- a/imxweb/projects/qbm/src/lib/data-tree-wrapper/data-tree-wrapper.module.ts +++ b/imxweb/projects/qbm/src/lib/data-tree-wrapper/data-tree-wrapper.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -32,20 +32,9 @@ import { DataTreeWrapperComponent } from './data-tree-wrapper.component'; import { DataSourceToolbarModule } from '../data-source-toolbar/data-source-toolbar.module'; import { DataTreeModule } from '../data-tree/data-tree.module'; - - @NgModule({ - declarations: [ - DataTreeWrapperComponent - ], - imports: [ - CommonModule, - DataSourceToolbarModule, - DataTreeModule, - TranslateModule - ], - exports: [ - DataTreeWrapperComponent - ] + declarations: [DataTreeWrapperComponent], + imports: [CommonModule, DataSourceToolbarModule, DataTreeModule, TranslateModule], + exports: [DataTreeWrapperComponent], }) -export class DataTreeWrapperModule { } +export class DataTreeWrapperModule {} diff --git a/imxweb/projects/qbm/src/lib/data-tree/checkable-tree/checkable-tree.component.html b/imxweb/projects/qbm/src/lib/data-tree/checkable-tree/checkable-tree.component.html index 9a6f69b5c..a27aaa49f 100644 --- a/imxweb/projects/qbm/src/lib/data-tree/checkable-tree/checkable-tree.component.html +++ b/imxweb/projects/qbm/src/lib/data-tree/checkable-tree/checkable-tree.component.html @@ -3,8 +3,13 @@ @@ -15,11 +20,15 @@ + + +

    + {{ node.display }} +

    +
    diff --git a/imxweb/projects/qbm/src/lib/data-tree/checkable-tree/checkable-tree.component.scss b/imxweb/projects/qbm/src/lib/data-tree/checkable-tree/checkable-tree.component.scss index 0fd21ea87..8bfeae86f 100644 --- a/imxweb/projects/qbm/src/lib/data-tree/checkable-tree/checkable-tree.component.scss +++ b/imxweb/projects/qbm/src/lib/data-tree/checkable-tree/checkable-tree.component.scss @@ -11,7 +11,7 @@ flex: 1 1 auto; text-align: left; min-width: 0; - > ::ng-deep .mat-button-wrapper { + > ::ng-deep .mdc-button__label { color: $color-gray-100; overflow: hidden; text-overflow: ellipsis; @@ -31,10 +31,6 @@ text-overflow: ellipsis; white-space: nowrap; } - mat-progress-bar { - flex-grow: 1; - width: auto; - } } .imx-tree-node:hover { @@ -71,7 +67,7 @@ } .imx-tree-node:hover { background-color: $color-gray-0; - color: $color-gray-100 + color: $color-gray-100; } } } diff --git a/imxweb/projects/qbm/src/lib/data-tree/checkable-tree/checkable-tree.component.ts b/imxweb/projects/qbm/src/lib/data-tree/checkable-tree/checkable-tree.component.ts index 6349cf3a8..589f5eb4d 100644 --- a/imxweb/projects/qbm/src/lib/data-tree/checkable-tree/checkable-tree.component.ts +++ b/imxweb/projects/qbm/src/lib/data-tree/checkable-tree/checkable-tree.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -41,7 +41,7 @@ import { import { MatCheckboxChange } from '@angular/material/checkbox'; import { Subscription } from 'rxjs'; -import { CollectionLoadParameters, IEntity } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, IEntity } from '@imx-modules/imx-qbm-dbts'; import { ClassloggerService } from '../../classlogger/classlogger.service'; import { SnackBarService } from '../../snackbar/snack-bar.service'; import { TreeDatabase } from '../tree-database'; @@ -63,10 +63,12 @@ export class CheckableTreeComponent implements OnChanges, AfterViewInit, OnDestr /** the {@link FlatTreeControl| FlatTreeControl} of @angular/cdk */ public treeControl: FlatTreeControl; - @ContentChild(TemplateRef, { static: true }) public templateRef: TemplateRef; + //@ContentChild(TemplateRef, { static: true }) public templateRef: TemplateRef; + @ContentChild('customDisplayTemplate', { static: true }) public customDisplayTemplate: TemplateRef; + @ContentChild('additionalTemplate', { static: true }) public templateRef: TemplateRef; /** currently selected entities */ - @Input() public selectedEntities: IEntity[] = []; + @Input() public selectedEntities: (IEntity | undefined)[] = []; /** the service providing the data for the {@link TreeDatasource| TreeDatasource} */ @Input() public database: TreeDatabase; @@ -111,7 +113,10 @@ export class CheckableTreeComponent implements OnChanges, AfterViewInit, OnDestr private subscriptions: Subscription[] = []; - constructor(private readonly snackBar: SnackBarService, private readonly logger: ClassloggerService) { + constructor( + private readonly snackBar: SnackBarService, + private readonly logger: ClassloggerService, + ) { this.treeControl = new FlatTreeControl(this.getLevel, this.isExpandable); } @@ -129,7 +134,7 @@ export class CheckableTreeComponent implements OnChanges, AfterViewInit, OnDestr this.treeDataSource.dataChange.subscribe((elem) => { this.treeRendered.emit(); this.updateCheckedTreeNodes(elem); - }) + }), ); this.logger.debug(this, `toggle Node of the selected entity to load its children`); @@ -167,7 +172,7 @@ export class CheckableTreeComponent implements OnChanges, AfterViewInit, OnDestr if (data) { this.initializeTreeData(); } - }) + }), ); } } @@ -189,7 +194,9 @@ export class CheckableTreeComponent implements OnChanges, AfterViewInit, OnDestr */ public add(childEntity: IEntity, uidParent: string) { const node = this.treeDataSource.data.find((elem) => this.getId(elem.item) === uidParent); - this.treeDataSource.addChildNode(node, childEntity); + if (node) { + this.treeDataSource.addChildNode(node, childEntity); + } } /** @@ -199,7 +206,9 @@ export class CheckableTreeComponent implements OnChanges, AfterViewInit, OnDestr */ public updateNode(entity: IEntity, newNodeInfo: TreeNodeInfo) { const node = this.getNode(entity); - this.treeDataSource.updateNode(node, newNodeInfo); + if (node) { + this.treeDataSource.updateNode(node, newNodeInfo); + } } /** @@ -208,8 +217,15 @@ export class CheckableTreeComponent implements OnChanges, AfterViewInit, OnDestr */ public deleteNode(entity: IEntity, withChildren: boolean) { const node = this.getNode(entity); + if (!node) { + return; + } const parent = this.getParentNode(node); - this.treeDataSource.removeNode(node,withChildren); + this.treeDataSource.removeNode(node, withChildren); + + if (!parent) { + return; + } const des = this.treeControl.getDescendants(parent).filter((elem) => !elem.isLoadMoreNode); if (des.length === 0 || withChildren) { @@ -227,16 +243,17 @@ export class CheckableTreeComponent implements OnChanges, AfterViewInit, OnDestr public isExpanded(entity: IEntity): boolean { const node = this.getNode(entity); - return this.treeControl.isExpanded(node); + return node ? this.treeControl.isExpanded(node) : false; } - public getEntityById(id: string): IEntity { + public getEntityById(id: string): IEntity | undefined { const node = this.treeDataSource.data.find((elem) => this.getId(elem.item) === id); return node?.item; } public hasChildren(entity: IEntity): boolean { - return this.isExpandable(this.getNode(entity)); + const node = this.getNode(entity); + return node ? this.isExpandable(node) : false; } /** returns true, if the node has childnodes */ @@ -246,6 +263,10 @@ export class CheckableTreeComponent implements OnChanges, AfterViewInit, OnDestr /** Emits the selected treenode. */ public selectNode(node: TreeNode): void { + if (!node.isSelectable) { + // node is not selectable + return; + } if (this.withMultiSelect) { this.checklistSelection.toggle(node); this.emitNodeCheckedEvent(node, this.checklistSelection.isSelected(node)); @@ -317,11 +338,11 @@ export class CheckableTreeComponent implements OnChanges, AfterViewInit, OnDestr this.treeDataSource?.init(await this.database.initialize(this.navigationState)); } - private getSelectedItem(): string { + private getSelectedItem(): string | null | undefined { return this.selectedEntities.length > 0 && this.selectedEntities[0] != null ? this.getId(this.selectedEntities[0]) : null; } - private getNode(entity: IEntity): TreeNode { + private getNode(entity: IEntity): TreeNode | undefined { return this.treeDataSource?.data.find((elem) => this.getId(elem.item) === this.getId(entity)); } @@ -382,8 +403,8 @@ export class CheckableTreeComponent implements OnChanges, AfterViewInit, OnDestr } } - private updateSelectedEntities(current: IEntity, checked: boolean): void { - if (checked) { + private updateSelectedEntities(current: IEntity | undefined, checked: boolean): void { + if (checked && current) { this.selectedEntities.push(current); } else { const index = this.selectedEntities.findIndex((elem) => this.getId(elem) === this.getId(current)); @@ -391,7 +412,7 @@ export class CheckableTreeComponent implements OnChanges, AfterViewInit, OnDestr } } - private getId(entity: IEntity): string { - return this.database ? this.database.getId(entity) : entity.GetKeys()[0]; + private getId(entity: IEntity | undefined): string { + return this.database ? this.database.getId(entity) : entity?.GetKeys()[0] ?? ''; } } diff --git a/imxweb/projects/qbm/src/lib/data-tree/data-tree-no-results.scss b/imxweb/projects/qbm/src/lib/data-tree/data-tree-no-results.scss index 9e69be81a..a7b0f9d45 100644 --- a/imxweb/projects/qbm/src/lib/data-tree/data-tree-no-results.scss +++ b/imxweb/projects/qbm/src/lib/data-tree/data-tree-no-results.scss @@ -1,6 +1,6 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; -.imx-data-tree-no-results { +.imx-no-results { text-align: center; display: flex; margin: 20px 0; @@ -8,11 +8,6 @@ justify-content: center; flex-direction: column; align-items: center; - - .eui-icon { - font-size: 100px; - color: rgba($black-c, 0.55); - } p { margin: 0; diff --git a/imxweb/projects/qbm/src/lib/data-tree/data-tree-search-results/data-tree-search-results.component.html b/imxweb/projects/qbm/src/lib/data-tree/data-tree-search-results/data-tree-search-results.component.html index 6cf093f2b..2c7b66ec5 100644 --- a/imxweb/projects/qbm/src/lib/data-tree/data-tree-search-results/data-tree-search-results.component.html +++ b/imxweb/projects/qbm/src/lib/data-tree/data-tree-search-results/data-tree-search-results.component.html @@ -1,11 +1,20 @@ - - + +
    @@ -18,8 +27,13 @@ - + @@ -32,20 +46,22 @@ - -
    +
    -
    -

    - {{ result.GetDisplayLong() }} -

    -
    - - + (click)="resultClicked(result)" + > +
    +

    + {{ result.GetDisplayLong() }} +

    + +
    - \ No newline at end of file + diff --git a/imxweb/projects/qbm/src/lib/data-tree/data-tree-search-results/data-tree-search-results.component.scss b/imxweb/projects/qbm/src/lib/data-tree/data-tree-search-results/data-tree-search-results.component.scss index e101d8410..edc6de0e5 100644 --- a/imxweb/projects/qbm/src/lib/data-tree/data-tree-search-results/data-tree-search-results.component.scss +++ b/imxweb/projects/qbm/src/lib/data-tree/data-tree-search-results/data-tree-search-results.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; :host { @@ -15,16 +15,10 @@ .imx-search-results { flex: 1 1 auto; overflow-y: auto; + ::ng-deep.mat-pseudo-checkbox { display: none !important; } - ::ng-deep.mat-list-option { - height: auto; - } - - ::ng-deep.mat-list-item { - height: auto; - } } .imx-selected-candidate { @@ -34,12 +28,6 @@ .imx-search-results-multi { flex: 1 1 auto; overflow-y: auto; - ::ng-deep.mat-list-option { - background-color: transparent; - } - ::ng-deep.mat-list-option:hover { - background-color: $asher-gray; - } } .imx-candidate-option { @@ -104,8 +92,7 @@ flex: 1 1 auto; text-align: left; min-width: 0; - - > ::ng-deep .mat-button-wrapper { + > ::ng-deep .mdc-button__label { color: $black; overflow: hidden; text-overflow: ellipsis; @@ -131,9 +118,8 @@ background-color: $asher-gray; } -.eui-dark-theme{ - :host{ - +.eui-dark-theme { + :host { .imx-background-highlight { background-color: $color-gray-60; } @@ -154,27 +140,20 @@ background-color: $color-gray-60; } - .imx-search-results-multi { - ::ng-deep.mat-list-option:hover { - background-color: $color-gray-60; - } - } - .imx-search-result-item { - > ::ng-deep .mat-button-wrapper { + > ::ng-deep .mdc-button__label { color: $color-gray-2; } } - .imx-search-result-item:hover{ + .imx-search-result-item:hover { background-color: $color-gray-60; } } } -.eui-contrast-theme{ - :host{ - +.eui-contrast-theme { + :host { .imx-background-highlight { background-color: $color-gray-80; } @@ -195,19 +174,13 @@ background-color: $color-gray-80; } - .imx-search-results-multi { - ::ng-deep.mat-list-option:hover { - background-color: $color-gray-80; - } - } - .imx-search-result-item { - > ::ng-deep .mat-button-wrapper { + > ::ng-deep .mdc-button__label { color: $color-gray-0; } } - .imx-search-result-item:hover{ + .imx-search-result-item:hover { background-color: $color-gray-80; } } diff --git a/imxweb/projects/qbm/src/lib/data-tree/data-tree-search-results/data-tree-search-results.component.ts b/imxweb/projects/qbm/src/lib/data-tree/data-tree-search-results/data-tree-search-results.component.ts index aa69e939a..9bb1bb74f 100644 --- a/imxweb/projects/qbm/src/lib/data-tree/data-tree-search-results/data-tree-search-results.component.ts +++ b/imxweb/projects/qbm/src/lib/data-tree/data-tree-search-results/data-tree-search-results.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,24 +28,23 @@ import { Component, ContentChild, EventEmitter, Input, OnChanges, Output, Simple import { MatSelectionListChange } from '@angular/material/list'; import { PageEvent } from '@angular/material/paginator'; -import { CollectionLoadParameters, IEntity } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, IEntity } from '@imx-modules/imx-qbm-dbts'; import { ClassloggerService } from '../../classlogger/classlogger.service'; +import { SettingsService } from '../../settings/settings-service'; import { TreeDatabase } from '../tree-database'; -import { SettingsService} from '../../settings/settings-service'; @Component({ selector: 'imx-data-tree-search-results', templateUrl: './data-tree-search-results.component.html', - styleUrls: ['./data-tree-search-results.component.scss', '../data-tree-no-results.scss'] + styleUrls: ['./data-tree-search-results.component.scss', '../data-tree-no-results.scss'], }) /** A component, that can display the search result for a{@link DataTreeComponent|data tree} */ export class DataTreeSearchResultsComponent implements OnChanges { - /** determines whether the control allows multiselect or not */ @Input() public withMultiSelect: boolean; /** currently selected entities */ - @Input() public selectedEntities: IEntity[] = []; + @Input() public selectedEntities: (IEntity | undefined)[] = []; /** * This text will be displayed when a search or filter is applied but there is no data as a result @@ -93,7 +92,10 @@ export class DataTreeSearchResultsComponent implements OnChanges { /** event, that fires, after the checked nodes list has been updated */ @Output() public checkedNodesChanged = new EventEmitter(); - constructor(private readonly logger: ClassloggerService, private readonly settings: SettingsService) { + constructor( + private readonly logger: ClassloggerService, + private readonly settings: SettingsService, + ) { this.paginatorPageSize = this.settings?.DefaultPageSize ?? 25; } @@ -107,7 +109,7 @@ export class DataTreeSearchResultsComponent implements OnChanges { public compareFunction = (o1: any, o2: any) => { this.logger.log(this, 'compare', o1, o2); return this.getId(o1) === this.getId(o2); - } + }; /** * clears all selected nodes for the tree and listings @@ -118,14 +120,13 @@ export class DataTreeSearchResultsComponent implements OnChanges { public async reload(): Promise { this.loading = true; - const data = await this.database.getData(true, this.navigationState); + const data = await this.database?.getData(true, this.navigationState); this.loading = false; - this.paginatorLength = data.totalCount; - this.searchResults = data.entities; - this.selectedOptions = this.searchResults.filter(elem => this.entityIsChecked(elem)); + this.paginatorLength = data?.totalCount ?? 0; + this.searchResults = data?.entities ?? []; + this.selectedOptions = this.searchResults.filter((elem) => this.entityIsChecked(elem)); } - /** @ignore updates the selection list an emits the according events */ public onSelectionChanged(selection: MatSelectionListChange): void { this.updateSelectedEntities(selection.options[0].value, selection.options[0].selected); @@ -149,39 +150,44 @@ export class DataTreeSearchResultsComponent implements OnChanges { ...{ StartIndex: newState.pageIndex * newState.pageSize, PageSize: newState.pageSize, - } + }, }; - const data = await this.database.getData(true, state); + const data = await this.database?.getData(true, state); this.loading = false; - this.searchResults = data.entities; - this.selectedOptions = this.searchResults.filter(elem => this.entityIsChecked(elem)); - this.paginatorLength = data.totalCount; + this.searchResults = data?.entities ?? []; + this.selectedOptions = this.searchResults.filter((elem) => this.entityIsChecked(elem)); + this.paginatorLength = data?.totalCount ?? 0; } /** @ignore checks, if an element is selected or not */ public isSelected(value: IEntity): boolean { - if (this.withMultiSelect) { return false; } - return this.selectedEntities.some(elem => this.getId(elem) === this.getId(value)); + if (this.withMultiSelect) { + return false; + } + return this.selectedEntities.some((elem) => this.getId(elem) === this.getId(value)); } private updateSelectedEntities(current: IEntity, checked: boolean): void { if (checked) { this.selectedEntities.push(current); } else { - const index = this.selectedEntities.findIndex(elem => this.getId(elem) === this.getId(current)); + const index = this.selectedEntities.findIndex((elem) => this.getId(elem) === this.getId(current)); this.selectedEntities.splice(index, 1); } } private entityIsChecked(entity: IEntity): boolean { - this.logger.log(this, 'Keys', + this.logger.log( + this, + 'Keys', this.selectedEntities, - this.getId(entity), this.selectedEntities.some(elem => this.getId(entity) === this.getId(elem))); - return this.selectedEntities.some(elem => this.getId(entity) === this.getId(elem)); + this.getId(entity), + this.selectedEntities.some((elem) => this.getId(entity) === this.getId(elem)), + ); + return this.selectedEntities.some((elem) => this.getId(entity) === this.getId(elem)); } - private getId(entity: IEntity): string { + private getId(entity: IEntity | undefined): string { return TreeDatabase.getId(entity); - } - + } } diff --git a/imxweb/projects/qbm/src/lib/data-tree/data-tree-search-results/search-result-action.interface.ts b/imxweb/projects/qbm/src/lib/data-tree/data-tree-search-results/search-result-action.interface.ts index 6d92a35f8..0582e1c44 100644 --- a/imxweb/projects/qbm/src/lib/data-tree/data-tree-search-results/search-result-action.interface.ts +++ b/imxweb/projects/qbm/src/lib/data-tree/data-tree-search-results/search-result-action.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { IEntity } from 'imx-qbm-dbts'; +import { IEntity } from '@imx-modules/imx-qbm-dbts'; export interface SearchResultAction { action: (entity: IEntity) => Promise; diff --git a/imxweb/projects/qbm/src/lib/data-tree/data-tree.component.html b/imxweb/projects/qbm/src/lib/data-tree/data-tree.component.html index 699c91b18..3a7964925 100644 --- a/imxweb/projects/qbm/src/lib/data-tree/data-tree.component.html +++ b/imxweb/projects/qbm/src/lib/data-tree/data-tree.component.html @@ -7,7 +7,6 @@ [disabled]="selectedEntities.length === 0" mat-flat-button class="imx-data-table-selection-info mat-caption" - (click)="onOpenSelectionDialog()" data-imx-identifier="datatree-button-show-selected" > {{ '#LDS#Selected items' | translate }} ({{ selectedEntities.length }}) @@ -30,6 +29,9 @@ + + +
    @@ -53,8 +55,12 @@ -
    +

    + + +

    {{data.display}}

    +
    diff --git a/imxweb/projects/qbm/src/lib/data-tree/data-tree.component.scss b/imxweb/projects/qbm/src/lib/data-tree/data-tree.component.scss index e3fd0e09a..441f57b0a 100644 --- a/imxweb/projects/qbm/src/lib/data-tree/data-tree.component.scss +++ b/imxweb/projects/qbm/src/lib/data-tree/data-tree.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; :host { display: flex; @@ -7,30 +7,12 @@ flex: 1 1 auto; } -.multi-select-formcontrol-eui-search { - width: 100%; - display: flex; - - .eui-search { - width: 100%; - } - - ::ng-deep .eui-search.mat-form-field.mat-form-field-appearance-outline { - min-width: 280px; - flex: 1; - margin-bottom: 10px; - } -} - -.mat-form-field { - flex: 1; -} - .imx-data-tree-content { flex: 1 1 auto; overflow-y: auto; display: flex; flex-direction: column; + padding-left: 10px; } .imx-search-selection { @@ -40,10 +22,6 @@ overflow-y: auto; } -.mat-tree-node .mat-icon-button.mat-button-base { - flex: 0 0 40px; -} - .imx-selected-items-button-containter { display: flex; flex-direction: row; @@ -58,4 +36,4 @@ .hidden { display: none; -} \ No newline at end of file +} diff --git a/imxweb/projects/qbm/src/lib/data-tree/data-tree.component.ts b/imxweb/projects/qbm/src/lib/data-tree/data-tree.component.ts index b011e1874..5387daea9 100644 --- a/imxweb/projects/qbm/src/lib/data-tree/data-tree.component.ts +++ b/imxweb/projects/qbm/src/lib/data-tree/data-tree.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -41,13 +41,14 @@ import { EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; import { Subscription } from 'rxjs'; -import { CollectionLoadParameters, IEntity } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, IEntity } from '@imx-modules/imx-qbm-dbts'; +import { calculateSidesheetWidth } from '../base/sidesheet-helper'; import { ClassloggerService } from '../classlogger/classlogger.service'; import { CheckableTreeComponent } from './checkable-tree/checkable-tree.component'; import { DataTreeSearchResultsComponent } from './data-tree-search-results/data-tree-search-results.component'; import { TreeDatabase } from './tree-database'; -import { TreeSelectionListComponent } from './tree-selection-list/tree-selection-list.component'; import { TreeNodeInfo } from './tree-node'; +import { TreeSelectionListComponent } from './tree-selection-list/tree-selection-list.component'; @Component({ selector: 'imx-data-tree', @@ -74,7 +75,7 @@ export class DataTreeComponent implements OnChanges, OnDestroy { @Input() public withSelectedNodeHighlight: boolean; /** currently selected entities */ - @Input() public selectedEntities: IEntity[] = []; + @Input() public selectedEntities: (IEntity | undefined)[] = []; /** the service providing the data for the {@link TreeDatasource| TreeDatasource} */ @Input() public database: TreeDatabase; @@ -119,6 +120,7 @@ export class DataTreeComponent implements OnChanges, OnDestroy { @ViewChild(DataTreeSearchResultsComponent) public searchResults: DataTreeSearchResultsComponent; @ContentChild(TemplateRef, { static: true }) public templateRef: TemplateRef; + @ContentChild('customDisplay') public customDisplay: TemplateRef; private subscriptions: Subscription[] = []; @@ -126,7 +128,7 @@ export class DataTreeComponent implements OnChanges, OnDestroy { public sidesheet: EuiSidesheetService, private readonly logger: ClassloggerService, private readonly translator: TranslateService, - private readonly changeDetector: ChangeDetectorRef + private readonly changeDetector: ChangeDetectorRef, ) {} public async ngOnChanges(changes: SimpleChanges): Promise { @@ -138,7 +140,7 @@ export class DataTreeComponent implements OnChanges, OnDestroy { this.database.busyService.busyStateChanged.subscribe((value: boolean) => { this.isLoading = value; this.changeDetector.detectChanges(); - }) + }), ); } this.isLoading = this.database?.busyService?.isBusy ?? false; @@ -182,7 +184,7 @@ export class DataTreeComponent implements OnChanges, OnDestroy { return this.simpleTree?.hasChildren(entity); } - public getEntityById(id: string): IEntity { + public getEntityById(id: string): IEntity | undefined { return this.simpleTree?.getEntityById(id); } @@ -217,7 +219,7 @@ export class DataTreeComponent implements OnChanges, OnDestroy { * @param entity entity, for identifying the node */ public deleteNode(entity: IEntity, withDescendants: boolean) { - this.simpleTree?.deleteNode(entity,withDescendants); + this.simpleTree?.deleteNode(entity, withDescendants); } /** @ignore opens a side sheet containing the {@link TreeSelectionListComponent|selected elements} */ @@ -226,19 +228,19 @@ export class DataTreeComponent implements OnChanges, OnDestroy { title: await this.translator.get('#LDS#Heading Selected Items').toPromise(), panelClass: 'imx-sidesheet', padding: '0', - width: '50%', + width: calculateSidesheetWidth(800, 0.5), testId: 'data-tree-selected-elements-sidesheet', data: this.selectedEntities, }); this.subscriptions.push( sidesheetRef.afterClosed().subscribe((result) => { - this.logger.log(this, 'The side sheet was closed', result); - }) + this.logger.log(this, 'The sidesheet was closed', result); + }), ); } public hasSearchResults(): boolean { - return this.navigationState?.search && this.navigationState?.search !== null && this.navigationState?.search !== ''; + return (this.navigationState?.search ?? '') !== ''; } } diff --git a/imxweb/projects/qbm/src/lib/data-tree/data-tree.module.ts b/imxweb/projects/qbm/src/lib/data-tree/data-tree.module.ts index 7739565c8..31f441cb9 100644 --- a/imxweb/projects/qbm/src/lib/data-tree/data-tree.module.ts +++ b/imxweb/projects/qbm/src/lib/data-tree/data-tree.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -47,14 +47,13 @@ import { CheckableTreeComponent } from './checkable-tree/checkable-tree.componen import { DataTreeSearchResultsComponent } from './data-tree-search-results/data-tree-search-results.component'; import { BusyIndicatorModule } from '../busy-indicator/busy-indicator.module'; - @NgModule({ declarations: [ DataTreeComponent, MatSelectionListMultipleDirective, TreeSelectionListComponent, CheckableTreeComponent, - DataTreeSearchResultsComponent + DataTreeSearchResultsComponent, ], imports: [ ClassloggerModule, @@ -71,10 +70,8 @@ import { BusyIndicatorModule } from '../busy-indicator/busy-indicator.module'; TranslateModule, MatPaginatorModule, MatCardModule, - BusyIndicatorModule + BusyIndicatorModule, ], - exports: [ - DataTreeComponent - ] + exports: [DataTreeComponent], }) -export class DataTreeModule { } +export class DataTreeModule {} diff --git a/imxweb/projects/qbm/src/lib/data-tree/entity-tree-database.ts b/imxweb/projects/qbm/src/lib/data-tree/entity-tree-database.ts index 4cd315dc2..6fbed1f89 100644 --- a/imxweb/projects/qbm/src/lib/data-tree/entity-tree-database.ts +++ b/imxweb/projects/qbm/src/lib/data-tree/entity-tree-database.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,19 +24,18 @@ * */ -import { CollectionLoadParameters, IEntity } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, IEntity } from '@imx-modules/imx-qbm-dbts'; import { BusyService } from '../base/busy.service'; import { TreeDatabase } from './tree-database'; import { TreeNodeResultParameter } from './tree-node-result-parameter.interface'; export class EntityTreeDatabase extends TreeDatabase { - constructor( private readonly getEntities: (parameters: CollectionLoadParameters) => Promise, - busyService: BusyService + busyService: BusyService, ) { super(); - this.busyService = busyService + this.busyService = busyService; } public async getData(showLoading: boolean, parameters: CollectionLoadParameters = {}): Promise { @@ -46,7 +45,7 @@ export class EntityTreeDatabase extends TreeDatabase { try { entities = await this.getEntities(parameters); - } finally { + } finally { isBusy?.endBusy(); } diff --git a/imxweb/projects/qbm/src/lib/data-tree/mat-selection-list-multiple-directive.ts b/imxweb/projects/qbm/src/lib/data-tree/mat-selection-list-multiple-directive.ts index ef7ad90aa..ed08fe964 100644 --- a/imxweb/projects/qbm/src/lib/data-tree/mat-selection-list-multiple-directive.ts +++ b/imxweb/projects/qbm/src/lib/data-tree/mat-selection-list-multiple-directive.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,25 +29,24 @@ import { Directive, Host, Input, OnChanges } from '@angular/core'; import { MatListOption, MatSelectionList } from '@angular/material/list'; @Directive({ - // tslint:disable-next-line: directive-selector - selector: 'mat-selection-list[multiple]' + // eslint-disable-next-line @angular-eslint/directive-selector + selector: 'mat-selection-list[multiple]', }) // TODO Later: kann wieder weg, wenn wir mal auf einen neuere Material - Version migrieren export class MatSelectionListMultipleDirective implements OnChanges { + @Input() public multiple: boolean; + private matSelectionList: MatSelectionList; - @Input() public multiple: boolean; - private matSelectionList: MatSelectionList; + constructor(@Host() matSelectionList: MatSelectionList) { + this.matSelectionList = matSelectionList; + } - constructor(@Host() matSelectionList: MatSelectionList) { - this.matSelectionList = matSelectionList; + public ngOnChanges(): void { + if (this.multiple) { + this.matSelectionList.selectedOptions = new SelectionModel(true, this.matSelectionList.selectedOptions.selected); + } else { + const selected = this.matSelectionList.selectedOptions.selected.splice(0, 1); + this.matSelectionList.selectedOptions = new SelectionModel(false, selected); } - - public ngOnChanges(): void { - if (this.multiple) { - this.matSelectionList.selectedOptions = new SelectionModel(true, this.matSelectionList.selectedOptions.selected); - } else { - const selected = this.matSelectionList.selectedOptions.selected.splice(0, 1); - this.matSelectionList.selectedOptions = new SelectionModel(false, selected); - } - } -} \ No newline at end of file + } +} diff --git a/imxweb/projects/qbm/src/lib/data-tree/node-checked-change.interface.ts b/imxweb/projects/qbm/src/lib/data-tree/node-checked-change.interface.ts index f6a712537..031a87137 100644 --- a/imxweb/projects/qbm/src/lib/data-tree/node-checked-change.interface.ts +++ b/imxweb/projects/qbm/src/lib/data-tree/node-checked-change.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { IEntity } from 'imx-qbm-dbts'; +import { IEntity } from '@imx-modules/imx-qbm-dbts'; export interface NodeCheckedChange { /** entities for the node including parents */ diff --git a/imxweb/projects/qbm/src/lib/data-tree/tree-database.ts b/imxweb/projects/qbm/src/lib/data-tree/tree-database.ts index 0e2f85360..fb1bcac05 100644 --- a/imxweb/projects/qbm/src/lib/data-tree/tree-database.ts +++ b/imxweb/projects/qbm/src/lib/data-tree/tree-database.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,22 +24,22 @@ * */ -import { Subject, BehaviorSubject } from 'rxjs'; import { EventEmitter } from '@angular/core'; +import { BehaviorSubject, Subject } from 'rxjs'; -import { CollectionLoadParameters, IEntity } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, IEntity } from '@imx-modules/imx-qbm-dbts'; +import { BusyService } from '../base/busy.service'; import { TreeNode } from './tree-node'; import { TreeNodeResultParameter } from './tree-node-result-parameter.interface'; -import { BusyService } from '../base/busy.service'; /** * Data-provider for the data-tree. * When expanding a node in the tree, the data source of the tree will need to fetch children by using this class. */ export abstract class TreeDatabase { - public readonly initialized = new Subject(); + public readonly initialized = new Subject(); public busyService: BusyService; - public dataReloaded$ = new BehaviorSubject(undefined); + public dataReloaded$: BehaviorSubject = new BehaviorSubject(undefined); /** set this parameter to true, if your implementation supports searching */ public canSearch = false; @@ -62,13 +62,13 @@ export abstract class TreeDatabase { protected hasChildrenColumnName = 'HasChildren'; protected rootData: IEntity[] = []; - protected rootNodes: TreeNode[]; + public rootNodes: TreeNode[]; /** Initial data from database */ public async initialize(navigationState: CollectionLoadParameters = {}): Promise { // load the root entities const isBusy = this.busyService?.beginBusy(); - let entities: TreeNodeResultParameter; + let entities: TreeNodeResultParameter | undefined; try { entities = await this.getData(true, { ...navigationState, ...{ ParentKey: '' } }); } finally { @@ -101,21 +101,21 @@ export abstract class TreeDatabase { /** return children for a given tree node including the information, if more elements are available on the server */ public async getChildren(node: TreeNode, startIndex: number): Promise<{ nodes: TreeNode[]; canLoadMore: boolean }> { const isBusy = this.busyService?.beginBusy(); - let entities: TreeNodeResultParameter; + let entities: TreeNodeResultParameter | undefined; try { entities = await this.getData(false, { ParentKey: node.name, StartIndex: startIndex }); } finally { isBusy?.endBusy(); } - const nodes = this.createSortedNodes(entities.entities, node.level + 1); + const nodes = this.createSortedNodes(entities?.entities ?? [], node.level + 1); return { - nodes: entities.entities.map((entity) => nodes.find((x) => this.getId(x.item) === this.getId(entity))), - canLoadMore: entities.canLoadMore, + nodes: entities?.entities.map((entity) => nodes.find((x) => this.getId(x.item) === this.getId(entity)) ?? new TreeNode()) ?? [], + canLoadMore: entities?.canLoadMore ?? false, }; } /** abstract function, which have to be implemented */ - public async getData(showLoading: boolean, parameter: CollectionLoadParameters = {}): Promise { + public async getData(showLoading: boolean, parameter: CollectionLoadParameters = {}): Promise { return undefined; } @@ -139,16 +139,16 @@ export abstract class TreeDatabase { this.identifierColumnName ? item.GetColumn(this.identifierColumnName).GetValue() : '', this.getId(item), levelNumber, - item.GetColumn(this.hasChildrenColumnName).GetValue() + item.GetColumn(this.hasChildrenColumnName).GetValue(), ); } /** gets an unique id by combining all id parts */ - public getId(entity: IEntity): string { + public getId(entity: IEntity | undefined): string { return TreeDatabase.getId(entity); } - public static getId(entity: IEntity): string { + public static getId(entity: IEntity | undefined): string { return entity?.GetKeys() ? entity.GetKeys().join(',') : ''; } } diff --git a/imxweb/projects/qbm/src/lib/data-tree/tree-datasource.spec.ts b/imxweb/projects/qbm/src/lib/data-tree/tree-datasource.spec.ts index a15ef026a..d99e04b64 100644 --- a/imxweb/projects/qbm/src/lib/data-tree/tree-datasource.spec.ts +++ b/imxweb/projects/qbm/src/lib/data-tree/tree-datasource.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,38 +30,39 @@ import { CollectionViewer, SelectionChange } from '@angular/cdk/collections'; import { TreeDatabase } from './tree-database'; import { TreeNode } from './tree-node'; import { TreeDatasource } from './tree-datasource'; -import { CollectionLoadParameters, IEntity } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, IEntity } from '@imx-modules/imx-qbm-dbts'; describe('TreeDatabase', () => { - const createEntity = (key, parent?) => ({ - GetColumn: __ => ({ GetDisplayValue: () => '' }), - GetDisplay: () => '', - GetKeys: () => [key], - parent - } as IEntity & { parent?: string; }); + const createEntity = (key, parent?) => + ({ + GetColumn: (__) => ({ GetDisplayValue: () => '' }), + GetDisplay: () => '', + GetKeys: () => [key], + parent, + }) as IEntity & { parent?: string }; let treeControl: FlatTreeControl; let dummyEntity: IEntity; beforeEach(() => { treeControl = new FlatTreeControl( - node => node.level, - node => node.item.GetColumn('HasChildren').GetValue() + (node) => node.level, + (node) => node.item.GetColumn('HasChildren').GetValue(), ); dummyEntity = createEntity('dummy'); }); it('should disconnect all subscriptions', () => { - const treeDatasource = new TreeDatasource(treeControl, new class extends TreeDatabase {}()); + const treeDatasource = new TreeDatasource(treeControl, new (class extends TreeDatabase {})()); treeDatasource.connect({ viewChange: {} } as CollectionViewer); - expect(treeDatasource['subscriptions'].length > 0 && treeDatasource['subscriptions'].every(s => s.closed)).toBeFalsy(); + expect(treeDatasource['subscriptions'].length > 0 && treeDatasource['subscriptions'].every((s) => s.closed)).toBeFalsy(); treeDatasource.disconnect(); - expect(treeDatasource['subscriptions'].length > 0 && treeDatasource['subscriptions'].every(s => s.closed)).toBeTruthy(); + expect(treeDatasource['subscriptions'].length > 0 && treeDatasource['subscriptions'].every((s) => s.closed)).toBeTruthy(); }); describe('toggle node', () => { @@ -74,38 +75,27 @@ describe('TreeDatabase', () => { let treeDatabase: TreeDatabase; beforeEach(() => { - treeDatabase = new class extends TreeDatabase { + treeDatabase = new (class extends TreeDatabase { private readonly justSomeParentEntity = createEntity('keyParent1'); private readonly justSomeEntity = createEntity('keyOfSomeEntity'); - + constructor() { super(); - - this['rootData'] = [ - this.justSomeParentEntity, - entityUnderTest, - this.justSomeEntity - ]; - this['rootNodes'] = [ - toNode(this.justSomeParentEntity), - nodeUnderTest, - toNode(this.justSomeEntity) - ]; + + this['rootData'] = [this.justSomeParentEntity, entityUnderTest, this.justSomeEntity]; + this['rootNodes'] = [toNode(this.justSomeParentEntity), nodeUnderTest, toNode(this.justSomeEntity)]; } - + readonly getChildren = jasmine.createSpy('getChildren').and.callFake((node, __) => ({ - nodes: this.children.filter(child => child.parent === node.name).map(child => toNode(child, 1)) + nodes: this.children.filter((child) => child.parent === node.name).map((child) => toNode(child, 1)), })); - - private readonly children = [ - child, - createEntity('keyAnotherChild', this.justSomeParentEntity.GetKeys().join()) - ]; - }(); + + private readonly children = [child, createEntity('keyAnotherChild', this.justSomeParentEntity.GetKeys().join())]; + })(); }); it('expands', async () => { - // Arrange + // Arrange const expectedNumOfNodesAfterExpand = 4; const treeDatasource = new TreeDatasource(treeControl, treeDatabase); @@ -117,7 +107,7 @@ describe('TreeDatabase', () => { // Check expect(treeDatabase.getChildren).toHaveBeenCalled(); - const childNode = treeDatasource.data.find(d => d.name === child.GetKeys().join()); + const childNode = treeDatasource.data.find((d) => d.name === child.GetKeys().join()); expect(childNode.isLoading).toEqual(false); @@ -135,7 +125,7 @@ describe('TreeDatabase', () => { const index = treeDatasource.data.indexOf(nodeUnderTest); treeDatasource.data.splice(index + 1, 0, childNode); // fake expand - + // Act await treeDatasource.handleTreeControl({ removed: [nodeUnderTest] } as SelectionChange); @@ -148,30 +138,30 @@ describe('TreeDatabase', () => { }); }); - for(const testcase of [ + for (const testcase of [ { description: 'add node', change: { added: [ new TreeNode(dummyEntity, 'uid-parent1', 'uid-parent1', 0, true), - new TreeNode(dummyEntity, 'uid-parent2', 'uid-parent2', 0, true) - ] + new TreeNode(dummyEntity, 'uid-parent2', 'uid-parent2', 0, true), + ], }, - expectGetChildren: 2 + expectGetChildren: 2, }, { description: 'remove node', change: { - removed: [new TreeNode(dummyEntity, 'uid-parent1', 'uid-parent1', 0, true)] + removed: [new TreeNode(dummyEntity, 'uid-parent1', 'uid-parent1', 0, true)], }, - expectGetChildren: 0 + expectGetChildren: 0, }, ]) { it(`can handle the tree control (${testcase.description})`, async () => { // Arrange - const treeDatabase = new class extends TreeDatabase { + const treeDatabase = new (class extends TreeDatabase { readonly getChildren = jasmine.createSpy('getChildren'); - }(); + })(); const treeDatasource = new TreeDatasource(treeControl, treeDatabase); @@ -187,25 +177,65 @@ describe('TreeDatabase', () => { } for (const testcase of [ - { desciption: 'for root', entities:[dummyEntity], node: new TreeNode(dummyEntity, 'uid-parent1', 'uid-parent1', 0), ParentKey: '', startIndex: 0, hasmore: true, expectedLenght: 2 }, - { desciption: 'for children', entities:[dummyEntity], node: new TreeNode(dummyEntity, 'uid-parent1', 'uid-parent1', 0), ParentKey: 'uid-parent1', startIndex: 0, hasmore: true, expectedLenght: 2 }, - { desciption: 'for root', entities:[dummyEntity], node: new TreeNode(dummyEntity, 'uid-parent1', 'uid-parent1', 0), ParentKey: '', startIndex: 0, hasmore: false, expectedLenght: 1 }, - { desciption: 'for children', entities:[dummyEntity], node: new TreeNode(dummyEntity, 'uid-parent1', 'uid-parent1', 0), ParentKey: 'uid-parent1', startIndex: 0, hasmore: false, expectedLenght: 1 }, - { desciption: 'without nodes', entities:[], node: new TreeNode(dummyEntity, 'uid-parent1', 'uid-parent1', 0), ParentKey: 'uid-parent1', startIndex: 0, hasmore: false, expectedLenght: 0 } + { + desciption: 'for root', + entities: [dummyEntity], + node: new TreeNode(dummyEntity, 'uid-parent1', 'uid-parent1', 0), + ParentKey: '', + startIndex: 0, + hasmore: true, + expectedLenght: 2, + }, + { + desciption: 'for children', + entities: [dummyEntity], + node: new TreeNode(dummyEntity, 'uid-parent1', 'uid-parent1', 0), + ParentKey: 'uid-parent1', + startIndex: 0, + hasmore: true, + expectedLenght: 2, + }, + { + desciption: 'for root', + entities: [dummyEntity], + node: new TreeNode(dummyEntity, 'uid-parent1', 'uid-parent1', 0), + ParentKey: '', + startIndex: 0, + hasmore: false, + expectedLenght: 1, + }, + { + desciption: 'for children', + entities: [dummyEntity], + node: new TreeNode(dummyEntity, 'uid-parent1', 'uid-parent1', 0), + ParentKey: 'uid-parent1', + startIndex: 0, + hasmore: false, + expectedLenght: 1, + }, + { + desciption: 'without nodes', + entities: [], + node: new TreeNode(dummyEntity, 'uid-parent1', 'uid-parent1', 0), + ParentKey: 'uid-parent1', + startIndex: 0, + hasmore: false, + expectedLenght: 0, + }, ]) { it(`can load more (${testcase.desciption})`, async () => { //Arrange - const treeDatabase = new class extends TreeDatabase { + const treeDatabase = new (class extends TreeDatabase { getData(showLoading: boolean, parameters: CollectionLoadParameters = {}): Promise { return Promise.resolve({ entities: testcase.entities, canLoadMore: testcase.hasmore, - totalCount: 999 + totalCount: 999, }); } - readonly createSortedNodes = (entities, __) => entities.map(ent => new TreeNode(ent, '', '')); - }(); + readonly createSortedNodes = (entities, __) => entities.map((ent) => new TreeNode(ent, '', '')); + })(); const source = new TreeDatasource(treeControl, treeDatabase); @@ -215,6 +245,6 @@ describe('TreeDatabase', () => { // check expect(source.data.length).toEqual(testcase.expectedLenght); - }) + }); } -}); \ No newline at end of file +}); diff --git a/imxweb/projects/qbm/src/lib/data-tree/tree-datasource.ts b/imxweb/projects/qbm/src/lib/data-tree/tree-datasource.ts index d20be208c..ce3b8c2a2 100644 --- a/imxweb/projects/qbm/src/lib/data-tree/tree-datasource.ts +++ b/imxweb/projects/qbm/src/lib/data-tree/tree-datasource.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,13 +26,12 @@ import { CollectionViewer, SelectionChange } from '@angular/cdk/collections'; import { FlatTreeControl } from '@angular/cdk/tree'; -import { BehaviorSubject, Observable, merge, Subscription } from 'rxjs'; +import { BehaviorSubject, merge, Observable, Subscription } from 'rxjs'; import { concatMap, map } from 'rxjs/operators'; -import { TreeNode, TreeNodeInfo } from './tree-node'; +import { IEntity } from '@imx-modules/imx-qbm-dbts'; import { TreeDatabase } from './tree-database'; -import { IEntity } from 'imx-qbm-dbts'; -import * as _ from 'lodash'; +import { TreeNode, TreeNodeInfo } from './tree-node'; /** Datasource for the data-tree */ export class TreeDatasource { @@ -49,7 +48,10 @@ export class TreeDatasource { */ private subscriptions: Subscription[] = []; - constructor(private treeControl: FlatTreeControl, private dataService: TreeDatabase) {} + constructor( + private treeControl: FlatTreeControl, + private dataService: TreeDatabase, + ) {} public init(value: TreeNode[]): void { this.treeControl.dataNodes = value; @@ -76,7 +78,7 @@ export class TreeDatasource { if (treeDataHasChanged) { this.dataChange.next(this.data); } - }) + }), ); return merge(collectionViewer.viewChange, this.dataChange).pipe(map(() => this.data)); @@ -86,7 +88,7 @@ export class TreeDatasource { public async handleTreeControl(change: SelectionChange): Promise { if (change.added && change.added.length > 0) { return (await Promise.all(change.added.filter((node) => node.expandable).map((node) => this.toggleNode(node, true)))).some( - (result) => result === true + (result) => result === true, ); } @@ -97,10 +99,12 @@ export class TreeDatasource { .filter((node) => node.expandable) .slice() .reverse() - .map((node) => this.toggleNode(node, false)) + .map((node) => this.toggleNode(node, false)), ) ).some((result) => result === true); } + + return false; } /** Loads more elements and adds them to the tree */ @@ -108,13 +112,13 @@ export class TreeDatasource { node.isLoading = true; try { const newData = await this.dataService.getData(false, { ParentKey, StartIndex: startIndex }); - const nodes = this.dataService.createSortedNodes(newData.entities, node.level); + const nodes = this.dataService.createSortedNodes(newData?.entities ?? [], node.level); if (nodes.length === 0) { return; } - if (newData.canLoadMore) { + if (newData?.canLoadMore) { nodes.push(new TreeNode(undefined, ParentKey + 'more', 'more', node.level, false, false, true)); } @@ -136,7 +140,7 @@ export class TreeDatasource { /** add an empty tree node at the top of the tree */ public addEmpyNodeToTop(): TreeNode { if (this.data?.length === 0 || this.data[0].identifier !== 'newObject') { - const emptyNode = new TreeNode(null, 'newObject', this.emptyNodeCaption, 0, false); + const emptyNode = new TreeNode(undefined, 'newObject', this.emptyNodeCaption, 0, false); this.data.splice(0, 0, ...[emptyNode]); this.dataChange.next(this.data); diff --git a/imxweb/projects/qbm/src/lib/data-tree/tree-node-result-parameter.interface.ts b/imxweb/projects/qbm/src/lib/data-tree/tree-node-result-parameter.interface.ts index 64d77ce42..82d7532d6 100644 --- a/imxweb/projects/qbm/src/lib/data-tree/tree-node-result-parameter.interface.ts +++ b/imxweb/projects/qbm/src/lib/data-tree/tree-node-result-parameter.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { IEntity } from 'imx-qbm-dbts'; +import { IEntity } from '@imx-modules/imx-qbm-dbts'; export interface TreeNodeResultParameter { /** loaded entities */ diff --git a/imxweb/projects/qbm/src/lib/data-tree/tree-node.ts b/imxweb/projects/qbm/src/lib/data-tree/tree-node.ts index 805026f3f..82acf8d13 100644 --- a/imxweb/projects/qbm/src/lib/data-tree/tree-node.ts +++ b/imxweb/projects/qbm/src/lib/data-tree/tree-node.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { IEntity } from 'imx-qbm-dbts'; +import { IEntity } from '@imx-modules/imx-qbm-dbts'; export interface TreeNodeInfo { item?: IEntity; @@ -34,27 +34,41 @@ export interface TreeNodeInfo { expandable?: boolean; isLoading?: boolean; isLoadMoreNode?: boolean; + isSelectable?: boolean; display?: string; + nodes?: TreeNodeInfo[]; } /** Class representing a single node in the DataTree with expandable and level information */ export class TreeNode implements TreeNodeInfo { /** the display of the bounded item of the node */ public get display(): string { - return this.item != null ? this.item.GetDisplay() : this.name; + return (this.item != null ? this.item.GetDisplay() : this.name) ?? ''; } constructor( - public item: IEntity, - public readonly identifier: string, + public item?: IEntity, + public readonly identifier?: string, public readonly name?: string, public readonly level: number = 1, public expandable: boolean = false, public isLoading: boolean = false, - public isLoadMoreNode: boolean = false + public isLoadMoreNode: boolean = false, + public isSelectable: boolean = true, + public nodes: TreeNodeInfo[] = [], ) {} public static createNodeFromInfo(info: TreeNodeInfo) { - return new TreeNode(info.item, info.identifier,info.name,info.level,info.expandable,info.isLoading,info.isLoadMoreNode); + return new TreeNode( + info.item, + info.identifier, + info.name, + info.level, + info.expandable, + info.isLoading, + info.isLoadMoreNode, + info.isSelectable, + info.nodes, + ); } } diff --git a/imxweb/projects/qbm/src/lib/data-tree/tree-selection-list/tree-selection-list.component.scss b/imxweb/projects/qbm/src/lib/data-tree/tree-selection-list/tree-selection-list.component.scss index 1d316dd80..45903d7b9 100644 --- a/imxweb/projects/qbm/src/lib/data-tree/tree-selection-list/tree-selection-list.component.scss +++ b/imxweb/projects/qbm/src/lib/data-tree/tree-selection-list/tree-selection-list.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; :host { @@ -23,7 +23,7 @@ h2 { overflow: auto; } -.mat-list { +.mat-mdc-list { height: 100%; overflow: auto; } diff --git a/imxweb/projects/qbm/src/lib/data-tree/tree-selection-list/tree-selection-list.component.ts b/imxweb/projects/qbm/src/lib/data-tree/tree-selection-list/tree-selection-list.component.ts index 46a23cb0c..ac3f2ca7f 100644 --- a/imxweb/projects/qbm/src/lib/data-tree/tree-selection-list/tree-selection-list.component.ts +++ b/imxweb/projects/qbm/src/lib/data-tree/tree-selection-list/tree-selection-list.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,31 +27,29 @@ import { Component, Inject, OnInit } from '@angular/core'; import { EUI_SIDESHEET_DATA } from '@elemental-ui/core'; -import { DbObjectKey, IEntity} from 'imx-qbm-dbts'; +import { DbObjectKey, IEntity } from '@imx-modules/imx-qbm-dbts'; import { MetadataService } from '../../base/metadata.service'; import { CdrFactoryService } from '../../cdr/cdr-factory.service'; @Component({ templateUrl: './tree-selection-list.component.html', - styleUrls: ['./tree-selection-list.component.scss'] + styleUrls: ['./tree-selection-list.component.scss'], }) export class TreeSelectionListComponent implements OnInit { - - public items: { entities: IEntity[], tableName: string }[]; + public items: { entities: IEntity[]; tableName: string }[]; constructor( @Inject(EUI_SIDESHEET_DATA) public readonly data: IEntity[], - private readonly metadataProvider: MetadataService) { - } + private readonly metadataProvider: MetadataService, + ) {} public ngOnInit(): void { const allItems = this.data.map((elem: IEntity) => ({ entity: elem, tableName: this.getTableName(elem) })); - const tables = allItems.map(elem => elem.tableName).filter((v, i, a) => a.indexOf(v) === i); + const tables = allItems.map((elem) => elem.tableName).filter((v, i, a) => a.indexOf(v) === i); - this.items = tables.map(elem => ({ - entities: allItems.filter(ent => ent.tableName === elem).map(ent => ent.entity), - tableName: elem + this.items = tables.map((elem) => ({ + entities: allItems.filter((ent) => ent.tableName === elem).map((ent) => ent.entity), + tableName: elem, })); - } private getTableName(entity: IEntity): string { @@ -63,5 +61,4 @@ export class TreeSelectionListComponent implements OnInit { const tableName = DbObjectKey.FromXml(column.GetValue()).TableName; return this.metadataProvider.tables[tableName]?.DisplaySingular || tableName; } - } diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-auto-table/data-view-auto-table.component.html b/imxweb/projects/qbm/src/lib/data-view/data-view-auto-table/data-view-auto-table.component.html new file mode 100644 index 000000000..5bb5bc0bd --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-auto-table/data-view-auto-table.component.html @@ -0,0 +1,181 @@ + + + + + + + + + + + + + + + + + + + + +
    + #LDS#Group by: + {{ dataSource.groupByColumn()?.Display }} + +
    + {{ group?.Display[0].Display || '' }} +
    +
      + + +
    + + + + +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + {{ dataSource.GetColumnDisplay(column.ColumnName || '', dataSource?.entitySchema()) }} + + {{ element?.GetEntity()?.GetColumn(column.ColumnName)?.GetDisplayValue() }} + + + + {{ dataSource.GetColumnDisplay(column.ColumnName || '', dataSource?.entitySchema()) }} + + {{ element?.GetEntity()?.GetColumn(column.ColumnName)?.GetDisplayValue() }} +
    +
    + +
    + + +
    {{ noDataText | translate }}
    +
    + +
    +
      +
        +
      • {{ '#LDS#Check the spelling of the search terms you entered.' | translate }}
      • +
      • {{ '#LDS#Clear or use less filters.' | translate }}
      • +
      • {{ '#LDS#Enter different, more generic search terms.' | translate }}
      • +
      • {{ '#LDS#Fewer search terms lead to more matches. Try to reduce the search terms until you get a result.' | translate }}
      • +
      +
    +
    +
    +
    diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-auto-table/data-view-auto-table.component.scss b/imxweb/projects/qbm/src/lib/data-view/data-view-auto-table/data-view-auto-table.component.scss new file mode 100644 index 000000000..1ca0cafea --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-auto-table/data-view-auto-table.component.scss @@ -0,0 +1,95 @@ +/* You can add styles to this file, and also import other style files */ +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; + +:host { + height: 100%; + display: flex; + flex-direction: column; + overflow: auto; + + .imx-header-cell { + cursor: pointer; + padding-right: 20px; + } + + .imx-no-results { + text-align: center; + margin: 20px 0; + flex: 1 1 auto; + display: flex; + flex-direction: column; + justify-content: center; + &-title { + font-size: 64px; + font-weight: 700; + text-transform: uppercase; + } + li { + text-align: left; + list-style-type: unset; + font-weight: 600; + } + } + tr.expanded-detail-row { + height: 0; + &:hover { + background-color: unset; + } + } + + .expanded-group-row { + td { + border-bottom-width: 0; + } + } + .mat-mdc-cell { + .mat-mdc-icon-button { + justify-content: center; + } + } + .expanded-group-detail { + display: flex; + flex-direction: column; + width: 100%; + } + .mat-column-expand { + width: 40px; + } +} + +.imx-no-results { + &-title { + color: $color-gray-10; + } + li { + color: $color-gray-60; + } +} + +/* Dark theme */ +.eui-dark-theme { + :host { + .imx-no-results { + &-title { + color: $color-gray-20; + } + li { + color: $color-gray-10; + } + } + } +} + +/* High-contrast theme */ +.eui-contrast-theme { + :host { + .imx-no-results { + &-title { + color: $color-gray-0; + } + li { + color: $color-gray-0; + } + } + } +} diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-auto-table/data-view-auto-table.component.ts b/imxweb/projects/qbm/src/lib/data-view/data-view-auto-table/data-view-auto-table.component.ts new file mode 100644 index 000000000..142c82569 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-auto-table/data-view-auto-table.component.ts @@ -0,0 +1,210 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { animate, state, style, transition, trigger } from '@angular/animations'; +import { Component, ContentChildren, Input, QueryList, Signal, ViewChild, computed, effect } from '@angular/core'; +import { MatColumnDef, MatTable } from '@angular/material/table'; +import { isEqual } from 'lodash'; +import { QueuedActionState } from '../../processing-queue/processing-queue.interface'; +import { ImxTranslationProviderService } from '../../translation/imx-translation-provider.service'; +import { DataViewSource } from '../data-view-source'; +import { GroupInfoRow } from '../data-view.interface'; +/** + * + * @example + * Base example with default 'auto' mode. + * + * + * + * @example + * The fallowing exmaple show how to use the 'manual mode, set selection option and use sorting on manual column. + * To use sorting on manual column you need to add the 'matSort' directive to the imx-data-view-auto-table and add 'mat-sort-header' directive to the required th element. + * + * + * + * {{ identitySchema?.Columns[this.displayColumns.DISPLAY_PROPERTYNAME]?.Display }} + * + *
    {{ item.GetEntity().GetDisplay() }}
    + *
    {{ item.DefaultEmailAddress.Column.GetDisplayValue() }}
    + * + * + *
    + * + * {{ identitySchema?.Columns.IsSecurityIncident?.Display }} + * + *
    + * {{ '#LDS#Security risk' | translate }} + *
    + * + *
    + *
    + */ +@Component({ + selector: 'imx-data-view-auto-table', + templateUrl: './data-view-auto-table.component.html', + styleUrls: ['./data-view-auto-table.component.scss'], + animations: [ + trigger('detailExpand', [ + state('collapsed', style({ height: '0px', minHeight: '0' })), + state('expanded', style({ height: '*' })), + transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')), + ]), + ], +}) +export class DataViewAutoTableComponent { + /** + * Input the dataViewSource service. It handles all the action and the data loading. This input property is required. + */ + @Input({ required: true }) public dataSource: DataViewSource; + + /** + * Set the no data title. + */ + @Input() public noDataText: string; + /** + * Set the icon type, which shown, when there is no data. + */ + @Input() public noDataIcon = 'content-alert'; + /** + * Indicates, if multiselect is enabled. + */ + @Input() public selectable = false; + /** + * If set to 'auto' (= default) the data table will check the 'displayedColumns' input field and build a visual presentation. + * If set to 'manual' the data table render all the material columns in the content of the data view auto table component. In manual mode the additional columns also available. + */ + @Input() public mode: 'auto' | 'manual' = 'auto'; + + /** + * TODO: Refine if needed + * If mode is 'auto', then specify the name of the column you want a queue status badge to appear under. If left blank, then no status badge will appear, even if item is in the processing queue. + * This input has no effect on 'manual', as the injected ng-container should contain the reference to the imx-data-view-status component + */ + // @Input() public queueStatusColumnName: string; + + @ContentChildren(MatColumnDef) columnDefs: QueryList; + @ViewChild(MatTable, { static: true }) table: MatTable; + /** + * Array of the display columns. + */ + public namesOfDisplayedColumns: string[] = []; + /** + * Array of the grouped display columns. + */ + public groupColumnsToDisplayWithExpand = ['Display', 'expand']; + /** + * Signal about grouping is applied. + */ + public isGroupingApplied: Signal = computed(() => !!this.dataSource.groupByColumn()); + + public stateOptions = QueuedActionState; + private cacheColumnDefs: string[] = []; + + constructor( + public readonly translateProvider: ImxTranslationProviderService, + public readonly groupedDataSource: DataViewSource, + ) { + effect(() => { + if (this.dataSource.columnsToDisplay() && this.columnDefs && this.table) { + this.columnDefs.forEach((columnDef) => { + if (this.cacheColumnDefs.indexOf(columnDef.name) === -1) { + this.table.addColumnDef(columnDef); + this.cacheColumnDefs.push(columnDef.name); + } + }); + if (this.selectable) { + this.namesOfDisplayedColumns = ['select', ...this.dataSource?.columnsToDisplay()?.map((column) => column.ColumnName || '')]; + } else { + this.namesOfDisplayedColumns = this.dataSource?.columnsToDisplay()?.map((column) => column.ColumnName || ''); + } + } + }); + } + + /** + * Select or clear all the currenlty visible rows. + */ + public toggleAllRows(): void { + if (this.dataSource.isAllSelected()) { + this.dataSource.selection.clear(); + this.dataSource.nestedSelection = new Map(); + return; + } + + this.dataSource.selection.select(this.dataSource.getAllSelectableEntities()); + } + + /** + * Expand the selected row in grouped data table. + * @param group the selected grouped row. + */ + public expandGroup(group: GroupInfoRow): void { + if (!!group.Count && group.Count > 0) { + group.expanded = !group.expanded; + } + } + + /** + * Calls from the template when a selection is changing in the nested tables. Updates the nested selection and calls DataViewSource SelectionModelWrapper setSelection function. + * @param selection The selected array. + * @param tableIndex The nested table index. + */ + public onNestedSelectionChange(selection: any[], tableIndex: number): void { + this.dataSource.nestedSelection.set(tableIndex, selection); + let nestedSelection: any[] = []; + this.dataSource.nestedSelection.forEach((tableSelection) => { + nestedSelection.push(...tableSelection); + }); + this.dataSource.selection.setSelection(nestedSelection); + } + + /** + * Returns the selected nested table selection. + * @param index The selected nested table index. + */ + public getNestedSelection(index: number): any[] { + return this.dataSource.nestedSelection.get(index) || []; + } + + /** + * Remove the selectedItem from the nested table selection. + * @param selectedItem The selected item to remove. + */ + public onRemoveSelection(selectedItem: any): void { + this.dataSource.nestedSelection.forEach((value, key) => { + const newValue = value.filter((item) => !isEqual(item, selectedItem)); + this.dataSource.nestedSelection.set(key, newValue); + }); + } +} diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-chipbar/data-view-chipbar.component.html b/imxweb/projects/qbm/src/lib/data-view/data-view-chipbar/data-view-chipbar.component.html new file mode 100644 index 000000000..384811e5f --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-chipbar/data-view-chipbar.component.html @@ -0,0 +1,67 @@ +
    + + {{ '#LDS#Keywords' | translate }}: + + + {{ item.value }} + + + + + {{ '#LDS#Filters' | translate }}: + + + + + {{ '#LDS#Custom filter' | translate }} ({{ item.value?.Expression?.Expressions?.length }}) + + + + + + + + + + {{ getPredefinedFilterDisplay(item) }} + + + + + + + + {{ !!dataSource.filterTreeData().Description ? dataSource.filterTreeData().Description + ': ' : '' + }}{{ dataSource.filterTreeSelection()?.display }} + + + + +
    diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-chipbar/data-view-chipbar.component.scss b/imxweb/projects/qbm/src/lib/data-view/data-view-chipbar/data-view-chipbar.component.scss new file mode 100644 index 000000000..86a97463f --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-chipbar/data-view-chipbar.component.scss @@ -0,0 +1,22 @@ +@import '@elemental-ui/core/src/styles/_palette.scss'; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/mixins'; + +.imx-chipbar{ + display: flex; + gap: 8px; + flex-wrap: wrap; + justify-content: flex-end; + align-items: center; + margin-bottom: 16px; +} +.imx-chip-avatar { + width: 50px; +} + +.mat-mdc-chip { + height: 32px; +} +span{ + font-size: 14px; +} diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-chipbar/data-view-chipbar.component.ts b/imxweb/projects/qbm/src/lib/data-view/data-view-chipbar/data-view-chipbar.component.ts new file mode 100644 index 000000000..c084bfb53 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-chipbar/data-view-chipbar.component.ts @@ -0,0 +1,187 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Component, Input, Signal, computed } from '@angular/core'; +import { FilterType } from '@imx-modules/imx-qbm-dbts'; +import { DataSourceToolbarFilter } from '../../data-source-toolbar/data-source-toolbar-filters.interface'; +import { DataViewSource } from '../data-view-source'; +import { ExpressionFilter, KeywordFilter, SelectedFilter, SelectedFilterType } from '../data-view.interface'; + +/** + * @example + * + */ +@Component({ + selector: 'imx-data-view-chipbar', + templateUrl: './data-view-chipbar.component.html', + styleUrls: ['./data-view-chipbar.component.scss'], +}) +export class DataViewChipbarComponent { + /** + * Input the dataViewSource service. It handles all the action and the data loading. This input property is required. + */ + @Input({ required: true }) public dataSource: DataViewSource; + /** + * Signal, that filtering the all the selected predefinedFilter. + */ + public predefinedFilters: Signal = computed(() => { + let filters: DataSourceToolbarFilter[] = []; + this.dataSource + .predefinedFilters() + .filter((filter) => !!filter?.CurrentValue) + .map((filter) => { + if (!!filter.Delimiter) { + filter.CurrentValue?.split(filter.Delimiter).map((splitedValue) => { + filters.push({ ...filter, CurrentValue: splitedValue }); + }); + } else { + filters.push(filter); + } + }); + return filters; + }); + /** + * Signal, that filters all the search type filters. + */ + public keywords: Signal = computed( + () => this.dataSource.selectedFilters().filter((filter) => filter.type === SelectedFilterType.Keyword) as KeywordFilter[], + ); + /** + * Signal, that filter all the custom filters. + */ + public customFilters: Signal = computed( + () => this.dataSource.selectedFilters().filter((filter) => filter.type === SelectedFilterType.Custom) as ExpressionFilter[], + ); + /** + * Signal, that calculates the chip bar existence. + */ + public showChipBar: Signal = computed( + () => this.dataSource.selectedFilters()?.length > 0 || this.predefinedFilters().length > 0 || !!this.dataSource.filterTreeSelection(), + ); + + public showResetButton: Signal = computed( + () => (this.keywords()?.length || 0) + (this.customFilters()?.length || 0) + (this.predefinedFilters()?.length || 0) > 1, + ); + + /** + * Return the predefined filter selected option display value. + * @param item selected predefined filter + * @returns The display of the predefined filter. + */ + public getPredefinedFilterDisplay(item: DataSourceToolbarFilter): string { + if (item.CurrentValue == null) { + return ''; + } + let found = item.Options?.find((element) => element.Value === item.CurrentValue); + return found?.Display || ''; + } + + /** + * Remove the selected filter from the DataViewSource selectedFilters signal and from the DataViewSource state signal + * @param item The selected filter. + */ + public removeItem(item: SelectedFilter): void { + this.dataSource.selectedFilters.update((filters) => filters.filter((filter) => filter.value != item.value)); + + switch (item.type) { + case SelectedFilterType.Custom: + this.dataSource.state.update((state) => ({ + ...state, + filter: state.filter?.filter((filter) => filter.Type !== FilterType.Expression), + })); + break; + case SelectedFilterType.Keyword: + this.dataSource.state.update((state) => ({ ...state, filter: state.filter?.filter((filter) => filter.Value1 !== item.value) })); + break; + } + this.dataSource.updateState(); + } + + /** + * Remove the selected predefined filter from the DataViewSource predefinedFilters signal and from the DataViewSource state signal. + * @param item The selected predefined filter. + */ + public removePredefinedItem(item: DataSourceToolbarFilter): void { + const updatedValue = !!item.Delimiter ? this.getUpdatedPredefinedFilterValue(item) : undefined; + this.dataSource.state.update((state) => { + if (item.Name) { + state[item.Name] = updatedValue; + } + return state; + }); + this.dataSource.predefinedFilters.update((predefinedFilters) => + predefinedFilters.map((predefinedFilter) => ({ + ...predefinedFilter, + CurrentValue: item.Name === predefinedFilter.Name ? updatedValue : predefinedFilter.CurrentValue, + })), + ); + this.dataSource.updateState(); + } + /** + * Remove the filter tree filter from the DataViewSource filterTreeSelection signal and from the DataViewSource state signal. + */ + public removeFilterTree(): void { + this.dataSource.state.update((state) => { + const filter = + state.filter?.filter((filter) => filter.ColumnName !== this.dataSource.filterTreeSelection()?.filter?.ColumnName) || []; + return { ...state, filter }; + }); + this.dataSource.filterTreeSelection.set(undefined); + this.dataSource.updateState(); + } + + /** + * Clear all the filters from the chipbar. + */ + public onClearAll(): void { + this.dataSource.selectedFilters.set([]); + this.dataSource.state.update((state) => ({ ...state, filter: undefined, search: undefined })); + this.dataSource.predefinedFilters.update((predefinedFilters) => + predefinedFilters.map((filter) => { + this.dataSource.state.update((state) => { + if (filter.Name) { + state[filter.Name] = undefined; + } + return state; + }); + return { ...filter, CurrentValue: undefined }; + }), + ); + this.dataSource.filterTreeSelection.set(undefined); + this.dataSource.updateState(); + } + + private getUpdatedPredefinedFilterValue(item: DataSourceToolbarFilter): string | undefined { + let updateValue: string | undefined; + const filter = this.dataSource.predefinedFilters().find((filter) => filter.Name === item.Name); + if (!!filter && !!filter.Delimiter) { + updateValue = filter?.CurrentValue?.split(filter.Delimiter) + .filter((value) => value !== item.CurrentValue) + .join(filter.Delimiter); + } + return updateValue; + } +} diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-filter/data-view-filter.component.html b/imxweb/projects/qbm/src/lib/data-view/data-view-filter/data-view-filter.component.html new file mode 100644 index 000000000..b14ee6288 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-filter/data-view-filter.component.html @@ -0,0 +1,10 @@ + diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-filter/data-view-filter.component.scss b/imxweb/projects/qbm/src/lib/data-view/data-view-filter/data-view-filter.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-filter/data-view-filter.component.ts b/imxweb/projects/qbm/src/lib/data-view/data-view-filter/data-view-filter.component.ts new file mode 100644 index 000000000..f308b9579 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-filter/data-view-filter.component.ts @@ -0,0 +1,177 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Component, Input, Signal, computed } from '@angular/core'; +import { EuiSidesheetService } from '@elemental-ui/core'; +import { FilterType, SqlWizardExpression } from '@imx-modules/imx-qbm-dbts'; +import { TranslateService } from '@ngx-translate/core'; +import { calculateSidesheetWidth } from '../../base/sidesheet-helper'; +import { DataSourceToolbarSelectedFilter } from '../../data-source-toolbar/data-source-toolbar-filters.interface'; +import { FilterWizardComponent } from '../../data-source-toolbar/filter-wizard/filter-wizard.component'; +import { FilterWizardResult, FilterWizardSidesheetData } from '../../data-source-toolbar/filter-wizard/filter-wizard.interfaces'; +import { FilterWizardService } from '../../data-source-toolbar/filter-wizard/filter-wizard.service'; +import { DataViewSource } from '../data-view-source'; +import { ExpressionFilter, SelectedFilter, SelectedFilterType } from '../data-view.interface'; + +@Component({ + selector: 'imx-data-view-filter', + templateUrl: './data-view-filter.component.html', + styleUrls: ['./data-view-filter.component.scss'], +}) +export class DataViewFilterComponent { + /** + * Input the DataViewSource service. It handles all the action and the data loading. This input property is required. + */ + @Input({ required: true }) public dataSource: DataViewSource; + /** + * This unique id is required to use only the related navigationStateChanged events from FilterWizardService. + */ + private id: string = new Date().toString(); + /** + * Signal, that computes the SqlWizardExpression from the DataSourceToolbarCustomComponent.SelectedFilters signal. + */ + private filterExpressions: Signal = computed( + () => + this.dataSource + .selectedFilters() + .filter((item) => item.type === SelectedFilterType.Custom) + .map((filter: ExpressionFilter) => filter.value)[0], + ); + /** + * Signal, that computed the selected predefined filters from DataViewSource predefinedFilters signal. + */ + private getSelectedPredefinedFilters: Signal = computed(() => { + let selectedFilters: DataSourceToolbarSelectedFilter[] = []; + this.dataSource.predefinedFilters().forEach((item) => + selectedFilters.push({ + filter: item, + selectedOption: { Value: item.CurrentValue }, + }), + ); + + return selectedFilters; + }); + private filterType: Signal = computed(() => this.dataSource.filterTreeData().Description || ''); + + constructor( + public readonly filterService: FilterWizardService, + private readonly sidesheetService: EuiSidesheetService, + readonly translate: TranslateService, + ) { + // Updates the predefined filters from the filterWizardService + this.filterService.navigationStateChanged.subscribe((state) => { + if (state.id === this.id) { + this.dataSource.state.set(state.params); + this.dataSource.predefinedFilters.update((predefinedFilters) => + predefinedFilters.map((predefinedFilter) => ({ + ...predefinedFilter, + CurrentValue: state.selectedFilters?.find((filter) => filter.filter?.Name === predefinedFilter.Name)?.filter?.CurrentValue, + })), + ); + } + }); + } + + /** + * Show the filter wizard sidesheet component and update the state and the related signals in dataViewSource service. + */ + public async onShowFilterWizard(): Promise { + const componentData: FilterWizardSidesheetData = { + id: this.id, + settings: { + dataSource: this.dataSource.collectionData(), + navigationState: this.dataSource.state(), + entitySchema: this.dataSource.entitySchema(), + filters: this.dataSource.predefinedFilters(), + dataModel: this.dataSource.dataModel(), + }, + filterExpression: this.filterExpressions(), + selectedFilters: this.getSelectedPredefinedFilters(), + isDataSourceLocal: false, + filterTreeParameter: { + filterTreeParameter: this.dataSource.filterTree, + preSelection: this.dataSource.filterTreeSelection() || {}, + type: this.filterType(), + }, + }; + const sidesheetRef = this.sidesheetService.open(FilterWizardComponent, { + title: await this.translate.instant('#LDS#Heading Filter Data'), + icon: 'filter', + width: calculateSidesheetWidth(800, 0.5), + padding: '0px', + testId: 'filter-wizard-sidesheet', + disableClose: true, + data: componentData, + }); + + sidesheetRef.afterClosed().subscribe((result: FilterWizardResult) => { + if (!result) { + return; + } + if (!!result?.treeFilter && !!result?.treeFilter?.filter) { + const otherFilter = (this.dataSource.state().filter ?? []).filter( + (elem) => elem.ColumnName !== result.treeFilter?.filter?.ColumnName, + ); + this.dataSource.filterTreeSelection.set(result.treeFilter); + const filter = result.treeFilter.filter != null ? [result.treeFilter.filter].concat(otherFilter) : otherFilter; + this.dataSource.state.update((state) => ({ ...state, filter })); + } else { + this.dataSource.state.update((state) => { + const filter = + state.filter?.filter((filter) => filter.ColumnName !== this.dataSource.filterTreeSelection()?.filter?.ColumnName) || []; + return { ...state, filter }; + }); + this.dataSource.filterTreeSelection.set(undefined); + } + if (result?.expression?.Expression?.Expressions?.length === 0) { + this.removeFilterWizard(); + return; + } + this.dataSource.state.update((state) => { + const filter = state.filter?.filter((filter) => filter.Type !== FilterType.Expression) || []; + filter.push({ Type: FilterType.Expression, Expression: result?.expression.Expression }); + return { + ...state, + filter, + }; + }); + this.dataSource.selectedFilters.update((filters) => { + const updatedFilters: SelectedFilter[] = filters.filter((filter) => filter.type !== SelectedFilterType.Custom); + updatedFilters.push({ type: SelectedFilterType.Custom, value: result.expression }); + return updatedFilters; + }); + this.dataSource.updateState(); + }); + } + + /** + * Remove the filter wizard expression from the DataViewSource state signal and update calls DataViewSource updateState function. + */ + public removeFilterWizard(): void { + this.dataSource.state.update((state) => ({ ...state, filter: state.filter?.filter((x) => x.Expression == null) })); + this.dataSource.updateState(); + } +} diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-group/data-view-group.component.html b/imxweb/projects/qbm/src/lib/data-view/data-view-group/data-view-group.component.html new file mode 100644 index 000000000..50a5d3df6 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-group/data-view-group.component.html @@ -0,0 +1,8 @@ + + diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-group/data-view-group.component.ts b/imxweb/projects/qbm/src/lib/data-view/data-view-group/data-view-group.component.ts new file mode 100644 index 000000000..f28d9fea3 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-group/data-view-group.component.ts @@ -0,0 +1,69 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Component, effect, Input, OnInit } from '@angular/core'; +import { FormControl } from '@angular/forms'; +import { EuiSelectFeedbackMessages } from '@elemental-ui/core'; +import { TranslateService } from '@ngx-translate/core'; +import { DataViewSource } from '../data-view-source'; + +@Component({ + selector: 'imx-data-view-group', + templateUrl: './data-view-group.component.html', +}) +export class DataViewGroupComponent implements OnInit { + /** + * Input the DataViewSource service. It handles all the action and the data loading. This input property is required. + */ + @Input({ required: true }) public dataSource: DataViewSource; + formControl = new FormControl('', { nonNullable: true }); + feedbackMessages: EuiSelectFeedbackMessages; + + constructor(private readonly translateService: TranslateService) { + this.feedbackMessages = { + ...this.feedbackMessages, + search: this.translateService.instant('#LDS#Search'), + }; + effect(() => { + if (!!this.dataSource.groupByColumn() && this.dataSource.groupByColumn()?.ColumnName !== this.formControl.value) { + this.formControl.setValue(this.dataSource.groupByColumn()?.ColumnName || '', { emitEvent: false }); + } + }); + } + + ngOnInit(): void { + // Change the DataViewSource groupByColumn signal and rerender the table on template group option change event. + this.formControl.valueChanges.subscribe((column) => { + const selectedOption = this.dataSource.groupOptions.find((option) => option.value === column); + this.dataSource.groupByColumn.set(selectedOption?.clientProperty); + if (column !== null) { + this.dataSource.state.update((state) => ({ ...state, StartIndex: 0 })); + this.dataSource.selection.clear(); + this.dataSource.updateState(); + } + }); + } +} diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-nested-table/data-view-nested-table.component.html b/imxweb/projects/qbm/src/lib/data-view/data-view-nested-table/data-view-nested-table.component.html new file mode 100644 index 000000000..5e5edb2e3 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-nested-table/data-view-nested-table.component.html @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + {{ dataSource.GetColumnDisplay(column.ColumnName || '', dataSource?.entitySchema()) }} + + {{ element?.GetEntity()?.GetColumn(column.ColumnName)?.GetDisplayValue() }} + + {{ dataSource.GetColumnDisplay(column.ColumnName || '', dataSource?.entitySchema()) }} + + {{ element?.GetEntity()?.GetColumn(column.ColumnName)?.GetDisplayValue() }} +
    +
    + +
    + +

    +
    +
    + diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-nested-table/data-view-nested-table.component.ts b/imxweb/projects/qbm/src/lib/data-view/data-view-nested-table/data-view-nested-table.component.ts new file mode 100644 index 000000000..8d8652230 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-nested-table/data-view-nested-table.component.ts @@ -0,0 +1,204 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { + ChangeDetectorRef, + Component, + computed, + effect, + EventEmitter, + Input, + OnInit, + Output, + QueryList, + Signal, + ViewChild, + WritableSignal, +} from '@angular/core'; +import { MatColumnDef, MatTable } from '@angular/material/table'; +import { CollectionLoadParameters, DataModel, EntitySchema, FilterData, IClientProperty } from '@imx-modules/imx-qbm-dbts'; +import { isEqual } from 'lodash'; +import { DataViewSource } from '../data-view-source'; +import { ExecuteFunction, HightlightEntityFunction } from '../data-view.interface'; + +@Component({ + selector: 'imx-data-view-nested-table', + templateUrl: './data-view-nested-table.component.html', + providers: [DataViewSource], +}) +export class DataViewNestedTableComponent implements OnInit { + /** + * Required input property to use the same execute function as the parent component. + */ + @Input({ required: true }) execute: ExecuteFunction; + /** + * Required input property to use the same schema as the parent component. + */ + @Input({ required: true }) schema: EntitySchema; + /** + * Required input property to use the same table columns as the parent component. + */ + @Input({ required: true }) columnsToDisplay: IClientProperty[]; + /** + * Optional input property to use the same data model as the parent component. + */ + @Input() dataModel?: DataModel; + /** + * Table specific filters. + */ + @Input() filterData?: FilterData[]; + /** + * Filter and search params inherited from the parent component. + */ + @Input() params: CollectionLoadParameters; + /** + * Inherited table row selectable. + */ + @Input() selectable: boolean; + /** + * Use selection setter/getter to update the data source selection if the user uncheck some rows in the show selected only table. + */ + @Input() set selection(selection: any[]) { + this._selection = selection; + this.dataSource.selection.setSelection(selection); + } + get selection(): any[] { + return this._selection; + } + /** + * Inherited writableSignal to use on table row click action. + */ + @Input() highlightedEntity: WritableSignal; + + /** + * + */ + + @Input() public highlightedExecute: HightlightEntityFunction | undefined; + /** + * Set the no data title. + */ + @Input() public noDataText; + /** + * Set the icon type, which shown, when there is no data. + */ + @Input() public noDataIcon = 'content-alert'; + /** + * Inherited table mode from auto table component. + */ + @Input() public mode: 'auto' | 'manual'; + @Input() columnDefs: QueryList; + @Input() additionalColumns: IClientProperty[] = []; + /** + * An event emitter on table selection change. + */ + @Output() selectionChange: EventEmitter = new EventEmitter(); + /** + * Array of the display columns. Add selectable column if selectable input property is true. + */ + public namesOfDisplayedColumns: Signal = computed(() => { + if (this.selectable) { + return ['select', ...this.dataSource?.columnsToDisplay()?.map((column) => column.ColumnName || '')]; + } + return this.dataSource?.columnsToDisplay()?.map((column) => column.ColumnName || ''); + }); + /** + * Signal hide paginator from the user when the DataViewSource total count is lower the the lowest page size option. + */ + public hidePaginator: Signal = computed(() => this.dataSource.totalCount() < this.dataSource.pageSizeOptions[0]); + + @ViewChild(MatTable, { static: true }) table: MatTable; + private _selection: any[] = []; + private cacheColumnDefs: string[] = []; + + constructor( + public readonly dataSource: DataViewSource, + public changeDetectionRef: ChangeDetectorRef, + ) { + // use effect to update the parent highlightedEntity with the current highlightedEntity. + effect( + () => { + if (this.dataSource.highlightedEntity() && !!this.highlightedEntity) { + this.highlightedEntity.set(this.dataSource.highlightedEntity()); + } + }, + { allowSignalWrites: true }, + ); + effect(() => { + if (this.dataSource.columnsToDisplay() && this.columnDefs && this.table) { + this.columnDefs.forEach((columnDef) => { + if (this.cacheColumnDefs.indexOf(columnDef.name) === -1) { + this.table.addColumnDef(columnDef); + this.cacheColumnDefs.push(columnDef.name); + } + }); + } + }); + } + + ngOnInit(): void { + // Update the DataViewSource state before call init. + this.dataSource.state.update((state) => ({ + ...this.params, + ...state, + filter: [...(this.filterData || []), ...(this.params.filter || [])], + search: this.params?.search, + })); + this.dataSource.additionalColumns.set(this.additionalColumns); + this.dataSource.init({ + execute: this.execute, + schema: this.schema, + columnsToDisplay: this.columnsToDisplay, + dataModel: this.dataModel, + selectionChange: (selection: any[]) => { + this.onSelectionChange(selection); + }, + highlightEntity: this.highlightedExecute, + }); + } + + /** + * Calls when a nested table selection is changed and emit the selectionChange Event. + * @param selection All the currently selected items. + */ + public onSelectionChange(selection: any[]): void { + if (!isEqual(this.selection, selection)) { + this.selectionChange.emit(selection); + } + } + + /** + * Select or clear all the currenlty visible rows. + */ + public toggleAllRows(): void { + if (this.dataSource.isAllSelected()) { + this.dataSource.selection.clear(); + return; + } + + this.dataSource.selection.select(this.dataSource.getAllSelectableEntities()); + } +} diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-paginator/data-view-paginator.component.html b/imxweb/projects/qbm/src/lib/data-view/data-view-paginator/data-view-paginator.component.html new file mode 100644 index 000000000..eeaa9726d --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-paginator/data-view-paginator.component.html @@ -0,0 +1,13 @@ + + + + diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-paginator/data-view-paginator.component.scss b/imxweb/projects/qbm/src/lib/data-view/data-view-paginator/data-view-paginator.component.scss new file mode 100644 index 000000000..61ac96f2c --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-paginator/data-view-paginator.component.scss @@ -0,0 +1,8 @@ +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; + +:host{ + + .mat-mdc-paginator{ + border:none; + } +} diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-paginator/data-view-paginator.component.ts b/imxweb/projects/qbm/src/lib/data-view/data-view-paginator/data-view-paginator.component.ts new file mode 100644 index 000000000..fe2fc0b54 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-paginator/data-view-paginator.component.ts @@ -0,0 +1,73 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Component, Input, Signal, computed } from '@angular/core'; +import { PageEvent } from '@angular/material/paginator'; +import { DataViewSource } from '../data-view-source'; + +@Component({ + selector: 'imx-data-view-paginator', + templateUrl: './data-view-paginator.component.html', + styleUrls: ['./data-view-paginator.component.scss'], +}) +export class DataViewPaginatorComponent { + /** + * Input the dataViewSource service. It handles all the action and the data loading. This input property is required. + */ + @Input({ required: true }) public dataSource: DataViewSource; + /** + * Signal get page size from DataViewSource state signal. + */ + public pageSize: Signal = computed(() => + this.dataSource.showOnlySelected() + ? this.dataSource.selection.selected.length + : this.dataSource.state().PageSize || this.dataSource.pageSizeOptions[0], + ); + /** + * Signal calculates the page index from DataViewSource state signal and pageSize. + */ + public pageIndex: Signal = computed(() => { + return this.dataSource.showOnlySelected() ? 0 : (this.dataSource.state().StartIndex || 0) / this.pageSize(); + }); + /** + * Signal hide page size selection UI from the user when the DataViewSource total count is lower the the lowest page size option. + */ + public hidePageSize: Signal = computed( + () => this.dataSource.totalCount() < this.dataSource.pageSizeOptions[0] || this.dataSource.showOnlySelected(), + ); + public totalCount: Signal = computed(() => + this.dataSource.showOnlySelected() ? this.dataSource.selection.selected.length : this.dataSource?.totalCount(), + ); + + /** + * Update the DataViewSource state and reload the table data on pagination change. + * @param event The current page state from the mat-paginator. + */ + public async onStateChanged(event: PageEvent): Promise { + this.dataSource.state.update((state) => ({ ...state, PageSize: event.pageSize, StartIndex: event.pageIndex * event.pageSize })); + await this.dataSource.updateState(); + } +} diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-search/data-view-search.component.html b/imxweb/projects/qbm/src/lib/data-view/data-view-search/data-view-search.component.html new file mode 100644 index 000000000..d6444a871 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-search/data-view-search.component.html @@ -0,0 +1,2 @@ + + diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-search/data-view-search.component.scss b/imxweb/projects/qbm/src/lib/data-view/data-view-search/data-view-search.component.scss new file mode 100644 index 000000000..a07a4db69 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-search/data-view-search.component.scss @@ -0,0 +1,3 @@ +:host{ + width: 400px; +} diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-search/data-view-search.component.ts b/imxweb/projects/qbm/src/lib/data-view/data-view-search/data-view-search.component.ts new file mode 100644 index 000000000..aab459642 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-search/data-view-search.component.ts @@ -0,0 +1,125 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Component, effect, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { FormControl } from '@angular/forms'; +import { FilterType } from '@imx-modules/imx-qbm-dbts'; +import { debounceTime } from 'rxjs/operators'; +import { DataViewSource } from '../data-view-source'; +import { KeywordFilter, SelectedFilterType } from '../data-view.interface'; + +@Component({ + selector: 'imx-data-view-search', + templateUrl: './data-view-search.component.html', +}) +export class DataViewSearchComponent implements OnInit { + /** + * Input the dataViewSource service. It handles all the action and the data loading. This input property is required. + */ + @Input({ required: true }) public dataSource: DataViewSource; + /** + * Event to emit all the search params on change. + */ + @Output() public onSearchChange = new EventEmitter(); + /** + * FormControl to tracks the value and valueChange on eui-search component. + */ + public searchControl: FormControl = new FormControl('', { nonNullable: true }); + /** + * Private reset property to not call search accidentally. + */ + private reset = false; + private searchParams: string | undefined; + + constructor() { + effect(() => { + const newSearchParams = this.dataSource.state().search; + if (newSearchParams !== this.searchParams) { + this.searchParams = newSearchParams; + this.onSearchChange.emit(newSearchParams); + this.searchControl.setValue(newSearchParams || '', { emitEvent: false }); + } + }); + } + + public ngOnInit(): void { + // Call onSearch on searchControl valueChange after a 1000ms standby. + this.searchControl.valueChanges.pipe(debounceTime(1000)).subscribe((value) => this.onSearch(value)); + } + + /** + * Update DataViewSource setKeywords signal and reload data. + * @param keywords The value of the searchControl. + */ + public onSearch(keywords: string): void { + if (this.reset) { + this.reset = false; + return; + } + + this.dataSource.setKeywords(keywords); + } + + /** + * Add all keywords in searchControl value to the DataViewSource selectedfilters signal. + */ + public onAssignKeywords(): void { + setTimeout(() => { + if (this.isSearchEmpty) { + return; + } + this.reset = true; + let keywords = this.searchControl.value + .split(' ') + .map((item) => item.trim()) + .filter( + (item) => + item?.length > 0 && + !this.dataSource.selectedFilters().some((filter) => filter.type === SelectedFilterType.Keyword && filter.value === item), + ); + this.dataSource.selectedFilters.update((filters) => [ + ...filters, + ...keywords.map((keyword) => ({ type: SelectedFilterType.Keyword, value: keyword }) as KeywordFilter), + ]); + this.dataSource.state.update((state) => ({ + ...state, + filter: [...(state.filter || []), ...keywords.map((keyword) => ({ Type: FilterType.Search, Value1: keyword }))], + })); + this.searchControl.reset(''); + this.reset = false; + }); + } + + /** + * Calcuates the searchControls is really empty or not. + */ + private get isSearchEmpty(): boolean { + return this.searchControl.value == null || + (typeof this.searchControl.value === 'string' && this.searchControl.value.trim().length === 0) + ? true + : false; + } +} diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-selection/data-view-selection.component.html b/imxweb/projects/qbm/src/lib/data-view/data-view-selection/data-view-selection.component.html new file mode 100644 index 000000000..7f7dec396 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-selection/data-view-selection.component.html @@ -0,0 +1,25 @@ +
    +
    + {{ '#LDS#Selected items' | translate }}: {{ totalSelected() > 0 ? totalSelected() : ('#LDS#None' | translate) }} +
    + + + {{ '#LDS#Show selected only' | translate }} + +
    diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-selection/data-view-selection.component.scss b/imxweb/projects/qbm/src/lib/data-view/data-view-selection/data-view-selection.component.scss new file mode 100644 index 000000000..200a7290e --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-selection/data-view-selection.component.scss @@ -0,0 +1,58 @@ +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; + +:host{ + margin-right: auto; + .imx-selection{ + display: flex; + align-items: center; + &-info{ + font-size: 14px; + font-weight: 600; + margin-right: 8px; + &-title{ + font-weight: 400; + margin-right: 8px; + } + } + .mdc-icon-button{ + margin-right: 0; + } + } +} + + +:host{ + .imx-selection{ + &-info{ + &-itle{ + color: $color-gray-10; + } + } + } +} + +/* Dark theme */ +.eui-dark-theme { + :host{ + .imx-selection { + &-info { + &-title{ + color: $color-gray-20; + } + } + } + } +} + +/* High-contrast theme */ +.eui-contrast-theme { + :host{ + .imx-selection { + &-info{ + &-title{ + color: $color-gray-0; + } + } + } + } +} diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-selection/data-view-selection.component.ts b/imxweb/projects/qbm/src/lib/data-view/data-view-selection/data-view-selection.component.ts new file mode 100644 index 000000000..1e5c4761c --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-selection/data-view-selection.component.ts @@ -0,0 +1,76 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Component, Input, Signal, computed } from '@angular/core'; +import { MatSlideToggleChange } from '@angular/material/slide-toggle'; +import { DataViewSource } from '../data-view-source'; + +/** + * @example + * + */ + +@Component({ + selector: 'imx-data-view-selection', + templateUrl: './data-view-selection.component.html', + styleUrls: ['./data-view-selection.component.scss'], +}) +export class DataViewSelectionComponent { + /** + * Input the dataViewSource service. It handles all the action and the data loading. This input property is required. + */ + @Input({ required: true }) public dataSource: DataViewSource; + /** + * The value of the material slide toggle. + */ + public showSelected: boolean = false; + /** + * Get the total selected number and update the showSelected value in every selection change. + */ + public totalSelected: Signal = computed(() => { + this.dataSource.selectionChanged(); + if (this.dataSource.selection.selected.length == 0) { + this.showSelected = false; + } + return this.dataSource.selection.selected.length || 0; + }); + + /** + * Calls selection wrapper clear function. + */ + public onClearSelection(): void { + this.dataSource.nestedSelection = new Map(); + this.dataSource.selection.clear(); + } + + /** + * Update showOnlySelected signal in every slide toggle change event. + * @param $event The slide toggle change event object. + */ + public onShowSelectionChanged($event: MatSlideToggleChange) { + this.dataSource.showOnlySelected.set($event.checked); + } +} diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-settings/data-view-settings.component.html b/imxweb/projects/qbm/src/lib/data-view/data-view-settings/data-view-settings.component.html new file mode 100644 index 000000000..3088f58fa --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-settings/data-view-settings.component.html @@ -0,0 +1,85 @@ + + + + + + + + + + + + + +
    + + diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-settings/data-view-settings.component.ts b/imxweb/projects/qbm/src/lib/data-view/data-view-settings/data-view-settings.component.ts new file mode 100644 index 000000000..8afab64f4 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-settings/data-view-settings.component.ts @@ -0,0 +1,262 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Component, EventEmitter, Input, Output, Signal, computed } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { EuiSidesheetService } from '@elemental-ui/core'; +import { FilterType, IClientProperty } from '@imx-modules/imx-qbm-dbts'; +import { TranslateService } from '@ngx-translate/core'; +import { firstValueFrom } from 'rxjs'; +import { calculateSidesheetWidth } from '../../base/sidesheet-helper'; +import { ConfirmationService } from '../../confirmation/confirmation.service'; +import { DataExportComponent } from '../../data-export/data-export.component'; +import { AdditionalInfosComponent } from '../../data-source-toolbar/additional-infos/additional-infos.component'; +import { DataSourceToolbarSettings } from '../../data-source-toolbar/data-source-toolbar-settings'; +import { DSTViewConfig } from '../../data-source-toolbar/data-source-toolbar-view-config.interface'; +import { SaveConfigDialogComponent } from '../../data-source-toolbar/save-config-dialog/save-config-dialog.component'; +import { SnackBarService } from '../../snackbar/snack-bar.service'; +import { DataViewSource } from '../data-view-source'; + +@Component({ + selector: 'imx-data-view-settings', + templateUrl: './data-view-settings.component.html', +}) +export class DataViewSettingsComponent { + /** + * Input the dataViewSource service. It handles all the action and the data loading. This input property is required. + */ + @Input({ required: true }) public dataSource: DataViewSource; + /** + * Event to emit a DSTViewConfig for post/put via the viewConfig.putViewConfig function. + */ + @Output() public updateConfig = new EventEmitter(); + /** + * Event to emit an DSTViewConfig.Id for delete via the viewConfig.deleteViewConfig function. + */ + @Output() public deleteConfigById = new EventEmitter(); + /** + * Checks if we have any saved configs at all in settings.viewConfig.viewConfigs. + */ + public hasSavedConfigs: Signal = computed( + () => !!this.dataSource.viewConfig()?.viewConfigs && !!this.dataSource.viewConfig()?.viewConfigs?.length, + ); + + constructor( + private readonly sidesheetService: EuiSidesheetService, + private readonly translateService: TranslateService, + private dialog: MatDialog, + private readonly confirm: ConfirmationService, + private readonly snackbar: SnackBarService, + ) {} + + /** + * Opens the dataExportSidesheet component with the required settings. + */ + public openExportSidesheet(): void { + const settings: DataSourceToolbarSettings = { + dataSource: this.dataSource.collectionData(), + navigationState: this.dataSource.state(), + entitySchema: this.dataSource.entitySchema(), + dataModel: this.dataSource.dataModel(), + exportMethod: this.dataSource.exportFunction, + displayedColumns: this.dataSource.columnsToDisplay(), + }; + this.sidesheetService.open(DataExportComponent, { + title: this.translateService.instant('#LDS#Heading Export Data'), + padding: '0px', + width: calculateSidesheetWidth(), + icon: 'export', + data: settings, + }); + } + + /** + * Call DataViewSource resetView on reset view button click. + */ + public onResetViewAndTree(): void { + this.dataSource.resetView(); + } + + /** + * Opens AdditionalInfosComponent dialog to add/remove columns and change column order. + */ + public async updateAdditionalColumns(): Promise { + this.dialog + .open(AdditionalInfosComponent, { + width: 'min(1200px,70%)', + autoFocus: false, + height: 'min(700px,70%)', + data: { + dataModel: this.dataSource.dataModel(), + entitySchema: this.dataSource.entitySchema(), + displayedColumns: this.dataSource.initialColumnsToDisplay, + additionalPropertyNames: this.dataSource.optionalColumns(), + additionalColumns: [], + preselectedProperties: [...this.dataSource.columnsToDisplay()], + }, + panelClass: 'imx-toolbar-dialog', + }) + .afterClosed() + .subscribe(async (result: { all: IClientProperty[]; optionals: IClientProperty[] }) => { + if (result) { + const needsReload = result.optionals.some( + (res) => this.dataSource.additionalColumns().find((sel) => sel.ColumnName === res.ColumnName) == null, + ); + if (needsReload) { + const additionalColumnNames = result.optionals.map((column) => column.ColumnName || ''); + this.dataSource.updateEntitySchema(additionalColumnNames); + this.dataSource.state.update((state) => ({ ...state, withProperties: additionalColumnNames.join(',') })); + await this.dataSource.updateState(true); + } + this.dataSource.additionalColumns.set(result.optionals); + this.dataSource.columnsToDisplay.set(result.all); + } + }); + } + + /** + * Opens the SaveConfigDialogComponent dialog and setup the a new config than emit that. + */ + public async saveConfig(): Promise { + const displayName = await firstValueFrom(this.dialog.open(SaveConfigDialogComponent).afterClosed()); + if (!displayName) { + return; + } + const existingConfig = this.dataSource.viewConfig()?.viewConfigs?.find((config) => config.DisplayName === displayName); + if (existingConfig && existingConfig.Id === 'Default') { + this.snackbar.open({ + key: '#LDS#Overwriting the Default view is not allowed.', + }); + return; + } + if ( + existingConfig && + !(await this.confirm.confirmDelete( + '#LDS#Heading Overwrite View', + '#LDS#A view with the entered name already exists. Do you want to overwrite the already existing view with the new view?', + )) + ) { + return; + } + const config: DSTViewConfig = { + Id: existingConfig?.Id, + ViewId: this.dataSource.viewConfig()?.viewId, + DisplayName: displayName, + Filter: [ + ...(this.dataSource.state().filter || []), + ...(this.dataSource.state().search ? [{ Type: FilterType.Search, Value1: this.dataSource.state().search }] : []), + ], + GroupBy: this.dataSource.groupByColumn()?.ColumnName, + OrderBy: this.dataSource.state()?.OrderBy, + AdditionalTableColumns: this.dataSource.additionalColumns().map((column) => column.ColumnName || ''), + AdditionalParameters: this.dataSource + .predefinedFilters() + .filter((filter) => !!filter.CurrentValue) + .reduce( + (prevFilters: { [key: string]: string }, filter) => ({ ...prevFilters, [filter.Name || '']: filter.CurrentValue || '' }), + {}, + ), + UseAsDefault: false, + }; + this.snackbar.open({ + key: '#LDS#The view has been successfully saved.', + }); + this.updateConfig.emit(config); + } + + /** + * Checks the required config is dafault or not. + * @param config The selected DSTViewConfig. + * @returns Is config default. + */ + public isDefaultId(config: DSTViewConfig): boolean { + return config?.Id == 'Default'; + } + + /** + * Checks the required config is used as favourite or not. + * @param config The selected DSTViewConfig + * @returns Config is favourite. + */ + public isConfigDefault(config: DSTViewConfig): boolean { + return config.UseAsDefault; + } + + /** + * Changes the default icons on screen to reflect which config is default, emits a updateConfig signal to change the data on the server + * @param config the selected DSTViewConfig + */ + public toggleDefaultConfig(config: DSTViewConfig): void { + // Find the currently chosen default + const lastDefaultConfig = this.dataSource.viewConfig()?.viewConfigs?.find((config) => config.UseAsDefault); + if (!config.UseAsDefault) { + if (lastDefaultConfig && lastDefaultConfig.Id !== config.Id) { + // Set the previous config to false, as long as it isn't the same as the incoming config + lastDefaultConfig.UseAsDefault = false; + } + } + config.UseAsDefault = !config.UseAsDefault; + if (!config?.IsReadOnly) { + // We can safely update the chosen config as default, API will handle the others + this.updateConfig.emit(config); + } + if (config?.IsReadOnly && lastDefaultConfig) { + // We need to update the last config to be not-default, we cannot update the chosen as it is read-sonly + lastDefaultConfig?.IsReadOnly ? null : this.updateConfig.emit(lastDefaultConfig); + } + } + + /** + * Opens the SaveConfigDialogComponent dialog and update the selected config DisplayName. + * @param config the selected DSTViewConfig + */ + public async changeConfigName(config: DSTViewConfig): Promise { + await this.dialog + .open(SaveConfigDialogComponent, { + data: { + currentName: config.DisplayName, + }, + }) + .afterClosed() + .subscribe((displayName) => { + if (displayName) { + config.DisplayName = displayName; + this.updateConfig.emit(config); + } + }); + } + + /** + * Remove the selected config after show confirm dialog by the id of the config. + * @param id The id of the config. + */ + public async removeConfigIndex(id?: string): Promise { + if (id && !(await this.confirm.confirmDelete('#LDS#Heading Delete View', '#LDS#Are you sure you want to delete the view?'))) { + return; + } + this.deleteConfigById.emit(id); + } +} diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-source-factory.service.ts b/imxweb/projects/qbm/src/lib/data-view/data-view-source-factory.service.ts new file mode 100644 index 000000000..424b5740b --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-source-factory.service.ts @@ -0,0 +1,51 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Injectable } from '@angular/core'; +import { TypedEntity } from '@imx-modules/imx-qbm-dbts'; +import { ClassloggerService } from '../classlogger/classlogger.service'; +import { ConfirmationService } from '../confirmation/confirmation.service'; +import { ProcessingQueueService } from '../processing-queue/processing-queue.service'; +import { SettingsService } from '../settings/settings-service'; +import { SqlWizardApiService } from '../sqlwizard/sqlwizard-api.service'; +import { DataViewSource } from './data-view-source'; + +@Injectable({ + providedIn: 'root', +}) +export class DataViewSourceFactoryService { + constructor( + private readonly settings: SettingsService, + private readonly log: ClassloggerService, + private readonly confirmService: ConfirmationService, + private readonly sqlWizardApiService: SqlWizardApiService, + private readonly queueService: ProcessingQueueService, + ) {} + + public getDataSource(): DataViewSource { + return new DataViewSource(this.settings, this.log, this.confirmService, this.sqlWizardApiService, this.queueService); + } +} diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-source.spec.ts b/imxweb/projects/qbm/src/lib/data-view/data-view-source.spec.ts new file mode 100644 index 000000000..07128708c --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-source.spec.ts @@ -0,0 +1,138 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { computed, signal } from '@angular/core'; +import { Sort } from '@angular/material/sort'; +import { MatTableDataSource } from '@angular/material/table'; +import { FilterTreeData, MethodDefinition, TypedEntity } from '@imx-modules/imx-qbm-dbts'; +import { debounce } from 'lodash'; +import { Observable, of } from 'rxjs'; +import { ClassloggerService } from '../classlogger/classlogger.service'; +import { ConfirmationService } from '../confirmation/confirmation.service'; +import { DSTViewConfig } from '../data-source-toolbar/data-source-toolbar-view-config.interface'; +import { SelectionModelWrapper } from '../data-source-toolbar/selection-model-wrapper'; +import { SettingsService } from '../settings/settings-service'; +import { SqlWizardApiService } from '../sqlwizard/sqlwizard-api.service'; +import { DataViewSource } from './data-view-source'; +import { DataViewInitParameters, GroupInfoRow, WritableEntitySchema } from './data-view.interface'; +export const FakeDataViewSource: Pick = { + collectionData: signal({ totalCount: 0, Data: [] }), + entitySchema: signal({ Columns: {}, LocalColumns: {} }), + dataModel: signal(undefined), + execute: () => Promise.resolve({ totalCount: 0, Data: [] }), + entitySubject: signal([]), + entitySubject$: of([]), + count: 0, + totalCount: signal(0), + getAllSelectableEntities: signal([]), + data: [], + loading: signal(false), + isLimitReached: signal(false), + selection: new SelectionModelWrapper(), + selectionChanged: signal(undefined), + selectionChangeFunction: undefined, + showOnlySelected: signal(false), + columnsToDisplay: signal([]), + initialColumnsToDisplay: [], + optionalColumns: signal([]), + additionalColumns: signal([]), + additionalListColumns: signal(undefined), + pageSizeOptions: [], + sortId: signal(undefined), + sortDirection: signal(''), + state: signal({ undefined }), + predefinedFilters: signal([]), + selectedFilters: signal([]), + exportFunction: { + getMethod: (withProperties: string, PageSize?: number) => ({}) as MethodDefinition, + }, + viewConfig: signal(undefined), + showFilters: signal(false), + currentSelectedEntityCount: signal(0), + isAllSelected: signal(false), + highlightedEntity: signal(undefined), + highlightedExecute: undefined, + itemStatus: { + enabled: () => true, + }, + groupOptions: [], + groupByColumn: signal(null), + groupData: signal(undefined), + groupedDataSource: computed(() => ({}) as MatTableDataSource), + nestedSelection: new Map(), + filterTree: { + filterMethode: (parentkey: string) => Promise.resolve({} as FilterTreeData), + }, + filterTreeData: signal({}), + filterTreeSelection: signal(undefined), + settings: new SettingsService(), + log: { + debug: () => {}, + info: () => {}, + } as unknown as ClassloggerService, + confirmService: {} as ConfirmationService, + sqlWizardApiService: {} as SqlWizardApiService, + ngOnDestroy: function (): void {}, + connect: function (): Observable { + return of([]); + }, + disconnect: function (): void {}, + init: async function (initParameters: DataViewInitParameters): Promise { + this.execute = initParameters.execute; + await this.updateState(); + return Promise.resolve(); + }, + updateState: async function (): Promise { + let collectionData = await this.execute(); + console.log(collectionData); + this.collectionData.set(collectionData); + return Promise.resolve(); + }, + sortChange: function (sortState: Sort): void {}, + abortCall: function (): void {}, + resetView: function (): Promise { + return Promise.resolve(); + }, + applyConfig: function (config: DSTViewConfig): Promise { + return Promise.resolve(); + }, + setKeywords: function (keywords: string): void {}, + updateEntitySchema: function (additionalColumnNames: string[]): void {}, + debouncedHighlightRow: debounce((entity: TypedEntity, event?) => {}, 250), + highlightRow: function (entity: TypedEntity, event?: MouseEvent): void {}, + isSortable: function (column: string | undefined): boolean { + return false; + }, + GetColumnDisplay: function (columnName: string, entitySchema?: WritableEntitySchema): string { + return ''; + }, + // initOptionalColumns: function (): void { + // + // }, + initFilters: function (initParameters: DataViewInitParameters): Promise { + return Promise.resolve(); + }, +}; diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-source.ts b/imxweb/projects/qbm/src/lib/data-view/data-view-source.ts new file mode 100644 index 000000000..67327f2dd --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-source.ts @@ -0,0 +1,622 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { DataSource, SelectionChange } from '@angular/cdk/collections'; +import { Injectable, OnDestroy, Signal, WritableSignal, computed, effect, signal } from '@angular/core'; +import { toObservable } from '@angular/core/rxjs-interop'; +import { Sort, SortDirection } from '@angular/material/sort'; +import { MatTableDataSource } from '@angular/material/table'; +import { EuiSelectOption } from '@elemental-ui/core'; +import { + CollectionLoadParameters, + DataModel, + DataModelProperty, + ExtendedTypedEntityCollection, + FilterTreeData, + FilterType, + GroupInfoData, + IClientProperty, + TypedEntity, + ValType, +} from '@imx-modules/imx-qbm-dbts'; +import { debounce } from 'lodash'; +import { Observable, Subscription } from 'rxjs'; +import { ClassloggerService } from '../classlogger/classlogger.service'; +import { ConfirmationService } from '../confirmation/confirmation.service'; +import { FilterTreeParameter } from '../data-source-toolbar/data-model/filter-tree-parameter'; +import { DataSourceItemStatus } from '../data-source-toolbar/data-source-item-status.interface'; +import { DataSourceToolbarExportMethod } from '../data-source-toolbar/data-source-toolbar-export-method.interface'; +import { DataSourceToolbarFilter } from '../data-source-toolbar/data-source-toolbar-filters.interface'; +import { DSTViewConfig, DataSourceToolbarViewConfig } from '../data-source-toolbar/data-source-toolbar-view-config.interface'; +import { FilterTreeSelectionParameter } from '../data-source-toolbar/filter-wizard/filter-tree-sidesheet/filter-tree-sidesheet.model'; +import { SelectionModelWrapper } from '../data-source-toolbar/selection-model-wrapper'; +import { QueuedActionState } from '../processing-queue/processing-queue.interface'; +import { ProcessingQueueService } from '../processing-queue/processing-queue.service'; +import { SettingsService } from '../settings/settings-service'; +import { SqlWizardApiService } from '../sqlwizard/sqlwizard-api.service'; +import { + DataViewInitParameters, + ExecuteFunction, + ExecuteGroupFunction, + GroupInfoRow, + HightlightEntityFunction, + SelectedFilter, + SelectedFilterType, + SelectionChangeFunction, + WritableEntitySchema, +} from './data-view.interface'; + +/** + * Set injectbale providedIn to null to have multiple instances of the DataViewSource service for every component. + */ +@Injectable({ + providedIn: null, +}) +export class DataViewSource implements DataSource, OnDestroy { + /** + * Collection of the generic type. + */ + public collectionData: WritableSignal> = signal({ totalCount: 0, Data: [] }); + public entitySchema: WritableSignal; + public dataModel: WritableSignal = signal(undefined); + public execute: ExecuteFunction; + public entitySubject: Signal = computed(() => { + this.selectionChanged(); + return this.showOnlySelected() ? this.selection.selected : this.collectionData().Data; + }); + public entitySubject$: Observable = toObservable(this.entitySubject); + public get count(): number { + return this.collectionData()?.Data.length | 0; + } + public totalCount: Signal = computed(() => { + return !!this.groupByColumn() ? this.groupData()?.TotalCount || 0 : this.collectionData()?.totalCount || 0; + }); + public getAllSelectableEntities: Signal = computed(() => this.entitySubject().filter((entity) => this.itemStatus.enabled(entity))); + + public get data(): T[] { + return this.collectionData()?.Data; + } + public loading: WritableSignal = signal(true); + public isLimitReached: Signal = computed(() => !!this.collectionData().IsLimitReached); + + // Selection + public readonly selection = new SelectionModelWrapper(); + public selectionChanged: WritableSignal | undefined> = signal(undefined); + public selectionChangeFunction: SelectionChangeFunction | undefined; + public showOnlySelected: WritableSignal = signal(false); + + // Table settings + public columnsToDisplay: WritableSignal = signal([]); + public initialColumnsToDisplay: IClientProperty[] = []; + public optionalColumns: WritableSignal<(IClientProperty | undefined)[]> = signal([]); + public additionalColumns: WritableSignal = signal([]); + public additionalListColumns: WritableSignal = signal(undefined); + public pageSizeOptions: number[] = []; + public sortId: WritableSignal = signal(undefined); + public sortDirection: WritableSignal = signal(''); + + // Filters & settings + public state: WritableSignal = signal({}); + public predefinedFilters: WritableSignal = signal([]); + public selectedFilters: WritableSignal = signal([]); + public exportFunction: DataSourceToolbarExportMethod; + public viewConfig: WritableSignal = signal(undefined); + public showFilters: WritableSignal = signal(false); + /** + * Signal, that calculates the number of the currently visible selected entities. + */ + public currentSelectedEntityCount: Signal = computed(() => { + this.selectionChanged(); + return this.entitySubject().filter((entity) => this.selection.isSelected(entity)).length; + }); + /** + * Signal, that calculates all the visible and selectable rows are selected. + */ + public isAllSelected: Signal = computed(() => this.currentSelectedEntityCount() == this.getAllSelectableEntities().length); + /** + * Row click event signal. + */ + public highlightedEntity: WritableSignal = signal(undefined, { equal: () => false }); + public highlightedExecute: HightlightEntityFunction | undefined; + + // If the item is defined, then use the queue to find if its in the queue or failed. Otherwise return true + private itemNotInQueueOrFailed = (item?: TypedEntity) => + item + ? [QueuedActionState.Failed, QueuedActionState.NotInQueue].includes( + this.queueService.pollAction(item.GetEntity().GetKeys().join(',')), + ) + : true; + /** + * Row status functions - enabled if queue status is failed or not in queue + */ + public itemStatus: DataSourceItemStatus = { + status: (item?) => (item ? this.queueService.pollAction(item.GetEntity().GetKeys().join(',')) : QueuedActionState.NotInQueue), + enabled: this.itemNotInQueueOrFailed, + // By default, enabled = rowEnabled + rowEnabled: this.itemNotInQueueOrFailed, + }; + + // Group + public groupOptions: EuiSelectOption[] = []; + public groupByColumn: WritableSignal = signal(null); + public groupData: WritableSignal = signal(undefined); + public groupedDataSource: Signal> = computed( + () => new MatTableDataSource(this.groupData()?.Groups ?? []), + ); + /** + * Map of nested selection. + */ + public nestedSelection: Map = new Map(); + private groupExecute: ExecuteGroupFunction; + + // Filter tree + public filterTree: FilterTreeParameter; + public filterTreeData: WritableSignal = signal({}); + public filterTreeSelection: WritableSignal = signal(undefined); + + private abortController = new AbortController(); + private subscription: Subscription; + + constructor( + public readonly settings: SettingsService, + public readonly log: ClassloggerService, + public readonly confirmService: ConfirmationService, + public readonly sqlWizardApiService: SqlWizardApiService, + private readonly queueService: ProcessingQueueService, + ) { + this.pageSizeOptions = this.settings.DefaultPageOptions; + this.state.set({ PageSize: this.settings?.DefaultPageSize, StartIndex: 0 }); + this.log.debug(this, 'Creating data source'); + this.subscription = this.selection.changed.subscribe((change) => { + this.selectionChanged.set(change); + if (this.selectionChangeFunction) { + this.selectionChangeFunction(this.selection.selected); + } + if (this.selection.selected.length === 0) { + this.showOnlySelected.set(false); + } + }); + effect( + () => { + // Check when a new group is submitted whether a selected item is in the queue, deselect if so + this.queueService._groups(); + this.selection.selected.forEach((item) => { + if (!this.itemStatus.enabled(item)) { + this.selection.unChecked(item); + } + }); + }, + { allowSignalWrites: true }, + ); + effect( + () => { + if (this.highlightedEntity() && this.highlightedExecute) { + this.highlightedExecute(this.highlightedEntity()); + this.highlightedEntity.set(undefined); + } + }, + { allowSignalWrites: true }, + ); + } + + ngOnDestroy(): void { + this.log.debug(this, 'Destroying data source'); + this.subscription?.unsubscribe(); + } + + /** + * Connects the data table with the data source. + * @returns Observable that emits a new value when the data changes. + */ + public connect(): Observable { + this.log.debug(this, 'Connect'); + return this.entitySubject$; + } + + /** + * Disconnects the data table from this data source. + */ + public disconnect(): void { + this.log.debug(this, 'Disconnect'); + } + + /** + * Initialize the data source and call updateState function. + * @param initParameters The initParameters object contains all the required values to setup the data source properly. + */ + public async init(initParameters: DataViewInitParameters): Promise { + this.loading.set(true); + this.log.info(this, 'Initializing data source'); + this.execute = initParameters.execute; + this.collectionData.set({ totalCount: 0, Data: [] }); + this.entitySchema = signal({ ...initParameters.schema, LocalColumns: initParameters.schema.Columns }); + this.initialColumnsToDisplay = initParameters.columnsToDisplay; + if (initParameters.viewConfig) { + this.viewConfig.set(initParameters.viewConfig); + } + this.dataModel.set(initParameters.dataModel); + this.setupGroupOptions(); + if (initParameters.groupExecute) { + this.groupExecute = initParameters.groupExecute; + } + if (initParameters.exportFunction) { + this.exportFunction = initParameters.exportFunction; + } + if (!initParameters.localSource) { + await this.initFilters(initParameters); + } + this.selectionChangeFunction = initParameters.selectionChange; + this.highlightedExecute = initParameters.highlightEntity; + this.initOptionalColumns(); + if (initParameters.dataModel?.DefaultConfigId && !initParameters.uniqueConfig) { + const config = this.viewConfig()?.viewConfigs?.find((config) => config.Id == this.dataModel()?.DefaultConfigId); + if (config) { + await this.applyConfig(config); + return; + } + } + await this.updateState(); + this.columnsToDisplay.set(initParameters.columnsToDisplay); + this.loading.set(false); + } + + /** + * Calls the required execute function to reload the data. + */ + public async updateState(forceUpdate: boolean = false): Promise { + this.log.info(this, 'Updating data source'); + this.abortCall(); + if (!!this.groupByColumn() && !!this.groupExecute && !forceUpdate) { + this.loading.set(true); + const groupData = await this.groupExecute(this.groupByColumn()?.ColumnName || '', this.state(), this.abortController.signal); + this.groupData.set(groupData); + this.loading.set(false); + } else if (this.execute) { + this.loading.set(true); + let collectionData = await this.execute(this.state(), this.abortController.signal); + if (!!collectionData) { + collectionData.Data.map((elem) => { + if (elem.GetEntity) { + elem.GetEntity()?.ApplySchema(this.entitySchema()); + } + }); + this.collectionData.set(collectionData); + } else { + this.collectionData.set({ totalCount: 0, Data: [] }); + } + this.loading.set(false); + } + } + + /** + * Update the data state sorting. + * @param sortState The current sort state. + */ + public sortChange(sortState: Sort) { + if (sortState.direction) { + this.sortId.set(sortState.active); + this.sortDirection.set(sortState.direction); + this.state.update((state) => ({ ...state, OrderBy: `${sortState.active} ${sortState.direction}` })); + } else { + this.sortId.set(undefined); + this.sortDirection.set(''); + this.state.update((state) => ({ ...state, OrderBy: undefined })); + } + this.log.debug(this, 'Sort change', sortState.active, sortState.direction); + this.updateState(); + } + + /** + * Abort the previous data loading. + */ + public abortCall(): void { + this.log.info(this, 'Aborting call'); + this.abortController.abort(); + this.abortController = new AbortController(); + this.abortController.signal; + } + + /** + * Reset filters, columns and page index to default and than reload the data after a confirm dialog confirmation. + */ + public async resetView(): Promise { + if ( + await this.confirmService.confirmDelete( + '#LDS#Heading Reset View', + '#LDS#If you reset the view, the search, sorting, filters and additional columns will be reset. Are you sure you want to reset the view?', + ) + ) { + this.state.set({ PageSize: this.settings?.DefaultPageSize, StartIndex: 0 }); + this.columnsToDisplay.set(this.initialColumnsToDisplay); + this.selectedFilters.set([]); + this.predefinedFilters.update((predefinedFilters) => predefinedFilters.map((filter) => ({ ...filter, CurrentValue: undefined }))); + this.filterTreeSelection.set(undefined); + this.sortId.set(undefined); + this.sortDirection.set(''); + this.updateState(); + } + } + + /** + * Apply the selected to config and reload the data table. + * @param config The selected view config. + */ + public async applyConfig(config: DSTViewConfig): Promise { + let columnsToDisplay = this.initialColumnsToDisplay; + let additionalColumns: IClientProperty[] = []; + this.state.set({ PageSize: this.settings?.DefaultPageSize, StartIndex: 0 }); + this.predefinedFilters.update((predefinedFilters) => predefinedFilters.map((filter) => ({ ...filter, CurrentValue: undefined }))); + this.selection.clear(); + //Update additional columns + if (!!config.AdditionalTableColumns?.length) { + additionalColumns = + (this.dataModel() + ?.Properties?.filter((property) => config?.AdditionalTableColumns?.includes(property?.Property?.ColumnName || '')) + .filter((property) => !this.initialColumnsToDisplay.some((column) => column.ColumnName === property?.Property?.ColumnName)) + .map((property) => property?.Property) + .filter((property) => !!property) as IClientProperty[]) || []; + columnsToDisplay = [...this.initialColumnsToDisplay, ...additionalColumns]; + this.state.update((state) => ({ ...state, withProperties: config?.AdditionalTableColumns?.join(',') })); + this.updateEntitySchema(config?.AdditionalTableColumns || []); + } + // Update predefined filters + if (config.AdditionalParameters) { + this.state.update((state) => ({ ...state, ...config.AdditionalParameters })); + this.predefinedFilters.update((filters) => + filters.map((filter) => { + if (!!config?.AdditionalParameters?.[filter.Name || '']) { + return { ...filter, CurrentValue: config?.AdditionalParameters?.[filter.Name || ''] }; + } else { + return filter; + } + }), + ); + } else { + this.predefinedFilters.set([...(this.dataModel()?.Filters || [])]); + } + this.additionalListColumns.set( + config.AdditionalListColumns?.map( + (column) => this.dataModel()?.Properties?.find((property) => property.Property?.ColumnName === column)?.Property, + ).filter(Boolean) as IClientProperty[], + ); + // Update selected filters + if (!!config.Filter?.length) { + this.state.update((state) => ({ ...state, filter: config.Filter?.filter((filter) => filter.Type === FilterType.Expression) })); + this.selectedFilters.set( + config.Filter?.map((filter) => { + let selectedFilter: SelectedFilter; + if (filter.Type == FilterType.Search) { + selectedFilter = { type: SelectedFilterType.Keyword, value: filter.Value1 }; + } else { + selectedFilter = { type: SelectedFilterType.Custom, value: { Expression: filter.Expression } }; + } + return selectedFilter; + }) || [], + ); + this.setKeywords( + config.Filter?.filter((filter) => filter.Type === FilterType.Search) + .map((filter) => filter.Value1) + .join(' ') || '', + ); + } else { + this.selectedFilters.set([]); + } + // Update order + if (!!config.OrderBy) { + this.state.update((state) => ({ ...state, OrderBy: config.OrderBy })); + const order = config.OrderBy.split(' '); + this.sortId.set(order[0]); + this.sortDirection.set((order[1].toLowerCase() as SortDirection) || 'asc'); + } + // Update grouping + if (!!config.GroupBy) { + const selectedOption = this.groupOptions.find((option) => option.value === config.GroupBy); + this.groupByColumn.set(selectedOption?.clientProperty); + } + await this.updateState(); + this.columnsToDisplay.set(columnsToDisplay); + this.additionalColumns.set(additionalColumns); + } + + /** + * Updates the state signal search property with the selected keywords + * @param keywords All the searched keywords seperate with a space. + */ + public setKeywords(keywords: string): void { + const alreadySearched = this.state() + .search?.split(' ') + .map((item) => item.trim()) + .every((searchItem) => this.selectedFilters().find((filter) => filter.value === searchItem)); + + let exisitingKeywords: string[] = this.selectedFilters() + .filter((item) => item.type === SelectedFilterType.Keyword) + .map((item) => (item.value as string).trim()); + let newKeywords: string[] = keywords + .split(' ') + .map((item) => item.trim()) + .filter((item) => item?.length > 0 && !exisitingKeywords.includes(item)); + + this.state.update((state) => ({ ...state, search: newKeywords.join(' '), StartIndex: undefined })); + const noAction = alreadySearched && !keywords; + if (noAction) { + return; + } + this.updateState(); + } + + /** + * Updates the entitySchem with the required LocalColumns, because in entitySchema the column object is readonly. + * @param additionalColumnNames The array of the columns. + */ + public updateEntitySchema(additionalColumnNames: string[]): void { + this.entitySchema.update((entitySchema) => { + additionalColumnNames.map((column) => (entitySchema.LocalColumns[column] = this.getClientProperty(column))); + return entitySchema; + }); + } + + /** + * Used to prevent unintended multiple signal firing + */ + public debouncedHighlightRow = debounce((entity: T, event?) => this.highlightRow(entity, event), 250); + + /** + * Highlights (selects) the current row and emits an event. + * @param entity The selected row. + * @param event The mouse clicked event. + */ + public highlightRow(entity: T, event?: MouseEvent): void { + // Prevent emission for certain cases + if (event) { + // Make sure we aren't selecting text + if (event.view?.getSelection()?.type === 'Range') { + return; + } + + // Prevent button clicks from propogating as row clicks, Walk up node chain until we hit table looking if we are a button + let target: HTMLElement | null = event.target as HTMLElement; + while (target) { + if (target.tagName === 'BUTTON') { + return; + } + if (target.tagName === 'TABLE') { + break; + } + target = target.parentElement; + } + } + + // Emit a changed event (even if the same row was selected), to allow any listners to decide whether to act or not + // Signal is allowed if there is no rowEnabled function, or if the function is allowing it and the highlightedExecute function is initialized. + if (!!this.highlightedExecute && (!this.itemStatus.rowEnabled || this.itemStatus.rowEnabled(entity))) + this.highlightedEntity.set(entity); + } + + /** + * Checks a column sortable or not via the data model IsSortable property. + * @param column The selected column name. + * @returns Column is sortable. + */ + public isSortable(column: string | undefined): boolean { + if (!column || this?.dataModel() == null || !!this.groupByColumn()) { + return false; + } + const sortable = this?.dataModel()?.Properties?.find((prop: DataModelProperty) => prop.Property?.ColumnName === column); + return sortable == null ? false : sortable?.IsSortable; + } + + /** + * Returns the column display property or the column name to the table column header. + * @param columnName the selected column name. + * @param entitySchema the selected WritableEntitySchema + * @returns The displayed column header. + */ + public GetColumnDisplay(columnName: string, entitySchema?: WritableEntitySchema): string { + const column = entitySchema?.LocalColumns[columnName]; + if (column == null || column.Display == null) { + return columnName; + } + + return column.Display; + } + + /** + * Set option columns from the dataModel signal. + */ + private initOptionalColumns(): void { + this.optionalColumns.set( + this.dataModel() + ?.Properties?.filter((property) => property.IsAdditionalColumn) + .map((property) => property.Property) || [], + ); + } + + /** + * Initialize the filter properties. + * @param initParameters The initParameters object contains all the required values to setup the data source properly. + */ + async initFilters(initParameters: DataViewInitParameters): Promise { + if (!!initParameters.dataModel?.Filters?.length || initParameters.filterTree) { + this.showFilters.set(true); + } else { + const column = await this.sqlWizardApiService.getFilterProperties(this.entitySchema().TypeName ?? ''); + if (!!column.length && !!this.sqlWizardApiService.implemented) { + this.showFilters.set(true); + } + } + this.predefinedFilters.set([...(initParameters.dataModel?.Filters || [])]); + if (initParameters.filterTree) { + this.filterTree = initParameters.filterTree; + this.filterTreeData.set(await this.filterTree.filterMethode('')); + } + } + + /** + * Get IClientProperty from the entitySchema/dataModel or created it with string type. + * @param name column name + * @returns IClientProperty + */ + private getClientProperty(name: string): IClientProperty { + let property: IClientProperty | undefined; + if (this.entitySchema()) { + const key = + Object.keys(this.entitySchema().LocalColumns).find((elem) => elem.toLocaleLowerCase() === name.toLocaleLowerCase()) ?? name; + property = key != null ? this.entitySchema().LocalColumns[key] : undefined; + } + if (property == null) { + property = this.dataModel()?.Properties?.find( + (elem) => elem?.Property?.ColumnName?.toLocaleLowerCase() === name?.toLocaleLowerCase(), + )?.Property; + } + if (property == null) { + property = { ColumnName: name, Type: ValType.String }; + } + return property; + } + + /** + * Setup group options. Inherited from data model groupable properties. + */ + private setupGroupOptions(): void { + this.groupOptions = + this.dataModel() + ?.Properties?.filter((property) => property.IsGroupable) + .map((property) => ({ + display: property.Property?.Display || '', + value: property.Property?.ColumnName || '', + clientProperty: property.Property, + })) || []; + this.dataModel()?.GroupInfo?.map((groupInfo) => { + groupInfo.Options?.map((option) => { + const property: IClientProperty = { + Type: ValType.Text, + ColumnName: option.Value, + }; + this.groupOptions.push({ display: `${option.Display} - ${groupInfo.Display}`, value: option.Value, clientProperty: property }); + }); + }); + } +} diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-status/data-view-status.component.html b/imxweb/projects/qbm/src/lib/data-view/data-view-status/data-view-status.component.html new file mode 100644 index 000000000..df7c9ed61 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-status/data-view-status.component.html @@ -0,0 +1,34 @@ +@switch (status) { + @case (stateOptions.Waiting) { + {{ '#LDS#Queued' | translate }} + } + @case (stateOptions.Processing) { + {{ '#LDS#Processing' | translate }} + } + @case (stateOptions.Success) { + {{ '#LDS#Success' | translate }} + } + @case (stateOptions.Failed) { + {{ '#LDS#Failed' | translate }} + } +} diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-status/data-view-status.component.scss b/imxweb/projects/qbm/src/lib/data-view/data-view-status/data-view-status.component.scss new file mode 100644 index 000000000..715217fac --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-status/data-view-status.component.scss @@ -0,0 +1,4 @@ +:host{ + display: flex; + margin-bottom: 5px; +} diff --git a/imxweb/projects/qbm/src/lib/menu/menu-item/navigation-menu-item.ts b/imxweb/projects/qbm/src/lib/data-view/data-view-status/data-view-status.component.ts similarity index 65% rename from imxweb/projects/qbm/src/lib/menu/menu-item/navigation-menu-item.ts rename to imxweb/projects/qbm/src/lib/data-view/data-view-status/data-view-status.component.ts index 70aaf4daa..8ba148650 100644 --- a/imxweb/projects/qbm/src/lib/menu/menu-item/navigation-menu-item.ts +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-status/data-view-status.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,13 +24,15 @@ * */ -import { MenuItem } from './menu-item.interface'; +import { Component, Input } from '@angular/core'; +import { QueuedActionState } from '../../processing-queue/processing-queue.interface'; -/** Represents a menu item that activates a route when clicked. */ -export class NavigationMenuItem implements MenuItem { - constructor( - public readonly id: string, - public readonly route: string, - public readonly title: string, - public readonly description: string) {} +@Component({ + selector: 'imx-data-view-status', + templateUrl: './data-view-status.component.html', + styleUrl: './data-view-status.component.scss', +}) +export class DataViewStatusComponent { + public stateOptions = QueuedActionState; + @Input() status: QueuedActionState; } diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-toolbar/data-view-toolbar.component.html b/imxweb/projects/qbm/src/lib/data-view/data-view-toolbar/data-view-toolbar.component.html new file mode 100644 index 000000000..954a25f22 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-toolbar/data-view-toolbar.component.html @@ -0,0 +1,19 @@ +
    + + + + + + +
    + diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-toolbar/data-view-toolbar.component.scss b/imxweb/projects/qbm/src/lib/data-view/data-view-toolbar/data-view-toolbar.component.scss new file mode 100644 index 000000000..4bccc30ec --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-toolbar/data-view-toolbar.component.scss @@ -0,0 +1,9 @@ +.imx-data-view-controls{ + display: flex; + flex-wrap: wrap; + align-items: center; + width: 100%; + justify-content: flex-end; + gap: 8px; + margin-bottom: 16px; +} diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view-toolbar/data-view-toolbar.component.ts b/imxweb/projects/qbm/src/lib/data-view/data-view-toolbar/data-view-toolbar.component.ts new file mode 100644 index 000000000..f2361f446 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view-toolbar/data-view-toolbar.component.ts @@ -0,0 +1,67 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { DSTViewConfig } from '../../data-source-toolbar/data-source-toolbar-view-config.interface'; +import { DataViewSource } from '../data-view-source'; + +/** + * @example + * The updateConfig and the deleteConfigById Output only needs when you want to use the dataViewSettings component. + * + */ +@Component({ + selector: 'imx-data-view-toolbar', + templateUrl: './data-view-toolbar.component.html', + styleUrls: ['./data-view-toolbar.component.scss'], +}) +export class DataViewToolbarComponent { + /** + * Input the dataViewSource service. It handles all the action and the data loading. This input property is required. + */ + @Input({ required: true }) public dataSource: DataViewSource; + @Input() showSettings = true; + @Input() showSearch = true; + @Input() showGrouping = true; + /** + * Event to emit a DSTViewConfig for post/put via the viewConfig.putViewConfig function. + */ + @Output() public updateConfig = new EventEmitter(); + /** + * Event to emit an DSTViewConfig.Id for delete via the viewConfig.deleteViewConfig function. + */ + @Output() public deleteConfigById = new EventEmitter(); + /** + * Event to emit all the search params on change. Only need this event emitter if you use only the toolbar component. + */ + @Output() public onSearchChange = new EventEmitter(); + + constructor() {} +} diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view.interface.ts b/imxweb/projects/qbm/src/lib/data-view/data-view.interface.ts new file mode 100644 index 000000000..4f856b30e --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view.interface.ts @@ -0,0 +1,100 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { + CollectionLoadParameters, + DataModel, + EntitySchema, + GroupInfo, + GroupInfoData, + IClientProperty, + SqlWizardExpression, + TypedEntityCollectionData, +} from '@imx-modules/imx-qbm-dbts'; +import { FilterTreeParameter } from '../data-source-toolbar/data-model/filter-tree-parameter'; +import { DataSourceToolbarExportMethod } from '../data-source-toolbar/data-source-toolbar-export-method.interface'; +import { DataSourceToolbarViewConfig } from '../data-source-toolbar/data-source-toolbar-view-config.interface'; + +// Extends entity schema with a local column object to updates columns +export interface WritableEntitySchema extends EntitySchema { + LocalColumns: { + [id: string]: IClientProperty; + }; +} +// Type for execute function. +export type ExecuteFunction = { + (params: CollectionLoadParameters, signal: AbortSignal): Promise | undefined>; +}; +// Type for group execute function. +export type ExecuteGroupFunction = { + (columnName: string, params: CollectionLoadParameters, signal: AbortSignal): Promise; +}; +// Type for table selection change function. +export type SelectionChangeFunction = { + (selection: readonly T[]): void; +}; +// Type for table row highlight function. +export type HightlightEntityFunction = { + (entity: T): void; +}; +// Enum for the data table filter +export enum SelectedFilterType { + None, + Keyword, // search keyword + Custom, // expression filter +} + +export interface KeywordFilter { + type: SelectedFilterType.Keyword; + value: string; +} + +export interface ExpressionFilter { + type: SelectedFilterType.Custom; + value: SqlWizardExpression; +} +// Combined keyword and expression filter type. +export type SelectedFilter = KeywordFilter | ExpressionFilter; +// Group info interface with an expanded extension to handle group expanded actions. +export interface GroupInfoRow extends GroupInfo { + expanded?: boolean; +} + +// Interface for initialize data view. +export interface DataViewInitParameters { + execute: ExecuteFunction; + schema: EntitySchema; + columnsToDisplay: IClientProperty[]; + dataModel?: DataModel; + groupExecute?: ExecuteGroupFunction; + exportFunction?: DataSourceToolbarExportMethod; + viewConfig?: DataSourceToolbarViewConfig; + uniqueConfig?: boolean; + selectionChange?: SelectionChangeFunction; + filterTree?: FilterTreeParameter; + highlightEntity?: HightlightEntityFunction; + localSource?: boolean; +} diff --git a/imxweb/projects/qbm/src/lib/data-view/data-view.module.ts b/imxweb/projects/qbm/src/lib/data-view/data-view.module.ts new file mode 100644 index 000000000..2ba8faf4e --- /dev/null +++ b/imxweb/projects/qbm/src/lib/data-view/data-view.module.ts @@ -0,0 +1,109 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { DragDropModule } from '@angular/cdk/drag-drop'; +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatChipsModule } from '@angular/material/chips'; +import { MatRippleModule } from '@angular/material/core'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { MatSelectModule } from '@angular/material/select'; +import { MatSortModule } from '@angular/material/sort'; +import { MatTableModule } from '@angular/material/table'; +import { MatTabsModule } from '@angular/material/tabs'; +import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; +import { TranslateModule } from '@ngx-translate/core'; +import { DataTableModule } from '../data-table/data-table.module'; +import { BusyIndicatorModule } from './../busy-indicator/busy-indicator.module'; +import { DataViewAutoTableComponent } from './data-view-auto-table/data-view-auto-table.component'; +import { DataViewChipbarComponent } from './data-view-chipbar/data-view-chipbar.component'; +import { DataViewFilterComponent } from './data-view-filter/data-view-filter.component'; +import { DataViewGroupComponent } from './data-view-group/data-view-group.component'; +import { DataViewNestedTableComponent } from './data-view-nested-table/data-view-nested-table.component'; +import { DataViewPaginatorComponent } from './data-view-paginator/data-view-paginator.component'; +import { DataViewSearchComponent } from './data-view-search/data-view-search.component'; +import { DataViewSelectionComponent } from './data-view-selection/data-view-selection.component'; +import { DataViewSettingsComponent } from './data-view-settings/data-view-settings.component'; +import { DataViewStatusComponent } from './data-view-status/data-view-status.component'; +import { DataViewToolbarComponent } from './data-view-toolbar/data-view-toolbar.component'; +@NgModule({ + declarations: [ + DataViewAutoTableComponent, + DataViewPaginatorComponent, + DataViewSearchComponent, + DataViewToolbarComponent, + DataViewChipbarComponent, + DataViewGroupComponent, + DataViewNestedTableComponent, + DataViewSettingsComponent, + DataViewFilterComponent, + DataViewSelectionComponent, + DataViewStatusComponent, + ], + imports: [ + CommonModule, + CommonModule, + TranslateModule, + FormsModule, + ReactiveFormsModule, + EuiCoreModule, + EuiMaterialModule, + MatFormFieldModule, + MatIconModule, + MatInputModule, + MatButtonModule, + MatPaginatorModule, + MatTableModule, + MatChipsModule, + MatProgressBarModule, + DragDropModule, + MatSortModule, + MatTabsModule, + MatSelectModule, + MatRippleModule, + BusyIndicatorModule, + DataTableModule, + ], + exports: [ + DataViewAutoTableComponent, + DataViewPaginatorComponent, + DataViewSearchComponent, + DataViewToolbarComponent, + DataViewChipbarComponent, + DataViewGroupComponent, + DataViewSettingsComponent, + DataViewFilterComponent, + DataViewSelectionComponent, + DataViewStatusComponent, + ], +}) +export class DataViewModule {} diff --git a/imxweb/projects/qbm/src/lib/date/date.module.ts b/imxweb/projects/qbm/src/lib/date/date.module.ts index 005eb1204..539c14214 100644 --- a/imxweb/projects/qbm/src/lib/date/date.module.ts +++ b/imxweb/projects/qbm/src/lib/date/date.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,11 +30,11 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatMomentDateModule, MAT_MOMENT_DATE_FORMATS, MomentDateAdapter } from '@angular/material-moment-adapter'; import { A11yModule } from '@angular/cdk/a11y'; -import { OverlayModule} from '@angular/cdk/overlay'; +import { OverlayModule } from '@angular/cdk/overlay'; import { MatButtonModule } from '@angular/material/button'; -import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE } from '@angular/material/core'; -import { MatDatepickerModule } from '@angular/material/datepicker'; +import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE, MatNativeDateModule } from '@angular/material/core'; +import { MatDateSelectionModel, MatDatepickerModule } from '@angular/material/datepicker'; import { MatDividerModule } from '@angular/material/divider'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; @@ -52,18 +52,8 @@ import { ShortDatePipe } from './short-date.pipe'; import { LocalizedDatePipe } from './localized-date.pipe'; @NgModule({ - declarations: [ - ShortDatePipe, - LocalizedDatePipe, - DateComponent, - CalendarComponent, - TimePickerComponent - ], - exports: [ - DateComponent, - ShortDatePipe, - LocalizedDatePipe - ], + declarations: [ShortDatePipe, LocalizedDatePipe, DateComponent, CalendarComponent, TimePickerComponent], + exports: [DateComponent, ShortDatePipe, LocalizedDatePipe], imports: [ A11yModule, CommonModule, @@ -79,11 +69,13 @@ import { LocalizedDatePipe } from './localized-date.pipe'; MatProgressSpinnerModule, OverlayModule, ReactiveFormsModule, - TranslateModule + TranslateModule, + MatNativeDateModule, + MatDatepickerModule, ], providers: [ { provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE] }, { provide: MAT_DATE_FORMATS, useValue: MAT_MOMENT_DATE_FORMATS }, - ] + ], }) -export class DateModule { } +export class DateModule {} diff --git a/imxweb/projects/qbm/src/lib/date/date/calendar/calendar.component.html b/imxweb/projects/qbm/src/lib/date/date/calendar/calendar.component.html index e1e770cc2..01ad71d99 100644 --- a/imxweb/projects/qbm/src/lib/date/date/calendar/calendar.component.html +++ b/imxweb/projects/qbm/src/lib/date/date/calendar/calendar.component.html @@ -1,9 +1,11 @@ -
    - + + [startAt]="datePickerDate" + > @@ -29,9 +41,14 @@ (overlayOutsideClick)="isDatePickerOpen = false" > + [control]="shadowDate" + [min]="min" + [max]="max" + (close)="isDatePickerOpen = false" + cdkTrapFocus + [cdkTrapFocus]="true" + [cdkTrapFocusAutoCapture]="true" + > @@ -44,12 +61,15 @@ [cdkConnectedOverlayBackdropClass]="'mat-overlay-transparent-backdrop'" (overlayOutsideClick)="handleTimePickerOutsideClick($event)" > - - + + -
    {{ validationErrorKey | translate }} diff --git a/imxweb/projects/qbm/src/lib/date/date/date.component.scss b/imxweb/projects/qbm/src/lib/date/date/date.component.scss index db249c530..4b374974e 100644 --- a/imxweb/projects/qbm/src/lib/date/date/date.component.scss +++ b/imxweb/projects/qbm/src/lib/date/date/date.component.scss @@ -2,22 +2,3 @@ display: flex; align-items: center; } - -mat-form-field { - flex-grow: 1; - - [matsuffix] { - - margin-left: 10px; // set margin to infix - - eui-icon { - margin-top: unset; // correct margin-top: -16px from _material_fixes.scss - display: block; - } - - mat-spinner { - display: inline-flex; - margin-right: 10px; - } - } -} diff --git a/imxweb/projects/qbm/src/lib/date/date/date.component.ts b/imxweb/projects/qbm/src/lib/date/date/date.component.ts index 415b8769f..84f6122b3 100644 --- a/imxweb/projects/qbm/src/lib/date/date/date.component.ts +++ b/imxweb/projects/qbm/src/lib/date/date/date.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,11 +24,10 @@ * */ -import { Component, Input, OnInit, OnDestroy } from '@angular/core'; +import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { AbstractControl, UntypedFormControl, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; -import moment from 'moment-timezone'; -import { Moment } from 'moment-timezone'; +import moment, { Moment } from 'moment-timezone'; import { Subscription } from 'rxjs'; import { ClassloggerService } from '../../classlogger/classlogger.service'; import { DateParser } from './date-parser'; @@ -44,7 +43,7 @@ import { DateParser } from './date-parser'; @Component({ selector: 'imx-date', templateUrl: './date.component.html', - styleUrls: ['./date.component.scss'] + styleUrls: ['./date.component.scss'], }) export class DateComponent implements OnInit, OnDestroy { // ###################################################################################################### @@ -114,6 +113,8 @@ export class DateComponent implements OnInit, OnDestroy { */ @Input() public withTime = true; + @Input() validateOnlyOnChange: boolean = false; + /** * @ignore only public because of databinding in template * @@ -154,7 +155,9 @@ export class DateComponent implements OnInit, OnDestroy { * the result of the internal shadow form control. * Useful to avoid unecessay update loop when writing back the value to the input control. */ - private result: Moment; + private result: Moment | undefined; + + private isUpdated: boolean = false; /** * @ignore @@ -168,15 +171,14 @@ export class DateComponent implements OnInit, OnDestroy { * * The text <-> moment date and time parser. */ - private parser !: DateParser; + private parser!: DateParser; /** * Creates a new date editor component. * * @param logger The logger service to be injected. */ - constructor(private logger: ClassloggerService) { - } + constructor(private logger: ClassloggerService) {} /** * @ignore only public because of databinding in template @@ -214,17 +216,25 @@ export class DateComponent implements OnInit, OnDestroy { this.setupValidators(); this.handleControlChanged(); - this.subscriptions.push(this.control.valueChanges.subscribe(x => this.handleControlChanged())); - this.subscriptions.push(this.shadowText.valueChanges.subscribe(x => this.handleShadowTextChanged())); - this.subscriptions.push(this.shadowTime.valueChanges.subscribe(x => this.handleShadowTimeChanged())); - this.subscriptions.push(this.shadowDate.valueChanges.subscribe(x => this.handleShadowDateChanged())); + this.subscriptions.push( + this.control.valueChanges.subscribe((x) => { + if (this.shadowText.untouched) { + this.shadowText.markAsTouched({ emitEvent: false }); + } + this.isUpdated = true; + this.handleControlChanged(); + }), + ); + this.subscriptions.push(this.shadowText.valueChanges.subscribe((x) => this.handleShadowTextChanged())); + this.subscriptions.push(this.shadowTime.valueChanges.subscribe((x) => this.handleShadowTimeChanged())); + this.subscriptions.push(this.shadowDate.valueChanges.subscribe((x) => this.handleShadowDateChanged())); } /** * @ignore OnDestroy lifecycle hook */ public ngOnDestroy(): void { - this.subscriptions.forEach(s => s.unsubscribe()); + this.subscriptions.forEach((s) => s.unsubscribe()); } /** @@ -320,7 +330,7 @@ export class DateComponent implements OnInit, OnDestroy { if (this.shadowDate.value) { const d = moment(this.shadowDate.value); - value = moment({year: d.year(), month: d.month(), day: d.date()}); + value = moment({ year: d.year(), month: d.month(), day: d.date() }); } if (this.shadowTime.value) { @@ -344,8 +354,7 @@ export class DateComponent implements OnInit, OnDestroy { */ private handleControlChanged(): void { const updatedValue = this.control.value as Moment; - - if (updatedValue !== this.result) { + if (!updatedValue?.isSame(this.result)) { this.result = updatedValue; this.updateShadowText(this.getDateTextFromControl()); this.updateShadowDate(this.control.value); @@ -455,18 +464,22 @@ export class DateComponent implements OnInit, OnDestroy { * * validation method: validates the moment representation of a date (and time) */ - private validateMomentInDateRange(date: Moment): ValidationErrors | null { + private validateMomentInDateRange(date: Moment | undefined): ValidationErrors | null { + if (!this.isUpdated && this.validateOnlyOnChange) { + return null; + } if (date && !date.isValid()) { return { matDatepickerParse: true }; } - if (this.min && date < moment(this.min)) { + if (this.min && date && date < moment(this.min)) { return { matDatepickerMin: true }; } - if (this.max && date > moment(this.max)) { + if (this.max && date && date > moment(this.max)) { return { matDatepickerMax: true }; } - } + return null; + } } diff --git a/imxweb/projects/qbm/src/lib/date/date/time-picker/time-picker.component.html b/imxweb/projects/qbm/src/lib/date/date/time-picker/time-picker.component.html index 5212783c9..b07486fc2 100644 --- a/imxweb/projects/qbm/src/lib/date/date/time-picker/time-picker.component.html +++ b/imxweb/projects/qbm/src/lib/date/date/time-picker/time-picker.component.html @@ -1,6 +1,6 @@ - - - - - - +
    + + + + +
    diff --git a/imxweb/projects/qbm/src/lib/date/date/time-picker/time-picker.component.ts b/imxweb/projects/qbm/src/lib/date/date/time-picker/time-picker.component.ts index c740d96b6..92b6f1e27 100644 --- a/imxweb/projects/qbm/src/lib/date/date/time-picker/time-picker.component.ts +++ b/imxweb/projects/qbm/src/lib/date/date/time-picker/time-picker.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,8 +24,8 @@ * */ -import { Component, Input, EventEmitter, Output, HostListener } from '@angular/core'; -import { AbstractControl, FormControl } from '@angular/forms'; +import { Component, EventEmitter, HostListener, Input, Output } from '@angular/core'; +import { FormControl } from '@angular/forms'; import { Moment } from 'moment-timezone'; /** @@ -34,10 +34,9 @@ import { Moment } from 'moment-timezone'; @Component({ selector: 'imx-time-picker', templateUrl: './time-picker.component.html', - styleUrls: ['./time-picker.component.scss'] + styleUrls: ['./time-picker.component.scss'], }) export class TimePickerComponent { - /** * The input control for the time. * @@ -45,7 +44,7 @@ export class TimePickerComponent { */ // TODO: Check Upgrade - @Input() public control: AbstractControl; + @Input() public control: FormControl; /** * This event will be emitted when the user clicks on the close button. @@ -71,5 +70,4 @@ export class TimePickerComponent { event.stopPropagation(); this.close.emit(); } - } diff --git a/imxweb/projects/qbm/src/lib/date/localized-date.pipe.ts b/imxweb/projects/qbm/src/lib/date/localized-date.pipe.ts index bef4466dc..68e0a41a5 100644 --- a/imxweb/projects/qbm/src/lib/date/localized-date.pipe.ts +++ b/imxweb/projects/qbm/src/lib/date/localized-date.pipe.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,18 +28,15 @@ import { Pipe, PipeTransform, Injectable } from '@angular/core'; import { ImxTranslationProviderService } from '../translation/imx-translation-provider.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) @Pipe({ name: 'localizedDate', }) export class LocalizedDatePipe implements PipeTransform { - private readonly currentCulture: string; - constructor( - private readonly translationProviderService: ImxTranslationProviderService, - ) { + constructor(private readonly translationProviderService: ImxTranslationProviderService) { this.currentCulture = this.translationProviderService.CultureFormat; } @@ -54,6 +51,5 @@ export class LocalizedDatePipe implements PipeTransform { } } return new Date(value).toLocaleString(this.currentCulture); - } } diff --git a/imxweb/projects/qbm/src/lib/date/short-date.pipe.ts b/imxweb/projects/qbm/src/lib/date/short-date.pipe.ts index adbc9445a..8c6db0691 100644 --- a/imxweb/projects/qbm/src/lib/date/short-date.pipe.ts +++ b/imxweb/projects/qbm/src/lib/date/short-date.pipe.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,18 +28,15 @@ import { Pipe, PipeTransform, Injectable } from '@angular/core'; import { ImxTranslationProviderService } from '../translation/imx-translation-provider.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) @Pipe({ name: 'shortDate', }) export class ShortDatePipe implements PipeTransform { - private readonly currentCulture: string; - constructor( - private readonly translationProviderService: ImxTranslationProviderService, - ) { + constructor(private readonly translationProviderService: ImxTranslationProviderService) { this.currentCulture = this.translationProviderService.CultureFormat; } @@ -52,6 +49,5 @@ export class ShortDatePipe implements PipeTransform { return value; } return new Date(value).toLocaleDateString(this.currentCulture); - } } diff --git a/imxweb/projects/qbm/src/lib/disable-control/disable-control.directive.ts b/imxweb/projects/qbm/src/lib/disable-control/disable-control.directive.ts index 42bcfd8f9..404e536aa 100644 --- a/imxweb/projects/qbm/src/lib/disable-control/disable-control.directive.ts +++ b/imxweb/projects/qbm/src/lib/disable-control/disable-control.directive.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,20 +28,18 @@ import { Directive, Input, OnInit } from '@angular/core'; import { NgControl } from '@angular/forms'; @Directive({ - selector: '[imxDisableControl]' + selector: '[imxDisableControl]', }) export class DisableControlDirective implements OnInit { - @Input() set imxDisableControl(condition: boolean) { this.action = condition ? 'disable' : 'enable'; } private action: string; - constructor(private ngControl: NgControl) { - } + constructor(private ngControl: NgControl) {} public ngOnInit(): void { - this.ngControl.control[this.action](); + this.ngControl.control?.[this.action](); } } diff --git a/imxweb/projects/qbm/src/lib/disable-control/disable-control.module.ts b/imxweb/projects/qbm/src/lib/disable-control/disable-control.module.ts index 2b8f04208..983ba8b47 100644 --- a/imxweb/projects/qbm/src/lib/disable-control/disable-control.module.ts +++ b/imxweb/projects/qbm/src/lib/disable-control/disable-control.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,14 +29,8 @@ import { CommonModule } from '@angular/common'; import { DisableControlDirective } from './disable-control.directive'; @NgModule({ - declarations: [ - DisableControlDirective - ], - imports: [ - CommonModule - ], - exports: [ - DisableControlDirective - ] + declarations: [DisableControlDirective], + imports: [CommonModule], + exports: [DisableControlDirective], }) -export class DisableControlModule { } +export class DisableControlModule {} diff --git a/imxweb/projects/qbm/src/lib/dynamic-tabs/dynamic-tab-data-provider.directive.ts b/imxweb/projects/qbm/src/lib/dynamic-tabs/dynamic-tab-data-provider.directive.ts index 89102b3b6..fa271ec3e 100644 --- a/imxweb/projects/qbm/src/lib/dynamic-tabs/dynamic-tab-data-provider.directive.ts +++ b/imxweb/projects/qbm/src/lib/dynamic-tabs/dynamic-tab-data-provider.directive.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,8 +27,8 @@ import { Directive, Input } from '@angular/core'; @Directive({ - selector: '[imxDataProvider]' + selector: '[imxDataProvider]', }) export class DynamicTabDataProviderDirective { - @Input('imxDataProvider')public data: any; + @Input('imxDataProvider') public data: any; } diff --git a/imxweb/projects/qbm/src/lib/dynamic-tabs/dynamic-tabs.model.ts b/imxweb/projects/qbm/src/lib/dynamic-tabs/dynamic-tabs.model.ts index 2764fa1a5..909b68e90 100644 --- a/imxweb/projects/qbm/src/lib/dynamic-tabs/dynamic-tabs.model.ts +++ b/imxweb/projects/qbm/src/lib/dynamic-tabs/dynamic-tabs.model.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qbm/src/lib/dynamic-tabs/dynamic-tabs.module.ts b/imxweb/projects/qbm/src/lib/dynamic-tabs/dynamic-tabs.module.ts index 7930018df..a8443bbaa 100644 --- a/imxweb/projects/qbm/src/lib/dynamic-tabs/dynamic-tabs.module.ts +++ b/imxweb/projects/qbm/src/lib/dynamic-tabs/dynamic-tabs.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,17 +29,9 @@ import { CommonModule } from '@angular/common'; import { DynamicTabDataProviderDirective } from './dynamic-tab-data-provider.directive'; - - @NgModule({ - declarations: [ - DynamicTabDataProviderDirective - ], - imports: [ - CommonModule - ], - exports: [ - DynamicTabDataProviderDirective - ] + declarations: [DynamicTabDataProviderDirective], + imports: [CommonModule], + exports: [DynamicTabDataProviderDirective], }) -export class DynamicTabsModule { } +export class DynamicTabsModule {} diff --git a/imxweb/projects/qbm/src/lib/entity/entity-select/entity-select.component.html b/imxweb/projects/qbm/src/lib/entity/entity-select/entity-select.component.html index 20996769c..37d9a7c42 100644 --- a/imxweb/projects/qbm/src/lib/entity/entity-select/entity-select.component.html +++ b/imxweb/projects/qbm/src/lib/entity/entity-select/entity-select.component.html @@ -1,4 +1,9 @@ {{ title | translate }} - + diff --git a/imxweb/projects/qbm/src/lib/entity/entity-select/entity-select.component.ts b/imxweb/projects/qbm/src/lib/entity/entity-select/entity-select.component.ts index 41a839eba..67cf55eb7 100644 --- a/imxweb/projects/qbm/src/lib/entity/entity-select/entity-select.component.ts +++ b/imxweb/projects/qbm/src/lib/entity/entity-select/entity-select.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,21 +28,21 @@ import { AfterContentInit, Component, EventEmitter, Input, OnChanges, Output, Si import { AbstractControl, UntypedFormControl, Validators } from '@angular/forms'; import { EuiSelectOption } from '@elemental-ui/core'; -import { IClientProperty, IEntity } from 'imx-qbm-dbts'; +import { IClientProperty, IEntity } from '@imx-modules/imx-qbm-dbts'; @Component({ selector: 'imx-entity-select', templateUrl: './entity-select.component.html', - styleUrls: ['./entity-select.component.scss'] + styleUrls: ['./entity-select.component.scss'], }) export class EntitySelectComponent implements OnChanges, AfterContentInit { public readonly control = new UntypedFormControl(undefined, Validators.required); - public options: EuiSelectOption[]; + public options: EuiSelectOption[] | undefined; @Input() public title: string; @Input() public placeholder: string; - @Input() public display: { primary?: IClientProperty, secondary?: IClientProperty }; + @Input() public display: { primary?: IClientProperty; secondary?: IClientProperty }; @Input() public entities: IEntity[]; @Output() public controlCreated = new EventEmitter(); @@ -56,12 +56,12 @@ export class EntitySelectComponent implements OnChanges, AfterContentInit { public ngOnChanges(changes: SimpleChanges): void { if (changes['entities'] || changes['display']) { if (this.entities) { - this.options = this.entities.map(entity => ({ - display: this.display?.primary ? - (entity.GetColumn(this.display.primary.ColumnName).GetDisplayValue() || entity.GetDisplay()) : entity.GetDisplay(), - displayDetail: this.display?.secondary ? - entity.GetColumn(this.display.secondary.ColumnName).GetDisplayValue() : undefined, - value: entity + this.options = this.entities.map((entity) => ({ + display: this.display?.primary + ? entity.GetColumn(this.display.primary.ColumnName ?? '').GetDisplayValue() || entity.GetDisplay() + : entity.GetDisplay(), + displayDetail: this.display?.secondary ? entity.GetColumn(this.display.secondary.ColumnName ?? '').GetDisplayValue() : undefined, + value: entity, })); } else { this.options = undefined; @@ -74,13 +74,19 @@ export class EntitySelectComponent implements OnChanges, AfterContentInit { } public filter(option: EuiSelectOption, searchInputValue: string): boolean { - return option.display.toString().toUpperCase().trim().includes(searchInputValue.toUpperCase().trim()) - || (option.displayDetail && option.displayDetail.toString().toUpperCase().trim().includes(searchInputValue.toUpperCase().trim())); + return ( + (option.display.toString().toUpperCase().trim().includes(searchInputValue.toUpperCase().trim()) || + option.displayDetail?.toString().toUpperCase().trim().includes(searchInputValue.toUpperCase().trim())) ?? + false + ); } - - // TODO: Check Upgrade - // public onChange(event: any): void { - // this.selectionChange.emit(event.value); - // } + public selectionUpdated(arg: EuiSelectOption | EuiSelectOption[]) { + if (Object.hasOwn(arg, 'value')) { + // is a EuiSelectOption + this.selectionChange.emit((arg as EuiSelectOption).value); + } else { + this.selectionChange.emit(arg[0].value); + } + } } diff --git a/imxweb/projects/qbm/src/lib/entity/entity-select/entity-select.interface.ts b/imxweb/projects/qbm/src/lib/entity/entity-select/entity-select.interface.ts index 7292bb062..0f0f69ca3 100644 --- a/imxweb/projects/qbm/src/lib/entity/entity-select/entity-select.interface.ts +++ b/imxweb/projects/qbm/src/lib/entity/entity-select/entity-select.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,12 +24,12 @@ * */ -import { IClientProperty, IEntity } from 'imx-qbm-dbts'; +import { IClientProperty, IEntity } from '@imx-modules/imx-qbm-dbts'; export interface EntitySelect { entities: IEntity[]; placeholder: string; selectionChange: (entity: IEntity) => void; - display?: { primary?: IClientProperty, secondary?: IClientProperty }; + display?: { primary?: IClientProperty; secondary?: IClientProperty }; title?: string; } diff --git a/imxweb/projects/qbm/src/lib/entity/entity.module.ts b/imxweb/projects/qbm/src/lib/entity/entity.module.ts index d67af3497..95966b403 100644 --- a/imxweb/projects/qbm/src/lib/entity/entity.module.ts +++ b/imxweb/projects/qbm/src/lib/entity/entity.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,21 +24,22 @@ * */ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; import { TranslateModule } from '@ngx-translate/core'; -import { EntityService } from './entity.service'; -import { EntitySelectComponent } from './entity-select/entity-select.component'; -import { TypedEntitySelectComponent } from './typed-entity-select/typed-entity-select.component'; -import { TypedEntitySelectorComponent } from './typed-entity-select/typed-entity-selector/typed-entity-selector.component'; +import { DataSourceToolbarModule } from '../data-source-toolbar/data-source-toolbar.module'; +import { DataTableModule } from '../data-table/data-table.module'; import { FkAdvancedPickerModule } from '../fk-advanced-picker/fk-advanced-picker.module'; +import { EntitySelectComponent } from './entity-select/entity-select.component'; +import { EntityService } from './entity.service'; import { FkTableSelectComponent } from './fk-table-select/fk-table-select.component'; -import { DataTableModule } from '../data-table/data-table.module'; import { TypedEntityCandidateSidesheetComponent } from './typed-entity-candidate-sidesheet/typed-entity-candidate-sidesheet.component'; -import { DataSourceToolbarModule } from '../data-source-toolbar/data-source-toolbar.module'; +import { TypedEntitySelectComponent } from './typed-entity-select/typed-entity-select.component'; +import { TypedEntitySelectorComponent } from './typed-entity-select/typed-entity-selector/typed-entity-selector.component'; +import { SelectedElementsModule } from '../selected-elements/selected-elements.module'; @NgModule({ declarations: [ @@ -46,12 +47,9 @@ import { DataSourceToolbarModule } from '../data-source-toolbar/data-source-tool TypedEntitySelectComponent, TypedEntitySelectorComponent, FkTableSelectComponent, - TypedEntityCandidateSidesheetComponent - ], - exports: [ - EntitySelectComponent, - TypedEntitySelectComponent + TypedEntityCandidateSidesheetComponent, ], + exports: [EntitySelectComponent, TypedEntitySelectComponent], imports: [ CommonModule, EuiCoreModule, @@ -61,11 +59,9 @@ import { DataSourceToolbarModule } from '../data-source-toolbar/data-source-tool ReactiveFormsModule, TranslateModule, DataTableModule, - DataSourceToolbarModule - + DataSourceToolbarModule, + SelectedElementsModule ], - providers: [ - EntityService - ] + providers: [EntityService], }) -export class EntityModule { } +export class EntityModule {} diff --git a/imxweb/projects/qbm/src/lib/entity/entity.service.ts b/imxweb/projects/qbm/src/lib/entity/entity.service.ts index 521aad6c1..f895daa64 100644 --- a/imxweb/projects/qbm/src/lib/entity/entity.service.ts +++ b/imxweb/projects/qbm/src/lib/entity/entity.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,23 +26,29 @@ import { Injectable } from '@angular/core'; -import { EntityColumnData, IClientProperty, FkCandidateProvider, FkProviderItem, IEntityColumn, LocalEntityColumn } from 'imx-qbm-dbts'; +import { + EntityColumnData, + IClientProperty, + FkCandidateProvider, + FkProviderItem, + IEntityColumn, + LocalEntityColumn, +} from '@imx-modules/imx-qbm-dbts'; import { ImxTranslationProviderService } from '../translation/imx-translation-provider.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class EntityService { - constructor(private readonly translator: ImxTranslationProviderService) { } + constructor(private readonly translator: ImxTranslationProviderService) {} public createLocalEntityColumn( property: IClientProperty, fkProviderItems?: FkProviderItem[], data: EntityColumnData = {}, - putValueDelegate: (oldValue: any, newValue: any) => Promise = () => Promise.resolve() + putValueDelegate: (oldValue: any, newValue: any) => Promise = () => Promise.resolve(), ): IEntityColumn { - var fkProvider = fkProviderItems ? new FkCandidateProvider(fkProviderItems) : undefined; - return new LocalEntityColumn(property, this.translator, fkProvider, data, ((z, x, y) => putValueDelegate(x, y))); + return new LocalEntityColumn(property, this.translator, fkProvider, data, (z, x, y) => putValueDelegate(x, y)); } } diff --git a/imxweb/projects/qbm/src/lib/entity/fk-table-select/fk-table-select.component.html b/imxweb/projects/qbm/src/lib/entity/fk-table-select/fk-table-select.component.html index aa7e3bb6a..df92c504a 100644 --- a/imxweb/projects/qbm/src/lib/entity/fk-table-select/fk-table-select.component.html +++ b/imxweb/projects/qbm/src/lib/entity/fk-table-select/fk-table-select.component.html @@ -1,7 +1,13 @@ - + [filterFunction]="filter" + [options]="options" + [inputControl]="control" + [hideClearButton]="true" + data-imx-identifier="fk-table-select" +> diff --git a/imxweb/projects/qbm/src/lib/entity/fk-table-select/fk-table-select.component.scss b/imxweb/projects/qbm/src/lib/entity/fk-table-select/fk-table-select.component.scss index e110d11b2..248a268c5 100644 --- a/imxweb/projects/qbm/src/lib/entity/fk-table-select/fk-table-select.component.scss +++ b/imxweb/projects/qbm/src/lib/entity/fk-table-select/fk-table-select.component.scss @@ -1 +1 @@ -// add styles if necessary \ No newline at end of file +// add styles if necessary diff --git a/imxweb/projects/qbm/src/lib/entity/fk-table-select/fk-table-select.component.ts b/imxweb/projects/qbm/src/lib/entity/fk-table-select/fk-table-select.component.ts index cdf06af27..9d300eaa4 100644 --- a/imxweb/projects/qbm/src/lib/entity/fk-table-select/fk-table-select.component.ts +++ b/imxweb/projects/qbm/src/lib/entity/fk-table-select/fk-table-select.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,7 +28,7 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { UntypedFormControl } from '@angular/forms'; import { EuiSelectOption } from '@elemental-ui/core'; -import { IForeignKeyInfo } from 'imx-qbm-dbts'; +import { IForeignKeyInfo } from '@imx-modules/imx-qbm-dbts'; import { MetadataService } from '../../base/metadata.service'; @Component({ @@ -66,8 +66,12 @@ export class FkTableSelectComponent implements OnInit { return option.display.toString().toUpperCase().trim().includes(searchInputValue.toUpperCase().trim()); } - // TODO: Check Upgrade - // public onChange(event: any): void { - // this.selectionChanged.emit(event.value); - // } + public selectionUpdated(event: EuiSelectOption | EuiSelectOption[]) { + if (Object.hasOwn(event, 'value')) { + // is a EuiSelectOption + this.selectionChanged.emit((event as EuiSelectOption).value); + } else { + this.selectionChanged.emit(event[0].option); + } + } } diff --git a/imxweb/projects/qbm/src/lib/entity/typed-entity-candidate-sidesheet/typed-entity-candidate-sidesheet-parameter.interface.ts b/imxweb/projects/qbm/src/lib/entity/typed-entity-candidate-sidesheet/typed-entity-candidate-sidesheet-parameter.interface.ts index 67aca146d..81c8427de 100644 --- a/imxweb/projects/qbm/src/lib/entity/typed-entity-candidate-sidesheet/typed-entity-candidate-sidesheet-parameter.interface.ts +++ b/imxweb/projects/qbm/src/lib/entity/typed-entity-candidate-sidesheet/typed-entity-candidate-sidesheet-parameter.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,9 +24,9 @@ * */ -import { TypedEntity } from 'imx-qbm-dbts'; +import { TypedEntity } from '@imx-modules/imx-qbm-dbts'; export interface TypedEntityCandidateSidesheetParameter { - entities: TypedEntity []; + entities: TypedEntity[]; tables?: string[]; } diff --git a/imxweb/projects/qbm/src/lib/entity/typed-entity-candidate-sidesheet/typed-entity-candidate-sidesheet.component.html b/imxweb/projects/qbm/src/lib/entity/typed-entity-candidate-sidesheet/typed-entity-candidate-sidesheet.component.html index 737a41894..e66c77e7b 100644 --- a/imxweb/projects/qbm/src/lib/entity/typed-entity-candidate-sidesheet/typed-entity-candidate-sidesheet.component.html +++ b/imxweb/projects/qbm/src/lib/entity/typed-entity-candidate-sidesheet/typed-entity-candidate-sidesheet.component.html @@ -1,21 +1,30 @@ -
    - - +
    + +
    - - + +
    {{ item.GetEntity().GetDisplay() }}
    -
    {{ getTable(item) }}
    +
    {{ getTable(item) }}
    - - +
    diff --git a/imxweb/projects/qbm/src/lib/entity/typed-entity-candidate-sidesheet/typed-entity-candidate-sidesheet.component.scss b/imxweb/projects/qbm/src/lib/entity/typed-entity-candidate-sidesheet/typed-entity-candidate-sidesheet.component.scss index 5ae595d5d..6cebff944 100644 --- a/imxweb/projects/qbm/src/lib/entity/typed-entity-candidate-sidesheet/typed-entity-candidate-sidesheet.component.scss +++ b/imxweb/projects/qbm/src/lib/entity/typed-entity-candidate-sidesheet/typed-entity-candidate-sidesheet.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; :host { display: flex; @@ -7,27 +7,3 @@ height: 100%; } -.eui-sidesheet-content{ - display: flex; - flex-direction: column; - overflow: hidden; - height:100% -} - -.imx-sidesheet-content { - overflow: hidden; - display: flex; - flex-direction: column; - height:100% -} - - -.imx-table-container { - display: flex; - flex-direction: column; - overflow: hidden; - height: inherit; - max-width: 100%; - flex-direction: 1 1 auto; -} - diff --git a/imxweb/projects/qbm/src/lib/entity/typed-entity-candidate-sidesheet/typed-entity-candidate-sidesheet.component.ts b/imxweb/projects/qbm/src/lib/entity/typed-entity-candidate-sidesheet/typed-entity-candidate-sidesheet.component.ts index 7eba9ec06..6eb2d74c4 100644 --- a/imxweb/projects/qbm/src/lib/entity/typed-entity-candidate-sidesheet/typed-entity-candidate-sidesheet.component.ts +++ b/imxweb/projects/qbm/src/lib/entity/typed-entity-candidate-sidesheet/typed-entity-candidate-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,8 +26,8 @@ import { Component, Inject, OnInit } from '@angular/core'; import { EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { DataModelFilterOption, DbObjectKey, DisplayColumns, EntitySchema, IClientProperty, TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { TranslateService } from '@ngx-translate/core'; -import { DataModelFilterOption, DbObjectKey, DisplayColumns, EntitySchema, IClientProperty, TypedEntity } from 'imx-qbm-dbts'; import { MetadataService } from '../../base/metadata.service'; import { CdrFactoryService } from '../../cdr/cdr-factory.service'; import { DataSourceToolbarFilter } from '../../data-source-toolbar/data-source-toolbar-filters.interface'; @@ -95,12 +95,15 @@ export class TypedEntityCandidateSidesheetComponent implements OnInit { public navigate(source: TypedEntityTableFilter): void { this.navigationState = { ...this.navigationState, ...source }; const possible = source.table - ? this.searchedEntities.filter( - (elem) => CdrFactoryService.tryGetColumn(elem.GetEntity(), 'XObjectKey')?.GetValue()?.includes(source.table), + ? this.searchedEntities.filter((elem) => + CdrFactoryService.tryGetColumn(elem.GetEntity(), 'XObjectKey')?.GetValue()?.includes(source.table), ) : this.searchedEntities; - const data = possible.slice(this.navigationState.StartIndex, this.navigationState.StartIndex + this.navigationState.PageSize); + const data = possible.slice( + this.navigationState.StartIndex, + (this.navigationState.StartIndex ?? 0) + (this.navigationState.PageSize ?? 0), + ); this.dstSettings = { displayedColumns: this.displayedColumns, dataSource: { @@ -122,16 +125,18 @@ export class TypedEntityCandidateSidesheetComponent implements OnInit { return ''; } const tableName = DbObjectKey.FromXml(column.GetValue()).TableName; - return this.metaData.tables[tableName]?.DisplaySingular; + return this.metaData.tables[tableName]?.DisplaySingular ?? ''; } private getOptionsForFilter(): DataModelFilterOption[] { - return this.data.tables - .map((elem) => ({ Value: elem, Display: this.metaData.tables[elem]?.DisplaySingular })) - .filter((elem) => - this.data.entities.some( - (entity) => CdrFactoryService.tryGetColumn(entity.GetEntity(), 'XObjectKey')?.GetValue().includes(elem.Value), - ), - ); + return ( + this.data.tables + ?.map((elem) => ({ Value: elem, Display: this.metaData.tables[elem]?.DisplaySingular ?? '' })) + .filter((elem) => + this.data.entities.some((entity) => + CdrFactoryService.tryGetColumn(entity.GetEntity(), 'XObjectKey')?.GetValue().includes(elem.Value), + ), + ) ?? [] + ); } } diff --git a/imxweb/projects/qbm/src/lib/entity/typed-entity-candidate-sidesheet/typed-entity-table-filter.interface.ts b/imxweb/projects/qbm/src/lib/entity/typed-entity-candidate-sidesheet/typed-entity-table-filter.interface.ts index 7eeb702c0..6a3da4932 100644 --- a/imxweb/projects/qbm/src/lib/entity/typed-entity-candidate-sidesheet/typed-entity-table-filter.interface.ts +++ b/imxweb/projects/qbm/src/lib/entity/typed-entity-candidate-sidesheet/typed-entity-table-filter.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { CollectionLoadParameters } from 'imx-qbm-dbts'; +import { CollectionLoadParameters } from '@imx-modules/imx-qbm-dbts'; export interface TypedEntityTableFilter extends CollectionLoadParameters { table?: string; diff --git a/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-fk-data.interface.ts b/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-fk-data.interface.ts index c0711987e..b4c22da07 100644 --- a/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-fk-data.interface.ts +++ b/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-fk-data.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,12 +24,24 @@ * */ -import { CollectionLoadParameters, IForeignKeyInfo, TypedEntity, TypedEntityCollectionData } from 'imx-qbm-dbts'; +import { + ApiRequestOptions, + CollectionLoadParameters, + DataModel, + EntityCollectionData, + FilterTreeData, + IForeignKeyInfo, + TypedEntity, + TypedEntityCollectionData, +} from '@imx-modules/imx-qbm-dbts'; export interface TypedEntityFkData { - getTyped?: (parameters: CollectionLoadParameters) => Promise>; + getTyped?: (parameters: CollectionLoadParameters, opts?: ApiRequestOptions) => Promise>; isMultiValue: boolean; preselectedEntities?: TypedEntity[]; fkTables: ReadonlyArray; preselectedTableName: string; + GetFilterTree?: (parentKey: string, opts?: ApiRequestOptions) => Promise; + GetDataModel?: (opts?: ApiRequestOptions) => Promise; + Get?: (parameters: CollectionLoadParameters, opts?: ApiRequestOptions) => Promise; } diff --git a/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-select.component.html b/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-select.component.html index e98cbab22..c4e71cc23 100644 --- a/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-select.component.html +++ b/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-select.component.html @@ -1,13 +1,24 @@ - - {{ data?.valueWrapper?.display | translate }} - + {{ data?.valueWrapper?.display ?? '' | translate }} + + [required]="data?.valueWrapper?.isValueRequired" + />
    -
    diff --git a/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-select.component.scss b/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-select.component.scss index 61b9e0d07..69253c8fc 100644 --- a/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-select.component.scss +++ b/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-select.component.scss @@ -1,40 +1 @@ -:host { - display: flex; - flex-direction: column; - align-items: baseline; - - >* { - margin-right: 10px; - } -} - -input { - flex: 1; -} - -.mat-form-field { - width: 100%; -} - -.imx-spacer { - height: 30px; -} - -.mat-button { - min-width: initial; -} - -.mat-error { - margin-top: 5px; - font-size: small; -} - -.imx-suffix-container { - display: flex; - margin-bottom: 5px; - - > *:not(:last-child) { - margin-right: 5px; - margin-top: 0; - } -} +@import 'common/select'; diff --git a/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-select.component.ts b/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-select.component.ts index 9a341008f..5b56bb9d1 100644 --- a/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-select.component.ts +++ b/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-select.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,7 +29,8 @@ import { UntypedFormControl } from '@angular/forms'; import { EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { TypedEntity } from 'imx-qbm-dbts'; +import { TypedEntity } from '@imx-modules/imx-qbm-dbts'; +import { calculateSidesheetWidth } from '../../base/sidesheet-helper'; import { LdsReplacePipe } from '../../lds-replace/lds-replace.pipe'; import { TypedEntitySelectionData } from './typed-entity-selection-data.interface'; import { TypedEntitySelectorComponent } from './typed-entity-selector/typed-entity-selector.component'; @@ -37,7 +38,7 @@ import { TypedEntitySelectorComponent } from './typed-entity-selector/typed-enti @Component({ selector: 'imx-typed-entity-select', templateUrl: './typed-entity-select.component.html', - styleUrls: ['./typed-entity-select.component.scss'] + styleUrls: ['./typed-entity-select.component.scss'], }) export class TypedEntitySelectComponent implements OnInit { @Input() public data: TypedEntitySelectionData; @@ -54,8 +55,8 @@ export class TypedEntitySelectComponent implements OnInit { constructor( private readonly translate: TranslateService, private readonly ldsReplace: LdsReplacePipe, - private readonly sidesheet: EuiSidesheetService - ) { } + private readonly sidesheet: EuiSidesheetService, + ) {} public async ngOnInit(): Promise { if (this.data) { @@ -69,28 +70,29 @@ export class TypedEntitySelectComponent implements OnInit { } public async editAssignment(): Promise { - if (this.selected == null) { // beim erste Mal aus der Datenbank holen + if (this.selected == null) { + // beim erste Mal aus der Datenbank holen this.loading = true; this.selected = await this.data.getSelected(); this.loading = false; } - const selection = await this.sidesheet.open( - TypedEntitySelectorComponent, - { + const selection = await this.sidesheet + .open(TypedEntitySelectorComponent, { title: await this.translate.get(this.data.title || this.data.valueWrapper.display).toPromise(), padding: '0', - width: 'max(600px, 60%)', + width: calculateSidesheetWidth(), testId: `typed-entity-selector-${this.data.valueWrapper.name}`, data: { getTyped: this.data.dynamicFkRelation ? undefined : this.data.getTyped, isMultiValue: true, preselectedEntities: this.selected, fkTables: this.data.dynamicFkRelation?.tables, - preselectedTableName: this.data.dynamicFkRelation?.getSelectedTableName(this.selected) - } - } - ).afterClosed().toPromise(); + preselectedTableName: this.data.dynamicFkRelation?.getSelectedTableName(this.selected), + }, + }) + .afterClosed() + .toPromise(); if (selection) { if (!this.data.valueWrapper.canEdit) { @@ -109,17 +111,15 @@ export class TypedEntitySelectComponent implements OnInit { private async update(emitEvent: boolean): Promise { if (this.selected?.length > 0) { - const entities = this.selected.map(item => item.GetEntity()); + const entities = this.selected.map((item) => item.GetEntity()); this.control.setValue( - entities.length === 1 ? entities[0].GetDisplay() : - this.ldsReplace.transform(await this.translate.get('#LDS#{0} items selected').toPromise(), entities.length), - { emitEvent } + entities.length === 1 + ? entities[0].GetDisplay() + : this.ldsReplace.transform(await this.translate.get('#LDS#{0} items selected').toPromise(), entities.length), + { emitEvent }, ); } else { - this.control.setValue( - undefined, - { emitEvent } - ); + this.control.setValue(undefined, { emitEvent }); } } } diff --git a/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-selection-data.interface.ts b/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-selection-data.interface.ts index 66abb0fdc..f7635c3db 100644 --- a/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-selection-data.interface.ts +++ b/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-selection-data.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,17 +24,17 @@ * */ -import { CollectionLoadParameters, IForeignKeyInfo, TypedEntity, TypedEntityCollectionData } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, IForeignKeyInfo, TypedEntity, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { ValueWrapper } from '../../value-wrapper/value-wrapper'; -export interface TypedEntitySelectionData { +export interface TypedEntitySelectionData { valueWrapper: ValueWrapper; title?: string; getInitialDisplay: () => Promise; - getSelected: () => Promise; - getTyped?: (parameters: CollectionLoadParameters) => Promise>; + getSelected: () => Promise; + getTyped?: (parameters: CollectionLoadParameters) => Promise | undefined>; dynamicFkRelation?: { tables: ReadonlyArray; - getSelectedTableName: (selected: TypedEntity[]) => string; + getSelectedTableName: (selected: T[]) => string; }; } diff --git a/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-selector/typed-entity-selector.component.html b/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-selector/typed-entity-selector.component.html index b76524af9..2d2500c77 100644 --- a/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-selector/typed-entity-selector.component.html +++ b/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-selector/typed-entity-selector.component.html @@ -1,30 +1,44 @@
    - + (selectionChanged)="tableSelectionChanged($event)" + > - +
    -
    -
    - - - -
    +
    +
    - -
    diff --git a/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-selector/typed-entity-selector.component.scss b/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-selector/typed-entity-selector.component.scss index d896c0bac..62415c052 100644 --- a/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-selector/typed-entity-selector.component.scss +++ b/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-selector/typed-entity-selector.component.scss @@ -18,38 +18,7 @@ max-height: calc(100% - 60px); } -.imx-button-bar { - display: flex; - justify-content: space-between; - margin-bottom: 0; - padding: 16px 32px; - border-top: 1px solid rgba(0, 0, 0, 0.12); - background-color: $white; - - button { - margin-left: 10px; - } - - .justify-start { - margin-right: auto; - } - - .imx-selected-items { - margin: 0; - line-height: 36px; - } - - ::ng-deep .eui-badge .eui-badge-content { - font-size: 14px; - line-height: 14px; - } - - .imx-badge { - background-color: unset; - } -} - -.imx-fk-selector{ +.imx-fk-selector { overflow: hidden; display: flex; flex-direction: column; @@ -58,17 +27,11 @@ .eui-dark-theme { :host { background-color: $color-gray-80; - .imx-button-bar { - background-color: $color-gray-70; - } } } .eui-contrast-theme { :host { background-color: $color-gray-100; - .imx-button-bar { - background-color: $color-gray-90; - } } } diff --git a/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-selector/typed-entity-selector.component.ts b/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-selector/typed-entity-selector.component.ts index c54a4f21b..12fcdf26b 100644 --- a/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-selector/typed-entity-selector.component.ts +++ b/imxweb/projects/qbm/src/lib/entity/typed-entity-select/typed-entity-selector/typed-entity-selector.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,21 +25,20 @@ */ import { Component, Inject } from '@angular/core'; -import { EuiSidesheetRef, EuiSidesheetService, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { EUI_SIDESHEET_DATA, EuiSidesheetRef, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { IForeignKeyInfo, TypedEntity } from 'imx-qbm-dbts'; -import { TypedEntityCandidateSidesheetComponent } from '../../typed-entity-candidate-sidesheet/typed-entity-candidate-sidesheet.component'; +import { IForeignKeyInfo, TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { TypedEntityFkData } from '../typed-entity-fk-data.interface'; @Component({ selector: 'imx-typed-entity-selector', templateUrl: './typed-entity-selector.component.html', - styleUrls: ['./typed-entity-selector.component.scss'] + styleUrls: ['./typed-entity-selector.component.scss'], }) export class TypedEntitySelectorComponent { public selectedItems: TypedEntity[]; - public selectedFkTable: IForeignKeyInfo; + public selectedFkTable: IForeignKeyInfo | undefined; // TODO: Check Upgrade public readonly fkRelationData: TypedEntityFkData; @@ -48,11 +47,11 @@ export class TypedEntitySelectorComponent { @Inject(EUI_SIDESHEET_DATA) data: TypedEntityFkData, private sidesheet: EuiSidesheetService, private readonly translate: TranslateService, - private readonly sideSheetRef: EuiSidesheetRef + private readonly sideSheetRef: EuiSidesheetRef, ) { this.fkRelationData = data; - this.selectedItems = data.preselectedEntities?.slice(); + this.selectedItems = data.preselectedEntities?.slice() ?? []; if (data.fkTables?.length > 0) { this.selectedFkTable = this.getTable(data.preselectedTableName) ?? data.fkTables[0]; @@ -75,19 +74,7 @@ export class TypedEntitySelectorComponent { this.selectedFkTable = this.getTable(tableName); } - - public async showSelected(): Promise { - this.sidesheet.open(TypedEntityCandidateSidesheetComponent, { - title: await this.translate.get('#LDS#Heading Selected Items').toPromise(), - padding: '0', - width: 'max(550px, 55%)', - testId: 'typed-entity-selector-candidates-sidesheet', - data: { entities: this.selectedItems, tables: this.fkRelationData.fkTables?.map(elem => elem.TableName) } - } - ); - } - - private getTable(tableName: string): IForeignKeyInfo { - return this.fkRelationData.fkTables.find(item => item.TableName === tableName); + private getTable(tableName: string): IForeignKeyInfo | undefined { + return this.fkRelationData.fkTables.find((item) => item.TableName === tableName); } } diff --git a/imxweb/projects/qbm/src/lib/ext/ext.component.ts b/imxweb/projects/qbm/src/lib/ext/ext.component.ts index c5cf855ef..9a94716c8 100644 --- a/imxweb/projects/qbm/src/lib/ext/ext.component.ts +++ b/imxweb/projects/qbm/src/lib/ext/ext.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,10 +24,10 @@ * */ -import { ComponentFactoryResolver, Component, ViewChild, OnInit, Input } from '@angular/core'; +import { Component, ComponentFactoryResolver, Input, OnInit, ViewChild } from '@angular/core'; -import { ExtService } from './ext.service'; import { ExtDirective } from './ext.directive'; +import { ExtService } from './ext.service'; @Component({ selector: 'imx-ext', @@ -42,7 +42,10 @@ export class ExtComponent implements OnInit { @Input() public properties: { [property: string]: any }; - constructor(private componentFactoryResolver: ComponentFactoryResolver, private extService: ExtService) {} + constructor( + private componentFactoryResolver: ComponentFactoryResolver, + private extService: ExtService, + ) {} public ngOnInit(): void { this.loadComponent(); @@ -60,13 +63,15 @@ export class ExtComponent implements OnInit { viewContainerRef.clear(); extensions.forEach((element) => { - const c = viewContainerRef.createComponent(this.componentFactoryResolver.resolveComponentFactory(element.instance)); - c.instance.referrer = this.referrer; - c.instance.inputData = element.inputData; + if (element.instance) { + const c = viewContainerRef.createComponent(this.componentFactoryResolver.resolveComponentFactory(element.instance)); + c.instance.referrer = this.referrer; + c.instance.inputData = element.inputData; - if (this.properties) { - for (let key in this.properties) { - Reflect.set(c.instance, key, this.properties[key]); + if (this.properties) { + for (let key in this.properties) { + Reflect.set(c.instance, key, this.properties[key]); + } } } }); diff --git a/imxweb/projects/qbm/src/lib/ext/ext.directive.ts b/imxweb/projects/qbm/src/lib/ext/ext.directive.ts index c273ec2c8..ce9312bdd 100644 --- a/imxweb/projects/qbm/src/lib/ext/ext.directive.ts +++ b/imxweb/projects/qbm/src/lib/ext/ext.directive.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,7 +27,7 @@ import { Directive, ViewContainerRef } from '@angular/core'; @Directive({ - selector: '[imxExtd]' + selector: '[imxExtd]', }) export class ExtDirective { constructor(public viewContainerRef: ViewContainerRef) {} diff --git a/imxweb/projects/qbm/src/lib/ext/ext.module.ts b/imxweb/projects/qbm/src/lib/ext/ext.module.ts index be7691710..e8001aba7 100644 --- a/imxweb/projects/qbm/src/lib/ext/ext.module.ts +++ b/imxweb/projects/qbm/src/lib/ext/ext.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -33,6 +33,6 @@ import { ExtDirective } from './ext.directive'; @NgModule({ providers: [ExtService], declarations: [ExtComponent, ExtDirective], - exports: [ExtComponent, ExtDirective] + exports: [ExtComponent, ExtDirective], }) export class ExtModule {} diff --git a/imxweb/projects/qbm/src/lib/ext/ext.service.ts b/imxweb/projects/qbm/src/lib/ext/ext.service.ts index 8b55efa26..d279c22a8 100644 --- a/imxweb/projects/qbm/src/lib/ext/ext.service.ts +++ b/imxweb/projects/qbm/src/lib/ext/ext.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,10 +30,11 @@ import { IExtension } from './extension'; @Injectable() export class ExtService { + public get Registry(): { [id: string]: IExtension[] } { + return this.registry; + } - public get Registry(): { [id: string]: IExtension[]; } { return this.registry; } - - private registry: { [id: string]: IExtension[]; } = {}; + private registry: { [id: string]: IExtension[] } = {}; public register(key: string, obj: IExtension): void { if (!this.registry[key]) { @@ -45,22 +46,22 @@ export class ExtService { public async getFittingComponents(key: string, filter: (ext: T) => Promise): Promise { const ret: T[] = []; const ext = this.registry[key]; - if (!ext) { return []; } + if (!ext) { + return []; + } for (const part of ext) { const t = part as T; - if (t && await filter(t)) { + if (t && (await filter(t))) { ret.push(t); } } return ret; } - public async getFittingComponent(key: string): Promise { + public async getFittingComponent(key: string): Promise { if (this.registry[key]) { return this.registry[key][0] as T; } else { - return null; + return undefined; } } - } - diff --git a/imxweb/projects/qbm/src/lib/ext/extension.ts b/imxweb/projects/qbm/src/lib/ext/extension.ts index 307136d4b..11de2b354 100644 --- a/imxweb/projects/qbm/src/lib/ext/extension.ts +++ b/imxweb/projects/qbm/src/lib/ext/extension.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qbm/src/lib/file-selector/file-selector.service.spec.ts b/imxweb/projects/qbm/src/lib/file-selector/file-selector.service.spec.ts index 2df300ead..a81dd60cb 100644 --- a/imxweb/projects/qbm/src/lib/file-selector/file-selector.service.spec.ts +++ b/imxweb/projects/qbm/src/lib/file-selector/file-selector.service.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -34,9 +34,7 @@ describe('FileSelectorService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ - LoggerTestingModule - ] + imports: [LoggerTestingModule], }); service = TestBed.inject(FileSelectorService); }); @@ -48,7 +46,7 @@ describe('FileSelectorService', () => { it('emitFiles publishes file format error if file types mismatch', () => { // Arrange let fileFormatErrorIsSet; - const fs = service.fileFormatError.subscribe(() => fileFormatErrorIsSet = true); + const fs = service.fileFormatError.subscribe(() => (fileFormatErrorIsSet = true)); const files = [new Blob([], { type: 'some file type' }) as File]; @@ -57,9 +55,9 @@ describe('FileSelectorService', () => { { length: files.length, item: (index) => files[index], - 0: files[0] + 0: files[0], }, - 'some other file type' + 'some other file type', ); // Assert diff --git a/imxweb/projects/qbm/src/lib/file-selector/file-selector.service.ts b/imxweb/projects/qbm/src/lib/file-selector/file-selector.service.ts index c74d7a181..3b6a93260 100644 --- a/imxweb/projects/qbm/src/lib/file-selector/file-selector.service.ts +++ b/imxweb/projects/qbm/src/lib/file-selector/file-selector.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,15 +30,15 @@ import { Subject } from 'rxjs'; import { ClassloggerService } from '../classlogger/classlogger.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class FileSelectorService { - public readonly fileFormatError = new Subject(); + public readonly fileFormatError = new Subject(); public readonly fileSelected = new Subject(); - constructor(private logger: ClassloggerService) { } + constructor(private logger: ClassloggerService) {} - public emitFiles(files: FileList, acceptedFileFormat: string): void { + public emitFiles(files: FileList | null, acceptedFileFormat: string): void { if (files == null || files.length === 0) { this.logger.debug(this, 'No file selected.'); return; diff --git a/imxweb/projects/qbm/src/lib/filter-tile/filter-tile.component.html b/imxweb/projects/qbm/src/lib/filter-tile/filter-tile.component.html index 14cba0230..6255cdcd3 100644 --- a/imxweb/projects/qbm/src/lib/filter-tile/filter-tile.component.html +++ b/imxweb/projects/qbm/src/lib/filter-tile/filter-tile.component.html @@ -6,7 +6,7 @@
    - {{caption}} + {{ caption }}
    diff --git a/imxweb/projects/qbm/src/lib/filter-tile/filter-tile.component.scss b/imxweb/projects/qbm/src/lib/filter-tile/filter-tile.component.scss index 46a5c9813..a82b95bb0 100644 --- a/imxweb/projects/qbm/src/lib/filter-tile/filter-tile.component.scss +++ b/imxweb/projects/qbm/src/lib/filter-tile/filter-tile.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; .FilterIcon, @@ -72,26 +72,24 @@ .eui-dark-theme { :host { + .FilterContentClassChecked { + color: $color-gray-60; + } - .FilterContentClassChecked { - color: $color-gray-60; - } - - .FilterMatCard { - background-color: $color-gray-70; - } + .FilterMatCard { + background-color: $color-gray-70; + } } } .eui-contrast-theme { :host { + .FilterContentClassChecked { + color: $color-gray-80; + } - .FilterContentClassChecked { - color: $color-gray-80; - } - - .FilterMatCard { - background-color: $color-gray-90; - } + .FilterMatCard { + background-color: $color-gray-90; + } } } diff --git a/imxweb/projects/qbm/src/lib/filter-tile/filter-tile.component.ts b/imxweb/projects/qbm/src/lib/filter-tile/filter-tile.component.ts index 63d015dcc..fa75718cf 100644 --- a/imxweb/projects/qbm/src/lib/filter-tile/filter-tile.component.ts +++ b/imxweb/projects/qbm/src/lib/filter-tile/filter-tile.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,9 +31,7 @@ import { MatCheckboxDefaultOptions, MAT_CHECKBOX_DEFAULT_OPTIONS } from '@angula selector: 'imx-filter-tile', templateUrl: './filter-tile.component.html', styleUrls: ['./filter-tile.component.scss'], - providers: [ - {provide: MAT_CHECKBOX_DEFAULT_OPTIONS, useValue: { clickAction: 'noop' } as MatCheckboxDefaultOptions} - ] + providers: [{ provide: MAT_CHECKBOX_DEFAULT_OPTIONS, useValue: { clickAction: 'noop' } as MatCheckboxDefaultOptions }], }) export class FilterTileComponent { @Input() public caption: string; diff --git a/imxweb/projects/qbm/src/lib/fk-advanced-picker/candidate-entity.ts b/imxweb/projects/qbm/src/lib/fk-advanced-picker/candidate-entity.ts index 7a949bdbd..f920bfee2 100644 --- a/imxweb/projects/qbm/src/lib/fk-advanced-picker/candidate-entity.ts +++ b/imxweb/projects/qbm/src/lib/fk-advanced-picker/candidate-entity.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,19 +24,26 @@ * */ -import { TypedEntity, EntitySchema, DisplayColumns, ValType } from 'imx-qbm-dbts'; +import { DisplayColumns, EntitySchema, TypedEntity, ValType } from '@imx-modules/imx-qbm-dbts'; export class CandidateEntity extends TypedEntity { - public static GetEntitySchema(): EntitySchema { - const columns = { - XObjectKey: { - Type: ValType.String, - ColumnName: 'XObjectKey' - } - }; + public static GetEntitySchema(parentColumnName?: string, tablename?: string): EntitySchema { + const columns = { + XObjectKey: { + Type: ValType.String, + ColumnName: 'XObjectKey', + }, + }; - columns[DisplayColumns.DISPLAY_PROPERTYNAME] = DisplayColumns.DISPLAY_PROPERTY; - - return { Columns: columns }; + if (parentColumnName) { + columns[parentColumnName] = { + Type: ValType.String, + ColumnName: parentColumnName, + }; } + + columns[DisplayColumns.DISPLAY_PROPERTYNAME] = DisplayColumns.DISPLAY_PROPERTY; + + return { Columns: columns, TypeName: tablename }; + } } diff --git a/imxweb/projects/qbm/src/lib/fk-advanced-picker/candidate.interface.ts b/imxweb/projects/qbm/src/lib/fk-advanced-picker/candidate.interface.ts index a975fa89a..cd76010c7 100644 --- a/imxweb/projects/qbm/src/lib/fk-advanced-picker/candidate.interface.ts +++ b/imxweb/projects/qbm/src/lib/fk-advanced-picker/candidate.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,8 +24,8 @@ * */ -import { ValueStruct } from 'imx-qbm-dbts'; +import { ValueStruct } from '@imx-modules/imx-qbm-dbts'; export interface Candidate extends ValueStruct { - displayLong?: string; + displayLong?: string; } diff --git a/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-advanced-picker.component.html b/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-advanced-picker.component.html index a3fbef167..155f936e5 100644 --- a/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-advanced-picker.component.html +++ b/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-advanced-picker.component.html @@ -1,24 +1,25 @@
    + + {{ '#LDS#Select at least one item.' | translate }} +
    -
    -
    - -
    - - - -
    - - +
    \ No newline at end of file diff --git a/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-advanced-picker.component.scss b/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-advanced-picker.component.scss index bbac9a462..ce2c6d8f1 100644 --- a/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-advanced-picker.component.scss +++ b/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-advanced-picker.component.scss @@ -1,5 +1,7 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/mixins'; + :host { overflow-y: auto; display: flex; @@ -8,13 +10,7 @@ } .imx-dialog-content { - display: flex; - flex-direction: column; - overflow-y: auto; - height: 100%; - eui-alert{ - margin-bottom: 20px; - } + @include flex-column-container($overflow: auto, $height: 100%); > imx-fk-selector { display: flex; @@ -23,46 +19,9 @@ } } -.eui-sidesheet-actions { - display: flex; - justify-content: flex-end; - margin-bottom: 0; - - button { - margin-left: 10px; - } - - .justify-start { - margin-right: auto; - } - - .imx-badge { - background-color: unset; - } -} - .imx-icon-large { background-repeat: no-repeat; padding-left: 35px; min-width: 32px; min-height: 32px; } -.eui-dark-theme { - :host { - background-color: $color-gray-80; - - .imx-dialog-actions { - background: $color-gray-70; - } - } -} - -.eui-contrast-theme { - :host { - background-color: $color-gray-100; - - .imx-dialog-actions { - background: $color-gray-90; - } - } -} diff --git a/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-advanced-picker.component.ts b/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-advanced-picker.component.ts index 1f03c4a47..405d4fc55 100644 --- a/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-advanced-picker.component.ts +++ b/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-advanced-picker.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,20 +24,16 @@ * */ -import { Component, Inject, ViewChild, OnInit, ElementRef, OnDestroy } from '@angular/core'; +import { Component, ElementRef, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; import { Subscription } from 'rxjs'; -import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; -import { - TypedEntity, - IEntity, - IForeignKeyInfo, -} from 'imx-qbm-dbts'; -import { ClassloggerService } from '../classlogger/classlogger.service'; +import { IEntity, IForeignKeyInfo, TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { MetadataService } from '../base/metadata.service'; -import { ForeignKeyPickerData } from './foreign-key-picker-data.interface'; -import { FkSelectorComponent } from './fk-selector.component'; +import { ClassloggerService } from '../classlogger/classlogger.service'; import { ConfirmationService } from '../confirmation/confirmation.service'; +import { FkSelectorComponent } from './fk-selector.component'; +import { ForeignKeyPickerData } from './foreign-key-picker-data.interface'; @Component({ templateUrl: './fk-advanced-picker.component.html', @@ -58,7 +54,7 @@ export class FkAdvancedPickerComponent implements OnInit, OnDestroy { public readonly metadataProvider: MetadataService, private readonly logger: ClassloggerService, private readonly confirmation: ConfirmationService, - private readonly elementRef: ElementRef + private readonly elementRef: ElementRef, ) { this.closeClickSubscription = this.sidesheetRef.closeClicked().subscribe(async () => { if (!this.isChanged || (await this.confirmation.confirmLeaveWithUnsavedChanges())) { @@ -103,8 +99,10 @@ export class FkAdvancedPickerComponent implements OnInit, OnDestroy { table: this.selector.selectedTable, candidates: entityList.map((typedEntity) => { const entity = typedEntity.GetEntity(); + const columnName = this.selectedTable.fkColumnName ?? this.selectedTable.ColumnName; + const data = entity.GetColumn(columnName)?.GetValue(); return { - DataValue: this.getKey(entity), + DataValue: data !== '' ? data : this.getKey(entity), DisplayValue: entity.GetDisplay(), displayLong: entity.GetDisplayLong(), }; @@ -113,10 +111,10 @@ export class FkAdvancedPickerComponent implements OnInit, OnDestroy { } public onSelectedCandidatesChanges(): void { - this.isChanged = this.data.isMultiValue; + this.isChanged = this.data.isMultiValue ?? false; } - private getKey(entity: IEntity): string { + private getKey(entity: IEntity): string | undefined { if (this.data.fkRelations && this.data.fkRelations.length > 1) { this.logger.trace(this, 'Dynamic foreign key'); const xObjectKeyColumn = entity.GetColumn('XObjectKey'); @@ -126,10 +124,10 @@ export class FkAdvancedPickerComponent implements OnInit, OnDestroy { this.logger.trace(this, 'Foreign key'); try { - const parentColumn = entity.GetColumn(this.data.fkRelations[0].ColumnName); - if (parentColumn) { + const parentColumnValue = entity.GetColumn(this.data.fkRelations[0].ColumnName)?.GetValue() ?? ''; + if (parentColumnValue !== '') { this.logger.trace(this, 'Use value from explicit parent column'); - return parentColumn.GetValue(); + return parentColumnValue; } } catch (error) { this.logger.trace(this, 'tried to get parent column but failed', error); diff --git a/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-advanced-picker.module.ts b/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-advanced-picker.module.ts index a5ab90306..cd1176c4f 100644 --- a/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-advanced-picker.module.ts +++ b/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-advanced-picker.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-candidates/fk-candidate-entity-builder.service.ts b/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-candidates/fk-candidate-entity-builder.service.ts index 9dd54c0a4..c17bfc244 100644 --- a/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-candidates/fk-candidate-entity-builder.service.ts +++ b/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-candidates/fk-candidate-entity-builder.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,18 +26,23 @@ import { Injectable } from '@angular/core'; -import { EntityCollectionData, TypedEntityBuilder, TypedEntityCollectionData } from 'imx-qbm-dbts'; +import { EntityCollectionData, EntitySchema, TypedEntityBuilder, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { CandidateEntity } from '../candidate-entity'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class FkCandidateEntityBuilderService { - public readonly entitySchema = CandidateEntity.GetEntitySchema(); + public entitySchema: EntitySchema; private readonly builder = new TypedEntityBuilder(CandidateEntity); - public build(entityCollectionData: EntityCollectionData): TypedEntityCollectionData { + public build( + entityCollectionData: EntityCollectionData, + parentColumnName?: string, + tablename?: string, + ): TypedEntityCollectionData { + this.entitySchema = CandidateEntity.GetEntitySchema(parentColumnName, tablename); return this.builder.buildReadWriteEntities(entityCollectionData, this.entitySchema); } } diff --git a/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-candidates/fk-candidates-data.interface.ts b/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-candidates/fk-candidates-data.interface.ts index 8ea9a9e96..0b8337af6 100644 --- a/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-candidates/fk-candidates-data.interface.ts +++ b/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-candidates/fk-candidates-data.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,12 +24,21 @@ * */ -import { CollectionLoadParameters, EntityCollectionData, FilterTreeData, TypedEntity, TypedEntityCollectionData } from 'imx-qbm-dbts'; +import { + ApiRequestOptions, + CollectionLoadParameters, + DataModel, + EntityCollectionData, + FilterTreeData, + TypedEntity, + TypedEntityCollectionData, +} from '@imx-modules/imx-qbm-dbts'; export interface FkCandidatesData { - get: (parameters: CollectionLoadParameters) => Promise; - GetFilterTree?: (parentKey: string) => Promise; - getTyped?: (parameters: CollectionLoadParameters) => Promise>; - isMultiValue: boolean; + Get?: (parameters: CollectionLoadParameters, opts?: ApiRequestOptions) => EntityCollectionData | Promise; + GetFilterTree?: (parentKey: string, opts?: ApiRequestOptions) => Promise; + GetDataModel?: (opts?: ApiRequestOptions) => Promise; + getTyped?: (parameters: CollectionLoadParameters, opts?: ApiRequestOptions) => Promise>; + isMultiValue?: boolean; preselectedEntities?: TypedEntity[]; } diff --git a/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-candidates/fk-candidates.component.html b/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-candidates/fk-candidates.component.html index a1fc2e032..cfcff53a9 100644 --- a/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-candidates/fk-candidates.component.html +++ b/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-candidates/fk-candidates.component.html @@ -6,7 +6,7 @@ [preSelection]="data?.preselectedEntities" [alwaysVisible]="showToolbar" [busyService]="busyService" - [options]="['search', 'filterTree']" + [options]="['search', 'filter']" (search)="search($event)" data-imx-identifier="fk-candidates-dst" > @@ -21,10 +21,10 @@ [showSelectedItemsMenu]="showSelectedItemsMenu" data-imx-identifier="fk-candidates-datatable" > - +
    {{ item.GetEntity().GetDisplay() }}
    -
    {{ item.GetEntity().GetDisplayLong() }}
    +
    {{ item.GetEntity().GetDisplayLong() }}
    diff --git a/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-candidates/fk-candidates.component.scss b/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-candidates/fk-candidates.component.scss index 7dd3faf31..900d79d7a 100644 --- a/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-candidates/fk-candidates.component.scss +++ b/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-candidates/fk-candidates.component.scss @@ -9,15 +9,6 @@ .imx-subtext { font-size: smaller; } - - .imx-table-container { - display: flex; - flex-direction: column; - overflow: hidden; - height: inherit; - max-width: 100%; - flex: 1 1 auto; - } } // Theming section diff --git a/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-candidates/fk-candidates.component.ts b/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-candidates/fk-candidates.component.ts index 2a9082adc..8a7ef493c 100644 --- a/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-candidates/fk-candidates.component.ts +++ b/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-candidates/fk-candidates.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,18 +25,29 @@ */ import { OverlayRef } from '@angular/cdk/overlay'; -import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewChild } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, Output, ViewChild } from '@angular/core'; import { EuiLoadingService } from '@elemental-ui/core'; -import { CollectionLoadParameters, DisplayColumns, FilterData, IForeignKeyInfo, TypedEntity, ValType } from 'imx-qbm-dbts'; +import { + CollectionLoadParameters, + DataModel, + DisplayColumns, + EntitySchema, + FilterData, + IForeignKeyInfo, + TypedEntity, + ValType, +} from '@imx-modules/imx-qbm-dbts'; +import { BusyService } from '../../base/busy.service'; import { ClientPropertyForTableColumns } from '../../data-source-toolbar/client-property-for-table-columns'; import { DataSourceToolbarSettings } from '../../data-source-toolbar/data-source-toolbar-settings'; import { DataSourceToolbarComponent } from '../../data-source-toolbar/data-source-toolbar.component'; import { DataTableComponent } from '../../data-table/data-table.component'; +import { TypedEntityFkData } from '../../entity/typed-entity-select/typed-entity-fk-data.interface'; import { SettingsService } from '../../settings/settings-service'; +import { CandidateEntity } from '../candidate-entity'; import { FkCandidateEntityBuilderService } from './fk-candidate-entity-builder.service'; import { FkCandidatesData } from './fk-candidates-data.interface'; -import { BusyService } from '../../base/busy.service'; @Component({ selector: 'imx-fk-candidates', @@ -44,9 +55,9 @@ import { BusyService } from '../../base/busy.service'; styleUrls: ['./fk-candidates.component.scss'], }) export class FkCandidatesComponent implements OnChanges { - @Input() public data: FkCandidatesData; + @Input() public data: FkCandidatesData | TypedEntityFkData; @Input() public selectedFkTable: IForeignKeyInfo; - @Input() public showLongdisplay = false; + @Input() public showLongDisplay = false; @Input() public showSelectedItemsMenu = true; @Input() public noDataText: string; @Input() public busyService: BusyService; @@ -57,23 +68,23 @@ export class FkCandidatesComponent implements OnChanges { public readonly DisplayColumns = DisplayColumns; // Enables use of this static class in the Angular Template. public settings: DataSourceToolbarSettings; - public entitySchema = this.candidateBuilder.entitySchema; + public entitySchema: EntitySchema; + public dataModel: DataModel | undefined; @ViewChild(DataTableComponent) private readonly table: DataTableComponent; @ViewChild(DataSourceToolbarComponent) private readonly dst: DataSourceToolbarComponent; - private busyIndicator: OverlayRef; + private busyIndicator: OverlayRef | undefined; + private abortController: AbortController = new AbortController(); constructor( private readonly busyServiceElemental: EuiLoadingService, private readonly settingsService: SettingsService, - private readonly candidateBuilder: FkCandidateEntityBuilderService + private readonly candidateBuilder: FkCandidateEntityBuilderService, ) {} - public async ngOnChanges(changes: SimpleChanges): Promise { - if ((changes['data'] && this.data) || changes['selectedFkTable']) { - await this.getData({ StartIndex: 0, PageSize: this.settingsService.DefaultPageSize, filter: undefined, search: '' }); - } + public async ngOnChanges(): Promise { + await this.getData({ StartIndex: 0, PageSize: this.settingsService.DefaultPageSize }); } public async search(keywords: string): Promise { @@ -95,7 +106,7 @@ export class FkCandidatesComponent implements OnChanges { } public get showToolbar(): boolean { - return this.settings?.navigationState?.filter?.length > 0; + return !!this.settings?.navigationState?.filter?.length; } public async filterByTree(filters: FilterData[]): Promise { @@ -108,6 +119,10 @@ export class FkCandidatesComponent implements OnChanges { * @param newState the state of the data source */ public async getData(newState?: CollectionLoadParameters): Promise { + // Abort any previous calls + this.abortController.abort(); + this.abortController = new AbortController(); + let isBusy; if (this.busyService) { isBusy = this.busyService.beginBusy(); @@ -117,16 +132,20 @@ export class FkCandidatesComponent implements OnChanges { } } try { - let navigationState = this.settings?.navigationState || { PageSize: this.settingsService.DefaultPageSize, StartIndex: 0 }; - - if (newState) { - navigationState = { ...navigationState, ...newState }; - } + let navigationState = this.settings?.navigationState + ? { ...this.settings.navigationState, ...newState } + : { PageSize: this.settingsService.DefaultPageSize, StartIndex: 0 }; const dataSource = this.data.getTyped - ? await this.data.getTyped(navigationState) + ? await this.data.getTyped(navigationState, { signal: this.abortController.signal }) : this.candidateBuilder.build( - this.selectedFkTable ? await this.selectedFkTable.Get(navigationState) : await this.data.get(navigationState) + this.selectedFkTable + ? await this.selectedFkTable.Get(navigationState, { signal: this.abortController.signal }) + : this.data.Get + ? await this.data.Get(navigationState, { signal: this.abortController.signal }) + : { TotalCount: 0 }, + this.selectedFkTable?.fkColumnName, + this.selectedFkTable?.TableName, ); const displayedColumns: ClientPropertyForTableColumns[] = [DisplayColumns.DISPLAY_PROPERTY]; @@ -137,18 +156,23 @@ export class FkCandidatesComponent implements OnChanges { untranslatedDisplay: '#LDS#Selection', }); } + this.entitySchema = CandidateEntity.GetEntitySchema( + this.selectedFkTable?.fkColumnName ?? this.selectedFkTable?.ColumnName, + this.selectedFkTable?.TableName, + ); this.settings = { dataSource, displayedColumns, entitySchema: this.entitySchema, navigationState, + dataModel: this.data.GetDataModel ? await this.data.GetDataModel({ signal: this.abortController.signal }) : undefined, filterTree: { filterMethode: async (parentKey) => { return this.selectedFkTable - ? this.selectedFkTable.GetFilterTree(parentKey) + ? this.selectedFkTable.GetFilterTree(parentKey, { signal: this.abortController.signal }) : this.data.GetFilterTree - ? this.data.GetFilterTree(parentKey) - : { Elements: [] }; + ? this.data.GetFilterTree(parentKey, { signal: this.abortController.signal }) + : { Elements: [] }; }, multiSelect: false, }, diff --git a/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-selector.component.html b/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-selector.component.html index 8b6076d3d..5c1e81926 100644 --- a/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-selector.component.html +++ b/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-selector.component.html @@ -1,6 +1,11 @@
    - + {{ metadataProvider.tables[item.TableName]?.DisplaySingular || item.TableName }} @@ -21,7 +26,7 @@ - + - +
    {{ item?.GetEntity()?.GetDisplay() }}
    diff --git a/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-selector.component.scss b/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-selector.component.scss index 03d90773e..77062e511 100644 --- a/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-selector.component.scss +++ b/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-selector.component.scss @@ -14,42 +14,3 @@ overflow: hidden; } -.mat-radio-group { - display: flex; - - .mat-radio-button { - margin-right: 5px; - } -} - -.imx-table-container { - flex-grow: 1; - overflow: auto; - display: flex; - flex-direction: column; - - .imx-radio-button { - margin-right: 10px; - } - - .imx-main-display { - font-style: normal; - font-weight: 700; - font-size: 14px; - line-height: 48px; - } - - > imx-data-table { - display: flex; - flex-direction: column; - - .imx-selected-row { - color: $color-orange-60 - } - - ::ng-deep .mat-sidenav-container { - display: flex; - flex-direction: column; - } - } -} diff --git a/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-selector.component.ts b/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-selector.component.ts index c064a2e17..2b0b06933 100644 --- a/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-selector.component.ts +++ b/imxweb/projects/qbm/src/lib/fk-advanced-picker/fk-selector.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,31 +24,32 @@ * */ -import { Component, OnInit, ViewChild, Input, Output, EventEmitter } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; import { - TypedEntityBuilder, CollectionLoadParameters, - DisplayColumns, - ValType, - TypedEntity, - IForeignKeyInfo, - FilterType, CompareOperator, - DbObjectKey, + DataModel, DataModelFilter, + DbObjectKey, + DisplayColumns, + EntitySchema, FilterData, - DataModel, -} from 'imx-qbm-dbts'; -import { ClassloggerService } from '../classlogger/classlogger.service'; + FilterType, + IForeignKeyInfo, + TypedEntity, + TypedEntityBuilder, + ValType, +} from '@imx-modules/imx-qbm-dbts'; +import { BusyService } from '../base/busy.service'; import { MetadataService } from '../base/metadata.service'; +import { ClassloggerService } from '../classlogger/classlogger.service'; +import { ClientPropertyForTableColumns } from '../data-source-toolbar/client-property-for-table-columns'; import { DataSourceToolbarSettings } from '../data-source-toolbar/data-source-toolbar-settings'; -import { CandidateEntity } from './candidate-entity'; import { DataTableComponent } from '../data-table/data-table.component'; -import { ForeignKeyPickerData } from './foreign-key-picker-data.interface'; import { SettingsService } from '../settings/settings-service'; -import { ClientPropertyForTableColumns } from '../data-source-toolbar/client-property-for-table-columns'; -import { BusyService } from '../base/busy.service'; +import { CandidateEntity } from './candidate-entity'; +import { ForeignKeyPickerData } from './foreign-key-picker-data.interface'; @Component({ selector: 'imx-fk-selector', @@ -59,7 +60,7 @@ export class FkSelectorComponent implements OnInit { public settings: DataSourceToolbarSettings; public selectedTable: IForeignKeyInfo; public selectedCandidates: TypedEntity[] = []; - public preselectedEntities: TypedEntity[]; + public preselectedEntities: TypedEntity[] | null; public readonly DisplayColumns = DisplayColumns; // Enables use of this static class in Angular Templates. @@ -72,7 +73,7 @@ export class FkSelectorComponent implements OnInit { public busyService = new BusyService(); private readonly builder = new TypedEntityBuilder(CandidateEntity); - private readonly entitySchema = CandidateEntity.GetEntitySchema(); + public entitySchema: EntitySchema; private filters: DataModelFilter[]; private dataModel: DataModel; @@ -88,8 +89,9 @@ export class FkSelectorComponent implements OnInit { if (this.data.fkRelations && this.data.fkRelations.length > 0) { this.logger.trace(this, 'Pre-select the first candidate table'); this.selectedTable = this.data.fkRelations.find((fkr) => fkr.TableName === this.data.selectedTableName) || this.data.fkRelations[0]; + this.entitySchema = CandidateEntity.GetEntitySchema(this.selectedTable.ColumnName, this.selectedTable.TableName); this.dataModel = await this.selectedTable.GetDataModel(); - this.filters = this.dataModel.Filters; + this.filters = this.dataModel.Filters ?? []; } if (this.data.fkRelations && this.data.fkRelations.length > 0) { @@ -225,7 +227,7 @@ export class FkSelectorComponent implements OnInit { this.logger.debug(this, 'Getting preselected entities'); for (const key of this.data.idList) { - let table: IForeignKeyInfo; + let table: IForeignKeyInfo | undefined; if (this.data.fkRelations.length > 1) { const tableName = DbObjectKey.FromXml(key).TableName; table = this.data.fkRelations.find((fkr) => fkr.TableName === tableName); diff --git a/imxweb/projects/qbm/src/lib/fk-advanced-picker/foreign-key-picker-data.interface.ts b/imxweb/projects/qbm/src/lib/fk-advanced-picker/foreign-key-picker-data.interface.ts index d9d47bcc2..823169015 100644 --- a/imxweb/projects/qbm/src/lib/fk-advanced-picker/foreign-key-picker-data.interface.ts +++ b/imxweb/projects/qbm/src/lib/fk-advanced-picker/foreign-key-picker-data.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,13 +24,13 @@ * */ -import { IForeignKeyInfo, IWriteValue } from 'imx-qbm-dbts'; +import { IForeignKeyInfo, IWriteValue } from '@imx-modules/imx-qbm-dbts'; export interface ForeignKeyPickerData { - fkRelations: IForeignKeyInfo[]; - selectedTableName?: string; - idList?: string[]; - isMultiValue?: boolean; - isRequired?: boolean; - disabledIds?: string[]; + fkRelations: IForeignKeyInfo[]; + selectedTableName?: string; + idList?: string[]; + isMultiValue?: boolean; + isRequired?: boolean; + disabledIds?: string[]; } diff --git a/imxweb/projects/qbm/src/lib/fk-advanced-picker/foreign-key-selection.interface.ts b/imxweb/projects/qbm/src/lib/fk-advanced-picker/foreign-key-selection.interface.ts index acf1ecb11..3d66e16be 100644 --- a/imxweb/projects/qbm/src/lib/fk-advanced-picker/foreign-key-selection.interface.ts +++ b/imxweb/projects/qbm/src/lib/fk-advanced-picker/foreign-key-selection.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,10 +24,10 @@ * */ -import { IForeignKeyInfo } from 'imx-qbm-dbts'; +import { IForeignKeyInfo } from '@imx-modules/imx-qbm-dbts'; import { Candidate } from './candidate.interface'; export interface ForeignKeySelection { - table?: IForeignKeyInfo; - candidates?: Candidate[]; + table?: IForeignKeyInfo; + candidates?: Candidate[]; } diff --git a/imxweb/projects/qbm/src/lib/fk-container/dyn-fk-container.ts b/imxweb/projects/qbm/src/lib/fk-container/dyn-fk-container.ts index 0be40eeb7..1df0ad820 100644 --- a/imxweb/projects/qbm/src/lib/fk-container/dyn-fk-container.ts +++ b/imxweb/projects/qbm/src/lib/fk-container/dyn-fk-container.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { EntityData } from 'imx-qbm-dbts'; +import { EntityData } from '@imx-modules/imx-qbm-dbts'; import { FkContainer } from './fk-container'; export class DynFkContainer extends FkContainer { diff --git a/imxweb/projects/qbm/src/lib/fk-container/fk-container.ts b/imxweb/projects/qbm/src/lib/fk-container/fk-container.ts index 304619a1f..5e3316e59 100644 --- a/imxweb/projects/qbm/src/lib/fk-container/fk-container.ts +++ b/imxweb/projects/qbm/src/lib/fk-container/fk-container.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,14 +26,14 @@ import { ErrorHandler } from '@angular/core'; -import { EntityValue, EntityData, CollectionLoadParameters, EntityCollectionData } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, EntityCollectionData, EntityData, EntityValue } from '@imx-modules/imx-qbm-dbts'; export class FkContainer { public property: EntityValue; - public candidateCollection: EntityCollectionData; - public value: EntityData; + public candidateCollection: EntityCollectionData | undefined; + public value: EntityData | null; - constructor(private getProperty: () => EntityValue) { } + constructor(private getProperty: () => EntityValue) {} public async init(errorHandler: ErrorHandler, parameters?: CollectionLoadParameters): Promise { this.property = this.getProperty(); @@ -42,13 +42,13 @@ export class FkContainer { if (this.property.value) { if (this.candidateCollection) { - this.value = this.candidateCollection.Entities.find(candidate => this.getEntityKey(candidate) === this.property.value); + this.value = this.candidateCollection.Entities?.find((candidate) => this.getEntityKey(candidate) === this.property.value) ?? null; } if (this.value == null) { this.value = { Display: this.property.Column.GetDisplayValue(), - Keys: [this.property.value] + Keys: [this.property.value], }; } } else { @@ -57,17 +57,18 @@ export class FkContainer { } public setKey(value: EntityData): void { - this.property.value = this.getEntityKey(value); + this.property.value = this.getEntityKey(value) ?? ''; } - protected getEntityKey(data: EntityData): string { + protected getEntityKey(data: EntityData): string | undefined { return data && data.Keys && data.Keys.length > 0 ? data.Keys[0] : undefined; } private async getCandidateCollection( property: EntityValue, errorHandler: ErrorHandler, - parameters?: CollectionLoadParameters): Promise { + parameters?: CollectionLoadParameters, + ): Promise { const fkRelations = property.GetMetadata().GetFkRelations(); if (fkRelations) { diff --git a/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/fk-hierarchical-dialog.component.html b/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/fk-hierarchical-dialog.component.html index f943a0f11..36ecc3497 100644 --- a/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/fk-hierarchical-dialog.component.html +++ b/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/fk-hierarchical-dialog.component.html @@ -1,9 +1,14 @@
    - - {{'#LDS#Object type' | translate}} - + + {{ '#LDS#Object type' | translate }} + {{ metadataProvider.tables[item.TableName]?.DisplaySingular || item.TableName }} @@ -11,33 +16,53 @@ - - + + {{ metadataProvider.tables[item.TableName]?.DisplaySingular || item.TableName }} - +
    -
    - -
    - -
    diff --git a/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/fk-hierarchical-dialog.component.scss b/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/fk-hierarchical-dialog.component.scss index e63578165..7faf83ea1 100644 --- a/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/fk-hierarchical-dialog.component.scss +++ b/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/fk-hierarchical-dialog.component.scss @@ -1,64 +1,31 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/mixins'; :host { - overflow-y: auto; - display: flex; - flex-direction: column; - height: 100%; + @include flex-column-container($overflow: auto, $height: 100%); } -.imx-fk-table-radio { - > *:not(:last-child) { - margin-right: 30px; - } -} - .imx-dialog-content { - display: flex; - flex-direction: column; - overflow-y: auto; - height: 100%; -} - -.eui-sidesheet-actions { - display: flex; - justify-content: flex-end; - margin-bottom: 0; - - button { - margin-left: 10px; - } - - .justify-start { - margin-right: auto; - } + @include flex-column-container($overflow: auto, $height: 100%); } -::ng-deep .imx-under { - margin-top: 30px; -} .eui-dark-theme { :host { background-color: $color-gray-80; - .imx-sidesheet-content{ + + .imx-sidesheet-content { background: $color-gray-80; } - .imx-dialog-actions { - background-color: $color-gray-70; - } } } .eui-contrast-theme { :host { background-color: $color-gray-100; - .imx-sidesheet-content{ + + .imx-sidesheet-content { background: $color-gray-100; } - .imx-dialog-actions { - background-color: $color-gray-90; - } } } diff --git a/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/fk-hierarchical-dialog.component.spec.ts b/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/fk-hierarchical-dialog.component.spec.ts index 2c91b8dc2..d08bd8e0c 100644 --- a/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/fk-hierarchical-dialog.component.spec.ts +++ b/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/fk-hierarchical-dialog.component.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -35,8 +35,8 @@ import { FkHierarchicalDialogComponent } from './fk-hierarchical-dialog.componen import { FkHierarchicalDialogModule } from './fk-hierarchical-dialog.module'; const fkInfo = [ - { TableName: 'testtable1', ColumnName: 'testcolumn1', Get: (_) => undefined ,GetDataModel: () =>undefined}, - { TableName: 'testtable2', ColumnName: 'testcolumn2', Get: (_) => undefined, GetDataModel: () =>undefined }, + { TableName: 'testtable1', ColumnName: 'testcolumn1', Get: (_) => undefined, GetDataModel: () => undefined }, + { TableName: 'testtable2', ColumnName: 'testcolumn2', Get: (_) => undefined, GetDataModel: () => undefined }, ]; [ @@ -56,7 +56,8 @@ const fkInfo = [ beforeEach(() => { return MockBuilder([FkHierarchicalDialogComponent, TranslateModule.forRoot()]) .mock(FkHierarchicalDialogModule) - .mock(EuiSidesheetRef).mock(MetadataService,metadataServiceStub) + .mock(EuiSidesheetRef) + .mock(MetadataService, metadataServiceStub) .mock(ConfirmationService) .mock(EuiLoadingService) .mock(EUI_SIDESHEET_DATA, { @@ -77,5 +78,5 @@ const fkInfo = [ it('should create', () => { expect(component).toBeTruthy(); }); - }) + }), ); diff --git a/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/fk-hierarchical-dialog.component.ts b/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/fk-hierarchical-dialog.component.ts index d180286b4..04ee5f923 100644 --- a/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/fk-hierarchical-dialog.component.ts +++ b/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/fk-hierarchical-dialog.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,9 +25,8 @@ */ import { Component, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core'; -import { EuiLoadingService, EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { EUI_SIDESHEET_DATA, EuiLoadingService, EuiSidesheetRef } from '@elemental-ui/core'; import { Subscription } from 'rxjs'; -import { OverlayRef } from '@angular/cdk/overlay'; import { CollectionLoadParameters, @@ -38,15 +37,15 @@ import { IEntity, IForeignKeyInfo, TypedEntity, -} from 'imx-qbm-dbts'; +} from '@imx-modules/imx-qbm-dbts'; import { MetadataService } from '../base/metadata.service'; import { ClassloggerService } from '../classlogger/classlogger.service'; import { ConfirmationService } from '../confirmation/confirmation.service'; +import { FilterTreeParameter } from '../data-source-toolbar/data-model/filter-tree-parameter'; +import { DataTreeWrapperComponent } from '../data-tree-wrapper/data-tree-wrapper.component'; import { ForeignKeyPickerData } from '../fk-advanced-picker/foreign-key-picker-data.interface'; -import { HierarchicalFkDatabase } from './hierarchical-fk-database'; import { HierarchicalCandidate } from './hierarchical-candidate'; -import { DataTreeWrapperComponent } from '../data-tree-wrapper/data-tree-wrapper.component'; -import { FilterTreeParameter } from '../data-source-toolbar/data-model/filter-tree-parameter'; +import { HierarchicalFkDatabase } from './hierarchical-fk-database'; @Component({ selector: 'imx-fk-hierarchical-dialog', @@ -74,7 +73,7 @@ export class FkHierarchicalDialogComponent implements OnInit, OnDestroy { private logger: ClassloggerService, private readonly confirmation: ConfirmationService, public readonly metadataProvider: MetadataService, - @Inject(EUI_SIDESHEET_DATA) public readonly data: ForeignKeyPickerData + @Inject(EUI_SIDESHEET_DATA) public readonly data: ForeignKeyPickerData, ) { this.closeClickSubscription = this.sidesheetRef.closeClicked().subscribe(async () => { if (!this.isChanged || (await this.confirmation.confirmLeaveWithUnsavedChanges())) { @@ -96,7 +95,7 @@ export class FkHierarchicalDialogComponent implements OnInit, OnDestroy { public async ngOnInit(): Promise { await this.getPreselectedEntities(); - this.filters = (await this.hierarchyService?.fkTable?.GetDataModel())?.Filters; + this.filters = (await this.hierarchyService?.fkTable?.GetDataModel())?.Filters ?? []; this.tableNames = this.data.fkRelations?.map((elem) => elem.TableName); } @@ -118,7 +117,7 @@ export class FkHierarchicalDialogComponent implements OnInit, OnDestroy { if (!this.data.isMultiValue) { this.selectedEntities = [entity]; this.applySelection(); - } + } } public selectedNodesChanged(): void { @@ -144,7 +143,7 @@ export class FkHierarchicalDialogComponent implements OnInit, OnDestroy { }); } - private getKey(entity: IEntity): string { + private getKey(entity: IEntity): string | undefined { if (this.data.fkRelations && this.data.fkRelations.length > 1) { const xObjectKeyColumn = entity.GetColumn('XObjectKey'); return xObjectKeyColumn ? xObjectKeyColumn.GetValue() : undefined; @@ -156,8 +155,9 @@ export class FkHierarchicalDialogComponent implements OnInit, OnDestroy { private async getPreselectedEntities(): Promise { if (this.data.fkRelations && this.data.fkRelations.length > 0 && this.data.idList && this.data.idList.length > 0) { - let over: OverlayRef; - setTimeout(() => (over = this.busyService.show())); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } try { const preselectedTemp: TypedEntity[] = []; @@ -166,7 +166,7 @@ export class FkHierarchicalDialogComponent implements OnInit, OnDestroy { this.logger.debug(this, 'Getting preselected entities'); for (const key of this.data.idList) { - let table: IForeignKeyInfo; + let table: IForeignKeyInfo | undefined; if (this.data.fkRelations.length > 1) { const tableName = DbObjectKey.FromXml(key).TableName; table = this.data.fkRelations.find((fkr) => fkr.TableName === tableName); @@ -187,7 +187,7 @@ export class FkHierarchicalDialogComponent implements OnInit, OnDestroy { this.logger.debug(this, 'Getting preselected entity with navigation state', navigationState); const elements = await table.Get(navigationState); - if (elements.Entities.length) { + if (elements.Entities?.length) { const entity = await this.hierarchyService.buildEntityWithHasChilderen(elements.Entities[0], elements.Hierarchy); preselectedTemp.push(entity); } @@ -197,7 +197,7 @@ export class FkHierarchicalDialogComponent implements OnInit, OnDestroy { this.selectedEntityCandidates = this.selectedEntities.map((elem) => new HierarchicalCandidate(elem)); this.logger.debug(this, `Retrieved ${this.selectedEntities.length} preselected entities`); } finally { - setTimeout(() => this.busyService.hide(over)); + this.busyService.hide(); } } } diff --git a/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/fk-hierarchical-dialog.module.ts b/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/fk-hierarchical-dialog.module.ts index e235bf506..e8b2d0ac8 100644 --- a/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/fk-hierarchical-dialog.module.ts +++ b/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/fk-hierarchical-dialog.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -38,12 +38,11 @@ import { MatSelectModule } from '@angular/material/select'; import { FkHierarchicalDialogComponent } from './fk-hierarchical-dialog.component'; import { ConfirmationModule } from '../confirmation/confirmation.module'; import { DataTreeWrapperModule } from '../data-tree-wrapper/data-tree-wrapper.module'; - -import {SelectedElementsModule} from '../selected-elements/selected-elements.module'; + +import { SelectedElementsModule } from '../selected-elements/selected-elements.module'; import { EuiCoreModule } from '@elemental-ui/core'; @NgModule({ - declarations: [FkHierarchicalDialogComponent], imports: [ EuiCoreModule, @@ -58,9 +57,8 @@ import { EuiCoreModule } from '@elemental-ui/core'; MatSelectModule, DataTreeWrapperModule, ConfirmationModule, - SelectedElementsModule + SelectedElementsModule, ], - exports: [FkHierarchicalDialogComponent] - + exports: [FkHierarchicalDialogComponent], }) -export class FkHierarchicalDialogModule { } +export class FkHierarchicalDialogModule {} diff --git a/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/hierarchical-candidate.ts b/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/hierarchical-candidate.ts index 63839a599..df6d26ee9 100644 --- a/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/hierarchical-candidate.ts +++ b/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/hierarchical-candidate.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { DisplayColumns, EntitySchema, ValType } from 'imx-qbm-dbts'; +import { DisplayColumns, EntitySchema, ValType } from '@imx-modules/imx-qbm-dbts'; import { CandidateEntity } from '../fk-advanced-picker/candidate-entity'; export class HierarchicalCandidate extends CandidateEntity { @@ -32,12 +32,12 @@ export class HierarchicalCandidate extends CandidateEntity { const columns = { XObjectKey: { Type: ValType.String, - ColumnName: 'XObjectKey' + ColumnName: 'XObjectKey', }, HasChildren: { Type: ValType.Bool, - ColumnName: 'HasChildren' - } + ColumnName: 'HasChildren', + }, }; columns[DisplayColumns.DISPLAY_PROPERTYNAME] = DisplayColumns.DISPLAY_PROPERTY; diff --git a/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/hierarchical-fk-database.ts b/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/hierarchical-fk-database.ts index 772448c36..35aad7fea 100644 --- a/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/hierarchical-fk-database.ts +++ b/imxweb/projects/qbm/src/lib/fk-hierarchical-dialog/hierarchical-fk-database.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,6 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; import { EuiLoadingService } from '@elemental-ui/core'; import { @@ -35,28 +34,27 @@ import { IEntity, IForeignKeyInfo, TypedEntityBuilder, - ValType -} from 'imx-qbm-dbts'; + ValType, +} from '@imx-modules/imx-qbm-dbts'; import { TreeDatabase } from '../data-tree/tree-database'; import { TreeNodeResultParameter } from '../data-tree/tree-node-result-parameter.interface'; import { HierarchicalCandidate } from './hierarchical-candidate'; export class HierarchicalFkDatabase extends TreeDatabase { - public fkTable: IForeignKeyInfo; private readonly builder = new TypedEntityBuilder(HierarchicalCandidate); - constructor( - private busyLoadingService: EuiLoadingService - ) { + constructor(private busyLoadingService: EuiLoadingService) { super(); this.canSearch = true; } /** implements the getData methode of TreeDataBase */ - public async getData(showLoading: boolean, parameters: CollectionLoadParameters = { ParentKey: '' /* first level */ }) - : Promise { + public async getData( + showLoading: boolean, + parameters: CollectionLoadParameters = { ParentKey: '' /* first level */ }, + ): Promise { if (!this.fkTable) { return { entities: [], canLoadMore: false, totalCount: 0 }; } @@ -64,12 +62,11 @@ export class HierarchicalFkDatabase extends TreeDatabase { const opts = { PageSize: 25, StartIndex: 0, - ...parameters + ...parameters, }; - let over: OverlayRef; - if (showLoading) { - setTimeout(() => over = this.busyLoadingService.show()); + if (showLoading && this.busyLoadingService.overlayRefs.length === 0) { + this.busyLoadingService.show(); } let data: EntityCollectionData; @@ -78,14 +75,16 @@ export class HierarchicalFkDatabase extends TreeDatabase { data = await this.fkTable.Get(opts); } finally { if (showLoading) { - setTimeout(() => this.busyLoadingService.hide(over)); + this.busyLoadingService.hide(); } } if (data) { - const nodeEntities = await Promise.all(data.Entities.map(async (elem): Promise => { - return (await this.buildEntityWithHasChilderen(elem, data.Hierarchy)).GetEntity(); - })); + const nodeEntities = await Promise.all( + data.Entities?.map(async (elem): Promise => { + return (await this.buildEntityWithHasChilderen(elem, data?.Hierarchy)).GetEntity(); + }) ?? [], + ); this.dataChanged.emit(nodeEntities); return { entities: nodeEntities, canLoadMore: opts.StartIndex + opts.PageSize < data.TotalCount, totalCount: data.TotalCount }; } @@ -93,20 +92,22 @@ export class HierarchicalFkDatabase extends TreeDatabase { } /** adds a hasChildren column to the entity */ - public async buildEntityWithHasChilderen(entityData: EntityData, data: HierarchyData): Promise { - + public async buildEntityWithHasChilderen(entityData: EntityData, data: HierarchyData | undefined): Promise { const entity = this.builder.buildReadWriteEntity({ entitySchema: HierarchicalCandidate.GetEntitySchema(), entityData }); - entity.GetEntity().AddColumns([{ - Type: ValType.Bool, - IsMultiValued: true, - ColumnName: 'HasChildren', - MinLen: 0, - Display: '' - }]); - await entity.GetEntity().GetColumn('HasChildren') - .PutValue(data ? data.EntitiesWithHierarchy.some(elem => entityData.Keys.some(key => key === elem)) : false); + entity.GetEntity().AddColumns([ + { + Type: ValType.Bool, + IsMultiValued: true, + ColumnName: 'HasChildren', + MinLen: 0, + Display: '', + }, + ]); + await entity + .GetEntity() + .GetColumn('HasChildren') + .PutValue(data ? data.EntitiesWithHierarchy?.some((elem) => entityData.Keys?.some((key) => key === elem)) : false); return entity; } - } diff --git a/imxweb/projects/qbm/src/lib/help-contextual/help-contextual-dialog/help-contextual-dialog.component.html b/imxweb/projects/qbm/src/lib/help-contextual/help-contextual-dialog/help-contextual-dialog.component.html index 03058aad4..731596155 100644 --- a/imxweb/projects/qbm/src/lib/help-contextual/help-contextual-dialog/help-contextual-dialog.component.html +++ b/imxweb/projects/qbm/src/lib/help-contextual/help-contextual-dialog/help-contextual-dialog.component.html @@ -1,12 +1,12 @@
    -

    +

    {{ dialogHeading }}

    -
    {{detail | translate}}
    +
    {{ detail }}
    diff --git a/imxweb/projects/qbm/src/lib/help-contextual/help-contextual-dialog/help-contextual-dialog.component.scss b/imxweb/projects/qbm/src/lib/help-contextual/help-contextual-dialog/help-contextual-dialog.component.scss index ee584edb3..d17f6a2d5 100644 --- a/imxweb/projects/qbm/src/lib/help-contextual/help-contextual-dialog/help-contextual-dialog.component.scss +++ b/imxweb/projects/qbm/src/lib/help-contextual/help-contextual-dialog/help-contextual-dialog.component.scss @@ -1,12 +1,10 @@ -.dialog-content__links{ +.dialog-content__links { margin-top: 20px; display: flex; flex-direction: column; gap: 10px; } -.mat-dialog-actions{ - justify-content: end; -} -.mat-dialog-content{ + +.mat-mdc-dialog-content{ max-width: 50vw; } diff --git a/imxweb/projects/qbm/src/lib/help-contextual/help-contextual-dialog/help-contextual-dialog.component.ts b/imxweb/projects/qbm/src/lib/help-contextual/help-contextual-dialog/help-contextual-dialog.component.ts index 15fe51dde..2cd33ef4b 100644 --- a/imxweb/projects/qbm/src/lib/help-contextual/help-contextual-dialog/help-contextual-dialog.component.ts +++ b/imxweb/projects/qbm/src/lib/help-contextual/help-contextual-dialog/help-contextual-dialog.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,9 +24,9 @@ * */ -import { Component, Inject, OnInit } from '@angular/core'; +import { Component, Inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { ChapterLink, ContextualHelpItem } from 'imx-api-qbm'; +import { ChapterLink, ContextualHelpItem } from '@imx-modules/imx-api-qbm'; import { HelpContextualService } from '../help-contextual.service'; /** @@ -41,7 +41,7 @@ export class HelpContextualDialogComponent { constructor( private readonly helpContextService: HelpContextualService, public matDialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: ContextualHelpItem + @Inject(MAT_DIALOG_DATA) public data: ContextualHelpItem, ) {} /** @@ -67,8 +67,8 @@ export class HelpContextualDialogComponent { */ getHelpLink(link: ChapterLink): string { if (link.IsExternal) { - return link.Url; + return link.Url ?? ''; } - return this.helpContextService.getHelpLink(link.Url); + return link.Url ? this.helpContextService.getHelpLink(link.Url) : ''; } } diff --git a/imxweb/projects/qbm/src/lib/help-contextual/help-contextual.component.html b/imxweb/projects/qbm/src/lib/help-contextual/help-contextual.component.html index e6bfdb3fb..614def370 100644 --- a/imxweb/projects/qbm/src/lib/help-contextual/help-contextual.component.html +++ b/imxweb/projects/qbm/src/lib/help-contextual/help-contextual.component.html @@ -1,4 +1,4 @@ -{{title | translate}} +{{ title | translate }} diff --git a/imxweb/projects/qbm/src/lib/help-contextual/help-contextual.component.scss b/imxweb/projects/qbm/src/lib/help-contextual/help-contextual.component.scss index e5bfd9150..a4db3b33e 100644 --- a/imxweb/projects/qbm/src/lib/help-contextual/help-contextual.component.scss +++ b/imxweb/projects/qbm/src/lib/help-contextual/help-contextual.component.scss @@ -1,36 +1,17 @@ @import '@elemental-ui/core/src/styles/_eui_palette.scss'; -:host{ +:host { display: flex; align-items: center; } -span{ +span { color: $color-gray-60; font-size: 14px; } -::ng-deep{ - eui-sidesheet{ - .eui-sidesheet{ - .mat-toolbar{ - justify-content: start; - .eui-sidesheet__heading-text{ - flex-basis: auto; - } - imx-help-contextual{ - .mat-icon-button{ - color: $color-gray-0; - } - } - .eui-sidesheet-close{ - margin-left: auto; - } - } - } - } -} + .eui-dark-theme { :host { - span{ + span { color: $color-gray-10; } } diff --git a/imxweb/projects/qbm/src/lib/help-contextual/help-contextual.component.ts b/imxweb/projects/qbm/src/lib/help-contextual/help-contextual.component.ts index 98865e775..5b98bd91f 100644 --- a/imxweb/projects/qbm/src/lib/help-contextual/help-contextual.component.ts +++ b/imxweb/projects/qbm/src/lib/help-contextual/help-contextual.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,10 +25,10 @@ */ import { Component, Input, OnDestroy } from '@angular/core'; -import { HelpContextualValues, HelpContextualService, HELP_CONTEXTUAL } from './help-contextual.service'; -import { ActivatedRoute } from '@angular/router'; import { MatDialog } from '@angular/material/dialog'; +import { ActivatedRoute } from '@angular/router'; import { HelpContextualDialogComponent } from './help-contextual-dialog/help-contextual-dialog.component'; +import { HELP_CONTEXTUAL, HelpContextualService, HelpContextualValues } from './help-contextual.service'; /** * Help contextual component * @example @@ -56,18 +56,18 @@ import { HelpContextualDialogComponent } from './help-contextual-dialog/help-con @Component({ selector: 'imx-help-contextual', templateUrl: './help-contextual.component.html', - styleUrls: ['./help-contextual.component.scss'] + styleUrls: ['./help-contextual.component.scss'], }) -export class HelpContextualComponent implements OnDestroy{ +export class HelpContextualComponent implements OnDestroy { @Input() contextId: HelpContextualValues; - @Input() size: 's'| 'm' | 'l' | 'xl' = 'm'; + @Input() size: 's' | 'm' | 'l' | 'xl' = 'm'; @Input() title: string; constructor( private router: ActivatedRoute, private helpContextualService: HelpContextualService, - private dialog: MatDialog - ) { } + private dialog: MatDialog, + ) {} ngOnDestroy(): void { this.helpContextualService.setHelpContextId(null); @@ -75,23 +75,26 @@ export class HelpContextualComponent implements OnDestroy{ /** * The call opens the dialog with the contextual help data. */ - public async onShowHelp(): Promise{ - const contextualHelpData = await this.helpContextualService.getHelpContext(this.getContextId()); - this.dialog - .open(HelpContextualDialogComponent, { - data: contextualHelpData - }) + public async onShowHelp(): Promise { + const id = this.getContextId(); + if (!id) { + return; + } + const contextualHelpData = await this.helpContextualService.getHelpContext(id); + this.dialog.open(HelpContextualDialogComponent, { + data: contextualHelpData, + }); } /** * The call returns the selected context ID. * @returns {HelpContextualValues} */ - private getContextId(): HelpContextualValues{ - if(!!this.contextId){ + private getContextId(): HelpContextualValues | null { + if (!!this.contextId) { return this.contextId; } - if(!!this.helpContextualService.GetHelpContextId()){ + if (!!this.helpContextualService.GetHelpContextId()) { return this.helpContextualService.GetHelpContextId(); } let contextId: HelpContextualValues; diff --git a/imxweb/projects/qbm/src/lib/help-contextual/help-contextual.module.ts b/imxweb/projects/qbm/src/lib/help-contextual/help-contextual.module.ts index 3b1760ae4..f07e22c13 100644 --- a/imxweb/projects/qbm/src/lib/help-contextual/help-contextual.module.ts +++ b/imxweb/projects/qbm/src/lib/help-contextual/help-contextual.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -33,20 +33,9 @@ import { TranslateModule } from '@ngx-translate/core'; import { EuiCoreModule } from '@elemental-ui/core'; import { MatButtonModule } from '@angular/material/button'; - - @NgModule({ - declarations: [ - HelpContextualComponent, - HelpContextualDialogComponent - ], - imports: [ - CommonModule, - MatDialogModule, - TranslateModule, - EuiCoreModule, - MatButtonModule - ], - exports: [HelpContextualComponent] + declarations: [HelpContextualComponent, HelpContextualDialogComponent], + imports: [CommonModule, MatDialogModule, TranslateModule, EuiCoreModule, MatButtonModule], + exports: [HelpContextualComponent], }) -export class HelpContextualModule { } +export class HelpContextualModule {} diff --git a/imxweb/projects/qbm/src/lib/help-contextual/help-contextual.service.ts b/imxweb/projects/qbm/src/lib/help-contextual/help-contextual.service.ts index 7627e157c..0af6c10fc 100644 --- a/imxweb/projects/qbm/src/lib/help-contextual/help-contextual.service.ts +++ b/imxweb/projects/qbm/src/lib/help-contextual/help-contextual.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,9 +25,9 @@ */ import { Injectable } from '@angular/core'; -import { AppConfigService } from '../appConfig/appConfig.service'; -import { ContextualHelpItem } from 'imx-api-qbm'; +import { ContextualHelpItem } from '@imx-modules/imx-api-qbm'; import { TranslateService } from '@ngx-translate/core'; +import { AppConfigService } from '../appConfig/appConfig.service'; /** * Contains all the methods for help context. @@ -36,7 +36,7 @@ import { TranslateService } from '@ngx-translate/core'; providedIn: 'root', }) export class HelpContextualService { - private helpContextId: HelpContextualValues; + private helpContextId: HelpContextualValues | null; constructor( private appConfigService: AppConfigService, private translateService: TranslateService, @@ -48,7 +48,7 @@ export class HelpContextualService { * @returns the selected ContextualHelpItem */ public async getHelpContext(contextId: HelpContextualValues): Promise { - const lang = this.translateService.currentLang; + const lang = this.translateService.currentLang === 'de' ? 'de-DE' : this.translateService.currentLang; let contextItem: ContextualHelpItem; try { contextItem = await this.appConfigService.client.imx_help_context_get(contextId, lang); @@ -71,7 +71,7 @@ export class HelpContextualService { * The call sets the stored help context ID. * @param {HelpContextualValues} */ - public setHelpContextId(contextId: HelpContextualValues): void { + public setHelpContextId(contextId: HelpContextualValues | null): void { this.helpContextId = contextId; } @@ -79,7 +79,7 @@ export class HelpContextualService { * The call returns the stored help context ID. * @returns {HelpContextualValues} */ - public GetHelpContextId(): HelpContextualValues { + public GetHelpContextId(): HelpContextualValues | null { return this.helpContextId; } } @@ -92,6 +92,7 @@ export class HelpContextualService { export const HELP_CONTEXTUAL = { Default: 'default', StatisticsPage: 'statistics-page', + StatisticsFavoritesOrdering: 'statistics-favorites-ordering', NewRequest: 'new-request', NewRequestRecommendedProduct: 'new-request-recommended-product', NewRequestReferenceUser: 'new-request-reference-user', @@ -178,6 +179,7 @@ export const HELP_CONTEXTUAL = { ServiceItemsEdit: 'service-items-edit', ApprovalWorkflowManager: 'approval-workflow-manager', ApprovalWorkflowManagerCreate: 'approval-workflow-manager-create', + ApprovalWorkflowManagerEdit: 'approval-workflow-manager-edit', Reports: 'reports', ReportsCreate: 'reports-create', ReportsEdit: 'reports-edit', @@ -187,6 +189,8 @@ export const HELP_CONTEXTUAL = { Profile: 'profile', ProfileMultipleIdentities: 'profile-multiple-identities', Addressbook: 'addressbook', + RoleEntitlements: 'role-entitlements', + ProcessingQueue: 'processing-queue', } as const; type ObjectValues = T[keyof T]; export type HelpContextualValues = ObjectValues; diff --git a/imxweb/projects/qbm/src/lib/hyperview/connector-provider.spec.ts b/imxweb/projects/qbm/src/lib/hyperview/connector-provider.spec.ts index 71392c96c..f646eb9a7 100644 --- a/imxweb/projects/qbm/src/lib/hyperview/connector-provider.spec.ts +++ b/imxweb/projects/qbm/src/lib/hyperview/connector-provider.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,32 +28,27 @@ import { ConnectorProvider } from './connector-provider'; import { clearStylesFromDOM } from '../testing/clear-styles.spec'; describe('ConnectorProvider', () => { - afterAll(() => { clearStylesFromDOM(); }); it('should create an instance', () => { - expect(() => { const connector = new ConnectorProvider(true); - }).not.toThrowError(); }); [ { description: 'should return no connectors', nbOfElements: 1, expectedConnectors: 0 }, { description: 'should return one connectors', nbOfElements: 2, expectedConnectors: 1 }, - { description: 'should return three connectors', nbOfElements: 3, expectedConnectors: 2 } - ].forEach(testcase => { - + { description: 'should return three connectors', nbOfElements: 3, expectedConnectors: 2 }, + ].forEach((testcase) => { it('getConnectors ' + testcase.description, () => { - // Arrange const singleElement = { position: 'bla', - element: document.createElement('div') - } + element: document.createElement('div'), + }; const hvElements = []; for (let i = 0; i < testcase.nbOfElements; i++) { @@ -62,8 +57,8 @@ describe('ConnectorProvider', () => { const hvSettings = { enforceVerticalLayout: false, - elements: hvElements - } + elements: hvElements, + }; expect(() => { // Act @@ -78,25 +73,23 @@ describe('ConnectorProvider', () => { [ { description: 'should return no connectors', isHierarchical: false, expectedConnectors: 0 }, - { description: 'should return one connectors', isVertical: true, expectedConnectors: 1 } - ].forEach(testcase => { - + { description: 'should return one connectors', isVertical: true, expectedConnectors: 1 }, + ].forEach((testcase) => { it('getConnectors should create different connectors according to the layout ' + testcase.description, () => { - // Arrange const hvElements = []; for (let i = 0; i < 3; i++) { const singleElement = { position: i, - element: document.createElement('div' + i) - } + element: document.createElement('div' + i), + }; hvElements.push(singleElement); } const hvSettings = { enforceVerticalLayout: false, - elements: hvElements - } + elements: hvElements, + }; expect(() => { // Act @@ -114,10 +107,7 @@ describe('ConnectorProvider', () => { expect(conn.element1.tagName).toBe('DIV' + index); } }); - }).not.toThrowError(); }); }); - - }); diff --git a/imxweb/projects/qbm/src/lib/hyperview/connector-provider.ts b/imxweb/projects/qbm/src/lib/hyperview/connector-provider.ts index ed490f1ee..15bdccab8 100644 --- a/imxweb/projects/qbm/src/lib/hyperview/connector-provider.ts +++ b/imxweb/projects/qbm/src/lib/hyperview/connector-provider.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,24 +28,23 @@ import { HvSettings } from './hyperview-types'; import { Connector } from './connector'; export interface IConnectorProvider { - getConnectors(settings: HvSettings): Connector[]; + getConnectors(settings: HvSettings): Connector[]; } export class ConnectorProvider implements IConnectorProvider { + private hierarchical: boolean; - private hierarchical: boolean; + constructor(hierarchical: boolean) { + this.hierarchical = hierarchical; + } - constructor(hierarchical: boolean) { - this.hierarchical = hierarchical; - } - - public getConnectors(settings: HvSettings): Connector[] { - const es = settings.elements; - const res: Connector[] = []; - for (let i = 1; i < es.length; i++) { - const srcIndex = this.hierarchical ? 0 : i - 1; - res.push(new Connector(es[srcIndex].element, es[i].element)); - } - return res; + public getConnectors(settings: HvSettings): Connector[] { + const es = settings.elements; + const res: Connector[] = []; + for (let i = 1; i < es.length; i++) { + const srcIndex = this.hierarchical ? 0 : i - 1; + res.push(new Connector(es[srcIndex].element, es[i].element)); } + return res; + } } diff --git a/imxweb/projects/qbm/src/lib/hyperview/connector.spec.ts b/imxweb/projects/qbm/src/lib/hyperview/connector.spec.ts index aea562a6d..fa30150f9 100644 --- a/imxweb/projects/qbm/src/lib/hyperview/connector.spec.ts +++ b/imxweb/projects/qbm/src/lib/hyperview/connector.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,15 +28,13 @@ import { Connector } from './connector'; import { clearStylesFromDOM } from '../testing/clear-styles.spec'; describe('Connector', () => { - afterAll(() => { clearStylesFromDOM(); }); it('should create an visible connector', () => { - const element = document.createElement('div'); - expect( () => { + expect(() => { const connector = new Connector(element, element); expect(connector.isHidden).toBeFalsy(); }).not.toThrowError(); diff --git a/imxweb/projects/qbm/src/lib/hyperview/connector.ts b/imxweb/projects/qbm/src/lib/hyperview/connector.ts index 3c2e68833..151c3814f 100644 --- a/imxweb/projects/qbm/src/lib/hyperview/connector.ts +++ b/imxweb/projects/qbm/src/lib/hyperview/connector.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,10 +28,13 @@ * The connector class */ export class Connector { - public isHidden: boolean; - public data: string; + public isHidden: boolean; + public data: string; - constructor(public readonly element1: HTMLElement, public readonly element2: HTMLElement) { - this.isHidden = false; - } + constructor( + public readonly element1: HTMLElement, + public readonly element2: HTMLElement, + ) { + this.isHidden = false; + } } diff --git a/imxweb/projects/qbm/src/lib/hyperview/connectors.spec.ts b/imxweb/projects/qbm/src/lib/hyperview/connectors.spec.ts index 6a1a4b340..a72e30222 100644 --- a/imxweb/projects/qbm/src/lib/hyperview/connectors.spec.ts +++ b/imxweb/projects/qbm/src/lib/hyperview/connectors.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,42 +24,23 @@ * */ -import * as TypeMoq from 'typemoq'; - -import { Connectors } from './connectors'; -import { Connector } from './connector'; import { clearStylesFromDOM } from '../testing/clear-styles.spec'; +import { Connector } from './connector'; +import { Connectors } from './connectors'; describe('Connectors', () => { - afterAll(() => { clearStylesFromDOM(); }); it('should create connectors in each direction', () => { + const elem1 = { offsetLeft: 10, offsetWidth: 0, offsetTop: 0, offsetHeight: 0 }; + const elem2 = { offsetLeft: 50, offsetWidth: 0, offsetTop: 50, offsetHeight: 0 }; + const elem3 = { offsetLeft: 75, offsetWidth: 0, offsetTop: 25, offsetHeight: 0 }; - const elem1 = TypeMoq.Mock.ofType(); - elem1.setup(e => e.offsetLeft).returns(() => 10); - elem1.setup(e => e.offsetWidth).returns(() => 0); - elem1.setup(e => e.offsetTop).returns(() => 10); - elem1.setup(e => e.offsetHeight).returns(() => 0); - - const elem2 = TypeMoq.Mock.ofType(); - elem2.setup(e => e.offsetLeft).returns(() => 50); - elem2.setup(e => e.offsetWidth).returns(() => 0); - elem2.setup(e => e.offsetTop).returns(() => 50); - elem2.setup(e => e.offsetHeight).returns(() => 0); - - const elem3 = TypeMoq.Mock.ofType(); - elem3.setup(e => e.offsetLeft).returns(() => 75); - elem3.setup(e => e.offsetWidth).returns(() => 0); - elem3.setup(e => e.offsetTop).returns(() => 25); - elem3.setup(e => e.offsetHeight).returns(() => 0); - - - const html1 = elem1.object; - const html2 = elem2.object; - const html3 = elem3.object; + const html1 = elem1 as unknown as HTMLElement; + const html2 = elem2 as unknown as HTMLElement; + const html3 = elem3 as unknown as HTMLElement; const conn1 = new Connector(html1, html2); const conn2 = new Connector(html1, html1); @@ -75,5 +56,4 @@ describe('Connectors', () => { expect(connectors.maxValue.Y).toBe(50); }).not.toThrowError(); }); - }); diff --git a/imxweb/projects/qbm/src/lib/hyperview/connectors.ts b/imxweb/projects/qbm/src/lib/hyperview/connectors.ts index 95c4a857f..615effa1e 100644 --- a/imxweb/projects/qbm/src/lib/hyperview/connectors.ts +++ b/imxweb/projects/qbm/src/lib/hyperview/connectors.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -35,13 +35,11 @@ interface Coord { * Class managing the connectors for a hyperview control. */ export class Connectors { - public readonly connectorList: Connector[]; public readonly maxValue = { X: 0, Y: 0 }; constructor(connectors: Connector[]) { - // get connectors from xml this.connectorList = connectors; @@ -52,7 +50,6 @@ export class Connectors { * Repaints all connectors. */ private repaintAllConnectors(): void { - // reset maxValue this.maxValue.X = 0; this.maxValue.Y = 0; @@ -66,7 +63,6 @@ export class Connectors { * Draws a connector between two shapes. */ private drawConnector(connector: Connector): void { - // get centerpoints of both shapes const p1 = this.centerPoint(connector.element1); const p2 = this.centerPoint(connector.element2); @@ -82,10 +78,10 @@ export class Connectors { m = deltaY / deltaX; } if (m < 0) { - middle = (p1.X + (deltaX * 0.3)); + middle = p1.X + deltaX * 0.3; curveCoords += middle + ' ' + p1.Y + ' ' + middle + ' ' + p2.Y; } else { - middle = (p1.Y + (deltaY * 0.3)); + middle = p1.Y + deltaY * 0.3; curveCoords += p1.X + ' ' + middle + ' ' + p2.X + ' ' + middle; } curveCoords += ' ' + p2.X + ' ' + p2.Y; @@ -102,8 +98,8 @@ export class Connectors { */ private centerPoint(element: HTMLElement): Coord { return { - X: Math.round(element.offsetLeft + (element.offsetWidth / 2)), - Y: Math.round(element.offsetTop + (element.offsetHeight / 2)) + X: Math.round(element.offsetLeft + element.offsetWidth / 2), + Y: Math.round(element.offsetTop + element.offsetHeight / 2), }; } } diff --git a/imxweb/projects/qbm/src/lib/hyperview/hyperview-layout-hierarchical.ts b/imxweb/projects/qbm/src/lib/hyperview/hyperview-layout-hierarchical.ts index 41025c81c..591467fab 100644 --- a/imxweb/projects/qbm/src/lib/hyperview/hyperview-layout-hierarchical.ts +++ b/imxweb/projects/qbm/src/lib/hyperview/hyperview-layout-hierarchical.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,6 @@ * */ - import { IConnectorProvider, ConnectorProvider } from './connector-provider'; import { toPixelString, HvElement, HvCell, Size, HyperViewLayout, LayoutResult } from './hyperview-types'; import { ClassloggerService } from '../classlogger/classlogger.service'; @@ -33,7 +32,6 @@ import { ClassloggerService } from '../classlogger/classlogger.service'; * Hyperview layouter that arranges the elements hierarchical. */ export class HyperviewLayoutHierarchical implements HyperViewLayout { - /** * Returns a list of all possible positions for a shape in a hyperview. */ @@ -46,21 +44,21 @@ export class HyperviewLayoutHierarchical implements HyperViewLayout { 'MiddleRight', 'BottomLeft', 'BottomCenter', - 'BottomRight' + 'BottomRight', ]; // waiting for https://github.com/Microsoft/TypeScript/issues/13042 private vLayoutElements: { - [id: string /* should be keyof Positions*/]: HvCell + [id: string /* should be keyof Positions*/]: HvCell; }; constructor( private readonly elements: HvElement[], - private logger: ClassloggerService + private logger: ClassloggerService, ) { this.elements = elements; const elems = this.elements; - const centerElement = elems.findIndex(e => e.position === 'MiddleCenter'); + const centerElement = elems.findIndex((e) => e.position === 'MiddleCenter'); if (centerElement === -1) { throw new Error('A shape with MiddleCenter position is required for hierarchical layout.'); } else if (centerElement !== 0) { @@ -70,7 +68,6 @@ export class HyperviewLayoutHierarchical implements HyperViewLayout { elems[centerElement] = elems[0]; elems[0] = swap; } - } /** @@ -108,9 +105,8 @@ export class HyperviewLayoutHierarchical implements HyperViewLayout { const finalSize = this.getSumSizeOfElements(this.elements); return { - size: finalSize + size: finalSize, }; - } /** @@ -120,14 +116,17 @@ export class HyperviewLayoutHierarchical implements HyperViewLayout { return new ConnectorProvider(true /* hierarchical layout */); } - private layoutTopShapes(sCenter: Size, elems: { [id: string]: HvCell; }): Size { + private layoutTopShapes(sCenter: Size, elems: { [id: string]: HvCell }): Size { // layout top left shapes sCenter = this.getMaxSize(elems['MiddleCenter'].size, elems['TopCenter'].size.width, elems['MiddleLeft'].size.height); this.layoutElements(elems['TopLeft'], sCenter, -2, -1, false, false); // layout top center shapes - sCenter = this.getMaxSize(elems['MiddleCenter'].size, elems['TopCenter'].size.width, - Math.max(elems['MiddleLeft'].size.height, elems['MiddleRight'].size.height)); + sCenter = this.getMaxSize( + elems['MiddleCenter'].size, + elems['TopCenter'].size.width, + Math.max(elems['MiddleLeft'].size.height, elems['MiddleRight'].size.height), + ); this.layoutElements(elems['TopCenter'], sCenter, 0, -1, true, false); // layout top right shapes @@ -136,14 +135,17 @@ export class HyperviewLayoutHierarchical implements HyperViewLayout { return sCenter; } - private layoutBottomShapes(sCenter: Size, elems: { [id: string]: HvCell; }): Size { + private layoutBottomShapes(sCenter: Size, elems: { [id: string]: HvCell }): Size { // layout bottom left shapes sCenter = this.getMaxSize(elems['MiddleCenter'].size, elems['BottomCenter'].size.width, elems['MiddleLeft'].size.height); this.layoutElements(elems['BottomLeft'], sCenter, -2, 1, false, false); // layout bottom center shapes - sCenter = this.getMaxSize(elems['MiddleCenter'].size, elems['BottomCenter'].size.width, - Math.max(elems['MiddleLeft'].size.height, elems['MiddleRight'].size.height)); + sCenter = this.getMaxSize( + elems['MiddleCenter'].size, + elems['BottomCenter'].size.width, + Math.max(elems['MiddleLeft'].size.height, elems['MiddleRight'].size.height), + ); this.layoutElements(elems['BottomCenter'], sCenter, 0, 1, true, false); // layout bottom right shapes @@ -152,7 +154,7 @@ export class HyperviewLayoutHierarchical implements HyperViewLayout { return sCenter; } - private layoutMiddleShapes(sCenter: Size, elems: { [id: string]: HvCell; }): Size { + private layoutMiddleShapes(sCenter: Size, elems: { [id: string]: HvCell }): Size { // layout middle left shapes sCenter = this.getMaxSize(elems['MiddleCenter'].size, 0, elems['MiddleLeft'].size.height); this.layoutElements(elems['MiddleLeft'], sCenter, -1, 0, false, true); @@ -168,7 +170,7 @@ export class HyperviewLayoutHierarchical implements HyperViewLayout { private clearVLayoutElements(): void { this.vLayoutElements = {}; - this.positions.forEach(p => { + this.positions.forEach((p) => { this.vLayoutElements[p] = { elements: [], size: { width: 0, height: 0 } }; }); } @@ -177,7 +179,6 @@ export class HyperviewLayoutHierarchical implements HyperViewLayout { * Normalizes the view by moving all components into the visible area. */ private normalize(): void { - // the control should now be placed in the edge const clientcenter = { X: 0, Y: 0 }; @@ -195,15 +196,15 @@ export class HyperviewLayoutHierarchical implements HyperViewLayout { miny = -miny; minx = Math.max(minx, clientcenter.X); - miny = Math.max(miny, clientcenter.Y * 3 / 5); + miny = Math.max(miny, (clientcenter.Y * 3) / 5); // move elements this.elements.forEach((node, index) => { const oldLeft = node.element.style.left; const oldTop = node.element.style.top; - const left = toPixelString((node.element.offsetLeft + minx)); - const top = toPixelString((node.element.offsetTop + miny)); + const left = toPixelString(node.element.offsetLeft + minx); + const top = toPixelString(node.element.offsetTop + miny); node.element.style.left = left; node.element.style.top = top; @@ -230,7 +231,7 @@ export class HyperviewLayoutHierarchical implements HyperViewLayout { * @param bY taken from the class */ private layoutElements(regElements: HvCell, centerSize: Size, dx: number, dy: number, bX: boolean, bY: boolean): void { - const padding = 50; + const padding = 20; const regSize = regElements.size; let cX = 0; let cY = 0; @@ -260,8 +261,8 @@ export class HyperviewLayoutHierarchical implements HyperViewLayout { cY -= regElement.element.offsetHeight + padding; } - regElement.element.style.left = toPixelString((cX - (bX ? regSize.width / 2 : 0))); - regElement.element.style.top = toPixelString((cY - (bY ? regSize.height / 2 : 0))); + regElement.element.style.left = toPixelString(cX - (bX ? regSize.width / 2 : 0)); + regElement.element.style.top = toPixelString(cY - (bY ? regSize.height / 2 : 0)); // AutoReset in Outer regions if (Math.abs(dx) > 1) { @@ -274,12 +275,14 @@ export class HyperviewLayoutHierarchical implements HyperViewLayout { cY = regElement.element.offsetTop; } + // translate the following shapes by this shape's width if (dx > 0) { - cX += regSize.width + padding; + cX += regElement.element.offsetWidth + padding; } + // translate the following shapes by this shape's height if (dy > 0) { - cY += regSize.height + padding; + cY += regElement.element.offsetHeight + padding; } } } @@ -290,14 +293,14 @@ export class HyperviewLayoutHierarchical implements HyperViewLayout { private getMaxSizeOfElements(vElements: HvElement[]): Size { let layoutSize: Size = { width: 0, - height: 0 + height: 0, }; for (const node of vElements) { const shape = node.element; layoutSize = { width: Math.max(layoutSize.width, shape.offsetWidth), - height: Math.max(layoutSize.height, shape.offsetHeight) + height: Math.max(layoutSize.height, shape.offsetHeight), }; } @@ -307,14 +310,14 @@ export class HyperviewLayoutHierarchical implements HyperViewLayout { private getSumSizeOfElements(vElements: HvElement[]): Size { let layoutSize: Size = { width: 0, - height: 0 + height: 0, }; for (const node of vElements) { const shape = node.element; layoutSize = { width: Math.max(layoutSize.width, shape.offsetLeft + shape.offsetWidth), - height: Math.max(layoutSize.height, shape.offsetTop + shape.offsetHeight) + height: Math.max(layoutSize.height, shape.offsetTop + shape.offsetHeight), }; } diff --git a/imxweb/projects/qbm/src/lib/hyperview/hyperview-layout-horizontal.spec.ts b/imxweb/projects/qbm/src/lib/hyperview/hyperview-layout-horizontal.spec.ts index 005ca79a6..02b58504e 100644 --- a/imxweb/projects/qbm/src/lib/hyperview/hyperview-layout-horizontal.spec.ts +++ b/imxweb/projects/qbm/src/lib/hyperview/hyperview-layout-horizontal.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,7 +28,6 @@ import { HyperviewLayoutHorizontal } from './hyperview-layout-horizontal'; import { clearStylesFromDOM } from '../testing/clear-styles.spec'; describe('HyperviewLayoutHorizontal', () => { - const htmlElement = document.createElement('div'); afterAll(() => { @@ -39,8 +38,8 @@ describe('HyperviewLayoutHorizontal', () => { const elements = []; const middlecenter = { position: 'MiddleCenter', - element: htmlElement - } + element: htmlElement, + }; elements.push(middlecenter); expect(() => { diff --git a/imxweb/projects/qbm/src/lib/hyperview/hyperview-layout-horizontal.ts b/imxweb/projects/qbm/src/lib/hyperview/hyperview-layout-horizontal.ts index ccd5de94b..c0181aa90 100644 --- a/imxweb/projects/qbm/src/lib/hyperview/hyperview-layout-horizontal.ts +++ b/imxweb/projects/qbm/src/lib/hyperview/hyperview-layout-horizontal.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,14 +24,13 @@ * */ -import { IConnectorProvider, ConnectorProvider } from './connector-provider'; +import { ConnectorProvider, IConnectorProvider } from './connector-provider'; import { HvElement, HyperViewLayout, LayoutResult, toPixelString } from './hyperview-types'; /** * Hyperview layouter that arranges the elements in a horizontal line. */ export class HyperviewLayoutHorizontal implements HyperViewLayout { - private elements: HvElement[]; constructor(elements: HvElement[]) { @@ -55,7 +54,7 @@ export class HyperviewLayoutHorizontal implements HyperViewLayout { if (index > 0) { const previousElement = es[index - 1].element; - element.style.left = toPixelString((previousElement.offsetLeft + previousElement.offsetWidth + 10)); + element.style.left = toPixelString(previousElement.offsetLeft + previousElement.offsetWidth + 10); element.style.zIndex = '100'; } }); @@ -63,15 +62,16 @@ export class HyperviewLayoutHorizontal implements HyperViewLayout { // calculate the maximum height const maxw = this.getMaxHeight(); - firstElement.style.top = toPixelString(((maxw - firstElement.offsetHeight) / 2)); + firstElement.style.top = toPixelString((maxw - firstElement.offsetHeight) / 2); es.slice(1).forEach((node) => { const element = node.element; - element.style.top = toPixelString(((maxw - element.offsetHeight) / 2)); + element.style.top = toPixelString((maxw - element.offsetHeight) / 2); }); return { size: { width: 0, height: maxw } }; } + return { size: { width: 0, height: 0 } }; } /** diff --git a/imxweb/projects/qbm/src/lib/hyperview/hyperview-layout-vertical.spec.ts b/imxweb/projects/qbm/src/lib/hyperview/hyperview-layout-vertical.spec.ts index 145531e72..539e1404e 100644 --- a/imxweb/projects/qbm/src/lib/hyperview/hyperview-layout-vertical.spec.ts +++ b/imxweb/projects/qbm/src/lib/hyperview/hyperview-layout-vertical.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,12 +24,10 @@ * */ - import { HyperviewLayoutVertical } from './hyperview-layout-vertical'; import { clearStylesFromDOM } from '../testing/clear-styles.spec'; describe('HyperviewLayoutVertical', () => { - afterAll(() => { clearStylesFromDOM(); }); @@ -40,8 +38,8 @@ describe('HyperviewLayoutVertical', () => { const elements = []; const middlecenter = { position: 'MiddleCenter', - element: htmlElement - } + element: htmlElement, + }; elements.push(middlecenter); expect(() => { @@ -62,4 +60,3 @@ describe('HyperviewLayoutVertical', () => { }).not.toThrowError(); }); }); - diff --git a/imxweb/projects/qbm/src/lib/hyperview/hyperview-layout-vertical.ts b/imxweb/projects/qbm/src/lib/hyperview/hyperview-layout-vertical.ts index 700cd681f..c0510739e 100644 --- a/imxweb/projects/qbm/src/lib/hyperview/hyperview-layout-vertical.ts +++ b/imxweb/projects/qbm/src/lib/hyperview/hyperview-layout-vertical.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,14 +24,13 @@ * */ -import { HyperViewLayout, toPixelString, HvElement, LayoutResult } from './hyperview-types'; -import { IConnectorProvider, ConnectorProvider } from './connector-provider'; +import { ConnectorProvider, IConnectorProvider } from './connector-provider'; +import { HvElement, HyperViewLayout, LayoutResult, toPixelString } from './hyperview-types'; /** * Layouter that aligns the elements vertically, with the root node at the top. */ export class HyperviewLayoutVertical implements HyperViewLayout { - private elements: HvElement[]; constructor(elements: HvElement[]) { @@ -44,19 +43,16 @@ export class HyperviewLayoutVertical implements HyperViewLayout { public layout(): LayoutResult { const es = this.elements; if (es.length > 0) { - // get the maximum width const maxw = this.getMaxWidth(); es.forEach((node, index) => { const element = node.element; element.style.position = 'absolute'; - element.style.left = toPixelString(((maxw - element.offsetWidth) / 2)); + element.style.left = toPixelString((maxw - element.offsetWidth) / 2); if (index > 0) { const previousElement = es[index - 1].element; - element.style.top = toPixelString((previousElement.offsetTop + - previousElement.offsetHeight + - 10)); + element.style.top = toPixelString(previousElement.offsetTop + previousElement.offsetHeight + 10); } else { element.style.top = '0px'; } @@ -64,6 +60,7 @@ export class HyperviewLayoutVertical implements HyperViewLayout { return { size: { width: maxw, height: 0 } }; } + return { size: { width: 0, height: 0 } }; } /** diff --git a/imxweb/projects/qbm/src/lib/hyperview/hyperview-types.ts b/imxweb/projects/qbm/src/lib/hyperview/hyperview-types.ts index f4f136e8d..f59b70f32 100644 --- a/imxweb/projects/qbm/src/lib/hyperview/hyperview-types.ts +++ b/imxweb/projects/qbm/src/lib/hyperview/hyperview-types.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qbm/src/lib/hyperview/hyperview.component.html b/imxweb/projects/qbm/src/lib/hyperview/hyperview.component.html index e7c3d3db8..64cc94bdb 100644 --- a/imxweb/projects/qbm/src/lib/hyperview/hyperview.component.html +++ b/imxweb/projects/qbm/src/lib/hyperview/hyperview.component.html @@ -1,4 +1,12 @@ -
    +
    1. - - + + + +
    2. @@ -26,34 +41,31 @@
      diff --git a/imxweb/projects/qbm/src/lib/hyperview/hyperview.component.scss b/imxweb/projects/qbm/src/lib/hyperview/hyperview.component.scss index c0aa4469f..21dc4ea6a 100644 --- a/imxweb/projects/qbm/src/lib/hyperview/hyperview.component.scss +++ b/imxweb/projects/qbm/src/lib/hyperview/hyperview.component.scss @@ -12,9 +12,9 @@ height: calc(100% - 74px); overflow: hidden; position: relative; - transform-origin: center center; margin: 5px; - .imx-hyperview-container{ + transform-origin: center center; + .imx-hyperview-container { position: absolute; transform-origin: top left; > ol { @@ -90,12 +90,11 @@ path { stroke-linejoin: miter; stroke-miterlimit: 4; stroke-linecap: butt; - opacity: 1.0; + opacity: 1; } //theming -:host{ - +:host { .imx-hyperview > ol { background-color: $color-gray-80; } @@ -125,11 +124,10 @@ path { } } - .eui-dark-theme { - :host{ + :host { .connector { - stroke: $color-gray-30 + stroke: $color-gray-30; } .imx-hyperview > ol { @@ -140,9 +138,6 @@ path { .hyperviewelement:hover { box-shadow: 0 0 5px 3px rgba($color: $color-gray-0, $alpha: 0.7); } - button{ - background: $color-gray-70; - } .imx-hyperview-toolbar { background-color: $color-gray-70; } @@ -150,9 +145,9 @@ path { } .eui-contrast-theme { - :host{ + :host { .connector { - stroke: $color-gray-10 + stroke: $color-gray-10; } .imx-hyperview > ol { diff --git a/imxweb/projects/qbm/src/lib/hyperview/hyperview.component.ts b/imxweb/projects/qbm/src/lib/hyperview/hyperview.component.ts index 9fd8bb173..994af36f3 100644 --- a/imxweb/projects/qbm/src/lib/hyperview/hyperview.component.ts +++ b/imxweb/projects/qbm/src/lib/hyperview/hyperview.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,39 +25,39 @@ */ import { + AfterViewChecked, + AfterViewInit, + ChangeDetectorRef, Component, - ViewChild, ElementRef, - Input, - ChangeDetectorRef, EventEmitter, - Renderer2, - AfterViewInit, + Input, OnDestroy, - AfterViewChecked, + Output, QueryList, + Renderer2, + ViewChild, ViewChildren, - Output, } from '@angular/core'; -import { Connectors } from './connectors'; +import { ShapeData } from '@imx-modules/imx-api-qbm'; +import { Subscription } from 'rxjs'; +import { ClassloggerService } from '../classlogger/classlogger.service'; import { Connector } from './connector'; +import { Connectors } from './connectors'; import { HyperviewLayoutHierarchical } from './hyperview-layout-hierarchical'; -import { HyperviewLayoutVertical } from './hyperview-layout-vertical'; import { HyperviewLayoutHorizontal } from './hyperview-layout-horizontal'; +import { HyperviewLayoutVertical } from './hyperview-layout-vertical'; import { - HyperViewLayout, - HvSettings, HvElement, - ShapeClickArgs, - toPixelString, - LayoutResult, + HvSettings, + HyperViewLayout, HyperViewNavigation, HyperViewNavigationEnum, + LayoutResult, + ShapeClickArgs, + toPixelString, } from './hyperview-types'; -import { ShapeData } from 'imx-api-qbm'; -import { ClassloggerService } from '../classlogger/classlogger.service'; -import { Subscription } from 'rxjs'; export enum ShapeType { ListShape, @@ -115,11 +115,14 @@ export class HyperviewComponent implements AfterViewInit, OnDestroy, AfterViewCh /** * Creates a new hyperview component. */ - constructor(private changeDetectorRef: ChangeDetectorRef, private logger: ClassloggerService, private renderer: Renderer2) {} + constructor( + private changeDetectorRef: ChangeDetectorRef, + private logger: ClassloggerService, + private renderer: Renderer2, + ) {} ngAfterViewInit(): void { this.setupLayout(); - // register for element resize const observer = new ResizeObserver(() => { this.logger.trace(this, 'resize event detected, marking layout as dirty'); @@ -133,7 +136,7 @@ export class HyperviewComponent implements AfterViewInit, OnDestroy, AfterViewCh this.shapeList.changes.subscribe(() => { this.logger.trace(this, 'shape list changed, marking layout as dirty'); this.layoutDirty = true; - }) + }), ); } @@ -188,7 +191,7 @@ export class HyperviewComponent implements AfterViewInit, OnDestroy, AfterViewCh const layouter = this.buildLayouter(); const layoutResult = layouter.layout(); this.connectors = new Connectors(layouter.getConnectorProvider().getConnectors(this.settings)).connectorList.filter( - (connector) => !connector.isHidden + (connector) => !connector.isHidden, ); this.scrollToMiddleCenterShape(layoutResult); this.changeDetectorRef.detectChanges(); @@ -221,7 +224,7 @@ export class HyperviewComponent implements AfterViewInit, OnDestroy, AfterViewCh public get selectedHyperviewCaption(): string { const centerObj = this.shapes.filter((shape) => shape.LayoutType === 'MiddleCenter')?.[0]; - return !!centerObj?.HeaderText ? `${centerObj.HeaderText}: ${centerObj.Caption}` : centerObj?.Caption; + return !!centerObj?.HeaderText ? `${centerObj.HeaderText}: ${centerObj.Caption}` : centerObj?.Caption ?? ''; } private scrollToMiddleCenterShape(layoutResult: LayoutResult) { @@ -279,7 +282,9 @@ export class HyperviewComponent implements AfterViewInit, OnDestroy, AfterViewCh this.middleCenterShape = { position: this.middleCenterPosition, element }; res.push(this.middleCenterShape); } else { - res.push({ position, element }); + if (position) { + res.push({ position, element }); + } } } diff --git a/imxweb/projects/qbm/src/lib/hyperview/hyperview.module.ts b/imxweb/projects/qbm/src/lib/hyperview/hyperview.module.ts index 4087b0d08..06a37a6f3 100644 --- a/imxweb/projects/qbm/src/lib/hyperview/hyperview.module.ts +++ b/imxweb/projects/qbm/src/lib/hyperview/hyperview.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,7 +28,7 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { MatBadgeModule } from '@angular/material/badge'; import { MatTooltipModule } from '@angular/material/tooltip'; -import { EuiCoreModule } from '@elemental-ui/core'; +import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; import { HyperviewComponent } from './hyperview.component'; import { PropertyShapeComponent } from './propertyshape.component'; @@ -36,29 +36,12 @@ import { ListShapeComponent } from './listshape.component'; import { SimpleShapeComponent } from './simpleshape.component'; import { ShapeComponent } from './shape.component'; import { ZoomPanDirective } from './zoom-pan.directive'; -import { MatIconModule } from '@angular/material/icon'; import { TranslateModule } from '@ngx-translate/core'; import { MatButtonModule } from '@angular/material/button'; @NgModule({ - declarations: [ - HyperviewComponent, - PropertyShapeComponent, - ListShapeComponent, - SimpleShapeComponent, - ShapeComponent, - ZoomPanDirective - ], - imports: [ - CommonModule, - EuiCoreModule, - MatTooltipModule, - MatBadgeModule, - TranslateModule, - MatButtonModule, - ], - exports: [ - HyperviewComponent - ], + declarations: [HyperviewComponent, PropertyShapeComponent, ListShapeComponent, SimpleShapeComponent, ShapeComponent, ZoomPanDirective], + imports: [CommonModule, EuiCoreModule, EuiMaterialModule, MatTooltipModule, MatBadgeModule, TranslateModule, MatButtonModule], + exports: [HyperviewComponent], }) export class HyperViewModule { } diff --git a/imxweb/projects/qbm/src/lib/hyperview/listshape.component.scss b/imxweb/projects/qbm/src/lib/hyperview/listshape.component.scss index d91472870..a8f19e825 100644 --- a/imxweb/projects/qbm/src/lib/hyperview/listshape.component.scss +++ b/imxweb/projects/qbm/src/lib/hyperview/listshape.component.scss @@ -14,7 +14,8 @@ a { padding-left: 1em; } -a:hover, a:focus { +a:hover, +a:focus { text-decoration: none; cursor: pointer; } @@ -22,11 +23,17 @@ a:hover, a:focus { // Theming li { - > a,a:active,a:focus,a:hover,a:link,visited { + > a, + a:active, + a:focus, + a:hover, + a:link, + visited { color: $color-gray-0; } } -a:hover, a:focus { +a:hover, +a:focus { background-color: rgba($color: $color-gray-100, $alpha: 0.2); } diff --git a/imxweb/projects/qbm/src/lib/hyperview/listshape.component.ts b/imxweb/projects/qbm/src/lib/hyperview/listshape.component.ts index 87dc77809..b09f63ad1 100644 --- a/imxweb/projects/qbm/src/lib/hyperview/listshape.component.ts +++ b/imxweb/projects/qbm/src/lib/hyperview/listshape.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,7 +26,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { ShapeData, ShapeListEntry } from 'imx-api-qbm'; +import { ShapeData, ShapeListEntry } from '@imx-modules/imx-api-qbm'; import { ShapeClickArgs } from './hyperview-types'; /** @@ -39,6 +39,7 @@ import { ShapeClickArgs } from './hyperview-types'; }) export class ListShapeComponent { @Input() public shape: ShapeData; + @Input() public selected: EventEmitter = new EventEmitter(); @Output() public changeShapeSize = new EventEmitter(); @@ -51,7 +52,7 @@ export class ListShapeComponent { */ public click(elem: ShapeListEntry): void { if (this.isLinkEnabled(elem)) { - this.selected.emit({ objectKey: elem.ObjectKey, caption: this.shape.Caption }); + this.selected.emit({ objectKey: elem.ObjectKey ?? '', caption: this.shape.Caption ?? '' }); } } diff --git a/imxweb/projects/qbm/src/lib/hyperview/propertyshape.component.html b/imxweb/projects/qbm/src/lib/hyperview/propertyshape.component.html index 8ce8b553a..4e1c161af 100644 --- a/imxweb/projects/qbm/src/lib/hyperview/propertyshape.component.html +++ b/imxweb/projects/qbm/src/lib/hyperview/propertyshape.component.html @@ -1,7 +1,15 @@
      -
      +
      {{ property.Property }}
      diff --git a/imxweb/projects/qbm/src/lib/hyperview/propertyshape.component.scss b/imxweb/projects/qbm/src/lib/hyperview/propertyshape.component.scss index 4e54925b8..92433e221 100644 --- a/imxweb/projects/qbm/src/lib/hyperview/propertyshape.component.scss +++ b/imxweb/projects/qbm/src/lib/hyperview/propertyshape.component.scss @@ -1,4 +1,5 @@ -@import "variables.scss"; +@import 'base/variables'; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; table.LayoutpPropItems { cursor: pointer; @@ -7,17 +8,17 @@ table.LayoutpPropItems { .LayoutpPropRow { display: flex; - min-height: $propcellminheight; + min-height: $IMX_PropCell_MinHeight; } .LayoutpPropCol { - min-height: $propcellminheight; + min-height: $IMX_PropCell_MinHeight; padding: 2px 4px 3px 0; vertical-align: top; } .LayoutpPropCol:nth-child(1) { - width: $propertylabelwidth; + width: $IMX_PropertyLabel_Width; } .LayoutpPropCol0 { @@ -42,7 +43,15 @@ table.LayoutpPropItems { display: table-cell; } +.imx-hyperview-link { + &:focus, + &:hover { + cursor: pointer; + background-color: rgba($color: $color-gray-100, $alpha: 0.2); + } +} + .endsSection { border-bottom: solid 1px lightgray; - margin-bottom: .5em; -} \ No newline at end of file + margin-bottom: 0.5em; +} diff --git a/imxweb/projects/qbm/src/lib/hyperview/propertyshape.component.ts b/imxweb/projects/qbm/src/lib/hyperview/propertyshape.component.ts index ccbb59c85..4f17ea55f 100644 --- a/imxweb/projects/qbm/src/lib/hyperview/propertyshape.component.ts +++ b/imxweb/projects/qbm/src/lib/hyperview/propertyshape.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,11 +24,11 @@ * */ -import { Component, Input, EventEmitter } from '@angular/core'; +import { Component, EventEmitter, Input } from '@angular/core'; -import { ShapeData } from 'imx-api-qbm'; +import { ShapeData, ShapeProperty } from '@imx-modules/imx-api-qbm'; +import { EntityColumnData } from '@imx-modules/imx-qbm-dbts'; import { ShapeClickArgs } from './hyperview-types'; -import { EntityColumnData } from 'imx-qbm-dbts'; /** * A shape component that lists all {@link ShapeProperties|properties} of an object. @@ -43,7 +43,21 @@ export class PropertyShapeComponent { @Input() public selected: EventEmitter = new EventEmitter(); - public GetPropertyDisplayValue(property: EntityColumnData): string { - return property.DisplayValue != null ? property.DisplayValue : property.Value; + public GetPropertyDisplayValue(property: EntityColumnData | undefined): string { + return property?.DisplayValue != null ? property.DisplayValue : property?.Value ?? ''; + } + + /** + * Emit selection event for this {@link ShapeProperty|element}. + * @param shape the element the user clicked + */ + public onClick(shape: ShapeProperty): void { + if (this.isLinkEnabled() && !!shape.ObjectKey) { + this.selected.emit({ objectKey: shape.ObjectKey, caption: this.GetPropertyDisplayValue(shape.Value) }); + } + } + + public isLinkEnabled(): boolean { + return this.selected.observers.length > 0; } } diff --git a/imxweb/projects/qbm/src/lib/hyperview/shape.component.html b/imxweb/projects/qbm/src/lib/hyperview/shape.component.html index c8379ec0e..f3a5df149 100644 --- a/imxweb/projects/qbm/src/lib/hyperview/shape.component.html +++ b/imxweb/projects/qbm/src/lib/hyperview/shape.component.html @@ -1,11 +1,21 @@
      -

      +

      {{ shape.HeaderText }}
      -
      {{ shape.Caption }}
      -

      -
      +
      + {{ shape.Caption }} + ({{ ObjectCount }}) +
      +

    + -
    +
    diff --git a/imxweb/projects/qbm/src/lib/hyperview/shape.component.scss b/imxweb/projects/qbm/src/lib/hyperview/shape.component.scss index 522141435..c260ce63e 100644 --- a/imxweb/projects/qbm/src/lib/hyperview/shape.component.scss +++ b/imxweb/projects/qbm/src/lib/hyperview/shape.component.scss @@ -13,17 +13,16 @@ min-height: 32px; min-width: 32px; filter: grayscale(1) brightness(2); - -webkit-filter: grayscale(1) brightness(2); } .imx-shape-header-info { overflow: hidden; margin-right: 30px; - width:100%; + width: 100%; } .imx-hyperview-shape-content { - max-height: 300px; + max-height: 250px; overflow-x: hidden; overflow-y: auto; } @@ -31,11 +30,9 @@ .imx-hyperview-caption { text-overflow: ellipsis; overflow: hidden; - white-space: nowrap; font-weight: bold; - font-size: 14px; - text-align: left; - &--link{ + font-size: 16px; + &--link { cursor: pointer; } } @@ -49,9 +46,14 @@ font-weight: 400; } -// Theming +.objectcount { + font-style: italic; +} + +// theming -.imx-hyperview-caption--link:hover, .imx-hyperview-caption--link:focus { +.imx-hyperview-caption--link:hover, +.imx-hyperview-caption--link:focus { background-color: rgba($color: $color-gray-100, $alpha: 0.2); } diff --git a/imxweb/projects/qbm/src/lib/hyperview/shape.component.ts b/imxweb/projects/qbm/src/lib/hyperview/shape.component.ts index 85aefeb8e..271e41b9d 100644 --- a/imxweb/projects/qbm/src/lib/hyperview/shape.component.ts +++ b/imxweb/projects/qbm/src/lib/hyperview/shape.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,9 +24,9 @@ * */ -import { Component, EventEmitter, Input, OnInit } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { ShapeData } from 'imx-api-qbm'; +import { ShapeData } from '@imx-modules/imx-api-qbm'; import { ModelCssService } from '../model-css/model-css.service'; import { TableImageService } from '../table-image/table-image.service'; import { ShapeClickArgs } from './hyperview-types'; @@ -41,11 +41,16 @@ import { ShapeClickArgs } from './hyperview-types'; }) export class ShapeComponent implements OnInit { @Input() public shape: ShapeData; - @Input() public navigate = false; @Input() public selected: EventEmitter = new EventEmitter(); + @Input() public navigate = false; + @Output() public changeContentSize = new EventEmitter(); public imageClass: string; + public isExpanded = false; - constructor(private readonly imageService: TableImageService, private readonly modelCssService: ModelCssService) {} + constructor( + private readonly imageService: TableImageService, + private readonly modelCssService: ModelCssService, + ) {} public ngOnInit(): void { this.modelCssService.loadModelCss(); @@ -53,9 +58,14 @@ export class ShapeComponent implements OnInit { this.imageClass = this.shape.ImageUid ? this.imageService.getCss(this.shape.ImageUid, true) : this.imageService.getDefaultCss(true); } + public setExpandable(): void { + this.isExpanded = !this.isExpanded; + this.changeContentSize.emit(); + } + public click(): void { - if (this.navigate && !this.isShapeLayoutMiddle) { - this.selected?.emit({ objectKey: this.shape.ObjectKey, caption: this.shape.Caption }); + if (this.navigate && !this.isShapeLayoutMiddle && this.shape.ObjectKey) { + this.selected?.emit({ objectKey: this.shape.ObjectKey ?? '', caption: this.shape.Caption ?? '' }); } } @@ -63,6 +73,14 @@ export class ShapeComponent implements OnInit { return this.shape && this.shape.Elements ? this.shape.Elements.length : -1; } + public get canExpand() { + return this.ObjectCount > 10; // windows implementation also uses > 10 as threshold + } + public get showContent() { + if (!this.canExpand) return true; + return this.isExpanded; + } + public get isShapeLayoutMiddle(): boolean { return this.shape?.LayoutType === 'MiddleCenter'; } diff --git a/imxweb/projects/qbm/src/lib/hyperview/simpleshape.component.html b/imxweb/projects/qbm/src/lib/hyperview/simpleshape.component.html index 536487341..389aa4a00 100644 --- a/imxweb/projects/qbm/src/lib/hyperview/simpleshape.component.html +++ b/imxweb/projects/qbm/src/lib/hyperview/simpleshape.component.html @@ -1,3 +1,3 @@ -
    {{shape.Description}}
    -
    \ No newline at end of file +
    {{ shape.Description }}
    + diff --git a/imxweb/projects/qbm/src/lib/hyperview/simpleshape.component.ts b/imxweb/projects/qbm/src/lib/hyperview/simpleshape.component.ts index 5aa3190c4..fc6d41d23 100644 --- a/imxweb/projects/qbm/src/lib/hyperview/simpleshape.component.ts +++ b/imxweb/projects/qbm/src/lib/hyperview/simpleshape.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,14 +26,14 @@ import { Component, Input } from '@angular/core'; -import { ShapeData } from 'imx-api-qbm'; +import { ShapeData } from '@imx-modules/imx-api-qbm'; /** * A shape component that only display the description of an {@link ShapeData|shape}. */ @Component({ selector: 'imx-hyperview-simpleshape', - templateUrl: './simpleshape.component.html' + templateUrl: './simpleshape.component.html', }) export class SimpleShapeComponent { @Input() public shape: ShapeData; diff --git a/imxweb/projects/qbm/src/lib/hyperview/zoom-pan.directive.ts b/imxweb/projects/qbm/src/lib/hyperview/zoom-pan.directive.ts index 09dd22bbb..30dc1d23c 100644 --- a/imxweb/projects/qbm/src/lib/hyperview/zoom-pan.directive.ts +++ b/imxweb/projects/qbm/src/lib/hyperview/zoom-pan.directive.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -36,7 +36,10 @@ export class ZoomPanDirective { private relativePosition = { x: 0, y: 0 }; //mousemove should work only after the element is clicked private mouseDown = false; - constructor(private elementRef: ElementRef, private renderer: Renderer2) {} + constructor( + private elementRef: ElementRef, + private renderer: Renderer2, + ) {} @HostListener('wheel', ['$event']) private scaling(event: WheelEvent) { diff --git a/imxweb/projects/qbm/src/lib/icon-stack/icon-stack.component.html b/imxweb/projects/qbm/src/lib/icon-stack/icon-stack.component.html index ab894daf6..0a8dd8985 100644 --- a/imxweb/projects/qbm/src/lib/icon-stack/icon-stack.component.html +++ b/imxweb/projects/qbm/src/lib/icon-stack/icon-stack.component.html @@ -3,4 +3,4 @@
    -{{ text }} \ No newline at end of file +{{ text }} diff --git a/imxweb/projects/qbm/src/lib/icon-stack/icon-stack.component.scss b/imxweb/projects/qbm/src/lib/icon-stack/icon-stack.component.scss index 826946135..e8b95dcdd 100644 --- a/imxweb/projects/qbm/src/lib/icon-stack/icon-stack.component.scss +++ b/imxweb/projects/qbm/src/lib/icon-stack/icon-stack.component.scss @@ -1,5 +1,4 @@ -@import "variables.scss"; - +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; :host { display: flex; @@ -45,13 +44,13 @@ } .imx-iconStack eui-icon { - color: $black-6; + color: $color-gray-60; } .imx-ellipse { height: 13px; width: 90px; - background-color: $Imx_Ellipse_Gray; + background-color: $color-gray-10; border-radius: 50%; display: inline-block; } @@ -59,7 +58,7 @@ .imx-iconStack-text { width: 100%; font-size: 16px; - color: $black-6; + color: $color-gray-60; line-height: 4em; } } diff --git a/imxweb/projects/qbm/src/lib/icon-stack/icon-stack.component.ts b/imxweb/projects/qbm/src/lib/icon-stack/icon-stack.component.ts index 11bc8656a..3987d49ac 100644 --- a/imxweb/projects/qbm/src/lib/icon-stack/icon-stack.component.ts +++ b/imxweb/projects/qbm/src/lib/icon-stack/icon-stack.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,12 +29,10 @@ import { Component, Input } from '@angular/core'; @Component({ selector: 'imx-icon-stack', templateUrl: './icon-stack.component.html', - styleUrls: ['./icon-stack.component.scss'] + styleUrls: ['./icon-stack.component.scss'], }) -export class IconStackComponent { - +export class IconStackComponent { @Input() public icon1: string; @Input() public icon2: string; @Input() public text: string; - } diff --git a/imxweb/projects/qbm/src/lib/image/image-select/image-select.component.html b/imxweb/projects/qbm/src/lib/image/image-select/image-select.component.html index ba7290d2e..f4e07626f 100644 --- a/imxweb/projects/qbm/src/lib/image/image-select/image-select.component.html +++ b/imxweb/projects/qbm/src/lib/image/image-select/image-select.component.html @@ -1,33 +1,54 @@ - - {{ valueWrapper?.display | translate }} -
    - - -
    -
    - -
    - - -
    - - - + + {{ valueWrapper?.display ?? '' | translate }} + + +
    +
    + +
    + +
    + + +
    - {{ fileFormatHint || valueWrapper?.hint | translate }} + {{ (fileFormatHint || valueWrapper?.hint) ?? '' | translate }} {{ '#LDS#This field is mandatory.' | translate }} diff --git a/imxweb/projects/qbm/src/lib/image/image-select/image-select.component.scss b/imxweb/projects/qbm/src/lib/image/image-select/image-select.component.scss index 6f7e8cd45..c48c251b6 100644 --- a/imxweb/projects/qbm/src/lib/image/image-select/image-select.component.scss +++ b/imxweb/projects/qbm/src/lib/image/image-select/image-select.component.scss @@ -1,43 +1,3 @@ -:host { - ::ng-deep .mat-form-field-appearance-outline .mat-form-field-infix { - padding: 0; - margin-bottom: 10px; - } - - ::ng-deep .mat-form-field-infix input.mat-input-element { - margin-top: 0px; - } -} - -.mat-form-field { - width: 100%; -} - -.imx-form-input-container { - display: flex; - margin-top: 5px; - justify-content: space-between; - - .imx-suffix-container { - display: flex; - flex-direction: row; - } -} - -.imx-button-add { - margin-top: -5px; -} - -.imx-buttons-change-remove { - display: flex; - flex-direction: column; - margin-bottom: 10px; - - > * :not(:last-child) { - margin-bottom: 5px; - } -} - img { max-width: 200px; } diff --git a/imxweb/projects/qbm/src/lib/image/image-select/image-select.component.ts b/imxweb/projects/qbm/src/lib/image/image-select/image-select.component.ts index 71c39eed9..98db81192 100644 --- a/imxweb/projects/qbm/src/lib/image/image-select/image-select.component.ts +++ b/imxweb/projects/qbm/src/lib/image/image-select/image-select.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { AbstractControl, FormControl } from '@angular/forms'; +import { FormControl } from '@angular/forms'; import { Base64ImageService } from '../../images/base64-image.service'; import { ValueWrapper } from '../../value-wrapper/value-wrapper'; @@ -33,11 +33,11 @@ import { ValueWrapper } from '../../value-wrapper/value-wrapper'; @Component({ selector: 'imx-image-select', templateUrl: './image-select.component.html', - styleUrls: ['./image-select.component.scss'] + styleUrls: ['./image-select.component.scss'], }) export class ImageSelectComponent { // TODO: Check Upgrade - @Input() public control: AbstractControl; + @Input() public control: FormControl; @Input() public valueWrapper: ValueWrapper; @Input() public fileFormatHint: string; @Input() public hideRemoveButton: boolean; @@ -46,5 +46,5 @@ export class ImageSelectComponent { @Output() public change = new EventEmitter(); @Output() public remove = new EventEmitter(); - constructor(public readonly imageProvider: Base64ImageService) { } + constructor(public readonly imageProvider: Base64ImageService) {} } diff --git a/imxweb/projects/qbm/src/lib/image/image-view/image-view.component.html b/imxweb/projects/qbm/src/lib/image/image-view/image-view.component.html index b69cb7e41..0075826c8 100644 --- a/imxweb/projects/qbm/src/lib/image/image-view/image-view.component.html +++ b/imxweb/projects/qbm/src/lib/image/image-view/image-view.component.html @@ -1,5 +1,5 @@
    - {{ valueWrapper?.display | translate }} - - {{ defaultValue | translate }} -
    \ No newline at end of file + {{ valueWrapper.display | translate }} + + {{ defaultValue | translate }} +
    diff --git a/imxweb/projects/qbm/src/lib/image/image-view/image-view.component.scss b/imxweb/projects/qbm/src/lib/image/image-view/image-view.component.scss index a194cb6b8..434d7fa69 100644 --- a/imxweb/projects/qbm/src/lib/image/image-view/image-view.component.scss +++ b/imxweb/projects/qbm/src/lib/image/image-view/image-view.component.scss @@ -1,7 +1,6 @@ @import '@elemental-ui/core/src/styles/_palette.scss'; .imx-readonly-view { - margin-bottom: 20px; span { display: block; word-wrap: break-word; diff --git a/imxweb/projects/qbm/src/lib/image/image-view/image-view.component.ts b/imxweb/projects/qbm/src/lib/image/image-view/image-view.component.ts index dcd921c38..fd7740154 100644 --- a/imxweb/projects/qbm/src/lib/image/image-view/image-view.component.ts +++ b/imxweb/projects/qbm/src/lib/image/image-view/image-view.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -32,11 +32,11 @@ import { ValueWrapper } from '../../value-wrapper/value-wrapper'; @Component({ selector: 'imx-image-view', templateUrl: './image-view.component.html', - styleUrls: ['./image-view.component.scss'] + styleUrls: ['./image-view.component.scss'], }) export class ImageViewComponent { @Input() public valueWrapper: ValueWrapper; @Input() public defaultValue: string; - constructor(public readonly imageProvider: Base64ImageService) { } + constructor(public readonly imageProvider: Base64ImageService) {} } diff --git a/imxweb/projects/qbm/src/lib/image/image.module.ts b/imxweb/projects/qbm/src/lib/image/image.module.ts index f28851f91..753644cbe 100644 --- a/imxweb/projects/qbm/src/lib/image/image.module.ts +++ b/imxweb/projects/qbm/src/lib/image/image.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -38,14 +38,8 @@ import { ImageSelectComponent } from './image-select/image-select.component'; import { ImageViewComponent } from './image-view/image-view.component'; @NgModule({ - declarations: [ - ImageSelectComponent, - ImageViewComponent - ], - exports: [ - ImageSelectComponent, - ImageViewComponent - ], + declarations: [ImageSelectComponent, ImageViewComponent], + exports: [ImageSelectComponent, ImageViewComponent], imports: [ CommonModule, EuiCoreModule, @@ -55,7 +49,7 @@ import { ImageViewComponent } from './image-view/image-view.component'; MatInputModule, MatProgressSpinnerModule, ReactiveFormsModule, - TranslateModule - ] + TranslateModule, + ], }) -export class ImageModule { } +export class ImageModule {} diff --git a/imxweb/projects/qbm/src/lib/images/base64-image.service.spec.ts b/imxweb/projects/qbm/src/lib/images/base64-image.service.spec.ts index efed1abe7..8c8a95620 100644 --- a/imxweb/projects/qbm/src/lib/images/base64-image.service.spec.ts +++ b/imxweb/projects/qbm/src/lib/images/base64-image.service.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,14 +25,11 @@ */ import { TestBed } from '@angular/core/testing'; -import * as TypeMoq from 'typemoq'; +import { IWriteValue } from '@imx-modules/imx-qbm-dbts'; import { Base64ImageService } from './base64-image.service'; -import { IWriteValue } from 'imx-qbm-dbts'; describe('Base64ImageService', () => { - - it('should be created', () => { const service: Base64ImageService = TestBed.get(Base64ImageService); expect(service).toBeTruthy(); @@ -42,30 +39,29 @@ describe('Base64ImageService', () => { { description: 'null image', image: null, expect: { hasImage: false, data: '' } }, { description: 'empty image', image: '', expect: { hasImage: false, data: '' } }, { description: 'image', image: '5642', expect: { hasImage: true, data: 'data:image/png;base64,5642' } }, - ].forEach(testcase => + ].forEach((testcase) => it(`can handle an image with ${testcase.description}`, () => { // Arrange const service: Base64ImageService = TestBed.get(Base64ImageService); - const writeValueMock = TypeMoq.Mock.ofType>(); - writeValueMock.setup(wm => wm.value).returns(() => testcase.image); - + const writeValueMock = { value: testcase.image } as IWriteValue; // Act & Assert - expect(service.getImageUrl(writeValueMock.object)).toBe(testcase.expect.data); - })); - + expect(service.getImageUrl(writeValueMock)).toBe(testcase.expect.data); + }), + ); [ { description: 'null url', image: null, expect: '' }, { description: 'empty url', image: '', expect: '' }, { description: 'url', image: '5642', expect: '5642' }, { description: 'url', image: 'data:image/png;base64,5642', expect: '5642' }, - ].forEach(testcase => + ].forEach((testcase) => it(`can handle an url with ${testcase.description}`, () => { // Arrange const service: Base64ImageService = TestBed.get(Base64ImageService); // Act & Assert expect(service.getImageData(testcase.image)).toBe(testcase.expect); - })); + }), + ); }); diff --git a/imxweb/projects/qbm/src/lib/images/base64-image.service.ts b/imxweb/projects/qbm/src/lib/images/base64-image.service.ts index 7a2fd6f32..01fac91c6 100644 --- a/imxweb/projects/qbm/src/lib/images/base64-image.service.ts +++ b/imxweb/projects/qbm/src/lib/images/base64-image.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,10 +26,10 @@ import { Injectable } from '@angular/core'; import { SafeUrl } from '@angular/platform-browser'; -import { IWriteValue } from 'imx-qbm-dbts'; +import { IWriteValue } from '@imx-modules/imx-qbm-dbts'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class Base64ImageService { private readonly base64DataUrl = 'data:image/png;base64,'; diff --git a/imxweb/projects/qbm/src/lib/indexbar/indexbar.component.html b/imxweb/projects/qbm/src/lib/indexbar/indexbar.component.html index 96aec2537..de1abec11 100644 --- a/imxweb/projects/qbm/src/lib/indexbar/indexbar.component.html +++ b/imxweb/projects/qbm/src/lib/indexbar/indexbar.component.html @@ -1,7 +1,6 @@
    -
    - - -
    - {{EffectiveLabel()}} -
    \ No newline at end of file +
    + +
    + {{ EffectiveLabel() }} +
    diff --git a/imxweb/projects/qbm/src/lib/indexbar/indexbar.component.ts b/imxweb/projects/qbm/src/lib/indexbar/indexbar.component.ts index c6f3e61b4..bd29aa9ac 100644 --- a/imxweb/projects/qbm/src/lib/indexbar/indexbar.component.ts +++ b/imxweb/projects/qbm/src/lib/indexbar/indexbar.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,107 +27,98 @@ import { Component, Input } from '@angular/core'; @Component({ - templateUrl: "./indexbar.component.html", - selector: "imx-indexbar" + templateUrl: './indexbar.component.html', + selector: 'imx-indexbar', }) export class IndexBarComponent { - // TODO replace VI_Common_Color_Gray, VI_Common_Color_Badge_Important,VI_Common_Color_Badge_Success,VI_Common_Color_Badge_Warning + // TODO replace VI_Common_Color_Gray, VI_Common_Color_Badge_Important,VI_Common_Color_Badge_Success,VI_Common_Color_Badge_Warning - ColorFilling(): string { - return (this.IsHigh() ? this.ColorHigh() : (this.IsLow() ? this.ColorLow() : this.ColorMedium())); - } + ColorFilling(): string { + return this.IsHigh() ? this.ColorHigh() : this.IsLow() ? this.ColorLow() : this.ColorMedium(); + } - Container1Style(): string { - return this.HasFlexibleProgressbar - ? '' - : `display: inline-block; min-width:${this.TotalWidth()}px;` - } + Container1Style(): string { + return this.HasFlexibleProgressbar ? '' : `display: inline-block; min-width:${this.TotalWidth()}px;`; + } - Container2Style(): string { - return `width: calc(100% - ${this.LabelWidth + 2}px); height: 14px; border: solid 1px %VI_Common_Color_Gray%; display:inline-block;`; - } + Container2Style(): string { + return `width: calc(100% - ${this.LabelWidth + 2}px); height: 14px; border: solid 1px %VI_Common_Color_Gray%; display:inline-block;`; + } - Container3Style(): string { - const f = this.ColorFilling(); - return `background-color: ${f}; color: ${f}; display: block; overflow: hidden; width: ${this.Percentage()}%; height: 14px;`; - } + Container3Style(): string { + const f = this.ColorFilling(); + return `background-color: ${f}; color: ${f}; display: block; overflow: hidden; width: ${this.Percentage()}%; height: 14px;`; + } - Label1Style(): string { - return `vertical-align: top; text-align:right; float:right; width:${this.LabelWidth}px;`; - } + Label1Style(): string { + return `vertical-align: top; text-align:right; float:right; width:${this.LabelWidth}px;`; + } - ColorHigh(): string { - return this.EffectiveWarningOnHighValues() - ? '%VI_Common_Color_Badge_Important%' - : '%VI_Common_Color_Badge_Success%'; - } + ColorHigh(): string { + return this.EffectiveWarningOnHighValues() ? '%VI_Common_Color_Badge_Important%' : '%VI_Common_Color_Badge_Success%'; + } - ColorLow(): string { - return this.EffectiveWarningOnHighValues() - ? '%VI_Common_Color_Badge_Success%' - : '%VI_Common_Color_Badge_Important%'; - } + ColorLow(): string { + return this.EffectiveWarningOnHighValues() ? '%VI_Common_Color_Badge_Success%' : '%VI_Common_Color_Badge_Important%'; + } - ColorMedium(): string { - return "%VI_Common_Color_Badge_Warning%"; - } + ColorMedium(): string { + return '%VI_Common_Color_Badge_Warning%'; + } - EffectiveLabel(): string { - return this.Label || this.Percentage() + " %"; - } + EffectiveLabel(): string { + return this.Label || this.Percentage() + ' %'; + } - EffectiveProgress(): number { - return this.Progress < 0.0 ? 0.0 : (this.Progress > 1.0 ? 1.0 : this.Progress); - } + EffectiveProgress(): number { + return this.Progress < 0.0 ? 0.0 : this.Progress > 1.0 ? 1.0 : this.Progress; + } - EffectiveWarningOnHighValues(): boolean { - return this.WarningOnHighValues; - } + EffectiveWarningOnHighValues(): boolean { + return this.WarningOnHighValues; + } - IsHigh(): boolean { - return this.EffectiveProgress() > this.UpperLimit; - } + IsHigh(): boolean { + return this.EffectiveProgress() > this.UpperLimit; + } - IsLow(): boolean { - return this.EffectiveProgress() <= this.LowerLimit; - } + IsLow(): boolean { + return this.EffectiveProgress() <= this.LowerLimit; + } - Percentage(): number { - return Math.floor(100.0 * this.EffectiveProgress()); - } + Percentage(): number { + return Math.floor(100.0 * this.EffectiveProgress()); + } - TotalWidth(): number { - return this.LabelWidth >= 0 - ? this.ProgressbarWidth + this.LabelWidth + 2 - : this.ProgressbarWidth; - } + TotalWidth(): number { + return this.LabelWidth >= 0 ? this.ProgressbarWidth + this.LabelWidth + 2 : this.ProgressbarWidth; + } - /** Optional value overriding the default label text that displays the progress percentage. */ - @Input() Label: string; + /** Optional value overriding the default label text that displays the progress percentage. */ + @Input() Label: string; - @Input() LabelWidth: number = 35; + @Input() LabelWidth: number = 35; - /** Optional. Specify a value if you want to override default lower limit of 0.25 - * Progress values that are less or equal to this value will be marked with the lower color. */ - @Input() LowerLimit: number = 0.25; + /** Optional. Specify a value if you want to override default lower limit of 0.25 + * Progress values that are less or equal to this value will be marked with the lower color. */ + @Input() LowerLimit: number = 0.25; - /** The progress as double value between 0.0 (0%) and 1.0 (100%) */ - @Input() Progress: number; + /** The progress as double value between 0.0 (0%) and 1.0 (100%) */ + @Input() Progress: number; - @Input() Tooltip: string; + @Input() Tooltip: string; - /** Optional. Specify a value if you want to override the default upper limit of 0.5 - * Progress values that are greater than this value will be marked with the high color. */ - @Input() UpperLimit: number; + /** Optional. Specify a value if you want to override the default upper limit of 0.5 + * Progress values that are greater than this value will be marked with the high color. */ + @Input() UpperLimit: number; - /** Default behaviour is to apply warning colors for low values (high values are good, low values are bad). - * You can (optionally) specify WarningOnHighValues = true, if you want to invert this behavior. */ - @Input() WarningOnHighValues: boolean; + /** Default behaviour is to apply warning colors for low values (high values are good, low values are bad). + * You can (optionally) specify WarningOnHighValues = true, if you want to invert this behavior. */ + @Input() WarningOnHighValues: boolean; - /** Default value is 50 (meaning 50 px). If HasFlexibleProgressbar()=true this width will be ignored and the remaining space will be used. */ - @Input() ProgressbarWidth: number = 50; + /** Default value is 50 (meaning 50 px). If HasFlexibleProgressbar()=true this width will be ignored and the remaining space will be used. */ + @Input() ProgressbarWidth: number = 50; - /** Default value is true (meaning the progress bar has a flexible width, if set to false it has a fixed width - see ProgressBarWidth()) */ - @Input() HasFlexibleProgressbar: boolean = true; - -} \ No newline at end of file + /** Default value is true (meaning the progress bar has a flexible width, if set to false it has a fixed width - see ProgressBarWidth()) */ + @Input() HasFlexibleProgressbar: boolean = true; +} diff --git a/imxweb/projects/qbm/src/lib/info-modal-dialog/info-badge/info-badge.component.html b/imxweb/projects/qbm/src/lib/info-modal-dialog/info-badge/info-badge.component.html index 01d22d46d..71709ce80 100644 --- a/imxweb/projects/qbm/src/lib/info-modal-dialog/info-badge/info-badge.component.html +++ b/imxweb/projects/qbm/src/lib/info-modal-dialog/info-badge/info-badge.component.html @@ -1,3 +1,4 @@ - + + {{ badgeText }} + + diff --git a/imxweb/projects/qbm/src/lib/info-modal-dialog/info-badge/info-badge.component.scss b/imxweb/projects/qbm/src/lib/info-modal-dialog/info-badge/info-badge.component.scss deleted file mode 100644 index 226ab9e9a..000000000 --- a/imxweb/projects/qbm/src/lib/info-modal-dialog/info-badge/info-badge.component.scss +++ /dev/null @@ -1,30 +0,0 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; - -:host { - button { - transition: all .4s ease; - } -} - -// Theming -:host { - button { - color: $color-blue-60; - - &:hover { - color: $color-orange-60; - } - } -} - -.eui-dark-theme { - :host { - button { - color: $color-blue-40; - - &:hover { - color: $color-orange-40; - } - } - } -} diff --git a/imxweb/projects/qbm/src/lib/info-modal-dialog/info-badge/info-badge.component.ts b/imxweb/projects/qbm/src/lib/info-modal-dialog/info-badge/info-badge.component.ts index 7a1e30c89..37ab88e0c 100644 --- a/imxweb/projects/qbm/src/lib/info-modal-dialog/info-badge/info-badge.component.ts +++ b/imxweb/projects/qbm/src/lib/info-modal-dialog/info-badge/info-badge.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,8 +30,7 @@ import { InfoDialogComponent } from '../info-dialog/info-dialog.component'; @Component({ selector: 'imx-info-badge', - templateUrl: './info-badge.component.html', - styleUrls: ['./info-badge.component.scss'], + templateUrl: './info-badge.component.html' }) export class InfoBadgeComponent { // width string = '400px'. Sets the width of the appearing dialog. diff --git a/imxweb/projects/qbm/src/lib/info-modal-dialog/info-button/info-button.component.html b/imxweb/projects/qbm/src/lib/info-modal-dialog/info-button/info-button.component.html index f9f1c1575..904a6c404 100644 --- a/imxweb/projects/qbm/src/lib/info-modal-dialog/info-button/info-button.component.html +++ b/imxweb/projects/qbm/src/lib/info-modal-dialog/info-button/info-button.component.html @@ -1,3 +1,3 @@ - diff --git a/imxweb/projects/qbm/src/lib/info-modal-dialog/info-button/info-button.component.scss b/imxweb/projects/qbm/src/lib/info-modal-dialog/info-button/info-button.component.scss index 81d501e57..8f54c94fa 100644 --- a/imxweb/projects/qbm/src/lib/info-modal-dialog/info-button/info-button.component.scss +++ b/imxweb/projects/qbm/src/lib/info-modal-dialog/info-button/info-button.component.scss @@ -1,42 +1,5 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; +@import 'components/button'; -:host { - button { - transition: all .4s ease; - } -} - -// Theming -:host { - button { - color: $color-blue-60; - - &:hover { - color: $color-orange-60; - } - } -} - -.eui-dark-theme { - :host { - button { - color: $color-blue-40; - - &:hover { - color: $color-orange-40; - } - } - } -} - -.eui-contrast-theme { - :host { - button { - color: $color-blue-40; - - &:hover { - color: $color-orange-40; - } - } - } +.imx-button-icon-hover-warning { + @extend .imx-button-icon-hover-warning; } diff --git a/imxweb/projects/qbm/src/lib/info-modal-dialog/info-button/info-button.component.ts b/imxweb/projects/qbm/src/lib/info-modal-dialog/info-button/info-button.component.ts index 199ad03a5..c227c6789 100644 --- a/imxweb/projects/qbm/src/lib/info-modal-dialog/info-button/info-button.component.ts +++ b/imxweb/projects/qbm/src/lib/info-modal-dialog/info-button/info-button.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,14 +31,14 @@ import { InfoDialogComponent } from '../info-dialog/info-dialog.component'; @Component({ selector: 'imx-info-button', templateUrl: './info-button.component.html', - styleUrls: ['./info-button.component.scss'] + styleUrls: ['./info-button.component.scss'], }) -export class InfoButtonComponent { +export class InfoButtonComponent { // width string = '400px'. Sets the width of the appearing dialog. @Input() public width = '400px'; // title: string Gives the dialog an h3 title if present - @Input() public title: string = null; + @Input() public title: string | null = null; // templateRef: TemplateRef. Provides the content within the the dialog @Input() public templateRef: TemplateRef; @@ -46,19 +46,19 @@ export class InfoButtonComponent { // panelClass: string | string[]. Allows for a class to be applied to the overlay and the above templateRef to be styled. @Input() public panelClass: string | string[]; - constructor( - private dialogService: MatDialog - ) { } + constructor(private dialogService: MatDialog) {} public async showInfo(): Promise { - await this.dialogService.open(InfoDialogComponent, { - width: this.width, - data: { - title: this.title, - content: this.templateRef - }, - panelClass: this.panelClass - }).afterClosed().toPromise(); + await this.dialogService + .open(InfoDialogComponent, { + width: this.width, + data: { + title: this.title, + content: this.templateRef, + }, + panelClass: this.panelClass, + }) + .afterClosed() + .toPromise(); } - } diff --git a/imxweb/projects/qbm/src/lib/info-modal-dialog/info-dialog/info-dialog.component.html b/imxweb/projects/qbm/src/lib/info-modal-dialog/info-dialog/info-dialog.component.html index e66b73ff0..45e9f1d1d 100644 --- a/imxweb/projects/qbm/src/lib/info-modal-dialog/info-dialog/info-dialog.component.html +++ b/imxweb/projects/qbm/src/lib/info-modal-dialog/info-dialog/info-dialog.component.html @@ -1,8 +1,8 @@ -

    {{data.title}}

    +

    {{ data.title }}

    - + diff --git a/imxweb/projects/qbm/src/lib/info-modal-dialog/info-dialog/info-dialog.component.scss b/imxweb/projects/qbm/src/lib/info-modal-dialog/info-dialog/info-dialog.component.scss index 9cfa9afc4..c6c9bd60c 100644 --- a/imxweb/projects/qbm/src/lib/info-modal-dialog/info-dialog/info-dialog.component.scss +++ b/imxweb/projects/qbm/src/lib/info-modal-dialog/info-dialog/info-dialog.component.scss @@ -1,10 +1,6 @@ :host { - .mat-dialog-title { + .mat-mdc-dialog-title { margin: 0; font-weight: bold; } - - .mat-dialog-actions { - justify-content: flex-end; - } } diff --git a/imxweb/projects/qbm/src/lib/info-modal-dialog/info-dialog/info-dialog.component.ts b/imxweb/projects/qbm/src/lib/info-modal-dialog/info-dialog/info-dialog.component.ts index c237c0829..a12d4b2a4 100644 --- a/imxweb/projects/qbm/src/lib/info-modal-dialog/info-dialog/info-dialog.component.ts +++ b/imxweb/projects/qbm/src/lib/info-modal-dialog/info-dialog/info-dialog.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,14 +30,14 @@ import { MAT_DIALOG_DATA } from '@angular/material/dialog'; @Component({ selector: 'imx-info-dialog', templateUrl: './info-dialog.component.html', - styleUrls: ['./info-dialog.component.scss'] + styleUrls: ['./info-dialog.component.scss'], }) export class InfoDialogComponent { - constructor( - @Inject(MAT_DIALOG_DATA) public data: { - content: TemplateRef, - title?: string - } - ) { } + @Inject(MAT_DIALOG_DATA) + public data: { + content: TemplateRef; + title?: string; + }, + ) {} } diff --git a/imxweb/projects/qbm/src/lib/info-modal-dialog/info-modal-dialog.module.ts b/imxweb/projects/qbm/src/lib/info-modal-dialog/info-modal-dialog.module.ts index f0d36bcee..64d5bba69 100644 --- a/imxweb/projects/qbm/src/lib/info-modal-dialog/info-modal-dialog.module.ts +++ b/imxweb/projects/qbm/src/lib/info-modal-dialog/info-modal-dialog.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qbm/src/lib/jobqueue-overview/jobqueue-overview.component.html b/imxweb/projects/qbm/src/lib/jobqueue-overview/jobqueue-overview.component.html index 500a1f3d0..95dc1a9f0 100644 --- a/imxweb/projects/qbm/src/lib/jobqueue-overview/jobqueue-overview.component.html +++ b/imxweb/projects/qbm/src/lib/jobqueue-overview/jobqueue-overview.component.html @@ -9,13 +9,7 @@ - - - - -
    - -

    {{'#LDS#Currently, there is no data in any queues.' |translate}}

    -
    + +
    diff --git a/imxweb/projects/qbm/src/lib/jobqueue-overview/jobqueue-overview.component.scss b/imxweb/projects/qbm/src/lib/jobqueue-overview/jobqueue-overview.component.scss index 6826a21b6..030b08bca 100644 --- a/imxweb/projects/qbm/src/lib/jobqueue-overview/jobqueue-overview.component.scss +++ b/imxweb/projects/qbm/src/lib/jobqueue-overview/jobqueue-overview.component.scss @@ -1,6 +1,6 @@ @import '@elemental-ui/core/src/styles/_palette.scss'; -:host{ +:host { display: flex; flex-direction: column; align-items: flex-start; @@ -17,34 +17,19 @@ mat-form-field { display: flex; - - ::ng-deep .mat-form-field-wrapper { - padding-bottom: 0; - } } -mat-card { - flex: 1 1 auto; - display: flex; - flex-direction: column; -} - -.imx-billboard-card { +.imx-card-fill { align-self: normal; } -.imx-jobqueue-no-data { +.imx-no-results { height: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center; - .eui-icon { - font-size: 100px; - color: rgba($black-c, 0.55); - } - p { margin: 0; font-size: 18px; diff --git a/imxweb/projects/qbm/src/lib/jobqueue-overview/jobqueue-overview.component.ts b/imxweb/projects/qbm/src/lib/jobqueue-overview/jobqueue-overview.component.ts index d451da3a1..460df9d7f 100644 --- a/imxweb/projects/qbm/src/lib/jobqueue-overview/jobqueue-overview.component.ts +++ b/imxweb/projects/qbm/src/lib/jobqueue-overview/jobqueue-overview.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,16 +24,16 @@ * */ -import { Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges} from '@angular/core'; +import { ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; -import { ChartOptions, XTickConfiguration } from 'billboard.js'; +import { Chart, ChartOptions, XTickConfiguration } from 'billboard.js'; import { interval, Subscription } from 'rxjs'; -import { JobQueueDataSlice, JobQueueOverviewService } from './jobqueue-overview.service'; -import { XAxisInformation } from '../chart-options/x-axis-information'; import { LineChartOptions } from '../chart-options/line-chart-options'; -import { YAxisInformation } from '../chart-options/y-axis-information'; import { SeriesInformation } from '../chart-options/series-information'; +import { XAxisInformation } from '../chart-options/x-axis-information'; +import { YAxisInformation } from '../chart-options/y-axis-information'; +import { JobQueueOverviewService } from './jobqueue-overview.service'; @Component({ selector: 'imx-jobqueue-overview', @@ -42,7 +42,7 @@ import { SeriesInformation } from '../chart-options/series-information'; }) export class JobQueueOverviewComponent implements OnInit, OnDestroy, OnChanges { @Input() public isShowGraph: boolean; - public chartOptions: ChartOptions = null; + public chartOptions: ChartOptions | null = null; public routineSubscription: Subscription; public queueNames: string[]; @@ -60,33 +60,42 @@ export class JobQueueOverviewComponent implements OnInit, OnDestroy, OnChanges { public ylabel: string; public title: string; + private chart: Chart; + private xAxisConfig: XTickConfiguration = { culling: { max: 5 }, format: '%H:%M:%S', }; - constructor(private readonly jobQueueOverviewService: JobQueueOverviewService, private translateService: TranslateService) { + constructor( + private readonly jobQueueOverviewService: JobQueueOverviewService, + private translateService: TranslateService, + private changeDetectorRef: ChangeDetectorRef, + ) { this.queueNames = this.jobQueueOverviewService.queueNames; - this.translateService.get('#LDS#All queues').subscribe((trans: string) => (this.queue = trans)); + this.queue = translateService.instant('#LDS#All queues'); // Translate chart labels - this.translateService.get('#LDS#Time').subscribe((trans: string) => (this.timeText = trans)); - this.translateService.get('#LDS#Error').subscribe((trans: string) => (this.errorText = trans)); - this.translateService.get('#LDS#Waiting').subscribe((trans: string) => (this.waitingText = trans)); - this.translateService.get('#LDS#Ready').subscribe((trans: string) => (this.readyText = trans)); - this.translateService.get('#LDS#Processing').subscribe((trans: string) => (this.processingText = trans)); - this.translateService.get('#LDS#Finished').subscribe((trans: string) => (this.finishedText = trans)); - - this.translateService.get('#LDS#Number of processes').subscribe((trans: string) => (this.ylabel = trans)); - this.translateService.get('#LDS#Processes over time').subscribe((trans: string) => (this.title = trans)); + + this.timeText = translateService.instant('#LDS#Time'); + this.errorText = translateService.instant('#LDS#Error'); + this.waitingText = translateService.instant('#LDS#Waiting'); + this.readyText = translateService.instant('#LDS#Ready'); + this.processingText = translateService.instant('#LDS#Processing'); + this.finishedText = translateService.instant('#LDS#Finished'); + + this.ylabel = translateService.instant('#LDS#Number of processes'); + this.title = translateService.instant('#LDS#Processes over time'); } public async ngOnInit(): Promise { + this.buildOptions(); // Setup service if it isn't already available if (!this.jobQueueOverviewService.isAvailable) { await this.jobQueueOverviewService.setUp(); await this.jobQueueOverviewService.isAvailablePromise; } + // Setup an interval and subscribe const routine = interval(this.jobQueueOverviewService.configParams.RefreshIntervalSeconds * 1000); this.routineSubscription = routine.subscribe(() => this.updatePlot()); @@ -94,10 +103,15 @@ export class JobQueueOverviewComponent implements OnInit, OnDestroy, OnChanges { public ngOnChanges(changes: SimpleChanges): void { if (changes['isShowGraph']) { - this.updatePlot(); + this.changeDetectorRef.detectChanges(); + this.chart?.resize(this.getSize()); } } + public onChart(chart: Chart) { + this.chart = chart; + } + public updatePlot(): void { if (!this.isShowGraph) { // Don't do anything that isn't visible @@ -105,51 +119,67 @@ export class JobQueueOverviewComponent implements OnInit, OnDestroy, OnChanges { } const data = this.jobQueueOverviewService.getSlice(this.queue); - - if (data.Time.length > 0) { - this.setData(data); - this.chartOptions.onresize = () => this.setData(data); + const dateArray: any[] = data.Time ? [...data.Time] : []; + const errorArray: any[] = data.Error ? [...data.Error] : []; + const waitingArray: any[] = data.Waiting ? [...data.Waiting] : []; + const readyArray: any[] = data.Ready ? [...data.Ready] : []; + const processingArray: any[] = data.Processing ? [...data.Processing] : []; + const finishArray: any[] = data.Finished ? [...data.Finished] : []; + + //add data ID as first element in arrays + dateArray.unshift('x'); + errorArray.unshift(this.errorText); + waitingArray.unshift(this.waitingText); + readyArray.unshift(this.readyText); + processingArray.unshift(this.processingText); + finishArray.unshift(this.finishedText); + + if (!!data.Time?.length) { + this.chart?.load({ + resizeAfter: true, + append: true, + columns: [dateArray, errorArray, waitingArray, readyArray, processingArray, finishArray], + }); } } - public setData(data: JobQueueDataSlice): void { + private buildOptions() { // If there is actually data, show it - const xAxis = new XAxisInformation('date', data.Time, this.xAxisConfig); + const xAxis = new XAxisInformation('date', [], this.xAxisConfig); const yAxis = new YAxisInformation([ - new SeriesInformation(this.errorText, data.Error, 'red'), - new SeriesInformation(this.waitingText, data.Waiting, 'orange'), - new SeriesInformation(this.readyText, data.Ready, 'blue'), - new SeriesInformation(this.processingText, data.Processing, 'yellow'), - new SeriesInformation(this.finishedText, data.Finished, 'green'), + new SeriesInformation(this.errorText, [], 'red'), + new SeriesInformation(this.waitingText, [], 'orange'), + new SeriesInformation(this.readyText, [], 'blue'), + new SeriesInformation(this.processingText, [], 'violet'), + new SeriesInformation(this.finishedText, [], 'green'), ]); yAxis.tickConfiguration = { format: (l) => (Number.isInteger(l) && l > -1 ? l.toString() : ''), }; - yAxis.min = 0; - const lineChartOptions = new LineChartOptions(xAxis, yAxis); + yAxis.min = -1; + const lineChartOptions = new LineChartOptions( + xAxis, + yAxis, + this.translateService.instant('#LDS#Currently, there is no data in any queues.'), + ); lineChartOptions.showPoints = true; lineChartOptions.hideLegend = false; lineChartOptions.colorArea = false; lineChartOptions.canZoom = true; lineChartOptions.padding = { left: 20, right: 20, unit: 'px' }; this.chartOptions = lineChartOptions.options; - this.chartOptions.size = this.getSize(); - } - - public removeOldSVG(): void { - const svgElement = document.getElementsByClassName('bb')[0].firstChild; - if (svgElement) { - svgElement.remove(); + if (this.chartOptions.data) { + this.chartOptions.data.labels = { format: (v, id, i, j) => `${v}` }; } + this.chartOptions.size = { width: 0, height: 0 }; } public getSize(): { height: number; width: number } { - const graphCard = document.querySelector('.imx-billboard-card'); + const graphCard = document.querySelector('.imx-card-fill'); const emptyCard = document.querySelector('.imx-empty-card'); let divForSize: HTMLDivElement; if (graphCard) { // The graph is already displayed, use the current graph, remove previous svg first for resize problem - this.removeOldSVG(); divForSize = graphCard as HTMLDivElement; } else { // We haven't yet rendered the graph, use the empty display instead diff --git a/imxweb/projects/qbm/src/lib/jobqueue-overview/jobqueue-overview.module.ts b/imxweb/projects/qbm/src/lib/jobqueue-overview/jobqueue-overview.module.ts index a80f7ada1..c2fd907da 100644 --- a/imxweb/projects/qbm/src/lib/jobqueue-overview/jobqueue-overview.module.ts +++ b/imxweb/projects/qbm/src/lib/jobqueue-overview/jobqueue-overview.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qbm/src/lib/jobqueue-overview/jobqueue-overview.service.spec.ts b/imxweb/projects/qbm/src/lib/jobqueue-overview/jobqueue-overview.service.spec.ts index 4ddedb75c..d1731ff67 100644 --- a/imxweb/projects/qbm/src/lib/jobqueue-overview/jobqueue-overview.service.spec.ts +++ b/imxweb/projects/qbm/src/lib/jobqueue-overview/jobqueue-overview.service.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,9 +31,9 @@ import { ClassloggerService } from '../classlogger/classlogger.service'; import { AppConfigService } from '../appConfig/appConfig.service'; import { imx_SessionService } from '../session/imx-session.service'; import { JobQueueOverviewService, JobQueueDataSlice, JobQueueGroups } from './jobqueue-overview.service'; -import { EntityColumnData, EntityData } from 'imx-qbm-dbts'; +import { EntityColumnData, EntityData } from '@imx-modules/imx-qbm-dbts'; import { of } from 'rxjs'; -import { EventStreamConfig } from 'imx-api-qbm'; +import { EventStreamConfig } from '@imx-modules/imx-api-qbm'; describe('JobQueueOverviewService', () => { let service: JobQueueOverviewService; diff --git a/imxweb/projects/qbm/src/lib/jobqueue-overview/jobqueue-overview.service.ts b/imxweb/projects/qbm/src/lib/jobqueue-overview/jobqueue-overview.service.ts index 2dd168b3b..49f8677e6 100644 --- a/imxweb/projects/qbm/src/lib/jobqueue-overview/jobqueue-overview.service.ts +++ b/imxweb/projects/qbm/src/lib/jobqueue-overview/jobqueue-overview.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,12 +28,12 @@ import { ErrorHandler, Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { interval, Subscription } from 'rxjs'; -import { ClassloggerService } from '../classlogger/classlogger.service'; +import { EventStreamConfig } from '@imx-modules/imx-api-qbm'; +import { EntityCollectionChangeData, EntityData } from '@imx-modules/imx-qbm-dbts'; +import _ from 'lodash'; import { AppConfigService } from '../appConfig/appConfig.service'; +import { ClassloggerService } from '../classlogger/classlogger.service'; import { imx_SessionService } from '../session/imx-session.service'; -import { EventStreamConfig } from 'imx-api-qbm'; -import { EntityCollectionChangeData, EntityData } from 'imx-qbm-dbts'; -import _ from 'lodash'; export interface JobQueueGroups { Error?: number; @@ -77,7 +77,7 @@ export class JobQueueOverviewService { public session: imx_SessionService, public translateService: TranslateService, private logger: ClassloggerService, - private errorHandler: ErrorHandler + private errorHandler: ErrorHandler, ) { this.translateService.get('#LDS#All queues').subscribe((trans: string) => (this.totalStreamName = trans)); this.isAvailablePromise = new Promise((resolve, reject) => { @@ -124,8 +124,8 @@ export class JobQueueOverviewService { if (changeData.New) { // Loop over each entity to add to the queues changeData.New.forEach((entity) => { - name = entity.Columns['QueueName'].Value; - uid = entity.Columns['UID_QBMJobqueueOverview'].Value; + name = entity.Columns?.['QueueName']?.Value; + uid = entity.Columns?.['UID_QBMJobqueueOverview']?.Value; this.addEntity(name, entity); this.uidToName[uid] = name; }); @@ -154,9 +154,11 @@ export class JobQueueOverviewService { public addEntity(name: string, entity: EntityData, isTotal: boolean = false): void { // Add this entity to the list this.entities[name] = {}; - for (const [key, value] of Object.entries(entity.Columns)) { - if (key.includes('Count')) { - this.entities[name][key] = isTotal ? 0 : value.Value; + if (entity.Columns) { + for (const [key, value] of Object.entries(entity.Columns)) { + if (key.includes('Count')) { + this.entities[name][key] = isTotal ? 0 : value.Value; + } } } // Check if name is already in queue list, prevents total queue from being duped @@ -211,7 +213,7 @@ export class JobQueueOverviewService { } public pushTable(): void { - const currentTable: object[] = this.table.pop(); + const currentTable: object[] | undefined = this.table.pop(); const incomingTable: object[] = []; // Replace popped table, if not empty (faster as easier to pop) if (currentTable) { @@ -269,12 +271,14 @@ export class JobQueueOverviewService { return slice; } this.axisTimes.forEach((e, timeIndex) => { - for (const [name, value] of Object.entries(this.table[timeIndex][queueIndex])) { - // Append to array or initialize - if (slice[name]) { - slice[name].push(value); - } else { - slice[name] = [value]; + if (this.table[timeIndex][queueIndex]) { + for (const [name, value] of Object.entries(this.table[timeIndex][queueIndex])) { + // Append to array or initialize + if (slice[name]) { + slice[name].push(value); + } else { + slice[name] = [value]; + } } } }); diff --git a/imxweb/projects/qbm/src/lib/lds-replace/lds-replace.module.ts b/imxweb/projects/qbm/src/lib/lds-replace/lds-replace.module.ts index aef2cc8c9..8684828d0 100644 --- a/imxweb/projects/qbm/src/lib/lds-replace/lds-replace.module.ts +++ b/imxweb/projects/qbm/src/lib/lds-replace/lds-replace.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,14 +30,8 @@ import { CommonModule } from '@angular/common'; import { LdsReplacePipe } from './lds-replace.pipe'; @NgModule({ - declarations: [ - LdsReplacePipe - ], - exports: [ - LdsReplacePipe - ], - imports: [ - CommonModule - ] + declarations: [LdsReplacePipe], + exports: [LdsReplacePipe], + imports: [CommonModule], }) -export class LdsReplaceModule { } +export class LdsReplaceModule {} diff --git a/imxweb/projects/qbm/src/lib/lds-replace/lds-replace.pipe.ts b/imxweb/projects/qbm/src/lib/lds-replace/lds-replace.pipe.ts index 37ca96436..d1667f0cd 100644 --- a/imxweb/projects/qbm/src/lib/lds-replace/lds-replace.pipe.ts +++ b/imxweb/projects/qbm/src/lib/lds-replace/lds-replace.pipe.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,7 +27,7 @@ import { Pipe, PipeTransform, Injectable } from '@angular/core'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) @Pipe({ name: 'ldsReplace', diff --git a/imxweb/projects/qbm/src/lib/login/login.component.html b/imxweb/projects/qbm/src/lib/login/login.component.html index 78c5d4e4f..9fa5fa9d4 100644 --- a/imxweb/projects/qbm/src/lib/login/login.component.html +++ b/imxweb/projects/qbm/src/lib/login/login.component.html @@ -13,19 +13,55 @@

    {{ title }}

    - +
    - +
    -
    - + + -
    @@ -35,28 +71,78 @@

    + {{ '#LDS#Authentication' | translate }} - + {{ authConfig.display | translate }}
    -
    - -
    + +
    + + + +
    +
    + +
    + + + +
    +
    + +
    + + + +
    + +
    diff --git a/imxweb/projects/qbm/src/lib/login/login.component.scss b/imxweb/projects/qbm/src/lib/login/login.component.scss index 9f6f8ef26..46d948013 100644 --- a/imxweb/projects/qbm/src/lib/login/login.component.scss +++ b/imxweb/projects/qbm/src/lib/login/login.component.scss @@ -1,7 +1,6 @@ -@import "variables.scss"; +@use '@angular/material' as mat; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; - -$inputWidth: calc(40vw + 2px); +@import 'base/variables'; .imx-loginPage { display: flex; @@ -12,7 +11,7 @@ $inputWidth: calc(40vw + 2px); left: 0; right: 0; background-color: $iris-blue; - color: $white; + color: $color-gray-0; align-items: center; } @@ -23,7 +22,7 @@ $inputWidth: calc(40vw + 2px); left: 25px; right: 25px; bottom: 25px; - color: $white; + color: $color-gray-0; display: flex; flex-direction: column; align-items: center; @@ -31,8 +30,6 @@ $inputWidth: calc(40vw + 2px); h1 { text-align: center; } - - } .imx-loginMask { @@ -43,33 +40,24 @@ $inputWidth: calc(40vw + 2px); } .imx-loginInput { - margin: auto 12px; - ::ng-deep { - input { - width: $propertylabelwidth; - box-sizing: content-box; - margin-bottom: 9px; - height: 30px; - padding-left: 6px; - width: calc(40vw - 6px); - } - - .mat-form-field-infix { - color: $VI_Common_Color_Font; - } - - .mat-form-field-flex { - background-color: $white; - padding-left: 6px; - } - - .mat-form-field-appearance-legacy .mat-form-field-underline { - bottom: $ImxLoginControlsMargin; + .mat-mdc-form-field { + .mat-mdc-text-field-wrapper { + .mat-mdc-form-field-infix { + padding-top: 24px; + } + .mat-mdc-floating-label { + display: block; + top: 28px; + } + } } - - .mat-form-field-appearance-legacy .mat-form-field-wrapper { - padding-bottom: $ImxLoginControlsMargin; + } +} +.imx-loginData { + ::ng-deep { + .mat-mdc-form-field-subscript-wrapper { + min-height: 12px; } } } @@ -77,9 +65,6 @@ $inputWidth: calc(40vw + 2px); .imx-loginButtonpanel { text-align: center; padding: 30px 0; - margin: 0 auto; - width: 100%; - display: inline-block; button { margin: 0 10px; @@ -103,7 +88,7 @@ $inputWidth: calc(40vw + 2px); bottom: 10px; left: 0; right: 0; - color: $white; + color: $color-gray-0; } .imx-loginMessageContainer { @@ -119,11 +104,11 @@ $inputWidth: calc(40vw + 2px); .imx-loginTextBox { width: 224px; - color: $VI_Common_Color_Font !important; + color: $color-gray-80 !important; } a.imx-loginLink { - color: $white !important; + color: $color-gray-0 !important; } a.imx-loginLink:hover { @@ -150,11 +135,11 @@ a.imx-loginLink:hover { } mat-form-field { - width: $inputWidth; + width: $IMX_Login_Input_Width; } // on smaller screens -@media screen and (max-width : 600px) { +@media screen and (max-width: 600px) { .imx-loginInput { width: 95%; text-align: center; @@ -187,55 +172,31 @@ mat-form-field { } .eui-dark-theme { - :host{ - .imx-loginPage{ + :host { + .imx-loginPage { background-color: $color-gray-80; } - .imx-loginInput { + .imx-loginData { ::ng-deep { - input { - background-color: $color-gray-70; - border: none; - color: $color-gray-0; - } - - .mat-form-field-infix { - color: $color-gray-0 - } - - .mat-form-field-flex { - background-color:$color-gray-70; + .mat-mdc-text-field-wrapper { + background-color: $color-gray-70 !important; } - } } - } } .eui-contrast-theme { - :host{ - .imx-loginPage{ + :host { + .imx-loginPage { background-color: $color-gray-100; } - .imx-loginInput { + .imx-loginData { ::ng-deep { - input { - background-color: $color-gray-90; - border: none; - color: $color-gray-0; - } - - .mat-form-field-infix { - color: $color-gray-0 + .mat-mdc-text-field-wrapper { + background-color: $color-gray-90 !important; } - - .mat-form-field-flex { - background-color:$color-gray-90; - } - } } - } } diff --git a/imxweb/projects/qbm/src/lib/login/login.component.ts b/imxweb/projects/qbm/src/lib/login/login.component.ts index 81926c485..f0bc2650f 100644 --- a/imxweb/projects/qbm/src/lib/login/login.component.ts +++ b/imxweb/projects/qbm/src/lib/login/login.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,30 +24,46 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; -import { Component, OnInit, OnDestroy, ViewChild, ComponentFactoryResolver } from '@angular/core'; +import { + AfterViewChecked, + ChangeDetectorRef, + Component, + ComponentFactoryResolver, + OnDestroy, + OnInit, + QueryList, + ViewChild, + ViewChildren, +} from '@angular/core'; import { Router } from '@angular/router'; import { EuiLoadingService, EuiSplashScreenService, EuiTheme, EuiThemeService } from '@elemental-ui/core'; import { Subscription } from 'rxjs'; -import { Globals } from 'imx-qbm-dbts'; -import { ISessionState } from '../session/session-state'; -import { AuthenticationService } from '../authentication/authentication.service'; +import { HighContrastModeDetector } from '@angular/cdk/a11y'; +import { MatInput } from '@angular/material/input'; +import { Globals } from '@imx-modules/imx-qbm-dbts'; +import { ReCaptchaV3Service } from 'ng-recaptcha-2'; import { AppConfigService } from '../appConfig/appConfig.service'; -import { AuthConfigProvider } from '../authentication/auth-config-provider.interface'; +import { AuthConfigProvider, PreAuthStateType } from '../authentication/auth-config-provider.interface'; +import { AuthenticationService } from '../authentication/authentication.service'; +import { ErrorService } from '../base/error.service'; +import { CaptchaService } from '../captcha/captcha.service'; import { ClassloggerService } from '../classlogger/classlogger.service'; import { ExtDirective } from '../ext/ext.directive'; +import { ISessionState } from '../session/session-state'; import { SystemInfoService } from '../system-info/system-info.service'; -import { HighContrastModeDetector } from '@angular/cdk/a11y'; @Component({ selector: 'imx-login', templateUrl: './login.component.html', styleUrls: ['./login.component.scss'], }) -export class LoginComponent implements OnInit, OnDestroy { +export class LoginComponent implements OnInit, OnDestroy, AfterViewChecked { @ViewChild(ExtDirective, { static: true }) public directive: ExtDirective; + @ViewChildren('authPropertyInput') authPropertyInput: QueryList; + @ViewChildren('preAuthPropertyInput') preAuthPropertyInput: QueryList; + private firstTime: boolean = true; public title: string; public readonly product: { name: string; copyright: string } = { name: Globals.QIM_ProductNameFull, @@ -59,10 +75,12 @@ export class LoginComponent implements OnInit, OnDestroy { public configurationProviders: AuthConfigProvider[]; public logoUrl: string; public newUserConfigProvider: AuthConfigProvider; + public preAuthStateType = PreAuthStateType; private readonly newUserConfigProviderName = 'NewUser'; private readonly authProviderStorageKey = 'selectedAuthProvider'; private readonly subscriptions: Subscription[] = []; + private disposable: () => void; constructor( public readonly appConfigService: AppConfigService, @@ -74,24 +92,28 @@ export class LoginComponent implements OnInit, OnDestroy { private readonly splash: EuiSplashScreenService, private readonly busyService: EuiLoadingService, private readonly themeService: EuiThemeService, - private readonly detector: HighContrastModeDetector + private readonly detector: HighContrastModeDetector, + private readonly errorService: ErrorService, + private readonly captchaService: CaptchaService, + private readonly recaptchaV3Service: ReCaptchaV3Service, + private readonly changeDetection: ChangeDetectorRef, ) { this.title = this.appConfigService.Config.Title; this.subscriptions.push( this.appConfigService.onConfigTitleUpdated.subscribe(() => { this.title = this.appConfigService.Config.Title; - }) + }), ); this.subscriptions.push( - this.authentication.onSessionResponse.subscribe((sessionState: ISessionState) => { + this.authentication.onSessionResponse.subscribe(async (sessionState: ISessionState) => { this.logger.debug(this, 'LoginComponent - subscription - onSessionResponse'); this.logger.trace(this, 'sessionState', sessionState); const existingConfig = this.sessionState?.configurationProviders; this.sessionState = sessionState; if (this.sessionState.IsLoggedIn) { this.logger.debug(this, 'subscription - call navigate'); - this.router.navigate([this.appConfigService.Config.routeConfig.start], { queryParams: {} }); + await this.router.navigate([this.appConfigService.Config.routeConfig?.start], { queryParams: {} }); } else { // Cover the case where an error has occurred and the new sessionState does not contain the configurationProviders if (!this.sessionState.configurationProviders) { @@ -104,45 +126,29 @@ export class LoginComponent implements OnInit, OnDestroy { if (this.sessionState.configurationProviders && this.sessionState.configurationProviders.length > 0) { this.logger.debug(this, 'subscription - updating session config'); + this.sessionState.configurationProviders.map((configProvider) => { + configProvider.preAuthState = !!configProvider.preAuthState ? this.preAuthStateType.PreAuth : undefined; + }); this.selectedConfigProvider = this.sessionState.configurationProviders.find( - (authProvider) => authProvider.name === localStorage.getItem(this.authProviderStorageKey) + (authProvider) => authProvider.name === localStorage.getItem(this.authProviderStorageKey), ) || this.sessionState.configurationProviders[0]; this.onSelectAuthConfig(); } } - }) + }), ); + this.disposable = this.errorService.setTarget('login'); } - public async logoutOAuth(): Promise { - this.logger.debug(this, 'logoutOAuth'); - return this.authentication.logout(this.sessionState); - } - - public async login(): Promise { - this.logger.debug(this, 'LoginComponent - login'); - - if (this.selectedConfigProvider) { - if (this.selectedConfigProvider.isOAuth2) { - this.logger.debug(this, 'LoginComponent - login - oauth2'); - await this.authentication.oauthRedirect(this.selectedConfigProvider.name); - return; - } else if (this.selectedConfigProvider.customAuthFlow) { - throw new Error('Method not valid for a custom auth flow.'); - } - } + public ngAfterViewChecked(): void { + this.authPropertyInput.changes.subscribe(() => this.focusAuthProperty(false)); + this.preAuthPropertyInput.changes.subscribe(() => this.focusAuthProperty(true)); - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.busyService.show())); - try { - await this.authentication.login(this.loginData); - } finally { - this.logger.debug(this, 'LoginComponent - login - attempt completed'); - setTimeout(() => this.busyService.hide(overlayRef)); + if (this.firstTime) { + this.firstTime = false; + this.focusAuthProperty(true); } - - return Promise.resolve(); } public async ngOnInit(): Promise { @@ -170,16 +176,145 @@ export class LoginComponent implements OnInit, OnDestroy { } public ngOnDestroy(): void { + this.disposable(); this.subscriptions.forEach((subscription) => subscription.unsubscribe()); } + /** + * Calls authentication service logout function. + */ + public async logoutOAuth(): Promise { + this.logger.debug(this, 'logoutOAuth'); + return this.authentication.logout(this.sessionState); + } + + /** + * Calls the required login method. + */ + public async login(): Promise { + this.logger.debug(this, 'LoginComponent - login'); + + if (this.selectedConfigProvider) { + if (this.selectedConfigProvider.isOAuth2) { + this.logger.debug(this, 'LoginComponent - login - oauth2'); + await this.authentication.oauthRedirect(this.selectedConfigProvider.name); + return; + } else if (this.selectedConfigProvider.customAuthFlow) { + throw new Error('Method not valid for a custom auth flow.'); + } + } + + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } + try { + await this.authentication.login(this.loginData); + } finally { + this.logger.debug(this, 'LoginComponent - login - attempt completed'); + this.busyService.hide(); + } + + return Promise.resolve(); + } + + /** + * Updates the localStorage and calls initCustAuthFlowView with the selected configuration provider. + */ public onSelectAuthConfig(): void { this.logger.debug(this, 'LoginComponent - onSelectAuthConfig', this.selectedConfigProvider.name); localStorage.setItem(this.authProviderStorageKey, this.selectedConfigProvider.name); this.loginData = { Module: this.selectedConfigProvider.name }; + this.configurationProviders.map((provider) => { + if (provider.preAuthState === this.preAuthStateType.Captcha || provider.preAuthState === this.preAuthStateType.Auth) { + provider.preAuthState = this.preAuthStateType.PreAuth; + } + }); this.initCustomAuthFlowView(this.selectedConfigProvider); } + public async createNewAccount(): Promise { + // Prevent the content from being cleared incase the sidesheet is closed unsuccessfully + this.initCustomAuthFlowView(this.newUserConfigProvider, false); + } + + /** + * Checks if the login proceess needs captcha verification. + */ + public async checkPreAuth(): Promise { + let overlayRef = this.busyService.show(); + try { + const response = await this.authentication.preAuth(this.loginData); + if (response) { + this.setupCaptcha(); + } else { + this.selectedConfigProvider.preAuthState = this.preAuthStateType.Auth; + this.focusAuthProperty(false); + } + } finally { + this.busyService.hide(overlayRef); + } + } + + /** + * Setup the selected configuration provider to preAuth state. + */ + public async backToPreAuth(): Promise { + this.selectedConfigProvider.preAuthState = this.preAuthStateType.PreAuth; + this.selectedConfigProvider.authProps + ?.filter((authProp) => !authProp.disabled) + .map((authProp) => { + if (this.loginData && authProp.name) delete this.loginData[authProp.name]; + }); + } + + /** + * Verify the captcha with the recaptcha image component. + */ + public async onVerifyCaptcha(): Promise { + this.authentication.preAuthVerify(this.captchaService.Response); + } + + /** + * Checks, weather the form should be hidden. + */ + public get isFormHidden(): boolean { + return this.selectedConfigProvider?.isOAuth2 || !!this.selectedConfigProvider.preAuthProps?.length; + } + + /** + * Returns the selected configuration providere preAuthState. + */ + public get selectedProviderPreAuthState(): null | PreAuthStateType { + return this.selectedConfigProvider?.preAuthState ?? null; + } + + /** + * Checks, weather the login button should be hidden. + */ + public get showLoginButton(): boolean { + return this.selectedProviderPreAuthState == this.preAuthStateType.Auth || !this.selectedProviderPreAuthState; + } + + /** + * Checks, weather the back button should be hidden. + */ + public get showBackButton(): boolean { + return ( + this.selectedProviderPreAuthState == this.preAuthStateType.Auth || this.selectedProviderPreAuthState == this.preAuthStateType.Captcha + ); + } + + public get showCreateAccountButton(): boolean { + return ( + this.newUserConfigProvider && + (this.selectedProviderPreAuthState === this.preAuthStateType.PreAuth || + (!this.selectedProviderPreAuthState && !!this.selectedConfigProvider?.authProps?.length)) + ); + } + + /** + * Builds the login options. + */ private buildConfigurationProviders(): void { const providers = this.sessionState?.configurationProviders ?? []; @@ -197,6 +332,9 @@ export class LoginComponent implements OnInit, OnDestroy { this.configurationProviders = providers; } + /** + * Initializes the custom authentication by creating the entry component. + */ private initCustomAuthFlowView(configProvider: AuthConfigProvider, shouldClear = true): void { if (this.directive) { if (shouldClear) { @@ -204,14 +342,42 @@ export class LoginComponent implements OnInit, OnDestroy { } if (configProvider?.customAuthFlow) { this.directive.viewContainerRef.createComponent( - this.componentFactoryResolver.resolveComponentFactory(configProvider.customAuthFlow.getEntryComponent()) + this.componentFactoryResolver.resolveComponentFactory(configProvider.customAuthFlow.getEntryComponent()), ); } } } - public async createNewAccount(): Promise { - // Prevent the content from being cleared incase the sidesheet is closed unsuccessfully - this.initCustomAuthFlowView(this.newUserConfigProvider, false); + /** + * Setup captcha verification. + */ + private async setupCaptcha(): Promise { + if (this.captchaService.isReCaptchaV3) { + let overlayRef = this.busyService.show(); + this.recaptchaV3Service.execute('login').subscribe(async (result) => { + try { + await this.authentication.preAuthVerify(result); + this.selectedConfigProvider.preAuthState = this.preAuthStateType.Auth; + } finally { + this.busyService.hide(overlayRef); + } + }); + } else { + this.selectedConfigProvider.preAuthState = this.preAuthStateType.Captcha; + } + } + + /** + * Focuses an authentication property + * + */ + private focusAuthProperty(preAuth: boolean) { + this.changeDetection.detectChanges(); + const index = preAuth ? 0 : 1; + const iterable = preAuth ? this.preAuthPropertyInput.toArray() : this.authPropertyInput.toArray(); + if (iterable.length > index) { + iterable[index]?.focus(); + } + this.changeDetection.detectChanges(); } } diff --git a/imxweb/projects/qbm/src/lib/mast-head/mast-head-menu-item.interface.ts b/imxweb/projects/qbm/src/lib/mast-head/mast-head-menu-item.interface.ts index 3b509a2be..9039b72e2 100644 --- a/imxweb/projects/qbm/src/lib/mast-head/mast-head-menu-item.interface.ts +++ b/imxweb/projects/qbm/src/lib/mast-head/mast-head-menu-item.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qbm/src/lib/mast-head/mast-head-menu.interface.ts b/imxweb/projects/qbm/src/lib/mast-head/mast-head-menu.interface.ts index 8ce3ec48a..e690d231e 100644 --- a/imxweb/projects/qbm/src/lib/mast-head/mast-head-menu.interface.ts +++ b/imxweb/projects/qbm/src/lib/mast-head/mast-head-menu.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qbm/src/lib/mast-head/mast-head.component.html b/imxweb/projects/qbm/src/lib/mast-head/mast-head.component.html index 80d77b9e1..cf5542937 100644 --- a/imxweb/projects/qbm/src/lib/mast-head/mast-head.component.html +++ b/imxweb/projects/qbm/src/lib/mast-head/mast-head.component.html @@ -1,12 +1,25 @@ - + - + -

    +

    {{ productName }} {{ appConfig?.Config?.Title }}

    @@ -17,15 +30,22 @@
    - +
    - @@ -35,8 +55,7 @@ #LDS#Log out - @@ -44,8 +63,7 @@ - diff --git a/imxweb/projects/qbm/src/lib/mast-head/mast-head.component.scss b/imxweb/projects/qbm/src/lib/mast-head/mast-head.component.scss index 030d96cee..72df502f3 100644 --- a/imxweb/projects/qbm/src/lib/mast-head/mast-head.component.scss +++ b/imxweb/projects/qbm/src/lib/mast-head/mast-head.component.scss @@ -1,6 +1,3 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; -@import '@elemental-ui/core/src/styles/_eui_palette.scss'; - :host { .imx-masthead-app-name { display: inline-block; @@ -13,6 +10,8 @@ } .imx-masthead--controls { + display: flex; + button[mat-button] { &:not(.imx-masthead-user-button) { padding: 0; @@ -31,68 +30,9 @@ } } -/* hide the logo inside the masthead if needed */ -::ng-deep eui-masthead.no-logo eui-logo { - display: none; -} - .custom-logo { margin-left: -16px; max-height: 50px; max-width: 300px; cursor: pointer; -} - -@media only screen and (max-width: 768px) { - :host { - eui-masthead { - .mat-toolbar { - .mat-toolbar-row { - width: unset !important; - justify-content: center; - } - - .imx-masthead-space, - .imx-masthead--controls { - display: none; - } - } - } - - .eui-top-navigation-mobile { - .eui-top-navigation-mobile-footer { - .mat-button { - &.imx-masthead--icon-button { - width: 100%; - - .mat-button-wrapper span { - display: inline-block; - } - } - } - } - } - } -} - -.eui-dark-theme { - :host { - .mat-toolbar.mat-primary { - background-color: $color-blue-80; - color: $color-gray-5; - } - } -} - -.eui-contrast-theme { - :host { - ::ng-deep .eui-masthead { - background-color: $color-blue-80; - } - - .mat-toolbar.mat-primary { - background-color: $color-blue-80; - color: $color-gray-0; - } - } -} +} \ No newline at end of file diff --git a/imxweb/projects/qbm/src/lib/mast-head/mast-head.component.ts b/imxweb/projects/qbm/src/lib/mast-head/mast-head.component.ts index a1d8f6db5..9c9e5f2c6 100644 --- a/imxweb/projects/qbm/src/lib/mast-head/mast-head.component.ts +++ b/imxweb/projects/qbm/src/lib/mast-head/mast-head.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,24 +24,25 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; -import { Component, Input, OnDestroy } from '@angular/core'; +import { Component, effect, Input, OnDestroy } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { Router } from '@angular/router'; import { EuiLoadingService, EuiSidesheetService, EuiTopNavigationItem } from '@elemental-ui/core'; import { Subscription } from 'rxjs'; -import { AppConfigService } from '../appConfig/appConfig.service'; +import { TranslateService } from '@ngx-translate/core'; import { AboutComponent } from '../about/About.component'; +import { AppConfigService } from '../appConfig/appConfig.service'; import { AuthenticationService } from '../authentication/authentication.service'; -import { ISessionState } from '../session/session-state'; -import { MastHeadService } from './mast-head.service'; +import { calculateSidesheetWidth, isMobile } from '../base/sidesheet-helper'; import { ConfirmationService } from '../confirmation/confirmation.service'; -import { SystemInfoService } from '../system-info/system-info.service'; import { ConnectionComponent } from '../connection/connection.component'; -import { TranslateService } from '@ngx-translate/core'; import { ExtService } from '../ext/ext.service'; import { IExtension } from '../ext/extension'; +import { ProcessingQueueService } from '../processing-queue/processing-queue.service'; +import { ISessionState } from '../session/session-state'; +import { SystemInfoService } from '../system-info/system-info.service'; +import { MastHeadService } from './mast-head.service'; /** * Masthead of IMX web applications. It can contain dynamic menus or buttons, emitting menus/menu itmes when selected. @@ -104,10 +105,10 @@ import { IExtension } from '../ext/extension'; @Component({ selector: 'imx-mast-head', templateUrl: './mast-head.component.html', - styleUrls: ['./mast-head.component.scss'] + styleUrls: ['./mast-head.component.scss'], }) export class MastHeadComponent implements OnDestroy { - + public isQueueFinished: boolean; /** * When these {@link EuiTopNavigationItem|items} are set, the menu is displayed. */ @@ -118,17 +119,16 @@ export class MastHeadComponent implements OnDestroy { } public get isMobile(): boolean { - return document.body.offsetWidth <= 768; + return isMobile(); } public get isAuthenticated(): boolean { - return this.sessionState?.IsLoggedIn; + return this.sessionState?.IsLoggedIn ?? false; } public get isAppOverview(): boolean { return this.appConfig?.Config?.WebAppIndex === 'admin' && this.router.url === '/'; } - public get isAppAdminPortal(): boolean { return this.appConfig?.Config?.WebAppIndex === 'admin' && this.router.url === '/dashboard'; } @@ -146,6 +146,7 @@ export class MastHeadComponent implements OnDestroy { private readonly router: Router, private readonly dialog: MatDialog, private readonly confirmationService: ConfirmationService, + private queueService: ProcessingQueueService, private readonly busyService: EuiLoadingService, private readonly mastHeadService: MastHeadService, private readonly authentication: AuthenticationService, @@ -153,12 +154,13 @@ export class MastHeadComponent implements OnDestroy { private readonly translate: TranslateService, private readonly extService: ExtService, ) { - this.subscriptions.push(this.authentication.onSessionResponse.subscribe((sessionState: ISessionState) => - this.sessionState = sessionState - )); + effect(() => (this.isQueueFinished = this.queueService.isAllGroupsCompleted())); + this.subscriptions.push( + this.authentication.onSessionResponse.subscribe((sessionState: ISessionState) => (this.sessionState = sessionState)), + ); // apply custom logo from configuration - this.systemInfoService.getImxConfig().then(config => { + this.systemInfoService.getImxConfig().then((config) => { if (config.CompanyLogoUrl) { // make relative URL absolute if needed this.logoUrl = new URL(config.CompanyLogoUrl, this.appConfig.BaseUrl).href; @@ -167,15 +169,13 @@ export class MastHeadComponent implements OnDestroy { if (name) { this.productName = name; } - }); this.getDynamicExtensions(); } - public getDynamicExtensions(): void{ + public getDynamicExtensions(): void { this.extensions = this.extService.Registry['mastHead']; - } public showExtension(extension: IExtension): void { @@ -185,14 +185,14 @@ export class MastHeadComponent implements OnDestroy { } public ngOnDestroy(): void { - this.subscriptions.forEach(s => s.unsubscribe()); + this.subscriptions.forEach((s) => s.unsubscribe()); } /** * For navigating home, you know. */ public goHome(): void { - if (!this.isAppOverview) this.router.navigate([this.appConfig.Config.routeConfig.start], { queryParams: {} }); + if (!this.isAppOverview) this.router.navigate([this.appConfig.Config.routeConfig?.start], { queryParams: {} }); } /** @@ -208,30 +208,55 @@ export class MastHeadComponent implements OnDestroy { public async openConnection(): Promise { const data = await this.mastHeadService.getConnectionData(this.appConfig.Config.WebAppIndex); - await this.sideSheetService.open(ConnectionComponent, { - icon: 'rss', - title: this.translate.instant('#LDS#Heading Connection Information'), - padding: '0px', - width: 'max(700px, 60%)', - data: data - }).afterClosed().toPromise(); + await this.sideSheetService + .open(ConnectionComponent, { + icon: 'rss', + title: this.translate.instant('#LDS#Heading Connection Information'), + padding: '0px', + width: calculateSidesheetWidth(), + data: data, + }) + .afterClosed() + .toPromise(); } /** * Logs out and kills the session. */ public async logout(): Promise { - if (await this.confirmationService.confirm({ - Title: '#LDS#Heading Log Out', - Message: '#LDS#Are you sure you want to log out?', - identifier: 'confirm-logout-' - })) { - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.busyService.show())); + if ( + this.isQueueFinished && + (await this.confirmationService.confirm({ + Title: '#LDS#Heading Log Out', + Message: '#LDS#Are you sure you want to log out?', + identifier: 'confirm-logout-', + })) + ) { + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } + try { + this.queueService.clearProcessing(); + await this.authentication.logout(); + } finally { + this.busyService.hide(); + } + } + + if ( + !this.isQueueFinished && + (await this.confirmationService.confirm({ + Title: '#LDS#Heading Unfinished Processes', + Message: '#LDS#There are still background processes that are not completed. Are you sure you want to log out?', + identifier: 'confirm-logout-busy-queue', + })) + ) { + this.busyService.show(); try { + this.queueService.clearProcessing(); await this.authentication.logout(); } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } } } diff --git a/imxweb/projects/qbm/src/lib/mast-head/mast-head.module.ts b/imxweb/projects/qbm/src/lib/mast-head/mast-head.module.ts index 475913018..3cb5457dd 100644 --- a/imxweb/projects/qbm/src/lib/mast-head/mast-head.module.ts +++ b/imxweb/projects/qbm/src/lib/mast-head/mast-head.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,11 +27,12 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; -import { TranslateModule, TranslateLoader, MissingTranslationHandler } from '@ngx-translate/core'; +import { MissingTranslationHandler, TranslateLoader, TranslateModule } from '@ngx-translate/core'; -import { MastHeadComponent } from './mast-head.component'; -import { ImxTranslateLoader } from '../translation/imx-translate-loader'; +import { ExtModule } from '../ext/ext.module'; import { ImxMissingTranslationHandler } from '../translation/imx-missing-translation-handler'; +import { ImxTranslateLoader } from '../translation/imx-translate-loader'; +import { MastHeadComponent } from './mast-head.component'; import { MastHeadService } from './mast-head.service'; @NgModule({ @@ -40,22 +41,19 @@ import { MastHeadService } from './mast-head.service'; CommonModule, EuiCoreModule, EuiMaterialModule, + ExtModule, TranslateModule.forRoot({ loader: { provide: TranslateLoader, - useClass: ImxTranslateLoader + useClass: ImxTranslateLoader, }, missingTranslationHandler: { provide: MissingTranslationHandler, - useClass: ImxMissingTranslationHandler - } + useClass: ImxMissingTranslationHandler, + }, }), ], - exports: [ - MastHeadComponent - ], - providers: [ - MastHeadService - ] + exports: [MastHeadComponent], + providers: [MastHeadService], }) -export class MastHeadModule { } +export class MastHeadModule {} diff --git a/imxweb/projects/qbm/src/lib/mast-head/mast-head.service.ts b/imxweb/projects/qbm/src/lib/mast-head/mast-head.service.ts index 4ba16e36a..48627da8d 100644 --- a/imxweb/projects/qbm/src/lib/mast-head/mast-head.service.ts +++ b/imxweb/projects/qbm/src/lib/mast-head/mast-head.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,23 +25,23 @@ */ import { Injectable } from '@angular/core'; +import { SessionInfoData } from '@imx-modules/imx-api-qbm'; +import { TranslateService } from '@ngx-translate/core'; import { Subject } from 'rxjs'; -import { MastHeadMenu } from './mast-head-menu.interface'; -import { MastHeadMenuItem } from './mast-head-menu-item.interface'; import { AppConfigService } from '../appConfig/appConfig.service'; -import { TranslateService } from '@ngx-translate/core'; -import { SessionInfoData } from 'imx-api-qbm'; +import { MastHeadMenuItem } from './mast-head-menu-item.interface'; +import { MastHeadMenu } from './mast-head-menu.interface'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class MastHeadService { public itemClickedSubject: Subject = new Subject(); constructor( private readonly appConfig: AppConfigService, - private readonly translate: TranslateService - ) { } + private readonly translate: TranslateService, + ) {} public itemClicked(menuItem: MastHeadMenu | MastHeadMenuItem): void { this.itemClickedSubject.next(menuItem); @@ -67,6 +67,9 @@ export class MastHeadService { private getLocaleDocumentationPath(): string { const docPaths = this.appConfig.Config.LocalDocPath; const currentLanguage = this.translate.currentLang; + if (!docPaths) { + return ''; + } const directLocaleMatch = docPaths[currentLanguage]; // If the browser culture directly matches a key for documentation paths, then use that if (directLocaleMatch) { @@ -77,6 +80,6 @@ export class MastHeadService { const docKeys = Object.keys(docPaths); const matchingKey = docKeys.find((element) => element.includes(currentLanguageShort)); // If still no match, fallback to the first documentation path entry - return docPaths[matchingKey] ?? docPaths[docKeys[0]]; + return (matchingKey ? docPaths[matchingKey] : docPaths[docKeys[0]]) ?? docPaths[docKeys[0]]; } } diff --git a/imxweb/projects/qbm/src/lib/master-detail/master-detail.component.html b/imxweb/projects/qbm/src/lib/master-detail/master-detail.component.html deleted file mode 100644 index ee206cbd4..000000000 --- a/imxweb/projects/qbm/src/lib/master-detail/master-detail.component.html +++ /dev/null @@ -1,15 +0,0 @@ -
    -
    - -
    -
    -
    - -
    -
    -
    - -
    -
    -
    -
    \ No newline at end of file diff --git a/imxweb/projects/qbm/src/lib/master-detail/master-detail.component.scss b/imxweb/projects/qbm/src/lib/master-detail/master-detail.component.scss index ae0d0e492..2a6304611 100644 --- a/imxweb/projects/qbm/src/lib/master-detail/master-detail.component.scss +++ b/imxweb/projects/qbm/src/lib/master-detail/master-detail.component.scss @@ -1,5 +1,6 @@ @use '@angular/material' as mat; -@import "variables.scss"; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/variables'; $headerheight: 43; @@ -25,17 +26,17 @@ $headerheight: 43; } .imx-mdc-detail-opened .imx-mdc-master { - max-width: calc( 100% - 40em ); + max-width: calc(100% - 40em); width: 100%; } .imx-mdc-detail-close .imx-mdc-master { - max-width: calc( 100% - 30px ); + max-width: calc(100% - 30px); width: 100%; } .imx-mdc-detail { - background: none repeat scroll 0 0 mat.get-color-from-palette($asher-gray-palette, 900); + background: none repeat scroll 0 0 mat.m2-get-color-from-palette($asher-gray-palette, 900); border-width: 0; display: block; position: absolute; @@ -257,7 +258,7 @@ $headerheight: 43; /* #region VI_Styles_LayoutPanel_Default (Detail Pane CSS) */ .imx-mdc-detail .LayoutpPropRow { - border-bottom: 1px solid $VI_Common_Color_LightGray; + border-bottom: 1px solid $color-gray-30; line-height: 20px; min-height: 26px; } @@ -265,7 +266,7 @@ $headerheight: 43; .imx-mdc-detail .LayoutpPropCol0 { width: 146px; max-width: 146px; - color: $VI_Common_Color_Font; + color: $color-gray-80; } .imx-mdc-detail .LayoutpPropCol1 { @@ -273,8 +274,8 @@ $headerheight: 43; /* leave some space for scrollbars*/ max-width: 245px; /* #27180 */ - color: $VI_Common_Color_Font_Secondary; + color: $color-gray-80; font-weight: bold; } -/*#endregion*/ \ No newline at end of file +/*#endregion*/ diff --git a/imxweb/projects/qbm/src/lib/master-detail/master-detail.component.ts b/imxweb/projects/qbm/src/lib/master-detail/master-detail.component.ts deleted file mode 100644 index 00e43faa3..000000000 --- a/imxweb/projects/qbm/src/lib/master-detail/master-detail.component.ts +++ /dev/null @@ -1,139 +0,0 @@ -/* - * ONE IDENTITY LLC. PROPRIETARY INFORMATION - * - * This software is confidential. One Identity, LLC. or one of its affiliates or - * subsidiaries, has supplied this software to you under terms of a - * license agreement, nondisclosure agreement or both. - * - * You may not copy, disclose, or use this software except in accordance with - * those terms. - * - * - * Copyright 2023 One Identity LLC. - * ALL RIGHTS RESERVED. - * - * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR - * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, OR - * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE - * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE - * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING - * THIS SOFTWARE OR ITS DERIVATIVES. - * - */ - -import { Component, OnInit, ViewEncapsulation, HostBinding, ElementRef, ViewChild } from '@angular/core'; -import { DeviceStateService } from '../services/device-state.service'; -import * as elementResizeDetector from 'element-resize-detector'; - -/** @deprecated This component is deprecated and will be removed in a future release.*/ -@Component({ - selector: 'imx-master-detail', - templateUrl: './master-detail.component.html', - styleUrls: ['./master-detail.component.scss'], - encapsulation: ViewEncapsulation.None -}) -export class MasterDetailComponent implements OnInit { - public detailClosed: boolean; - public isSinglePanel: boolean; - public modalRoot: ElementRef; - - @HostBinding('class') public defaultHostClasses = 'imx-flex imx-flex-child'; - - @ViewChild('mdcContainer', { static: true }) public mdcContainer: ElementRef; - @ViewChild('mdcDetail', { static: true }) public mdcDetail: ElementRef; - - private detailContainerClass = 'imx-mdc-detailPopupContainer'; - - constructor(public deviceStateService: DeviceStateService) {} - - public ngOnInit() { - this.detailClosed = false; - this.isSinglePanel = false; - if (this.isPhoneDevice()) { - // initially close the detail panel on phone device - this.toggleDetailPane(false); - } - - const elementResizeDetectorMaker = elementResizeDetector; - - const erd = elementResizeDetectorMaker({ strategy: 'scroll' }); - erd.listenTo(this.mdcContainer.nativeElement, () => { - this.checkMode(); - }); - } - - // #region Css Classes - public masterDetailRootClasses() { - return { - 'imx-mdc-masterdetail imx-flex-child': true, - 'imx-mdc-detail-close': this.detailClosed, - 'imx-mdc-detail-opened': !this.detailClosed, - 'imx-mdc-singlePanel': this.isSinglePanel - }; - } - - public masterClasses() { - return 'imx-mdc-master imx-flex'; - } - - public detailClasses() { - return { - 'imx-mdc-detail imx-flex': true, - 'imx-mdc-detail-close': this.detailClosed - }; - } - - public detailHeaderClasses() { - return 'imx-mdc-detail-header'; - } - - public detailContentClasses() { - return { - 'imx-mdc-detail-content imx-flex': true, - 'imx-mdc-detail-closed': this.detailClosed - }; - } - - public detailContentWrapperClasses() { - return 'imx-mdc-detail-contentWrapper'; - } - // #endregion - - public getDeviceState(): string { - return this.deviceStateService.deviceState; - } - - public isPhoneDevice(): boolean { - return this.deviceStateService.isPhoneDevice(); - } - - public toggleDetailPane(state?: boolean) { - this.detailClosed = !this.detailClosed; - // force to open or to close the detail panel - state = typeof state === 'boolean' ? state : undefined; - if (state !== undefined) { - this.detailClosed = state; - } - } - - public openDetailPane() { - if (this.isPhoneDevice()) { - this.toggleDetailPane(false); - } - } - - public checkMode() { - const detailPopupContainer = this.mdcContainer; - if (this.isPhoneDevice()) { - this.isSinglePanel = true; - return; - } - // normal mode - // --> move detail panel next to the master panel - this.mdcContainer.nativeElement.appendChild(this.mdcDetail.nativeElement); - this.isSinglePanel = false; - } -} diff --git a/imxweb/projects/qbm/src/lib/menu/menu-item/menu-item.interface.ts b/imxweb/projects/qbm/src/lib/menu/menu-item/menu-item.interface.ts index 94cca45fc..13ed7a6f8 100644 --- a/imxweb/projects/qbm/src/lib/menu/menu-item/menu-item.interface.ts +++ b/imxweb/projects/qbm/src/lib/menu/menu-item/menu-item.interface.ts @@ -1,4 +1,4 @@ -import { ProjectConfig } from 'imx-api-qbm'; +import { ProjectConfig } from '@imx-modules/imx-api-qbm'; import { NavigationCommandsMenuItem } from './navigation-commands-menu-item.interface'; /* @@ -12,7 +12,7 @@ import { NavigationCommandsMenuItem } from './navigation-commands-menu-item.inte * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -33,7 +33,7 @@ export interface MenuItem { readonly id?: string; /** Display name. */ - readonly title: string; + readonly title?: string; /** Returns a descriptive text, intended for tooltips. */ readonly description?: string; @@ -52,7 +52,11 @@ export interface MenuItem { /** Submenu items. */ items?: MenuItem[]; - } -export type MenuFactory = (preProps: string[], features: string[], projectConfig?: ProjectConfig, groups?: string[]) => MenuItem; +export type MenuFactory = ( + preProps: string[], + features: string[], + projectConfig?: ProjectConfig, + groups?: string[], +) => MenuItem | undefined; diff --git a/imxweb/projects/qbm/src/lib/menu/menu-item/menu-item.spec.ts b/imxweb/projects/qbm/src/lib/menu/menu-item/menu-item.spec.ts index 88cd626e6..8e960f27b 100644 --- a/imxweb/projects/qbm/src/lib/menu/menu-item/menu-item.spec.ts +++ b/imxweb/projects/qbm/src/lib/menu/menu-item/menu-item.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,87 +24,53 @@ * */ -import { GroupMenuItem } from './group-menu-item'; -import { NavigationMenuItem } from './navigation-menu-item'; import { RelatedApplicationMenuItem } from './related-application-menu-item'; -describe('GroupMenuItem', () => { - [ - { - menuItems: [], - expectedEnabled: false - }, - { - menuItems: [{ title: '' }], - expectedEnabled: false - }, - { - menuItems: [{ title: '' }], - expectedEnabled: false - }, - { - menuItems: [{ title: '' }], - expectedEnabled: true - } - ].forEach(testcase => - it('should be created with ' + (testcase.menuItems ? testcase.menuItems.length.toString() : 'undefined') + ' menuItems', () => { - const item = new GroupMenuItem('', '', testcase.menuItems); - expect(item.items.length).toEqual(testcase.menuItems.length); - }) - ); -}); - -describe('NavigationMenuItem', () => { - it('should be created', () => { - expect(new NavigationMenuItem('', '', '', '')).toBeDefined(); - }); -}); - describe('RelatedApplicationMenuItem', () => { [ { - appDisplayType: 'NR', - expectedTrigger: () => {} + appDisplayType: 'NR', + expectedTrigger: () => {}, }, { - appDisplayType: 'PP', - expectedTrigger: () => {} + appDisplayType: 'PP', + expectedTrigger: () => {}, }, { - appDisplayType: 'NV', - expectedTrigger: () => {} + appDisplayType: 'NV', + expectedTrigger: () => {}, }, { - appDisplayType: 'NT', - expectedTrigger: () => {} - } - ].forEach(testcase => + appDisplayType: 'NT', + expectedTrigger: () => {}, + }, + ].forEach((testcase) => it('should be created', () => { - const app = { - uid: '', - displayName: '', - description: '', - displayType: testcase.appDisplayType as 'NR' | 'PP' | 'NV' | 'NT', - uidParent: '' - }; + const app = { + uid: '', + displayName: '', + description: '', + displayType: testcase.appDisplayType as 'NR' | 'PP' | 'NV' | 'NT', + uidParent: '', + }; - const item = new RelatedApplicationMenuItem(app); + const item = new RelatedApplicationMenuItem(app); - expect(item.id).toEqual(app.uid); - expect(item.title).toEqual(app.displayName); - expect(item.description).toEqual(app.description); - expect(() => item.Trigger()).not.toThrow(); - }) + expect(item.id).toEqual(app.uid); + expect(item.title).toEqual(app.displayName); + expect(item.description).toEqual(app.description); + expect(() => item.Trigger()).not.toThrow(); + }), ); it('should be created', () => { const item = new RelatedApplicationMenuItem({ - uid: '', - displayName: '', - description: '', - displayType: undefined as 'NR' | 'PP' | 'NV' | 'NT', - uidParent: '' + uid: '', + displayName: '', + description: '', + displayType: undefined as any, + uidParent: '', }); expect(() => item.Trigger()).toThrow(); - }) -}); \ No newline at end of file + }); +}); diff --git a/imxweb/projects/qbm/src/lib/menu/menu-item/navigation-commands-menu-item.interface.ts b/imxweb/projects/qbm/src/lib/menu/menu-item/navigation-commands-menu-item.interface.ts index 126e17bd7..42bfa0a07 100644 --- a/imxweb/projects/qbm/src/lib/menu/menu-item/navigation-commands-menu-item.interface.ts +++ b/imxweb/projects/qbm/src/lib/menu/menu-item/navigation-commands-menu-item.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qbm/src/lib/menu/menu-item/related-application-menu-item.ts b/imxweb/projects/qbm/src/lib/menu/menu-item/related-application-menu-item.ts index 6441cde61..ad2a4e52c 100644 --- a/imxweb/projects/qbm/src/lib/menu/menu-item/related-application-menu-item.ts +++ b/imxweb/projects/qbm/src/lib/menu/menu-item/related-application-menu-item.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,34 +29,44 @@ import { RelatedApplication } from './related-application.interface'; /** Menu item for a link to an external web site */ export class RelatedApplicationMenuItem implements MenuItem { - public get id(): string { return this.app.uid; } - public get title(): string { return this.app.displayName; } - public get description(): string { return this.app.description; } + public get id(): string { + return this.app.uid; + } + public get title(): string { + return this.app.displayName; + } + public get description(): string { + return this.app.description; + } - constructor(private readonly app: RelatedApplication) { } + constructor(private readonly app: RelatedApplication) {} - public Trigger(): void { - // TODO (TFS number 805756): VI.WebRuntime.RuntimeUtil.CheckIsValidUrlForRedirect(url); + public Trigger(): void { + // TODO (TFS number 805756): VI.WebRuntime.RuntimeUtil.CheckIsValidUrlForRedirect(url); - if (this.app.displayType === 'NV') { // content and navigation - // TODO (TFS number 805756): show in VI_Common_ExternalFormHost - return; - } - - if (this.app.displayType === 'PP') { // Popup - // TODO (TFS number 805756): show in popup - return; - } + if (this.app.displayType === 'NV') { + // content and navigation + // TODO (TFS number 805756): show in VI_Common_ExternalFormHost + return; + } - if (this.app.displayType === 'NR') { // Content - return; - } + if (this.app.displayType === 'PP') { + // Popup + // TODO (TFS number 805756): show in popup + return; + } - if (this.app.displayType === 'NT') { // Content, navigation and header - // TODO (TFS number 805756): directly redirect to the URL - return; - } + if (this.app.displayType === 'NR') { + // Content + return; + } - throw new Error('MenuItem - the related application has an invalid displayType.'); + if (this.app.displayType === 'NT') { + // Content, navigation and header + // TODO (TFS number 805756): directly redirect to the URL + return; } + + throw new Error('MenuItem - the related application has an invalid displayType.'); + } } diff --git a/imxweb/projects/qbm/src/lib/menu/menu-item/related-application.interface.ts b/imxweb/projects/qbm/src/lib/menu/menu-item/related-application.interface.ts index 4556f147d..0c385ce8f 100644 --- a/imxweb/projects/qbm/src/lib/menu/menu-item/related-application.interface.ts +++ b/imxweb/projects/qbm/src/lib/menu/menu-item/related-application.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,9 +26,9 @@ /** Represents a link to an external web site that should be displayed in the menu. */ export interface RelatedApplication { - displayType: 'NR' | 'PP' | 'NV' | 'NT'; - uid: string; - displayName: string; - description: string; - uidParent: string; + displayType: 'NR' | 'PP' | 'NV' | 'NT'; + uid: string; + displayName: string; + description: string; + uidParent: string; } diff --git a/imxweb/projects/qbm/src/lib/menu/menu.component.html b/imxweb/projects/qbm/src/lib/menu/menu.component.html deleted file mode 100644 index 8c0209df0..000000000 --- a/imxweb/projects/qbm/src/lib/menu/menu.component.html +++ /dev/null @@ -1,19 +0,0 @@ - diff --git a/imxweb/projects/qbm/src/lib/menu/menu.component.scss b/imxweb/projects/qbm/src/lib/menu/menu.component.scss deleted file mode 100644 index 69d6c73f0..000000000 --- a/imxweb/projects/qbm/src/lib/menu/menu.component.scss +++ /dev/null @@ -1,113 +0,0 @@ -@import "variables.scss"; -@import '@elemental-ui/core/src/styles/_eui_palette.scss'; - -:host { - background-color: $Imx_White-two; - - button.mat-menu-item, - div.mat-menu-content, - .mat-tab-link { - font-style: normal; - font-stretch: normal; - line-height: normal; - letter-spacing: normal; - color: $VI_Common_Color_Gray; - min-width: 10px !important; - } - - .mat-tab-link, - .mat-tab-link:focus, - .mat-tab-link:hover { - text-decoration: none; - } - - .mat-tab-link.mat-tab-label-active { - font-weight: bold; - } - - span { - display: inline-block !important; - } - - button.mat-menu-item { - box-sizing: border-box; - } - - button.mat-menu-item:hover, - button.mat-menu-item:focus { - background-color: $white !important; - } - - .mat-tab-nav-bar { - background-color: $white; - - .mat-tab-link { - font-size: 16px; - color: $black-6; - } - } - - .imx-mein-menu-item { - font-size: 16px; - padding: 10px 24px 10px 24px; - color: $black-7; - } -} - -.eui-dark-theme { - :host { - background-color: $color-gray-80; - - button.mat-menu-item, - div.mat-menu-content, - .mat-tab-link { - color: $color-gray-5; - } - - button.mat-menu-item:hover, - button.mat-menu-item:focus { - background-color: $color-gray-70; - } - - .mat-tab-nav-bar { - background-color: $color-gray-80; - - .mat-tab-link { - color: $color-gray-5; - } - } - - .imx-mein-menu-item { - color: $color-gray-5; - } - } -} - -.eui-contrast-theme { - :host { - background-color: $color-gray-100; - - button.mat-menu-item, - div.mat-menu-content, - .mat-tab-link { - color: $color-gray-0; - } - - button.mat-menu-item:hover, - button.mat-menu-item:focus { - background-color: $color-gray-90; - } - - .mat-tab-nav-bar { - background-color: $color-gray-100; - - .mat-tab-link { - color: $color-gray-0; - } - } - - .imx-mein-menu-item { - color: $color-gray-0; - } - } -} diff --git a/imxweb/projects/qbm/src/lib/menu/menu.component.ts b/imxweb/projects/qbm/src/lib/menu/menu.component.ts deleted file mode 100644 index b42e42a6b..000000000 --- a/imxweb/projects/qbm/src/lib/menu/menu.component.ts +++ /dev/null @@ -1,106 +0,0 @@ -/* - * ONE IDENTITY LLC. PROPRIETARY INFORMATION - * - * This software is confidential. One Identity, LLC. or one of its affiliates or - * subsidiaries, has supplied this software to you under terms of a - * license agreement, nondisclosure agreement or both. - * - * You may not copy, disclose, or use this software except in accordance with - * those terms. - * - * - * Copyright 2023 One Identity LLC. - * ALL RIGHTS RESERVED. - * - * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR - * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, OR - * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE - * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE - * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING - * THIS SOFTWARE OR ITS DERIVATIVES. - * - */ - -import { Component, ErrorHandler, Input } from '@angular/core'; -import { Router } from '@angular/router'; -import { TranslateService } from '@ngx-translate/core'; - -import { MenuItem } from './menu-item/menu-item.interface'; -import { ClassloggerService } from '../classlogger/classlogger.service'; - -/** - * Displays a menu and provides logic for navigation - */ -@Component({ - selector: 'imx-menu', - templateUrl: './menu.component.html', - styleUrls: ['./menu.component.scss'] -}) -export class MenuComponent { -/** - * The menu structure - */ - @Input() public menuItems: MenuItem[]; - - private errorMessageNonExistingRoute = ''; - - constructor( - private readonly router: Router, - private readonly logger: ClassloggerService, - private readonly errorHandler: ErrorHandler, - translator: TranslateService) { - translator.get('#LDS#the route does not exist').subscribe(value => this.errorMessageNonExistingRoute = value); - } - - public isActive(item: MenuItem): boolean { - if (!item) { - return false; - } - - if (item.items) { - for (const subitem of item.items) { - if (this.isActive(subitem)) { - return true; - } - } - } - - const itemIsActive = this.router.url === item.route; - - if (itemIsActive) { - this.logger.debug('currently active menu item:', item); - } - - return itemIsActive; - } - - public navigate(item: MenuItem): void { - if (item.trigger) { - this.logger.debug(this, 'call trigger'); - item.trigger(); - return; - } - - if (item.route) { - const route = this.router.config.find(configItem => configItem.path === item.route); - - if (route) { - this.logger.debug(this, 'navigate to route'); - this.router.navigate([`/${item.route}`]); - } else { - this.errorHandler.handleError(this.errorMessageNonExistingRoute); - } - - this.logger.trace(this, item.route); - } - - if (item.navigationCommands) { - this.logger.debug(this, 'navigate to route'); - this.router.navigate(item.navigationCommands.commands); - this.logger.trace(this, item.navigationCommands); - } - } -} diff --git a/imxweb/projects/qbm/src/lib/menu/menu.module.ts b/imxweb/projects/qbm/src/lib/menu/menu.module.ts deleted file mode 100644 index 88c66cad2..000000000 --- a/imxweb/projects/qbm/src/lib/menu/menu.module.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * ONE IDENTITY LLC. PROPRIETARY INFORMATION - * - * This software is confidential. One Identity, LLC. or one of its affiliates or - * subsidiaries, has supplied this software to you under terms of a - * license agreement, nondisclosure agreement or both. - * - * You may not copy, disclose, or use this software except in accordance with - * those terms. - * - * - * Copyright 2023 One Identity LLC. - * ALL RIGHTS RESERVED. - * - * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR - * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, OR - * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE - * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE - * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING - * THIS SOFTWARE OR ITS DERIVATIVES. - * - */ - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { MatMenuModule } from '@angular/material/menu'; -import { MatTabsModule } from '@angular/material/tabs'; -import { TranslateModule } from '@ngx-translate/core'; -import { MatButtonModule } from '@angular/material/button'; - -import { MenuComponent } from './menu.component'; -import { MenuService } from './menu.service'; - -@NgModule({ - declarations: [MenuComponent], - imports: [CommonModule, MatMenuModule, MatTabsModule, MatButtonModule, TranslateModule], - exports: [MenuComponent], - providers: [MenuService], -}) -export class MenuModule {} diff --git a/imxweb/projects/qbm/src/lib/menu/menu.service.ts b/imxweb/projects/qbm/src/lib/menu/menu.service.ts index e48dfe538..c8a45a0b8 100644 --- a/imxweb/projects/qbm/src/lib/menu/menu.service.ts +++ b/imxweb/projects/qbm/src/lib/menu/menu.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,19 +28,17 @@ import { Injectable } from '@angular/core'; import { EuiTopNavigationItem, EuiTopNavigationItemType } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { ProjectConfig } from 'imx-api-qbm'; +import { ProjectConfig } from '@imx-modules/imx-api-qbm'; import { MenuFactory, MenuItem } from './menu-item/menu-item.interface'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class MenuService { - private factories: MenuFactory[] = []; + private factories: (MenuFactory | undefined)[] = []; - constructor( - private readonly translate: TranslateService - ) { } + constructor(private readonly translate: TranslateService) {} public addMenuFactories(...factories: MenuFactory[]): void { this.factories.push(...factories); @@ -50,24 +48,32 @@ export class MenuService { this.factories = []; } - public async getMenuItems(preProps: string[], features: string[], allowEmpty: boolean = false, projectConfig?: ProjectConfig, groups?: string[]): Promise { + public async getMenuItems( + preProps: string[], + features: string[], + allowEmpty: boolean = false, + projectConfig?: ProjectConfig, + groups?: string[], + ): Promise { const menuItems: MenuItem[] = []; this.factories - .map(factory => factory(preProps, features, projectConfig, groups || [])) - .filter(menu => menu && (allowEmpty || (menu.items && menu.items.length > 0))) + .map((factory) => factory?.(preProps, features, projectConfig, groups)) + .filter((menu) => menu && (allowEmpty || (menu.items && menu.items.length > 0))) .sort((item1, item2) => this.compareMenuItems(item1, item2)) - .forEach(menu => { - const existing = menu.id != null && menuItems.find(item => item.id === menu.id); + .forEach((menu) => { + const existing = menu?.id != null && menuItems.find((item) => item.id === menu.id); if (existing) { if (existing.items) { // Here only splice it there are items, otherwise this is a flat home button and it already exists - existing.items.splice(-1, 0, ...menu.items); + existing.items.splice(-1, 0, ...(menu.items ?? [])); existing.items = this.sortMenuItems(existing.items); } } else { - menuItems.push(menu); - menu.items = this.sortMenuItems(menu.items); + if (menu?.id != null) { + menuItems.push(menu); + menu.items = this.sortMenuItems(menu.items ?? []); + } } }); @@ -79,7 +85,7 @@ export class MenuService { for (const menuItem of menuItems) { const hasSubItems = menuItem.items && menuItem.items.length > 0; - const caption = await this.translate.get(menuItem.title).toPromise(); + const caption = menuItem.title != null ? this.translate.instant(menuItem.title) : ''; const navItem: EuiTopNavigationItem = { type: hasSubItems ? EuiTopNavigationItemType.Menu : EuiTopNavigationItemType.RouterLink, text: caption, @@ -87,13 +93,13 @@ export class MenuService { matrixParams: 'exact', queryParams: 'exact', paths: 'subset', - fragment: 'exact' + fragment: 'exact', }, }; if (hasSubItems) { - navItem.items = await this.getNavigationItems(menuItem.items); + navItem.items = await this.getNavigationItems(menuItem.items ?? []); } else { - navItem.url = menuItem.route ? menuItem.route : menuItem.navigationCommands.commands; + navItem.url = menuItem.route ? menuItem.route : menuItem.navigationCommands?.commands; } navItems.push(navItem); } @@ -104,11 +110,12 @@ export class MenuService { if (!items) { return items; } - return items.sort((item1, item2) => this.compareMenuItems(item1, item2)) - .filter((item, index, array) => !item.id || index === array.findIndex(t => t.id === item.id)); + return items + .sort((item1, item2) => this.compareMenuItems(item1, item2)) + .filter((item, index, array) => !item.id || index === array.findIndex((t) => t.id === item.id)); } - private compareMenuItems(item1: MenuItem, item2: MenuItem): number { - return +!item1.sorting - +!item2.sorting || item1.sorting?.toString().localeCompare(item2.sorting?.toString()); + private compareMenuItems(item1: MenuItem | undefined, item2: MenuItem | undefined): number { + return (+!item1?.sorting - +!item2?.sorting || item1?.sorting?.toString().localeCompare(item2?.sorting?.toString() ?? '')) ?? 0; } } diff --git a/imxweb/projects/qbm/src/lib/message-dialog/message-dialog-result.enum.ts b/imxweb/projects/qbm/src/lib/message-dialog/message-dialog-result.enum.ts index a22930801..abacac1b5 100644 --- a/imxweb/projects/qbm/src/lib/message-dialog/message-dialog-result.enum.ts +++ b/imxweb/projects/qbm/src/lib/message-dialog/message-dialog-result.enum.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,5 +28,5 @@ export enum MessageDialogResult { OkResult, CancelResult, YesResult, - NoResult + NoResult, } diff --git a/imxweb/projects/qbm/src/lib/message-dialog/message-dialog.component.html b/imxweb/projects/qbm/src/lib/message-dialog/message-dialog.component.html index 4acc30ea5..059dc0087 100644 --- a/imxweb/projects/qbm/src/lib/message-dialog/message-dialog.component.html +++ b/imxweb/projects/qbm/src/lib/message-dialog/message-dialog.component.html @@ -1,15 +1,54 @@ -

    {{data.Title}}

    +
    + + +

    + {{ data.Title }} +

    +
    -
    {{ data.Message }}
    +
    + {{ message }} +
    - - - - - + + + + + diff --git a/imxweb/projects/qbm/src/lib/message-dialog/message-dialog.component.scss b/imxweb/projects/qbm/src/lib/message-dialog/message-dialog.component.scss index ad9d752d4..4d2113207 100644 --- a/imxweb/projects/qbm/src/lib/message-dialog/message-dialog.component.scss +++ b/imxweb/projects/qbm/src/lib/message-dialog/message-dialog.component.scss @@ -5,3 +5,10 @@ max-height: 400px; max-width: 600px; } +.mat-mdc-dialog-title { + align-items: end; + h2{ + font-weight: inherit; + font-size: inherit; + } +} diff --git a/imxweb/projects/qbm/src/lib/message-dialog/message-dialog.component.ts b/imxweb/projects/qbm/src/lib/message-dialog/message-dialog.component.ts index ffd33896b..1602e310b 100644 --- a/imxweb/projects/qbm/src/lib/message-dialog/message-dialog.component.ts +++ b/imxweb/projects/qbm/src/lib/message-dialog/message-dialog.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,24 +25,36 @@ */ import { Component, Inject } from '@angular/core'; -import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { MessageParameter } from './message-parameter.interface'; import { MessageDialogResult } from './message-dialog-result.enum'; +import { MessageDialogService } from './message-dialog.service'; +import { MessageParameter } from './message-parameter.interface'; @Component({ selector: 'imx-message-dialog', templateUrl: './message-dialog.component.html', - styleUrls: ['./message-dialog.component.scss'] + styleUrls: ['./message-dialog.component.scss'], }) export class MessageDialogComponent { public readonly MessageDialogResult = MessageDialogResult; // Enables use of this Enum in Angular Templates. constructor( public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: MessageParameter) { } + public messageDialogService: MessageDialogService, + @Inject(MAT_DIALOG_DATA) public data: MessageParameter, + ) {} public click(state: MessageDialogResult): void { this.dialogRef.close(state); } + + public get messages(): (string | undefined)[] { + let messages = this.messageDialogService.errorMessages$.value; + if (!!messages && messages.length > 0) { + return messages.filter((message, index) => index == messages.indexOf(message)); + } else { + return [this.data.Message]; + } + } } diff --git a/imxweb/projects/o3t/src/public_api.ts b/imxweb/projects/qbm/src/lib/message-dialog/message-dialog.service.ts similarity index 73% rename from imxweb/projects/o3t/src/public_api.ts rename to imxweb/projects/qbm/src/lib/message-dialog/message-dialog.service.ts index a50b468f8..30a9880f0 100644 --- a/imxweb/projects/o3t/src/public_api.ts +++ b/imxweb/projects/qbm/src/lib/message-dialog/message-dialog.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,11 +24,10 @@ * */ -/* - * Public API Surface of o3t - */ +import { Injectable } from '@angular/core'; +import { BehaviorSubject } from 'rxjs'; -export { O3TConfigModule } from './lib/o3t-config.module'; -export { ApiService } from './lib/api.service'; -export { TeamsComponent } from './lib/teams/teams.component'; -export { TeamsModule } from './lib/teams/teams.module'; +@Injectable() +export class MessageDialogService { + public errorMessages$: BehaviorSubject = new BehaviorSubject([]); +} diff --git a/imxweb/projects/qbm/src/lib/message-dialog/message-parameter.interface.ts b/imxweb/projects/qbm/src/lib/message-dialog/message-parameter.interface.ts index be2c84ee9..d0ac26d64 100644 --- a/imxweb/projects/qbm/src/lib/message-dialog/message-parameter.interface.ts +++ b/imxweb/projects/qbm/src/lib/message-dialog/message-parameter.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,4 +31,5 @@ export interface MessageParameter { Message?: string; identifier?: string; Parameter?: any[]; + icon?: string; } diff --git a/imxweb/projects/qbm/src/lib/model-css/model-css.service.ts b/imxweb/projects/qbm/src/lib/model-css/model-css.service.ts index fb1b11dc9..fe16e4cec 100644 --- a/imxweb/projects/qbm/src/lib/model-css/model-css.service.ts +++ b/imxweb/projects/qbm/src/lib/model-css/model-css.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,36 +24,35 @@ * */ -import { DOCUMENT } from "@angular/common"; -import { Inject, Injectable } from "@angular/core"; -import { AppConfigService } from "../appConfig/appConfig.service"; +import { DOCUMENT } from '@angular/common'; +import { Inject, Injectable } from '@angular/core'; +import { AppConfigService } from '../appConfig/appConfig.service'; @Injectable({ - providedIn: "root" + providedIn: 'root', }) export class ModelCssService { - constructor(@Inject(DOCUMENT) private readonly document: Document, - private readonly appConfig: AppConfigService) { } + constructor( + @Inject(DOCUMENT) private readonly document: Document, + private readonly appConfig: AppConfigService, + ) {} /** Loads the model stylesheet into the document, unless it has already * been loaded. */ public loadModelCss() { - const id = 'imx-model-css'; const existing = this.document.getElementById(id); // already loaded? - if (existing) - return; + if (existing) return; const head = this.document.getElementsByTagName('head')[0]; const style = this.document.createElement('link'); style.id = id; style.rel = 'stylesheet'; - style.href = this.appConfig.BaseUrl + "/imx/modelcss"; + style.href = this.appConfig.BaseUrl + '/imx/modelcss'; head.appendChild(style); } - -} \ No newline at end of file +} diff --git a/imxweb/projects/qbm/src/lib/multi-select-formcontrol/multi-select-formcontrol.component.html b/imxweb/projects/qbm/src/lib/multi-select-formcontrol/multi-select-formcontrol.component.html index 92a80c985..c5c5d8206 100644 --- a/imxweb/projects/qbm/src/lib/multi-select-formcontrol/multi-select-formcontrol.component.html +++ b/imxweb/projects/qbm/src/lib/multi-select-formcontrol/multi-select-formcontrol.component.html @@ -1,48 +1,68 @@
    - + - - {{ entityColumn?.GetMetadata().GetDisplay() }} - + + {{ entityColumn?.GetMetadata()?.GetDisplay() }} + - +
    {{ candidate.Display }}
    -
    - +
    - + - {{ (selectedElementsCaption) + ' ('+ selectedCandidates?.length + ')' }} + {{ selectedElementsCaption + ' (' + selectedCandidates?.length + ')' }} - + - +
    {{ selectedCandidate.Display }}
    - +
    -
    diff --git a/imxweb/projects/qbm/src/lib/multi-select-formcontrol/multi-select-formcontrol.component.scss b/imxweb/projects/qbm/src/lib/multi-select-formcontrol/multi-select-formcontrol.component.scss index 3f239c567..9c7e6ca77 100644 --- a/imxweb/projects/qbm/src/lib/multi-select-formcontrol/multi-select-formcontrol.component.scss +++ b/imxweb/projects/qbm/src/lib/multi-select-formcontrol/multi-select-formcontrol.component.scss @@ -7,36 +7,17 @@ gap: 12px; } -.imx-selected-candidates{ +.imx-selected-candidates { height: 218px; overflow: auto; } -.mat-spinner { - margin-top: 15px; -} - -.multi-select-formcontrol-eui-search { - width: 100%; - display: flex; - - .eui-search { - width: 100%; - } - - ::ng-deep .eui-search.mat-form-field.mat-form-field-appearance-outline { - min-width: 280px; - flex: 1; - margin-bottom: 10px; - } -} - .imx-multi-select-formcontrol-container { display: flex; flex-direction: column; } -.mat-list { +.mat-mdc-list { flex: 1; } @@ -76,28 +57,10 @@ } } -.mat-form-field { - flex: 1; -} - -mat-list-item:hover { - background-color: $color-gray-2; -} - eui-icon { cursor: pointer; } -mat-form-field { - button { - eui-icon { - font-size: 14px; - margin-right: 4px; - } - } -} - - @media only screen and (max-width: 1024px) { :host > div { grid-column-start: 1; diff --git a/imxweb/projects/qbm/src/lib/multi-select-formcontrol/multi-select-formcontrol.component.ts b/imxweb/projects/qbm/src/lib/multi-select-formcontrol/multi-select-formcontrol.component.ts index 95a5df3e8..a395d1677 100644 --- a/imxweb/projects/qbm/src/lib/multi-select-formcontrol/multi-select-formcontrol.component.ts +++ b/imxweb/projects/qbm/src/lib/multi-select-formcontrol/multi-select-formcontrol.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,20 +25,15 @@ */ import { ListRange } from '@angular/cdk/collections'; -import { OverlayRef } from '@angular/cdk/overlay'; import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; import { AfterViewInit, ChangeDetectorRef, Component, forwardRef, Input, OnChanges, OnDestroy, ViewChild } from '@angular/core'; -import { ControlValueAccessor, UntypedFormControl, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms'; import { MatSelectionListChange } from '@angular/material/list'; import { EuiLoadingService } from '@elemental-ui/core'; import { Subscription } from 'rxjs'; import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; -import { - CollectionLoadParameters, - EntityData, - IEntityColumn -} from 'imx-qbm-dbts'; +import { CollectionLoadParameters, EntityData, IEntityColumn } from '@imx-modules/imx-qbm-dbts'; import { MultiValueService } from '../multi-value/multi-value.service'; import { SettingsService } from '../settings/settings-service'; @@ -51,11 +46,10 @@ import { SettingsService } from '../settings/settings-service'; provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => MultiSelectFormcontrolComponent), multi: true, - } + }, ], }) export class MultiSelectFormcontrolComponent implements ControlValueAccessor, OnChanges, OnDestroy, AfterViewInit { - public candidatesTotalCount: number; public loading = false; public candidates: EntityData[]; @@ -79,7 +73,8 @@ export class MultiSelectFormcontrolComponent implements ControlValueAccessor, On private readonly multiValueProvider: MultiValueService, private readonly busyService: EuiLoadingService, private readonly settingsService: SettingsService, - private readonly changeDetectorRef: ChangeDetectorRef) { + private readonly changeDetectorRef: ChangeDetectorRef, + ) { this.initSearchControl(); } @@ -88,7 +83,7 @@ export class MultiSelectFormcontrolComponent implements ControlValueAccessor, On StartIndex: 0, PageSize: this.settingsService.DefaultPageSize, filter: undefined, - search: undefined + search: undefined, }); this.changeDetectorRef.detectChanges(); } @@ -109,29 +104,32 @@ export class MultiSelectFormcontrolComponent implements ControlValueAccessor, On public async ngAfterViewInit(): Promise { this.parameters = { PageSize: this.settingsService.DefaultPageSize, StartIndex: 0 }; - this.subscriptions.push(this.viewport.renderedRangeStream.subscribe(async (range: ListRange) => { - if (range.end === (this.settingsService.DefaultPageSize + this.parameters.StartIndex)) { - this.parameters.StartIndex += this.settingsService.DefaultPageSize; + this.subscriptions.push( + this.viewport.renderedRangeStream.subscribe(async (range: ListRange) => { + if (range.end === this.settingsService.DefaultPageSize + (this.parameters.StartIndex ?? 0)) { + if (!this.parameters.StartIndex) this.parameters.StartIndex = 0; + this.parameters.StartIndex += this.settingsService.DefaultPageSize; - const tmpCandidates = Object.assign([], this.candidates); - await this.loadData(this.parameters); + const tmpCandidates = Object.assign([], this.candidates); + await this.loadData(this.parameters); - this.candidates.unshift(...tmpCandidates); - this.changeDetectorRef.detectChanges(); - } - })); + this.candidates.unshift(...tmpCandidates); + this.changeDetectorRef.detectChanges(); + } + }), + ); } public ngOnDestroy(): void { - this.subscriptions.forEach(s => s.unsubscribe()); + this.subscriptions.forEach((s) => s.unsubscribe()); } public async updateSelected(selection: MatSelectionListChange): Promise { - const selectedChange: string[] = selection.options.map(option => option.value.Keys[0]); - if (this.selectedCandidates.findIndex(elem => selectedChange.includes(elem.Keys[0])) !== -1) { + const selectedChange: string[] = selection.options.map((option) => option.value.Keys[0]); + if (this.selectedCandidates.findIndex((elem) => selectedChange.includes(elem.Keys?.[0] ?? '')) !== -1) { return; } - this.selectedCandidates.push(...selection.options.map(option => option.value)); + this.selectedCandidates.push(...selection.options.map((option) => option.value)); if (this.pushMethod === 'auto') { await this.putNewValue(); } @@ -152,19 +150,21 @@ export class MultiSelectFormcontrolComponent implements ControlValueAccessor, On } public async pushValue(): Promise { - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } try { await this.putNewValue(); } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } } private async putNewValue(): Promise { - const value = this.selectedCandidates && this.selectedCandidates.length > 0 ? - this.multiValueProvider.getMultiValue(this.selectedCandidates.map(elem => elem.Keys[0])) - : ''; + const value = + this.selectedCandidates && this.selectedCandidates.length > 0 + ? this.multiValueProvider.getMultiValue(this.selectedCandidates.map((elem) => elem.Keys?.[0] ?? '')) + : ''; await this.entityColumn.PutValue(value); this.onChange(this.entityColumn); @@ -172,13 +172,13 @@ export class MultiSelectFormcontrolComponent implements ControlValueAccessor, On private initSearchControl(): void { this.searchControl.setValue(''); - this.subscriptions.push(this.searchControl.valueChanges - .pipe(distinctUntilChanged(), debounceTime(300)).subscribe(async (value) => { + this.subscriptions.push( + this.searchControl.valueChanges.pipe(distinctUntilChanged(), debounceTime(300)).subscribe(async (value) => { await this.loadData({ StartIndex: 0, PageSize: this.settingsService.DefaultPageSize, search: value }); this.viewport.scrollToIndex(0); this.changeDetectorRef.detectChanges(); - })); - + }), + ); } private async loadData(newState?: CollectionLoadParameters): Promise { @@ -187,12 +187,11 @@ export class MultiSelectFormcontrolComponent implements ControlValueAccessor, On return; } try { - setTimeout(() => this.loading = true); + this.loading = true; this.parameters = { ...this.parameters, ...newState }; - const data = await this.entityColumn.GetMetadata().GetFkRelations()[0].Get(this.parameters); - this.candidates = data.Entities; + this.candidates = data.Entities ?? []; } finally { this.loading = false; } diff --git a/imxweb/projects/qbm/src/lib/multi-select-formcontrol/multi-select-formcontrol.module.ts b/imxweb/projects/qbm/src/lib/multi-select-formcontrol/multi-select-formcontrol.module.ts index da0f642b6..bb5256e2e 100644 --- a/imxweb/projects/qbm/src/lib/multi-select-formcontrol/multi-select-formcontrol.module.ts +++ b/imxweb/projects/qbm/src/lib/multi-select-formcontrol/multi-select-formcontrol.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -52,8 +52,8 @@ import { MultiSelectFormcontrolComponent } from './multi-select-formcontrol.comp MatSelectModule, ReactiveFormsModule, ScrollingModule, - TranslateModule + TranslateModule, ], - exports: [MultiSelectFormcontrolComponent] + exports: [MultiSelectFormcontrolComponent], }) -export class MultiSelectFormcontrolModule { } +export class MultiSelectFormcontrolModule {} diff --git a/imxweb/projects/qbm/src/lib/multi-value/multi-value.module.ts b/imxweb/projects/qbm/src/lib/multi-value/multi-value.module.ts index 5ed6b70bc..d9287419e 100644 --- a/imxweb/projects/qbm/src/lib/multi-value/multi-value.module.ts +++ b/imxweb/projects/qbm/src/lib/multi-value/multi-value.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,11 +31,7 @@ import { MultiValueService } from './multi-value.service'; @NgModule({ declarations: [], - imports: [ - CommonModule - ], - providers: [ - MultiValueService - ] + imports: [CommonModule], + providers: [MultiValueService], }) -export class MultiValueModule { } +export class MultiValueModule {} diff --git a/imxweb/projects/qbm/src/lib/multi-value/multi-value.service.ts b/imxweb/projects/qbm/src/lib/multi-value/multi-value.service.ts index 0c9ce4562..ebe26a60c 100644 --- a/imxweb/projects/qbm/src/lib/multi-value/multi-value.service.ts +++ b/imxweb/projects/qbm/src/lib/multi-value/multi-value.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,20 +26,20 @@ import { Injectable } from '@angular/core'; -import { MultiValue } from 'imx-qbm-dbts'; +import { MultiValue } from '@imx-modules/imx-qbm-dbts'; /** * Service providing conversion to and from MultiValue with the default separator (MultiValueProperty.DefaultSeparator) */ @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class MultiValueService { - public getValues(value: string): string[] { - return value != null ? MultiValue.FromString(value).GetValues() : undefined; + public getValues(value: string | undefined): string[] | undefined { + return !!value ? MultiValue.FromString(value).GetValues() : undefined; } - public getMultiValue(values: string[]): string { - return values != null ? new MultiValue(values).GetStringValue() : undefined; + public getMultiValue(values: string[]): string | undefined { + return !!values ? new MultiValue(values).GetStringValue() : undefined; } } diff --git a/imxweb/projects/qbm/src/lib/object-history/object-history-api.service.ts b/imxweb/projects/qbm/src/lib/object-history/object-history-api.service.ts index ee7bd29f6..0bf6d48e5 100644 --- a/imxweb/projects/qbm/src/lib/object-history/object-history-api.service.ts +++ b/imxweb/projects/qbm/src/lib/object-history/object-history-api.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,11 +24,10 @@ * */ -import { HistoryData } from "imx-qbm-dbts"; -import { HistoryComparisonData } from "imx-api-qbm"; +import { HistoryData } from '@imx-modules/imx-qbm-dbts'; +import { HistoryComparisonData } from '@imx-modules/imx-api-qbm'; export abstract class ObjectHistoryApiService { - abstract getHistoryData(table: string, uid: string): Promise; - abstract getHistoryComparisonData(table: string, uid: string,options?: {CompareDate?: Date;}):Promise; -} \ No newline at end of file + abstract getHistoryComparisonData(table: string, uid: string, options?: { CompareDate?: Date }): Promise; +} diff --git a/imxweb/projects/qbm/src/lib/object-history/object-history-gridview/object-history-gridview.component.html b/imxweb/projects/qbm/src/lib/object-history/object-history-gridview/object-history-gridview.component.html index 68ba17a06..1ff7cd6fe 100644 --- a/imxweb/projects/qbm/src/lib/object-history/object-history-gridview/object-history-gridview.component.html +++ b/imxweb/projects/qbm/src/lib/object-history/object-history-gridview/object-history-gridview.component.html @@ -6,26 +6,34 @@
    - {{row.OldValue}} + {{ row.OldValue }} -
    -
    +
    +
    - {{columnDef.getValue(row)}} + {{ columnDef.getValue(row) }} - ({{'#LDS#No value'| translate}}) + ({{ '#LDS#No value' | translate }})
    {{ columnDef.getValue(row) }}
    - - + +
    - +

    diff --git a/imxweb/projects/qbm/src/lib/object-history/object-history-gridview/object-history-gridview.component.scss b/imxweb/projects/qbm/src/lib/object-history/object-history-gridview/object-history-gridview.component.scss index b200472da..9f3933930 100644 --- a/imxweb/projects/qbm/src/lib/object-history/object-history-gridview/object-history-gridview.component.scss +++ b/imxweb/projects/qbm/src/lib/object-history/object-history-gridview/object-history-gridview.component.scss @@ -2,8 +2,8 @@ display: flex; } -.imx-history-table-content{ +.imx-history-table-content { flex: 1 1 auto; display: flex; - flex-direction: column; -} \ No newline at end of file + flex-direction: column; +} diff --git a/imxweb/projects/qbm/src/lib/object-history/object-history-gridview/object-history-gridview.component.ts b/imxweb/projects/qbm/src/lib/object-history/object-history-gridview/object-history-gridview.component.ts index ba627bd2c..6474403bf 100644 --- a/imxweb/projects/qbm/src/lib/object-history/object-history-gridview/object-history-gridview.component.ts +++ b/imxweb/projects/qbm/src/lib/object-history/object-history-gridview/object-history-gridview.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,15 +24,15 @@ * */ +// eslint-disable-next-line max-classes-per-file import { Component, Input, OnChanges, OnInit, ViewChild } from '@angular/core'; +import { MatPaginator, PageEvent } from '@angular/material/paginator'; import { ActivatedRoute } from '@angular/router'; -import { PageEvent } from '@angular/material/paginator'; -import { MatPaginator } from '@angular/material/paginator'; import { TranslateService } from '@ngx-translate/core'; -import { ObjectHistoryEvent, TypedEntityCollectionData } from 'imx-qbm-dbts'; -import { ObjectHistoryParameters } from '../object-history.service'; +import { ObjectHistoryEvent, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { SettingsService } from '../../settings/settings-service'; +import { ObjectHistoryParameters } from '../object-history.service'; interface IColumn { id: string; @@ -46,7 +46,7 @@ class Column implements IColumn { public title: string; } -function getLocalDataForPage(allData: T[], state: { page: number, pageSize: number, skip: number }): T[] { +function getLocalDataForPage(allData: T[], state: { page: number; pageSize: number; skip: number }): T[] { if (state) { const currentIndex = state.page * state.pageSize; return allData.slice(currentIndex, currentIndex + state.pageSize); @@ -56,18 +56,18 @@ function getLocalDataForPage(allData: T[], state: { page: number, pageSize: n } // TODO: One class per file. -// tslint:disable-next-line: max-classes-per-file +// eslint-disable-next-line max-classes-per-file @Component({ selector: 'imx-object-history-gridview', templateUrl: './object-history-gridview.component.html', - styleUrls: ['./object-history-gridview.component.scss'] + styleUrls: ['./object-history-gridview.component.scss'], }) export class ObjectHistoryGridviewComponent implements OnInit, OnChanges { @Input() public historyData: ObjectHistoryEvent[]; @ViewChild(MatPaginator) private paginator: MatPaginator; public get columns(): string[] { - return this.columnDefs.map(c => c.id); + return this.columnDefs.map((c) => c.id); } public dataCollection: TypedEntityCollectionData; @@ -77,11 +77,11 @@ export class ObjectHistoryGridviewComponent implements OnInit, OnChanges { size: 5, sizeOptions: [20, 50, 100], showFirstLastButtons: false, - hidden: false + hidden: false, }; private displayChangeTypePropertyChange = 'PropertyChange'; - private stateCached: { page: number, pageSize: number, skip: number }; + private stateCached: { page: number; pageSize: number; skip: number }; private parameters: ObjectHistoryParameters; constructor( @@ -96,31 +96,31 @@ export class ObjectHistoryGridviewComponent implements OnInit, OnChanges { await this.addColumnDef({ id: 'ChangeTime', title: '#LDS#Modified on', - getValue: (row: ObjectHistoryEvent) => new Date(row.ChangeTime).toLocaleString(this.translationProvider.currentLang) + getValue: (row: ObjectHistoryEvent) => new Date(row.ChangeTime).toLocaleString(this.translationProvider.currentLang), }); await this.addColumnDef({ id: 'ChangeType', title: '#LDS#Type of change', - getValue: (row: ObjectHistoryEvent) => row.ChangeType + getValue: (row: ObjectHistoryEvent) => row.ChangeType ?? '', }); await this.addColumnDef({ id: 'LongDisplay', title: '#LDS#Name', - getValue: (row: ObjectHistoryEvent) => row.LongDisplay + getValue: (row: ObjectHistoryEvent) => row.LongDisplay ?? '', }); await this.addColumnDef({ id: 'Property', title: '#LDS#Type', - getValue: (row: ObjectHistoryEvent) => row.Property + getValue: (row: ObjectHistoryEvent) => row.Property ?? '', }); await this.addColumnDef({ id: 'User', title: '#LDS#User', - getValue: (row: ObjectHistoryEvent) => row.User + getValue: (row: ObjectHistoryEvent) => row.User ?? '', }); this.parameters = { table: this.activatedRoute.snapshot.paramMap.get('table'), - uid: this.activatedRoute.snapshot.paramMap.get('uid') + uid: this.activatedRoute.snapshot.paramMap.get('uid'), }; this.displayChangeTypePropertyChange = await this.translationProvider.get('#LDS#PropertyChange').toPromise(); @@ -129,7 +129,7 @@ export class ObjectHistoryGridviewComponent implements OnInit, OnChanges { public ngOnChanges(): void { this.parameters = { table: this.activatedRoute.snapshot.paramMap.get('table'), - uid: this.activatedRoute.snapshot.paramMap.get('uid') + uid: this.activatedRoute.snapshot.paramMap.get('uid'), }; this.refresh(); } @@ -138,7 +138,7 @@ export class ObjectHistoryGridviewComponent implements OnInit, OnChanges { this.updateDataCollection({ skip: e.pageIndex * e.pageSize, page: e.pageIndex, - pageSize: e.pageSize + pageSize: e.pageSize, }); } @@ -166,7 +166,7 @@ export class ObjectHistoryGridviewComponent implements OnInit, OnChanges { this.columnDefs.push(column); } - private updateDataCollection(state?: { page: number, pageSize: number, skip: number }): void { + private updateDataCollection(state?: { page: number; pageSize: number; skip: number }): void { if (state) { this.stateCached = state; } @@ -174,14 +174,14 @@ export class ObjectHistoryGridviewComponent implements OnInit, OnChanges { this.stateCached = { skip: this.paginatorConfig.index * this.paginatorConfig.size, page: this.paginatorConfig.index, - pageSize: this.paginatorConfig.size + pageSize: this.paginatorConfig.size, }; } this.dataCollection = { - tableName: this.parameters.table, + tableName: this.parameters.table ?? '', totalCount: this.historyData.length, - Data: getLocalDataForPage(this.historyData, this.stateCached) + Data: getLocalDataForPage(this.historyData, this.stateCached), }; } } diff --git a/imxweb/projects/qbm/src/lib/object-history/object-history-state-comparison/object-history-state-comparison.component.html b/imxweb/projects/qbm/src/lib/object-history/object-history-state-comparison/object-history-state-comparison.component.html index 5ef7a8bac..1696877f7 100644 --- a/imxweb/projects/qbm/src/lib/object-history/object-history-state-comparison/object-history-state-comparison.component.html +++ b/imxweb/projects/qbm/src/lib/object-history/object-history-state-comparison/object-history-state-comparison.component.html @@ -1,19 +1,24 @@
    -
    - - - {{ columnDef.getTitle() }} - - {{ columnDef.getValue(row) }} - - - - - -
    - - +
    + + + {{ columnDef.getTitle() }} + + {{ columnDef.getValue(row) }} + + + + +
    - \ No newline at end of file + + +
    diff --git a/imxweb/projects/qbm/src/lib/object-history/object-history-state-comparison/object-history-state-comparison.component.scss b/imxweb/projects/qbm/src/lib/object-history/object-history-state-comparison/object-history-state-comparison.component.scss index 4d7c483dd..9f3933930 100644 --- a/imxweb/projects/qbm/src/lib/object-history/object-history-state-comparison/object-history-state-comparison.component.scss +++ b/imxweb/projects/qbm/src/lib/object-history/object-history-state-comparison/object-history-state-comparison.component.scss @@ -1,9 +1,9 @@ :host { - display: flex; - } - - .imx-history-table-content{ - flex: 1 1 auto; - display: flex; - flex-direction: column; - } \ No newline at end of file + display: flex; +} + +.imx-history-table-content { + flex: 1 1 auto; + display: flex; + flex-direction: column; +} diff --git a/imxweb/projects/qbm/src/lib/object-history/object-history-state-comparison/object-history-state-comparison.component.ts b/imxweb/projects/qbm/src/lib/object-history/object-history-state-comparison/object-history-state-comparison.component.ts index 0d473f9f9..bdaf8aa6c 100644 --- a/imxweb/projects/qbm/src/lib/object-history/object-history-state-comparison/object-history-state-comparison.component.ts +++ b/imxweb/projects/qbm/src/lib/object-history/object-history-state-comparison/object-history-state-comparison.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,14 +25,13 @@ */ import { Component, Input, OnChanges, OnInit, ViewChild } from '@angular/core'; +import { MatPaginator, PageEvent } from '@angular/material/paginator'; import { ActivatedRoute } from '@angular/router'; -import { PageEvent } from '@angular/material/paginator'; -import { MatPaginator } from '@angular/material/paginator'; import { TranslateService } from '@ngx-translate/core'; -import { TypedEntityCollectionData } from 'imx-qbm-dbts'; +import { HistoryComparisonData } from '@imx-modules/imx-api-qbm'; +import { TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { SettingsService } from '../../settings/settings-service'; -import { HistoryComparisonData } from 'imx-api-qbm'; interface IColumn { id: string; @@ -46,7 +45,7 @@ class Column implements IColumn { public title: string; } -function getLocalDataForPage(allData: T[], state: { page: number, pageSize: number, skip: number }): T[] { +function getLocalDataForPage(allData: T[], state: { page: number; pageSize: number; skip: number }): T[] { if (state) { const currentIndex = state.page * state.pageSize; return allData.slice(currentIndex, currentIndex + state.pageSize); @@ -58,11 +57,11 @@ function getLocalDataForPage(allData: T[], state: { page: number, pageSize: n @Component({ selector: 'imx-object-history-state-comparison', templateUrl: './object-history-state-comparison.component.html', - styleUrls: ['./object-history-state-comparison.component.scss'] + styleUrls: ['./object-history-state-comparison.component.scss'], }) export class ObjectHistoryStateComparisonComponent implements OnInit, OnChanges { public get columns(): string[] { - return this.columnDefs.map(c => c.id); + return this.columnDefs.map((c) => c.id); } public dataCollection: TypedEntityCollectionData; @@ -72,18 +71,18 @@ export class ObjectHistoryStateComparisonComponent implements OnInit, OnChanges size: 5, sizeOptions: [20, 50, 100], showFirstLastButtons: false, - hidden: false + hidden: false, }; - @Input() public historyComparisonData: HistoryComparisonData[] ; + @Input() public historyComparisonData: HistoryComparisonData[]; - private stateCached: { page: number, pageSize: number, skip: number }; + private stateCached: { page: number; pageSize: number; skip: number }; @ViewChild(MatPaginator) private paginator: MatPaginator; constructor( private activatedRoute: ActivatedRoute, private translationProvider: TranslateService, - settings: SettingsService + settings: SettingsService, ) { this.paginatorConfig.size = settings.DefaultPageSize; } @@ -92,27 +91,27 @@ export class ObjectHistoryStateComparisonComponent implements OnInit, OnChanges await this.addColumnDef({ id: 'TableName', title: '#LDS#Changed property', - getValue: (row: HistoryComparisonData) => row.TableName + getValue: (row: HistoryComparisonData) => row.TableName, }); await this.addColumnDef({ id: 'ChangeType', title: '#LDS#Type of changed property', - getValue: (row: HistoryComparisonData) => row.ChangeType + getValue: (row: HistoryComparisonData) => row.ChangeType, }); await this.addColumnDef({ id: 'Property', title: '#LDS#Changed property', - getValue: (row: HistoryComparisonData) => row.Property + getValue: (row: HistoryComparisonData) => row.Property, }); await this.addColumnDef({ id: 'HistoryValueDisplay', title: '#LDS#Old value', - getValue: (row: HistoryComparisonData) => row.HistoryValueDisplay + getValue: (row: HistoryComparisonData) => row.HistoryValueDisplay, }); await this.addColumnDef({ id: 'CurrentValueDisplay', title: '#LDS#Current value', - getValue: (row: HistoryComparisonData) => row.CurrentValueDisplay + getValue: (row: HistoryComparisonData) => row.CurrentValueDisplay, }); } @@ -124,7 +123,7 @@ export class ObjectHistoryStateComparisonComponent implements OnInit, OnChanges this.updateDataCollection({ skip: e.pageIndex * e.pageSize, page: e.pageIndex, - pageSize: e.pageSize + pageSize: e.pageSize, }); } @@ -144,7 +143,7 @@ export class ObjectHistoryStateComparisonComponent implements OnInit, OnChanges this.columnDefs.push(column); } - private updateDataCollection(state?: { page: number, pageSize: number, skip: number }): void { + private updateDataCollection(state?: { page: number; pageSize: number; skip: number }): void { if (state) { this.stateCached = state; } @@ -152,16 +151,15 @@ export class ObjectHistoryStateComparisonComponent implements OnInit, OnChanges this.stateCached = { skip: this.paginatorConfig.index * this.paginatorConfig.size, page: this.paginatorConfig.index, - pageSize: this.paginatorConfig.size + pageSize: this.paginatorConfig.size, }; } let table = this.activatedRoute.snapshot.paramMap.get('table'); this.dataCollection = { - tableName: table, + tableName: table ?? '', totalCount: this.historyComparisonData.length, - Data: getLocalDataForPage(this.historyComparisonData, this.stateCached) + Data: getLocalDataForPage(this.historyComparisonData, this.stateCached), }; } } - diff --git a/imxweb/projects/qbm/src/lib/object-history/object-history-state-overview/object-history-state-overview.component.html b/imxweb/projects/qbm/src/lib/object-history/object-history-state-overview/object-history-state-overview.component.html index 5ef7a8bac..1696877f7 100644 --- a/imxweb/projects/qbm/src/lib/object-history/object-history-state-overview/object-history-state-overview.component.html +++ b/imxweb/projects/qbm/src/lib/object-history/object-history-state-overview/object-history-state-overview.component.html @@ -1,19 +1,24 @@
    -
    - - - {{ columnDef.getTitle() }} - - {{ columnDef.getValue(row) }} - - - - - -
    - - +
    + + + {{ columnDef.getTitle() }} + + {{ columnDef.getValue(row) }} + + + + +
    - \ No newline at end of file + + +
    diff --git a/imxweb/projects/qbm/src/lib/object-history/object-history-state-overview/object-history-state-overview.component.scss b/imxweb/projects/qbm/src/lib/object-history/object-history-state-overview/object-history-state-overview.component.scss index 2ee4b031f..9f3933930 100644 --- a/imxweb/projects/qbm/src/lib/object-history/object-history-state-overview/object-history-state-overview.component.scss +++ b/imxweb/projects/qbm/src/lib/object-history/object-history-state-overview/object-history-state-overview.component.scss @@ -1,10 +1,9 @@ :host { - display: flex; - } - - .imx-history-table-content{ - flex: 1 1 auto; - display: flex; - flex-direction: column; - } - \ No newline at end of file + display: flex; +} + +.imx-history-table-content { + flex: 1 1 auto; + display: flex; + flex-direction: column; +} diff --git a/imxweb/projects/qbm/src/lib/object-history/object-history-state-overview/object-history-state-overview.component.ts b/imxweb/projects/qbm/src/lib/object-history/object-history-state-overview/object-history-state-overview.component.ts index 5aa299236..6412e0d83 100644 --- a/imxweb/projects/qbm/src/lib/object-history/object-history-state-overview/object-history-state-overview.component.ts +++ b/imxweb/projects/qbm/src/lib/object-history/object-history-state-overview/object-history-state-overview.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,13 +24,13 @@ * */ +// eslint-disable-next-line max-classes-per-file import { Component, Input, OnChanges, OnInit, ViewChild } from '@angular/core'; +import { MatPaginator, PageEvent } from '@angular/material/paginator'; import { ActivatedRoute } from '@angular/router'; -import { PageEvent } from '@angular/material/paginator'; -import { MatPaginator } from '@angular/material/paginator'; import { TranslateService } from '@ngx-translate/core'; -import { IStateOverviewItem, TypedEntityCollectionData } from 'imx-qbm-dbts'; +import { IStateOverviewItem, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { SettingsService } from '../../settings/settings-service'; interface IColumn { @@ -45,7 +45,7 @@ class Column implements IColumn { public title: string; } -function getLocalDataForPage(allData: T[], state: { page: number, pageSize: number, skip: number }): T[] { +function getLocalDataForPage(allData: T[], state: { page: number; pageSize: number; skip: number }): T[] { if (state) { const currentIndex = state.page * state.pageSize; return allData.slice(currentIndex, currentIndex + state.pageSize); @@ -54,15 +54,15 @@ function getLocalDataForPage(allData: T[], state: { page: number, pageSize: n return allData; } -// tslint:disable-next-line: max-classes-per-file +// eslint-disable-next-line max-classes-per-file @Component({ selector: 'imx-object-history-state-overview', templateUrl: './object-history-state-overview.component.html', - styleUrls: ['./object-history-state-overview.component.scss'] + styleUrls: ['./object-history-state-overview.component.scss'], }) export class ObjectHistoryStateOverviewComponent implements OnInit, OnChanges { public get columns(): string[] { - return this.columnDefs.map(c => c.id); + return this.columnDefs.map((c) => c.id); } public dataCollection: TypedEntityCollectionData; @@ -72,18 +72,18 @@ export class ObjectHistoryStateOverviewComponent implements OnInit, OnChanges { size: 5, sizeOptions: [20, 50, 100], showFirstLastButtons: false, - hidden: false + hidden: false, }; @Input() public stateOverviewItems: IStateOverviewItem[]; - private stateCached: { page: number, pageSize: number, skip: number }; + private stateCached: { page: number; pageSize: number; skip: number }; @ViewChild(MatPaginator) private paginator: MatPaginator; constructor( private activatedRoute: ActivatedRoute, private translationProvider: TranslateService, - settings: SettingsService + settings: SettingsService, ) { this.paginatorConfig.size = settings.DefaultPageSize; } @@ -98,32 +98,32 @@ export class ObjectHistoryStateOverviewComponent implements OnInit, OnChanges { return textCurrent; } return new Date(date).toLocaleString(browserCulture); - } + }; await this.addColumnDef({ id: 'PropertyDisplay', title: '#LDS#Changed property', - getValue: (row: IStateOverviewItem) => row.PropertyDisplay + getValue: (row: IStateOverviewItem) => row.PropertyDisplay ?? '', }); await this.addColumnDef({ id: 'StateTypeDisplay', title: '#LDS#Type of changed property', - getValue: (row: IStateOverviewItem) => row.StateTypeDisplay + getValue: (row: IStateOverviewItem) => row.StateTypeDisplay ?? '', }); await this.addColumnDef({ id: 'ValueDisplay', title: '#LDS#Value', - getValue: (row: IStateOverviewItem) => row.ValueDisplay + getValue: (row: IStateOverviewItem) => row.ValueDisplay ?? '', }); await this.addColumnDef({ id: 'DateBegin', title: '#LDS#Value used since', - getValue: (row: IStateOverviewItem) => formatDate(row.DateBegin) + getValue: (row: IStateOverviewItem) => formatDate(row.DateBegin), }); await this.addColumnDef({ id: 'DateEnd', title: '#LDS#Value used until', - getValue: (row: IStateOverviewItem) => formatDate(row.DateEnd) + getValue: (row: IStateOverviewItem) => formatDate(row.DateEnd), }); } @@ -135,7 +135,7 @@ export class ObjectHistoryStateOverviewComponent implements OnInit, OnChanges { this.updateDataCollection({ skip: e.pageIndex * e.pageSize, page: e.pageIndex, - pageSize: e.pageSize + pageSize: e.pageSize, }); } @@ -155,7 +155,7 @@ export class ObjectHistoryStateOverviewComponent implements OnInit, OnChanges { this.columnDefs.push(column); } - private updateDataCollection(state?: { page: number, pageSize: number, skip: number }): void { + private updateDataCollection(state?: { page: number; pageSize: number; skip: number }): void { if (state) { this.stateCached = state; } @@ -163,15 +163,15 @@ export class ObjectHistoryStateOverviewComponent implements OnInit, OnChanges { this.stateCached = { skip: this.paginatorConfig.index * this.paginatorConfig.size, page: this.paginatorConfig.index, - pageSize: this.paginatorConfig.size + pageSize: this.paginatorConfig.size, }; } const table = this.activatedRoute.snapshot.paramMap.get('table'); this.dataCollection = { - tableName: table, + tableName: table ?? '', totalCount: this.stateOverviewItems.length, - Data: getLocalDataForPage(this.stateOverviewItems, this.stateCached) + Data: getLocalDataForPage(this.stateOverviewItems, this.stateCached), }; } } diff --git a/imxweb/projects/qbm/src/lib/object-history/object-history.component.html b/imxweb/projects/qbm/src/lib/object-history/object-history.component.html index 3639e8b5c..4e1d7ef9c 100644 --- a/imxweb/projects/qbm/src/lib/object-history/object-history.component.html +++ b/imxweb/projects/qbm/src/lib/object-history/object-history.component.html @@ -1,19 +1,30 @@
    -

    +

    {{ '#LDS#History' | translate }} -

    +

    - - - {{ mode.display }} - - - @@ -21,23 +32,39 @@

    mat-icon-button color="primary" [attr.data-imx-identifier]="'history-state-overview-mode-tooltip-button'" - matTooltip="{{ '#LDS#Here you can get an overview of all changes. Additionally, you can see how long the respective change was valid.' | translate }}" - matTooltipPosition="right" + matTooltip="{{ + '#LDS#Here you can get an overview of all changes. Additionally, you can see how long the respective change was valid.' + | translate + }}" > - + + {{ + '#LDS#Specify a date that lies in the past.' | translate + }} + {{ '#LDS#Specify a date.' | translate }} +
    @@ -46,7 +73,7 @@

    class="small" [dateControl]="timelineFromDateFormControl" [useClearIcon]="true" - [max]="timelineTo.date !== 'Invalid date' ? timelineTo.date : momentToday" + [max]="timelineToDateMoment" > size="s" >

    + + + + + +
    - + @@ -87,8 +144,14 @@

    - - + +
    diff --git a/imxweb/projects/qbm/src/lib/object-history/object-history.component.scss b/imxweb/projects/qbm/src/lib/object-history/object-history.component.scss index 6dff2fde7..f98e762e1 100644 --- a/imxweb/projects/qbm/src/lib/object-history/object-history.component.scss +++ b/imxweb/projects/qbm/src/lib/object-history/object-history.component.scss @@ -1,68 +1,30 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/mixins'; :host { - display: flex; - flex-direction: column; - overflow: hidden; - height: 100%; - - ::ng-deep eui-date-picker { - .mat-form-field-wrapper { - padding: 0; - - .mat-form-field-subscript-wrapper { - margin: 0; - } - - .mat-form-field-flex { - padding: 0 0 0 0.5rem; - - .mat-form-field-infix { - padding: 0.3rem 0 0.4rem 0; - width: 140px; - } - } - } - } - - ::ng-deep eui-time-picker { - .eui-time-picker-container { - display: flex; - align-items: center; - } - } + @include flex-column-container($overflow: hidden, $height: 100%); } - .imx-viewmode-content { + @include flex-column-container($overflow: auto); flex: 1 1 auto; - overflow: auto; - display: flex; - flex-direction: column; } .imx-viewmode-content-controls { margin-bottom: 1rem; + .eui-select{ + width: 180px; + } } -.imx-timeline-from, .imx-timeline-to, .imx-viewmode-content-controls { +.imx-timeline-from, +.imx-timeline-to, +.imx-viewmode-content-controls { display: flex; flex-direction: row; flex-wrap: wrap; - align-items: center; + align-items: baseline; gap: 6px; - ::ng-deep .mat-form-field-type-mat-select > .mat-form-field-wrapper { - padding: 0; - - .mat-form-field-infix { - border: none; - } - - .mat-form-field-subscript-wrapper { - margin: 0; - } - } - .timeline-to-text { margin-left: 1rem; } @@ -70,42 +32,21 @@ mat-button-toggle-group { margin-left: auto; - - ::ng-deep .mat-button-toggle-appearance-standard[ng-reflect-value='table'] .mat-button-toggle-label-content { - padding: 0 8px; - } } - -.eui-dark-theme { - :host { - mat-button-toggle-group { - .mat-button-toggle-checked { - background-color: $color-blue-40; - } - } - - .imx-viewmode-dropdown { - .mat-form-field-infix { - color: $color-gray-40; - } - - .mat-form-field-flex { - background-color: $color-gray-70; - } - } - } +.mdc-icon-button{ + line-height: 20px; } -.eui-contrast-theme { - :host { - .imx-viewmode-dropdown { - .mat-form-field-infix { - color: $color-gray-20; - } - - .mat-form-field-flex { - background-color: $color-gray-90; - } - } +.imx-filter-icon{ + font-size: 20px; + padding-right: 8px; + &-edit{ + color: $color-blue-60; + } + &-add{ + color: $color-green-60; + } + &-remove{ + color: $color-red-60; } } diff --git a/imxweb/projects/qbm/src/lib/object-history/object-history.component.ts b/imxweb/projects/qbm/src/lib/object-history/object-history.component.ts index b60d26dfd..d521dc547 100644 --- a/imxweb/projects/qbm/src/lib/object-history/object-history.component.ts +++ b/imxweb/projects/qbm/src/lib/object-history/object-history.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,29 +24,25 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; +// eslint-disable-next-line max-classes-per-file import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { EuiLoadingService } from '@elemental-ui/core'; +import { EuiLoadingService, EuiSelectOption } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { UntypedFormControl } from '@angular/forms'; -import { HistoryComparisonData } from 'imx-api-qbm'; -import { IStateOverviewItem, ObjectHistoryEvent } from 'imx-qbm-dbts'; +import { FormControl, UntypedFormControl, Validators } from '@angular/forms'; +import { HistoryComparisonData } from '@imx-modules/imx-api-qbm'; +import { IStateOverviewItem, ObjectHistoryEvent } from '@imx-modules/imx-qbm-dbts'; import { ObjectHistoryParameters, ObjectHistoryService } from './object-history.service'; import { DateAdapter } from '@angular/material/core'; -import moment from 'moment-timezone'; +import moment, { Moment } from 'moment-timezone'; import { Subscription } from 'rxjs'; import { ExtendedObjectHistoryEvent, TimelineDateTimeFilter } from '../timeline/timeline'; - -class ViewMode { - public value: string; - public display: string; -} +import { EventChangeType, EventChangeTypes, HistoryEventChangeType } from '../timeline/timeline.model'; // TODO: One class per file. -// tslint:disable-next-line: max-classes-per-file +// eslint-disable-next-line max-classes-per-file @Component({ selector: 'imx-object-history', templateUrl: './object-history.component.html', @@ -80,19 +76,27 @@ export class ObjectHistoryComponent implements OnInit, OnDestroy { return this.timelineTo.date + ' ' + this.timelineTo.time; } + public get timelineToDateMoment(): moment.Moment { + return this.timelineTo.date !== 'Invalid date' ? moment(this.timelineTo.date) : this.momentToday; + } + public lookIcons: string[] = ['attributes', 'table']; public selectedLook: string = 'timeline'; public viewModeValue: string; - public historyData: ObjectHistoryEvent[] = []; - public filteredHistoryData: ObjectHistoryEvent[] | ExtendedObjectHistoryEvent[] = []; + public historyData: ExtendedObjectHistoryEvent[] = []; + public filteredHistoryData: ExtendedObjectHistoryEvent[] = []; public stateOverviewItems: IStateOverviewItem[] = []; public historyComparisonData: HistoryComparisonData[] = []; - public viewModes: ViewMode[] = []; - public compareDateFormControl = new UntypedFormControl(); - public timelineFromDateFormControl = new UntypedFormControl(); - public timelineFromTimeFormControl = new UntypedFormControl(); - public timelineToDateFormControl = new UntypedFormControl(); - public timelineToTimeFormControl = new UntypedFormControl(); + public viewModes: EuiSelectOption[] = []; + public momentToday = moment(); + public compareDateFormControl = new UntypedFormControl(new Date(new Date().setHours(23, 59, 59, 999)), { + nonNullable: true, + validators: Validators.required, + }); + public timelineFromDateFormControl: FormControl = new FormControl(); + public timelineFromTimeFormControl: FormControl = new FormControl(); + public timelineToDateFormControl: FormControl = new FormControl(); + public timelineToTimeFormControl: FormControl = new FormControl(); public timelineFrom: TimelineDateTimeFilter = { date: 'Invalid date', time: 'Invalid date', @@ -101,7 +105,9 @@ export class ObjectHistoryComponent implements OnInit, OnDestroy { date: 'Invalid date', time: 'Invalid date', }; - public momentToday = moment(); + public viewModeControl: FormControl = new FormControl(this.viewModeGrid, { nonNullable: true }); + public eventChangeTypes = EventChangeTypes; + public selectedEventChangeTypes: EventChangeType[] = []; private subscriptions: Subscription[] = []; @@ -110,7 +116,7 @@ export class ObjectHistoryComponent implements OnInit, OnDestroy { private activatedRoute: ActivatedRoute, private busyService: EuiLoadingService, private historyService: ObjectHistoryService, - private dateAdapter: DateAdapter + private dateAdapter: DateAdapter, ) {} public async ngOnInit(): Promise { @@ -122,7 +128,6 @@ export class ObjectHistoryComponent implements OnInit, OnDestroy { this.addViewMode(this.viewModeStateComparison, '#LDS#State comparison'); this.viewModeValue = this.viewModeGrid; - this.compareDateFormControl.setValue(new Date(new Date().setHours(23, 59, 59, 999))); await this.refresh(true); } @@ -139,7 +144,7 @@ export class ObjectHistoryComponent implements OnInit, OnDestroy { this.subscriptions.push( this.timelineFromDateFormControl.valueChanges.subscribe((date) => { this.timelineFrom.date = moment(date).format('YYYY-MM-DD'); - this.getFilteredHistoryData(); + this.timelineFromTimeFormControl.setValue(date || moment().startOf('day')); }), this.timelineFromTimeFormControl.valueChanges.subscribe((time) => { this.timelineFrom.time = moment(time).format('HH:mm:ss'); @@ -147,18 +152,17 @@ export class ObjectHistoryComponent implements OnInit, OnDestroy { }), this.timelineToDateFormControl.valueChanges.subscribe((date) => { this.timelineTo.date = moment(date).format('YYYY-MM-DD'); - this.getFilteredHistoryData(); + this.timelineToTimeFormControl.setValue(date || moment().startOf('day')); }), this.timelineToTimeFormControl.valueChanges.subscribe((time) => { this.timelineTo.time = moment(time).format('HH:mm:ss'); this.getFilteredHistoryData(); - }) + }), ); } private getFilteredHistoryData() { - if (this.historyData && this.viewModeValue === this.viewModeGrid) - this.filteredHistoryData = this.filterByTime(this.historyData); + if (this.historyData && this.viewModeValue === this.viewModeGrid) this.filteredHistoryData = this.filterByTime(this.historyData); } public async onViewModeChange(): Promise { @@ -172,23 +176,26 @@ export class ObjectHistoryComponent implements OnInit, OnDestroy { } public async refresh(fetchRemote: boolean): Promise { - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.busyService.show())); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } try { this.historyData = []; this.stateOverviewItems = []; this.historyComparisonData = []; - const table = this.objectType || this.activatedRoute.snapshot.paramMap.get('table'); - const uid = this.objectUid || this.activatedRoute.snapshot.paramMap.get('uid'); + const table = (this.objectType || this.activatedRoute.snapshot.paramMap.get('table')) ?? ''; + const uid = (this.objectUid || this.activatedRoute.snapshot.paramMap.get('uid')) ?? ''; if (this.viewModeValue === this.viewModeGrid) { const parameters: ObjectHistoryParameters = { table, uid, }; - this.filteredHistoryData = this.historyData = await this.historyService.get(parameters, fetchRemote); + const fetched = await this.historyService.get(parameters, fetchRemote); + this.historyData = fetched.map((elem) => ({ ...elem, Time: '00:00:00' })); + this.getFilteredHistoryData(); } else if (this.viewModeValue === this.viewModeStateOverview) { const stateOverviewItems = await this.historyService.getStateOverviewItems(table, uid); if (stateOverviewItems) { @@ -199,8 +206,6 @@ export class ObjectHistoryComponent implements OnInit, OnDestroy { if (date) { this.historyComparisonData = await this.historyService.getHistoryComparisonData(table, uid, { CompareDate: date }); - } else { - this.historyComparisonData = await this.historyService.getHistoryComparisonData(table, uid); } } } catch { @@ -208,15 +213,32 @@ export class ObjectHistoryComponent implements OnInit, OnDestroy { this.stateOverviewItems = []; this.historyComparisonData = []; } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } } + /** + * Updates the selected change types and call getFilterHistoryData() function on user checkbox change event. + * @param type Type of event change type. + */ + public onFilterTypeChanged(type: EventChangeType): void { + if (this.selectedEventChangeTypes.indexOf(type) === -1) { + this.selectedEventChangeTypes.push(type); + } else { + this.selectedEventChangeTypes = this.selectedEventChangeTypes.filter((selectedType) => selectedType !== type); + } + this.getFilteredHistoryData(); + } + + /** + * Checks the event change type is selected or not. + */ + public getFilterTypeValue(type: EventChangeType): boolean { + return this.selectedEventChangeTypes.indexOf(type) > -1; + } + private async addViewMode(value: string, displayKey: string): Promise { - const viewMode = new ViewMode(); - viewMode.value = value; - viewMode.display = await this.translate.get(displayKey).toPromise(); - this.viewModes.push(viewMode); + this.viewModes.push({ display: this.translate.instant(displayKey), value }); } private resetTimelineForm(): void { @@ -229,23 +251,34 @@ export class ObjectHistoryComponent implements OnInit, OnDestroy { /** * Handles from and to filtering and loads the result after filtering */ - private filterByTime(data: ObjectHistoryEvent[]): ObjectHistoryEvent[] | ExtendedObjectHistoryEvent[] { + private filterByTime(data: ObjectHistoryEvent[]): ExtendedObjectHistoryEvent[] { if (this.timelineFromString === 'Invalid date' && this.timelineToString === 'Invalid date') { - return data; + return this.filterByType(data).map((elem) => ({ ...elem, Time: '00:00:00' })); } - const isFromValid = this.timelineFromString !== 'Invalid date'; const isToValid = this.timelineToString !== 'Invalid date'; + return this.filterByType(data) + .filter((elem) => { + const momentElemTime = moment(elem.ChangeTime); + const fromValidation = momentElemTime.isAfter(moment(this.timelineFromString), 'second'); + const toValidation = momentElemTime.isBefore(moment(this.timelineToString), 'second'); + if (isFromValid && !isToValid) return fromValidation; + if (!isFromValid && isToValid) return toValidation; + return fromValidation && toValidation; + }) + .map((elem) => ({ ...elem, Time: '00:00:00' })); + } + /** + * Handles filtering events by change type. + */ + private filterByType(data: ObjectHistoryEvent[]): ObjectHistoryEvent[] { return data.filter((elem) => { - const momentElemTime = moment(elem.ChangeTime); - const fromValidation = momentElemTime.isAfter(moment(this.timelineFromString), 'second'); - const toValidation = momentElemTime.isBefore(moment(this.timelineToString), 'second'); - - if (isFromValid && !isToValid) return fromValidation; - if (!isFromValid && isToValid) return toValidation; - - return fromValidation && toValidation; + if (!!this.selectedEventChangeTypes.length) { + return this.selectedEventChangeTypes.indexOf(HistoryEventChangeType[elem.ChangeTypeId || '']) > -1; + } else { + return true; + } }); } } diff --git a/imxweb/projects/qbm/src/lib/object-history/object-history.module.ts b/imxweb/projects/qbm/src/lib/object-history/object-history.module.ts index d5088363c..22bde547d 100644 --- a/imxweb/projects/qbm/src/lib/object-history/object-history.module.ts +++ b/imxweb/projects/qbm/src/lib/object-history/object-history.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -40,34 +40,23 @@ import { TranslateModule } from '@ngx-translate/core'; import { DataSourceToolbarModule } from '../data-source-toolbar/data-source-toolbar.module'; import { DataTableModule } from '../data-table/data-table.module'; import { ObjectHistoryGridviewComponent } from './object-history-gridview/object-history-gridview.component'; -import { ObjectHistoryComponent } from './object-history.component'; import { ObjectHistoryStateComparisonComponent } from './object-history-state-comparison/object-history-state-comparison.component'; import { ObjectHistoryStateOverviewComponent } from './object-history-state-overview/object-history-state-overview.component'; +import { ObjectHistoryComponent } from './object-history.component'; -import {MomentDateAdapter} from '@angular/material-moment-adapter'; - import {DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE} from '@angular/material/core'; import { MatButtonToggleModule } from '@angular/material/button-toggle'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatMenuModule } from '@angular/material/menu'; +import { EuiDateProviders } from '../base/elemental-defaults'; import { TimelineComponent } from '../timeline/timeline.component'; - export const EUI_DATE_FORMATS = { - parse: { - dateInput: ['LL', 'L'], - }, - display: { - dateInput: 'LL', - monthYearLabel: 'MMM YYYY', - dateA11yLabel: 'LL', - monthYearA11yLabel: 'MMMM YYYY', - }, - }; - @NgModule({ declarations: [ ObjectHistoryGridviewComponent, ObjectHistoryComponent, ObjectHistoryStateComparisonComponent, ObjectHistoryStateOverviewComponent, - TimelineComponent + TimelineComponent, ], imports: [ CommonModule, @@ -85,16 +74,10 @@ import { TimelineComponent } from '../timeline/timeline.component'; MatTableModule, MatPaginatorModule, MatButtonToggleModule, + MatMenuModule, + MatCheckboxModule, ], - providers: [ - {provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE]}, - {provide: MAT_DATE_FORMATS, useValue: EUI_DATE_FORMATS} - ], - exports: [ - ObjectHistoryComponent, - ObjectHistoryGridviewComponent - ] + providers: [...EuiDateProviders], + exports: [ObjectHistoryComponent, ObjectHistoryGridviewComponent], }) -export class ObjectHistoryModule { - -} +export class ObjectHistoryModule {} diff --git a/imxweb/projects/qbm/src/lib/object-history/object-history.service.ts b/imxweb/projects/qbm/src/lib/object-history/object-history.service.ts index 233ad5522..4e7a514f3 100644 --- a/imxweb/projects/qbm/src/lib/object-history/object-history.service.ts +++ b/imxweb/projects/qbm/src/lib/object-history/object-history.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,14 +25,14 @@ */ import { Injectable } from '@angular/core'; -import { HistoryComparisonData } from 'imx-api-qbm'; -import { IStateOverviewItem, ObjectHistoryEvent } from 'imx-qbm-dbts'; -import { ObjectHistoryApiService } from './object-history-api.service'; +import { HistoryComparisonData } from '@imx-modules/imx-api-qbm'; +import { IStateOverviewItem, ObjectHistoryEvent } from '@imx-modules/imx-qbm-dbts'; import { MetadataService } from '../base/metadata.service'; +import { ObjectHistoryApiService } from './object-history-api.service'; export interface ObjectHistoryParameters { - table: string; - uid: string; + table?: string | null; + uid?: string | null; } @Injectable({ @@ -48,9 +48,12 @@ export class ObjectHistoryService { public async get(parameters: ObjectHistoryParameters, fetchRemote: boolean = true): Promise { if (fetchRemote || this.dataCached == null) { - this.dataCached = (await this.apiService.getHistoryData(parameters.table, parameters.uid)) - .map((x) => x.Events) - .reduce((a, b) => a.concat(b)); + this.dataCached = + parameters.table && parameters.uid + ? (await this.apiService.getHistoryData(parameters.table, parameters.uid)) + ?.map((x) => x.Events) + ?.reduce((a, b) => (a ?? []).concat(b ?? [])) ?? [] + : []; } return this.dataCached; @@ -59,7 +62,7 @@ export class ObjectHistoryService { public async getStateOverviewItems(table: string, uid: string): Promise { let stateOverviewItems = (await this.apiService.getHistoryData(table, uid)) .map((x) => x.StateOverviewItems) - .reduce((a, b) => a.concat(b)); + .reduce((a, b) => (a ?? []).concat(b ?? [])); return stateOverviewItems; } @@ -67,8 +70,8 @@ export class ObjectHistoryService { let historyComparisonData = await this.apiService.getHistoryComparisonData(table, uid, options); // Update tableName with translated display name for await (const item of historyComparisonData) { - await this.metadataService.updateNonExisting([item.TableName]); - item.TableName = this.metadataService.tables[item.TableName].Display; + await this.metadataService.updateNonExisting([item.TableName ?? '']); + item.TableName = this.metadataService.tables[item.TableName ?? '']?.Display; } return historyComparisonData; } diff --git a/imxweb/projects/qbm/src/lib/ordered-list/ordered-list.component.html b/imxweb/projects/qbm/src/lib/ordered-list/ordered-list.component.html index 758d7f96a..36775f191 100644 --- a/imxweb/projects/qbm/src/lib/ordered-list/ordered-list.component.html +++ b/imxweb/projects/qbm/src/lib/ordered-list/ordered-list.component.html @@ -1,20 +1,28 @@
    - {{placeholder}} + {{ placeholder }}
    - - - {{ pr.Display }} + + + {{ pr?.Display }}
    -
    -
    - +
    +
    diff --git a/imxweb/projects/qbm/src/lib/ordered-list/ordered-list.component.scss b/imxweb/projects/qbm/src/lib/ordered-list/ordered-list.component.scss index 9bebb5471..9845e0986 100644 --- a/imxweb/projects/qbm/src/lib/ordered-list/ordered-list.component.scss +++ b/imxweb/projects/qbm/src/lib/ordered-list/ordered-list.component.scss @@ -1,10 +1,6 @@ @import '@elemental-ui/core/src/styles/_eui_palette.scss'; :host { - .buttonbar { - margin: 1em 0; - } - .ordered-list { min-height: 45px; display: block; @@ -31,14 +27,17 @@ .cdk-drag-preview { box-sizing: border-box; border-radius: 4px; - box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12); + box-shadow: + 0 5px 5px -3px rgba(0, 0, 0, 0.2), + 0 8px 10px 1px rgba(0, 0, 0, 0.14), + 0 3px 14px 2px rgba(0, 0, 0, 0.12); } - .ordered-list--placeholder { - min-height: 75px; - border-radius: 4px; - transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); - } +.ordered-list--placeholder { + min-height: 75px; + border-radius: 4px; + transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); +} .cdk-drag-animating { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); @@ -78,7 +77,7 @@ background: $color-gray-0; } - .ordered-list--placeholder { + .ordered-list--placeholder { background-color: $color-gray-30; border: dotted 2px $color-gray-40; } @@ -102,7 +101,7 @@ background: $color-gray-70; } - .ordered-list--placeholder { + .ordered-list--placeholder { background-color: $color-gray-50; border: dotted 2px $color-gray-20; } @@ -114,8 +113,8 @@ } } -.eui-contrast-theme{ -:host { +.eui-contrast-theme { + :host { .ordered-list { border: solid 1px $color-gray-0; background: $color-gray-100; @@ -126,7 +125,7 @@ background: $color-gray-100; } - .ordered-list--placeholder { + .ordered-list--placeholder { background-color: $color-gray-50; border: dotted 2px $color-gray-0; } diff --git a/imxweb/projects/qbm/src/lib/ordered-list/ordered-list.component.ts b/imxweb/projects/qbm/src/lib/ordered-list/ordered-list.component.ts index d07ad4732..a19bb23f5 100644 --- a/imxweb/projects/qbm/src/lib/ordered-list/ordered-list.component.ts +++ b/imxweb/projects/qbm/src/lib/ordered-list/ordered-list.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,8 +24,8 @@ * */ -import { Component, EventEmitter, Input, Output } from '@angular/core'; import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; import { EuiTheme } from '@elemental-ui/core'; @Component({ @@ -38,7 +38,7 @@ export class OrderedListComponent { @Input() deleteText: string; @Input() placeholder: string; @Input() testId: string = 'list'; - @Input() dataSource: { Name: string; Display: string }[] = []; + @Input() dataSource: { Name?: string; Display?: string }[] = []; @Input() data: string[] = []; @Input() isReadOnly: boolean = false; @@ -50,10 +50,10 @@ export class OrderedListComponent { return bodyClasses.contains(EuiTheme.LIGHT) ? EuiTheme.LIGHT : bodyClasses.contains(EuiTheme.DARK) - ? EuiTheme.DARK - : bodyClasses.contains(EuiTheme.CONTRAST) - ? EuiTheme.CONTRAST - : ''; + ? EuiTheme.DARK + : bodyClasses.contains(EuiTheme.CONTRAST) + ? EuiTheme.CONTRAST + : ''; } public drop(event: CdkDragDrop): void { @@ -67,7 +67,7 @@ export class OrderedListComponent { } public addNew(): void { - this.data.push(null); + this.data.push(''); this.notifyChange(); } diff --git a/imxweb/projects/qbm/src/lib/ordered-list/ordered-list.module.ts b/imxweb/projects/qbm/src/lib/ordered-list/ordered-list.module.ts index 19e8b5976..ead76ebe3 100644 --- a/imxweb/projects/qbm/src/lib/ordered-list/ordered-list.module.ts +++ b/imxweb/projects/qbm/src/lib/ordered-list/ordered-list.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,31 +24,18 @@ * */ -import { DragDropModule } from "@angular/cdk/drag-drop"; -import { CommonModule } from "@angular/common"; -import { NgModule } from "@angular/core"; -import { FormsModule } from "@angular/forms"; -import { MatSelectModule } from "@angular/material/select"; -import { EuiCoreModule, EuiMaterialModule } from "@elemental-ui/core"; -import { OrderedListComponent } from "./ordered-list.component"; +import { DragDropModule } from '@angular/cdk/drag-drop'; +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { MatSelectModule } from '@angular/material/select'; +import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; +import { OrderedListComponent } from './ordered-list.component'; /** Provides an editor component to edit an ordered list of elements. */ @NgModule({ - imports: [ - CommonModule, - DragDropModule, - MatSelectModule, - FormsModule, - EuiCoreModule, - EuiMaterialModule, - ], - declarations: [ - OrderedListComponent - ], - exports: [ - OrderedListComponent - ] + imports: [CommonModule, DragDropModule, MatSelectModule, FormsModule, EuiCoreModule, EuiMaterialModule], + declarations: [OrderedListComponent], + exports: [OrderedListComponent], }) -export class OrderedListModule { - -} \ No newline at end of file +export class OrderedListModule {} diff --git a/imxweb/projects/qbm/src/lib/parameterized-text/parameter-replacement.interface.ts b/imxweb/projects/qbm/src/lib/parameterized-text/parameter-replacement.interface.ts index 5b356673d..02f9e5d0d 100644 --- a/imxweb/projects/qbm/src/lib/parameterized-text/parameter-replacement.interface.ts +++ b/imxweb/projects/qbm/src/lib/parameterized-text/parameter-replacement.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qbm/src/lib/parameterized-text/parameterized-text.component.html b/imxweb/projects/qbm/src/lib/parameterized-text/parameterized-text.component.html index 302634871..3859b7c1e 100644 --- a/imxweb/projects/qbm/src/lib/parameterized-text/parameterized-text.component.html +++ b/imxweb/projects/qbm/src/lib/parameterized-text/parameterized-text.component.html @@ -3,4 +3,6 @@ {{ textToken.value }} {{ textToken.value }} + + {{ '; ' + additionalText }}
    diff --git a/imxweb/projects/qbm/src/lib/parameterized-text/parameterized-text.component.scss b/imxweb/projects/qbm/src/lib/parameterized-text/parameterized-text.component.scss index df55d109f..7dcd90bdc 100644 --- a/imxweb/projects/qbm/src/lib/parameterized-text/parameterized-text.component.scss +++ b/imxweb/projects/qbm/src/lib/parameterized-text/parameterized-text.component.scss @@ -7,5 +7,5 @@ .imx-parameter-container { word-wrap: break-word; + display: block !important } - diff --git a/imxweb/projects/qbm/src/lib/parameterized-text/parameterized-text.component.ts b/imxweb/projects/qbm/src/lib/parameterized-text/parameterized-text.component.ts index 3795b082d..8ff1a39c2 100644 --- a/imxweb/projects/qbm/src/lib/parameterized-text/parameterized-text.component.ts +++ b/imxweb/projects/qbm/src/lib/parameterized-text/parameterized-text.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -33,15 +33,16 @@ import { TextToken } from './text-token.interface'; @Component({ selector: 'imx-parameterized-text', templateUrl: './parameterized-text.component.html', - styleUrls: ['./parameterized-text.component.scss'] + styleUrls: ['./parameterized-text.component.scss'], }) export class ParameterizedTextComponent implements OnInit { public textTokens: TextToken[]; @Input() parameterizedText: ParameterizedText; + @Input() additionalText: string = ''; @Output() textReady = new EventEmitter(); - constructor(private readonly service: ParameterizedTextService) { } + constructor(private readonly service: ParameterizedTextService) {} public ngOnInit(): void { this.textTokens = this.service.createTextTokens(this.parameterizedText); diff --git a/imxweb/projects/qbm/src/lib/parameterized-text/parameterized-text.interface.ts b/imxweb/projects/qbm/src/lib/parameterized-text/parameterized-text.interface.ts index 647219ce7..99b1b3f54 100644 --- a/imxweb/projects/qbm/src/lib/parameterized-text/parameterized-text.interface.ts +++ b/imxweb/projects/qbm/src/lib/parameterized-text/parameterized-text.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,6 +26,6 @@ export interface ParameterizedText { value: string; - marker: { start: string; end: string; }; + marker: { start: string; end: string }; getParameterValue: (name: string) => string; } diff --git a/imxweb/projects/qbm/src/lib/parameterized-text/parameterized-text.module.ts b/imxweb/projects/qbm/src/lib/parameterized-text/parameterized-text.module.ts index 1f176e77b..2205b4e2e 100644 --- a/imxweb/projects/qbm/src/lib/parameterized-text/parameterized-text.module.ts +++ b/imxweb/projects/qbm/src/lib/parameterized-text/parameterized-text.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,14 +30,8 @@ import { CommonModule } from '@angular/common'; import { ParameterizedTextComponent } from './parameterized-text.component'; @NgModule({ - declarations: [ - ParameterizedTextComponent - ], - exports: [ - ParameterizedTextComponent - ], - imports: [ - CommonModule - ] + declarations: [ParameterizedTextComponent], + exports: [ParameterizedTextComponent], + imports: [CommonModule], }) -export class ParameterizedTextModule { } +export class ParameterizedTextModule {} diff --git a/imxweb/projects/qbm/src/lib/parameterized-text/parameterized-text.service.ts b/imxweb/projects/qbm/src/lib/parameterized-text/parameterized-text.service.ts index 7f9a7c722..78c91902b 100644 --- a/imxweb/projects/qbm/src/lib/parameterized-text/parameterized-text.service.ts +++ b/imxweb/projects/qbm/src/lib/parameterized-text/parameterized-text.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,16 +31,16 @@ import { ParameterizedText } from './parameterized-text.interface'; import { TextToken } from './text-token.interface'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ParameterizedTextService { public createTextTokens(text: ParameterizedText): TextToken[] { const parameters = this.getParametersWithMatchingValue(text); - const output = []; + const output: TextToken[] = []; let part = text.value; - for (const parameter of parameters) { + for (const parameter of parameters) { const tokens = part.split(parameter.delimiter); const head = tokens[0]; @@ -65,10 +65,10 @@ export class ParameterizedTextService { private getParametersWithMatchingValue(text: ParameterizedText): ParameterReplacement[] { const re = new RegExp('(' + text.marker.start + '[^"]+' + text.marker.end + ')', 'g'); return (text.value.match(re) ?? []) - .map(parameter => ({ + .map((parameter) => ({ delimiter: parameter, - replacement: text.getParameterValue(parameter.split(text.marker.start).join('').split(text.marker.end).join('')) + replacement: text.getParameterValue(parameter.split(text.marker.start).join('').split(text.marker.end).join('')), })) - .filter(parameter => parameter.replacement != null); + .filter((parameter) => parameter.replacement != null); } } diff --git a/imxweb/projects/qbm/src/lib/parameterized-text/text-token.interface.ts b/imxweb/projects/qbm/src/lib/parameterized-text/text-token.interface.ts index b84ec1eeb..d8ef35f84 100644 --- a/imxweb/projects/qbm/src/lib/parameterized-text/text-token.interface.ts +++ b/imxweb/projects/qbm/src/lib/parameterized-text/text-token.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qbm/src/lib/plugins/plugin-loader.service.ts b/imxweb/projects/qbm/src/lib/plugins/plugin-loader.service.ts deleted file mode 100644 index 1f674a722..000000000 --- a/imxweb/projects/qbm/src/lib/plugins/plugin-loader.service.ts +++ /dev/null @@ -1,260 +0,0 @@ -/* - * ONE IDENTITY LLC. PROPRIETARY INFORMATION - * - * This software is confidential. One Identity, LLC. or one of its affiliates or - * subsidiaries, has supplied this software to you under terms of a - * license agreement, nondisclosure agreement or both. - * - * You may not copy, disclose, or use this software except in accordance with - * those terms. - * - * - * Copyright 2023 One Identity LLC. - * ALL RIGHTS RESERVED. - * - * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR - * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, OR - * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE - * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE - * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING - * THIS SOFTWARE OR ITS DERIVATIVES. - * - */ - -import { Injectable, Injector, isDevMode, createNgModuleRef } from '@angular/core'; -import { HttpClient, HttpHeaders } from '@angular/common/http'; - -import { NodeAppInfo, PlugInInfo } from 'imx-api-qbm'; - -import { imx_SessionService } from '../session/imx-session.service'; -import { ClassloggerService } from '../classlogger/classlogger.service'; -import { AppConfig } from '../appConfig/appconfig.interface'; - -import * as AngularCore from '@angular/core'; -import * as AngularCommon from '@angular/common'; -import * as AngularCommonHttp from '@angular/common/http'; -import * as AngularRouter from '@angular/router'; -import * as BrowserAnimations from '@angular/platform-browser/animations'; -import * as PlatformBrowser from '@angular/platform-browser'; -import * as AngularMaterialCore from '@angular/material/core'; -import * as CdkDragDropAngularMaterial from '@angular/cdk/drag-drop'; -import * as CdkPortalAngularMaterial from '@angular/cdk/portal'; -import * as CdkScrollingAngularMaterial from '@angular/cdk/scrolling'; -import * as CdkStepperModule from '@angular/cdk/stepper'; -import * as CdkTableModule from '@angular/cdk/table'; -import * as CdkTreeModule from '@angular/cdk/tree'; -import * as CdkPlatformModule from '@angular/cdk/platform'; -import * as CdkTextFieldModule from '@angular/cdk/text-field'; -import * as CdkKeycodesModule from '@angular/cdk/keycodes'; -import * as BadgeModule from '@angular/material/badge'; -import * as BottomSheetModule from '@angular/material/bottom-sheet'; -import * as ButtonModule from '@angular/material/button'; -import * as ButtonToggleModule from '@angular/material/button-toggle'; -import * as MatCardModule from '@angular/material/card'; -import * as MatCheckboxModule from '@angular/material/checkbox'; -import * as MatChipsModule from '@angular/material/chips'; -import * as MatStepperModule from '@angular/material/stepper'; -import * as MatDatepickerModule from '@angular/material/datepicker'; -import * as MatDialogModule from '@angular/material/dialog'; -import * as MatDividerModule from '@angular/material/divider'; -import * as MatExpansionModule from '@angular/material/expansion'; -import * as MatGridListModule from '@angular/material/grid-list'; -import * as MatIconModule from '@angular/material/icon'; -import * as MatInputModule from '@angular/material/input'; -import * as MatListModule from '@angular/material/list'; -import * as MatMenuModule from '@angular/material/menu'; -import * as MatPaginatorModule from '@angular/material/paginator'; -import * as MatProgressBarModule from '@angular/material/progress-bar'; -import * as MatProgressSpinnerModule from '@angular/material/progress-spinner'; -import * as MatRadioModule from '@angular/material/radio'; -import * as MatSelectModule from '@angular/material/select'; -import * as MatSidenavModule from '@angular/material/sidenav'; -import * as MatSliderModule from '@angular/material/slider'; -import * as MatSlideToggleModule from '@angular/material/slide-toggle'; -import * as MatSnackBarModule from '@angular/material/snack-bar'; -import * as MatSortModule from '@angular/material/sort'; -import * as MatTableModule from '@angular/material/table'; -import * as MatTabsModule from '@angular/material/tabs'; -import * as MatToolbarModule from '@angular/material/toolbar'; -import * as MatTooltipModule from '@angular/material/tooltip'; -import * as MatTreeModule from '@angular/material/tree'; -import * as OverlayModule from '@angular/cdk/overlay'; -import * as MatFormFieldModule from '@angular/material/form-field'; -import * as MatAutocompleteModule from '@angular/material/autocomplete'; -import * as FormsModule from '@angular/forms'; -import * as NgxTranslateModule from '@ngx-translate/core'; -import * as QBMDBTS from 'imx-qbm-dbts'; -import * as ElementUICore from '@elemental-ui/core'; -import * as Rxjs from 'rxjs'; -import * as RxjsOperators from 'rxjs/operators'; -import * as BillboardJs from 'billboard.js'; -import * as tslibModule from 'tslib'; -import * as MomentTimezone from 'moment-timezone'; -import * as lodash from 'lodash'; - -declare var SystemJS: any; - -@Injectable({ - providedIn: 'root', -}) -export class PluginLoaderService { - private appInfo: NodeAppInfo; - private plugins: PlugInInfo[] = []; - - constructor( - private readonly session: imx_SessionService, - private readonly logger: ClassloggerService, - private readonly httpClient: HttpClient, - private readonly injector: Injector - ) { - SystemJS.config({ - meta: { - '*.mjs': { - babelOptions: { - es2015: false - } - } - }, - map: { - 'plugin-babel': 'systemjs-plugin-babel/plugin-babel.js', - 'systemjs-babel-build': 'systemjs-plugin-babel/systemjs-babel-browser.js', - }, - transpiler: 'plugin-babel', - }); - } - - public async loadModules(appName: string): Promise { - const apps: NodeAppInfo[] = await this.session.Client.imx_applications_get(); - - this.appInfo = apps.filter((app) => app.Name === appName)[0]; - - this.logger.debug(this, `▶️ Found config section for ${this.appInfo.DisplayName}`); - - if (this.appInfo.PlugIns == null || this.appInfo.PlugIns.length === 0) { - this.logger.debug(this, `❌ No plugins found`); - return; - } - - this.logger.debug(this, `▶️ Found ${this.appInfo.PlugIns.length} plugin(s)`); - - const host = window.location.href.split('html')[0]; - this.logger.debug(this, `💻 Host: ${host} `); - - let config: AppConfig; - - if (!isDevMode()) { - config = (await this.httpClient.get('appconfig.json').toPromise()) as AppConfig; - this.importDependencies(); - this.logger.debug(this, '▶️ Config. PROD mode.', config); - } - - let moduleList = []; - - for (const plugin of this.appInfo.PlugIns) { - this.logger.debug(this, `⚙️ Plugin: ${plugin.Container}`); - - try { - if (isDevMode()) { - this.logger.debug(this, '▶️ Importing module. DEV mode.'); - moduleList.push(import(`html/${plugin.Container}/fesm2015/${plugin.Container}.mjs`)); - } else { - this.logger.debug(this, '▶️ Importing module. PROD mode.'); - moduleList.push(SystemJS.import(`${host}html/${plugin.Container}/fesm2015/${plugin.Container}.mjs`)); - } - - this.plugins.push(plugin); - } catch (e) { - this.logger.error(this, `💥 Loading of ${plugin.Name} (${plugin.Container}) failed with the following error: ${e.message}`); - } - } - - let modules = await Promise.allSettled(moduleList); - for (let i = 0; i < modules.length; i++) { - try { - let m = modules[i] as any; - let module = m.value[this.plugins[i].Name as any]; - createNgModuleRef(module, this.injector); - this.logger.debug(this, '▶️ Instance ready'); - } catch (e) { - this.logger.error( - this, - `💥 Loading of ${this.plugins[i].Name} (${this.plugins[i].Container}) failed with the following error: ${e.message}` - ); - } - } - } - - private importDependencies(): void { - // Angular Modules - SystemJS.set('@angular/core', SystemJS.newModule(AngularCore)); - SystemJS.set('@angular/common', SystemJS.newModule(AngularCommon)); - SystemJS.set('@angular/router', SystemJS.newModule(AngularRouter)); - SystemJS.set('@angular/platform-browser/animations', SystemJS.newModule(BrowserAnimations)); - SystemJS.set('@angular/common/http', SystemJS.newModule(AngularCommonHttp)); - - // Angular Material Modules - SystemJS.set('@angular/material/core', SystemJS.newModule(AngularMaterialCore)); - SystemJS.set('@angular/cdk/drag-drop', SystemJS.newModule(CdkDragDropAngularMaterial)); - SystemJS.set('@angular/cdk/portal', SystemJS.newModule(CdkPortalAngularMaterial)); - SystemJS.set('@angular/cdk/scrolling', SystemJS.newModule(CdkScrollingAngularMaterial)); - SystemJS.set('@angular/cdk/stepper', SystemJS.newModule(CdkStepperModule)); - SystemJS.set('@angular/cdk/table', SystemJS.newModule(CdkTableModule)); - SystemJS.set('@angular/cdk/platform', SystemJS.newModule(CdkPlatformModule)); - SystemJS.set('@angular/cdk/tree', SystemJS.newModule(CdkTreeModule)); - SystemJS.set('@angular/cdk/text-field', SystemJS.newModule(CdkTextFieldModule)); - SystemJS.set('@angular/cdk/keycodes', SystemJS.newModule(CdkKeycodesModule)); - SystemJS.set('@angular/material/badge', SystemJS.newModule(BadgeModule)); - SystemJS.set('@angular/material/bottom-sheet', SystemJS.newModule(BottomSheetModule)); - SystemJS.set('@angular/material/button', SystemJS.newModule(ButtonModule)); - SystemJS.set('@angular/material/button-toggle', SystemJS.newModule(ButtonToggleModule)); - SystemJS.set('@angular/material/card', SystemJS.newModule(MatCardModule)); - SystemJS.set('@angular/material/checkbox', SystemJS.newModule(MatCheckboxModule)); - SystemJS.set('@angular/material/chips', SystemJS.newModule(MatChipsModule)); - SystemJS.set('@angular/material/stepper', SystemJS.newModule(MatStepperModule)); - SystemJS.set('@angular/material/datepicker', SystemJS.newModule(MatDatepickerModule)); - SystemJS.set('@angular/material/dialog', SystemJS.newModule(MatDialogModule)); - SystemJS.set('@angular/material/divider', SystemJS.newModule(MatDividerModule)); - SystemJS.set('@angular/material/expansion', SystemJS.newModule(MatExpansionModule)); - SystemJS.set('@angular/material/grid-list', SystemJS.newModule(MatGridListModule)); - SystemJS.set('@angular/material/icon', SystemJS.newModule(MatIconModule)); - SystemJS.set('@angular/material/input', SystemJS.newModule(MatInputModule)); - SystemJS.set('@angular/material/list', SystemJS.newModule(MatListModule)); - SystemJS.set('@angular/material/menu', SystemJS.newModule(MatMenuModule)); - SystemJS.set('@angular/material/paginator', SystemJS.newModule(MatPaginatorModule)); - SystemJS.set('@angular/material/progress-bar', SystemJS.newModule(MatProgressBarModule)); - SystemJS.set('@angular/material/progress-spinner', SystemJS.newModule(MatProgressSpinnerModule)); - SystemJS.set('@angular/material/radio', SystemJS.newModule(MatRadioModule)); - SystemJS.set('@angular/material/select', SystemJS.newModule(MatSelectModule)); - SystemJS.set('@angular/material/sidenav', SystemJS.newModule(MatSidenavModule)); - SystemJS.set('@angular/material/slider', SystemJS.newModule(MatSliderModule)); - SystemJS.set('@angular/material/slide-toggle', SystemJS.newModule(MatSlideToggleModule)); - SystemJS.set('@angular/material/snack-bar', SystemJS.newModule(MatSnackBarModule)); - SystemJS.set('@angular/material/sort', SystemJS.newModule(MatSortModule)); - SystemJS.set('@angular/material/table', SystemJS.newModule(MatTableModule)); - SystemJS.set('@angular/material/tabs', SystemJS.newModule(MatTabsModule)); - SystemJS.set('@angular/material/toolbar', SystemJS.newModule(MatToolbarModule)); - SystemJS.set('@angular/material/tooltip', SystemJS.newModule(MatTooltipModule)); - SystemJS.set('@angular/material/tree', SystemJS.newModule(MatTreeModule)); - SystemJS.set('@angular/material/autocomplete', SystemJS.newModule(MatAutocompleteModule)); - SystemJS.set('@angular/cdk/overlay', SystemJS.newModule(OverlayModule)); - SystemJS.set('@angular/material/form-field', SystemJS.newModule(MatFormFieldModule)); - SystemJS.set('@angular/forms', SystemJS.newModule(FormsModule)); - SystemJS.set('@angular/platform-browser', SystemJS.newModule(PlatformBrowser)); - - // Other stuff - SystemJS.set('@ngx-translate/core', SystemJS.newModule(NgxTranslateModule)); - SystemJS.set('rxjs', SystemJS.newModule(Rxjs)); - SystemJS.set('rxjs/operators', SystemJS.newModule(RxjsOperators)); - SystemJS.set('billboard.js', SystemJS.newModule(BillboardJs)); - SystemJS.set('tslib', SystemJS.newModule(tslibModule)); - SystemJS.set('moment-timezone', SystemJS.newModule(MomentTimezone)); - SystemJS.set('lodash', SystemJS.newModule(lodash)); - - // Our stuff - SystemJS.set('imx-qbm-dbts', SystemJS.newModule(QBMDBTS)); - SystemJS.set('@elemental-ui/core', SystemJS.newModule(ElementUICore)); - } -} diff --git a/imxweb/projects/qbm/src/lib/processing-queue/processing-queue.interface.ts b/imxweb/projects/qbm/src/lib/processing-queue/processing-queue.interface.ts new file mode 100644 index 000000000..60ee29d5b --- /dev/null +++ b/imxweb/projects/qbm/src/lib/processing-queue/processing-queue.interface.ts @@ -0,0 +1,173 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { computed, effect, Signal, signal, WritableSignal } from '@angular/core'; +import { v4 as uuid } from 'uuid'; + +export class ActionGroup implements QueuedActionGroup { + public uid: string; + public startDate: Date; + public actions: WritableSignal; + /** Action to take when group finishes */ + + /** + * Represents a collection of actions, state derived from the state of the actions + * @param display name of the group of actions, shows up on the accordion + * @param actions set of async tasks that will run in sequence + * @param groupAction optionally set a function to run once the group finishes + */ + constructor( + public display: string, + actions: Action[], + private groupAction?: () => Promise, + ) { + this.display = display; + this.uid = uuid(); + this.startDate = new Date(); + this.actions = signal(actions); + effect( + () => { + if (this.groupAction && CompletedActionStates.includes(this.state())) this.groupAction(); + }, + { allowSignalWrites: true }, + ); + } + + public finishedDate = computed(() => { + const allFinished = this.actions().every((action) => CompletedActionStates.includes(action.state())); + if (allFinished) { + if (this.groupAction) this.groupAction(); + return new Date(); + } else return undefined; + }); + + public taskCount = computed(() => this.actions().length); + public erroredActions = computed(() => this.actions().filter((action) => action.state() == QueuedActionState.Failed)); + public state = computed(() => { + const states = this.actions().map((action) => action.state()); + // If any subaction is processing, the group is in a processing state + if (states.some((state) => state == QueuedActionState.Processing)) return QueuedActionState.Processing; + // If any subaction failed, the group is in a failed state + else if (states.some((state) => state == QueuedActionState.Failed)) return QueuedActionState.Failed; + // If all subactions are success, the group is success. + else if (states.every((state) => state == QueuedActionState.Success)) return QueuedActionState.Success; + // If the above cases aren't met, then we are in waiting? + else return QueuedActionState.Waiting; + }); +} + +export class Action implements QueuedAction { + public uid: string; + public startDate: Date; + public state = signal(QueuedActionState.Waiting); + public error = signal(undefined); + + /** + * Represents a single chunk of work that will be sent off for processing. + * @param display header of task name + * @param display2 subtitle of task name, enter empty string for no display + * @param action async task to be run for this action, should return no values + */ + constructor( + public display: string, + public display2: string, + private action: () => Promise, + public objectKey: string = '', + ) { + this.startDate = new Date(); + this.uid = uuid(); + } + + public finishedDate = computed(() => (CompletedActionStates.includes(this.state()) ? new Date() : undefined)); + + /** + * Clear error state, try to process with the action function, and give a new finished datum + */ + public async execute(): Promise { + this.state.set(QueuedActionState.Processing); + try { + this.error.set(undefined); + await this.action(); + this.state.set(QueuedActionState.Success); + } catch (err) { + this.error.set(err); + this.state.set(QueuedActionState.Failed); + } + } +} + +export enum QueuedActionState { + Waiting, + Processing, + Success, + Failed, + NotInQueue, +} + +export const CompletedActionStates = [QueuedActionState.Failed, QueuedActionState.Success]; + +/** Public interface of a queued action group */ +export interface QueuedActionGroup { + readonly display: string; + + /** Returns the state of the group */ + readonly state: Signal; + + /** Unique id of this group */ + readonly uid: string; + + /** The set of all actions in the group */ + readonly actions: WritableSignal; + + /** The set of all actions that have an error state */ + readonly erroredActions: Signal; + + /** Date when the group was created */ + readonly startDate: Date; + + /** Date when the group had all actions reach a completed state */ + readonly finishedDate: Signal; +} + +/** Public interface of a queued action */ +export interface QueuedAction { + /** Bold header display for action */ + readonly display: string; + /** Sub header display for action */ + readonly display2: string; + /** Underlying object xml key */ + readonly objectKey: string; + /** Returns the state of this action. */ + readonly state: Signal; + /** Unique id for this action */ + readonly uid: string; + /** Returns the error if the state is Failed. */ + readonly error: WritableSignal; + /** Date when the action was queued. */ + readonly startDate: Date; + /** Date when the action reached a finished state */ + readonly finishedDate: Signal; +} diff --git a/imxweb/projects/qbm/src/lib/processing-queue/processing-queue.service.ts b/imxweb/projects/qbm/src/lib/processing-queue/processing-queue.service.ts new file mode 100644 index 000000000..4f7223e30 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/processing-queue/processing-queue.service.ts @@ -0,0 +1,136 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { computed, EnvironmentInjector, inject, Injectable, runInInjectionContext, signal, WritableSignal } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { SnackBarService } from '../snackbar/snack-bar.service'; +import { Action, ActionGroup, CompletedActionStates, QueuedActionState } from './processing-queue.interface'; + +/** Service to manage a queue of asynchronous actions. */ + +@Injectable({ providedIn: 'root' }) +export class ProcessingQueueService { + constructor( + private readonly snackbar: SnackBarService, + private readonly translate: TranslateService, + ) {} + /** Needed to ensure that group action effects can be run correctly */ + private environmentInjector = inject(EnvironmentInjector); + + /** Number of actions we check for before we use the queue service */ + public actionThreshold = 5; + + /** Array of all groups of actions */ + public _groups: WritableSignal = signal([]); + + /** Array of all actions, used for querying status in the pollAction function */ + private _actions = computed(() => this._groups().flatMap((group) => group.actions())); + + /** Internal flag for preventing all calls from happening simultaneously */ + private processing: boolean; + + /** Get the first action that is waiting to be processed */ + private firstWaitingAction = computed(() => this._actions().find((action) => action.state() == QueuedActionState.Waiting)); + + /** Boolean for if any GroupActions succeeded or failed */ + public hasCompletedGroups = computed(() => this._groups().some((group) => CompletedActionStates.includes(group.state()))); + + /** Boolean for if all GroupActions succeeded or failed */ + public isAllGroupsCompleted = computed(() => this._actions().every((action) => CompletedActionStates.includes(action.state()))); + + /** Total count of actions in the queue, regardless of state */ + public totalCount = computed(() => this._groups().reduce((acc, group) => acc + group.actions().length, 0)); + + /** Total count of actions in the queue that have errored */ + public errorCount = computed(() => this._groups().reduce((acc, group) => acc + group.erroredActions().length, 0)); + + /** + * Add a new group of actions to the queue for processing + * @param groupName display for the group of actions + * @param subactions list of actions to be processed as part of this group + * @param groupAction an action that will be run each time the group enters a finshed state from first or retries + */ + public submitGroupAction(groupName: string, actions: Action[], groupAction?: () => Promise): void { + let group: ActionGroup; + runInInjectionContext(this.environmentInjector, () => { + group = new ActionGroup(groupName, actions, groupAction); + }); + this._groups.update((currentGroups) => [...currentGroups, group]); + this.runNextActionIfAvailable(); + + this.snackbar.open({ key: this.translate.instant('#LDS#The actions are processed as background processes.') }); + } + + /** Clear out all groups in succeeded or failed states */ + public removeCompletedGroups(): void { + this._groups.update((currentGroups) => currentGroups.filter((group) => !CompletedActionStates.includes(group.state()))); + } + + /** + * Clear out all groups and set the processing flag to false. + */ + public clearProcessing(): void { + this._groups.update(() => []); + this.processing = false; + } + + /** + * Reprocess a single action that is already in the queue + * @param action that we want to put back into a waiting state and reprocess + */ + public onRetryAction(action: Action): void { + action.state.set(QueuedActionState.Waiting); + if (!this.processing) this.runNextActionIfAvailable(); + } + + /** + * Reprocess all failed actions in a group + * @param group that we want to put all failed actions into a waiting state and reprocess + */ + public onRetryGroup(group: ActionGroup): void { + group.erroredActions().forEach((action) => this.onRetryAction(action)); + } + + /** Runs all available action in sequence for as long as any actions are in the Waiting state. */ + private async runNextActionIfAvailable(): Promise { + if (!this.processing && this.firstWaitingAction()) { + this.processing = true; + await this.firstWaitingAction()!.execute(); + this.processing = false; + await this.runNextActionIfAvailable(); + } + } + + /** + * Check for specific action's state + * @param uid the unique id for this action to query state + */ + public pollAction(uid: string): QueuedActionState { + const action = this._actions().find((action) => action.objectKey == uid); + if (action) return action.state(); + else return QueuedActionState.NotInQueue; + } +} diff --git a/imxweb/projects/qbm/src/lib/processing-queue/processing-queue.spec.ts b/imxweb/projects/qbm/src/lib/processing-queue/processing-queue.spec.ts new file mode 100644 index 000000000..c1ce09287 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/processing-queue/processing-queue.spec.ts @@ -0,0 +1,132 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { fakeAsync, flush, TestBed, tick } from '@angular/core/testing'; +import { provideAnimations } from '@angular/platform-browser/animations'; +import { TranslateService } from '@ngx-translate/core'; +import { SnackBarService } from '../snackbar/snack-bar.service'; +import { Action } from './processing-queue.interface'; +import { ProcessingQueueService } from './processing-queue.service'; + +const lorem = + 'Omnis neque eius exercitationem omnis. Voluptatibus ut dignissimos commodi est. Praesentium hic cumque labore magnam qui accusantium. Cum et quos voluptatem provident adipisci blanditiis. Et est commodi eum eos iusto. Dolor ad nihil velit. Earum at eligendi et ullam numquam corporis accusamus. Error sint dolores sit dolores nobis. Aut nihil labore aliquam ullam reprehenderit dolorem id. Non placeat placeat ducimus omnis facilis et repellat vero. Vero velit id veniam ut asperiores. Tempora temporibus dolorem cumque voluptas nostrum cupiditate sint libero. Et et et perferendis numquam sint rerum beatae. Inventore omnis soluta vel harum beatae. Eius enim vel unde voluptatem molestias incidunt. Debitis quos eum delectus incidunt eos ut nesciunt. Tempora provident quo molestiae architecto officiis voluptatem accusamus. Dignissimos dolor qui illo hic ut est qui odit. Voluptatum dolores rem molestiae eligendi. Et et ut ab.'; + +const shortTime = 100; +const longTime = 1000; + +// One Fail, One complete -> Complete fail +const oneFailInTwo: Action[] = [ + new Action('Slow success', 'Runs for .1s', async () => {}), + new Action('Slow fail', 'Runs for .1s', async () => { + tick(shortTime); + throw lorem; + }), +]; + +// One Fail, One complete -> Complete fail +const longTasks: Action[] = [ + new Action('Fail', '', async () => { + tick(longTime); + }), + new Action('Complete', '', async () => {}), +]; + +describe('ProcessingQueueService', () => { + let service: ProcessingQueueService; + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ProcessingQueueService, SnackBarService, TranslateService, provideAnimations()], + }); + service = TestBed.inject(ProcessingQueueService); + }); + afterEach(() => service.clearProcessing()); + + it('should create', () => { + expect(service).toBeTruthy(); + }); + + it('should add a group, and after time have one error and one success, and then clear', fakeAsync(() => { + service.submitGroupAction(lorem, oneFailInTwo); + flush(); + + // Should have 1 error, number of actions length, and a completed group + expect([service.errorCount(), service.totalCount(), service.hasCompletedGroups()]).toEqual([1, oneFailInTwo.length, true]); + service.removeCompletedGroups(); + + // Should have no tasks left + expect(service.totalCount()).toEqual(0); + })); + + it('should add several groups, one long task, and removing completed groups should leave one group', fakeAsync(() => { + service.submitGroupAction(lorem, oneFailInTwo); + service.submitGroupAction(lorem, longTasks); + + // Should have two groups + expect(service._groups().length).toEqual([oneFailInTwo, longTasks].length); + flush(); + + // Should have a completed group + expect(service.hasCompletedGroups()).toBeTrue(); + + service.removeCompletedGroups(); + // Should have no completed groups + expect(service.hasCompletedGroups()).toBeFalse(); + + service.submitGroupAction(lorem, longTasks); + service.clearProcessing(); + // Should have no tasks even though a long running group was submitted + expect(service.totalCount()).toEqual(0); + })); + + it('should retry a failed action', fakeAsync(() => { + service.submitGroupAction(lorem, oneFailInTwo); + + flush(); + + service.onRetryAction(oneFailInTwo[1]); + // Should be retrying the only failed action, so no errors + expect([service.totalCount(), service.errorCount()]).toEqual([oneFailInTwo.length, 0]); + })); + + it('should retry a failed group, and increment the groupAction', fakeAsync(() => { + let count = 0; + const incCount = async () => { + count = count + 1; + return; + }; + + service.submitGroupAction(lorem, oneFailInTwo, incCount); + flush(); + + // Expect the group action to have been called once + expect(count).toEqual(1); + service.onRetryGroup(service._groups()[0]); + flush(); + + // Expect the group action to have been called twice + expect(count).toEqual(2); + })); +}); diff --git a/imxweb/projects/qbm/src/lib/progressbar/progressbar.component.html b/imxweb/projects/qbm/src/lib/progressbar/progressbar.component.html index 5e3ae84f2..52a71a054 100644 --- a/imxweb/projects/qbm/src/lib/progressbar/progressbar.component.html +++ b/imxweb/projects/qbm/src/lib/progressbar/progressbar.component.html @@ -1,7 +1,7 @@ -
    -
    {{caption}}
    -
    - -
    {{textVersion}}
    +
    +
    {{caption}}
    +
    + +
    {{textVersion}}
    -
    \ No newline at end of file +
    diff --git a/imxweb/projects/qbm/src/lib/progressbar/progressbar.component.scss b/imxweb/projects/qbm/src/lib/progressbar/progressbar.component.scss deleted file mode 100644 index 2cf1a87ed..000000000 --- a/imxweb/projects/qbm/src/lib/progressbar/progressbar.component.scss +++ /dev/null @@ -1,22 +0,0 @@ -.progressBar { - width: 150px; - justify-content: flex-start; - flex: 0 0 150px; - align-self: center; - margin-right: 10px; -} - -.progressBarTexts { - font-size: 12px; - text-align: center; -} - -.progressContainer { - display: flex; - flex-direction: column; -} - -.progressLine { - display: flex; - flex-direction: row; -} \ No newline at end of file diff --git a/imxweb/projects/qbm/src/lib/progressbar/progressbar.component.ts b/imxweb/projects/qbm/src/lib/progressbar/progressbar.component.ts index 5fdb2cc64..ee858da1e 100644 --- a/imxweb/projects/qbm/src/lib/progressbar/progressbar.component.ts +++ b/imxweb/projects/qbm/src/lib/progressbar/progressbar.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,10 +29,8 @@ import { Component, Input, OnInit } from '@angular/core'; @Component({ selector: 'imx-progressbar', templateUrl: './progressbar.component.html', - styleUrls: ['./progressbar.component.scss'] }) export class ImxProgressbarComponent implements OnInit { - @Input() public caption: string; @Input() public maxValue = 0; @Input() public value: number; @@ -41,11 +39,10 @@ export class ImxProgressbarComponent implements OnInit { public textVersion: string; public progressValue: number; - public ngOnInit(): void { - this.textVersion = this.maxValue === 0 ? '-' : - this.inPercent ? (this.value / this.maxValue) * 100 + '%' : this.value + '/' + this.maxValue; + this.textVersion = + this.maxValue === 0 ? '-' : this.inPercent ? (this.value / this.maxValue) * 100 + '%' : this.value + '/' + this.maxValue; - this.progressValue = this.inPercent ? this.value : this.maxValue === 0 ? 0 : (this.value * 100 / this.maxValue); + this.progressValue = this.inPercent ? this.value : this.maxValue === 0 ? 0 : (this.value * 100) / this.maxValue; } } diff --git a/imxweb/projects/qbm/src/lib/qbm.module.ts b/imxweb/projects/qbm/src/lib/qbm.module.ts index cf9adaa1f..83c89d778 100644 --- a/imxweb/projects/qbm/src/lib/qbm.module.ts +++ b/imxweb/projects/qbm/src/lib/qbm.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -45,24 +45,25 @@ import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; import { MatTabsModule } from '@angular/material/tabs'; import { MatToolbarModule } from '@angular/material/toolbar'; -import 'element-resize-detector'; import { NGXLogger } from 'ngx-logger'; -import { HttpClientModule } from '@angular/common/http'; +import { ClipboardModule } from '@angular/cdk/clipboard'; +import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSnackBarModule } from '@angular/material/snack-bar'; import { EuiCoreModule } from '@elemental-ui/core'; import { TranslateModule } from '@ngx-translate/core'; +import { ReCaptchaV3Service, RecaptchaV3Module } from 'ng-recaptcha-2'; import { AboutComponent } from './about/About.component'; -import { AboutService } from './about/About.service'; import { ApiClientAngularService } from './api-client/api-client-angular.service'; import { AppConfigService } from './appConfig/appConfig.service'; import { AutoCompleteComponent } from './auto-complete/auto-complete.component'; import { AutoCompleteModule } from './auto-complete/auto-complete.module'; import { GlobalErrorHandler } from './base/global-error-handler'; -import { MetadataService } from './base/metadata.service'; import { OpsupportDbObjectService } from './base/opsupport-db-object.service'; import { RegistryService } from './base/registry.service'; import { UserActionService } from './base/user-action.service'; +import { CaptchaModule } from './captcha/captcha.module'; import { CdrEditorComponent } from './cdr/cdr-editor/cdr-editor.component'; import { CdrRegistryService } from './cdr/cdr-registry.service'; import { CdrModule } from './cdr/cdr.module'; @@ -70,15 +71,18 @@ import { DefaultCdrEditorProvider } from './cdr/default-cdr-editor-provider'; import { FkCdrEditorProvider } from './cdr/fk-cdr-editor-provider'; import { ClassloggerModule } from './classlogger/classlogger.module'; import { ClassloggerService } from './classlogger/classlogger.service'; +import { ConnectionComponent } from './connection/connection.component'; import { DataExportModule } from './data-export/data-export.module'; import { DataSourceToolbarModule } from './data-source-toolbar/data-source-toolbar.module'; import { DataTableModule } from './data-table/data-table.module'; +import { DataViewModule } from './data-view/data-view.module'; import { DisableControlModule } from './disable-control/disable-control.module'; import { ExtComponent } from './ext/ext.component'; import { ExtDirective } from './ext/ext.directive'; import { ExtModule } from './ext/ext.module'; import { ExtService } from './ext/ext.service'; import { FilterTileComponent } from './filter-tile/filter-tile.component'; +import { HelpContextualModule } from './help-contextual/help-contextual.module'; import { HyperViewModule } from './hyperview/hyperview.module'; import { IconStackComponent } from './icon-stack/icon-stack.component'; import { InfoModalDialogModule } from './info-modal-dialog/info-modal-dialog.module'; @@ -86,18 +90,14 @@ import { JobQueueOverviewModule } from './jobqueue-overview/jobqueue-overview.mo import { LdsReplaceModule } from './lds-replace/lds-replace.module'; import { LoginComponent } from './login/login.component'; import { MastHeadModule } from './mast-head/mast-head.module'; -import { MasterDetailComponent } from './master-detail/master-detail.component'; -import { MenuModule } from './menu/menu.module'; import { MessageDialogComponent } from './message-dialog/message-dialog.component'; -import { PluginLoaderService } from './plugins/plugin-loader.service'; +import { MessageDialogService } from './message-dialog/message-dialog.service'; import { ImxProgressbarComponent } from './progressbar/progressbar.component'; import { imx_QBM_SearchService } from './search/search.service'; import { SearchBarComponent } from './searchbar/searchbar.component'; -import { SelectModule } from './select/select.module'; -import { DeviceStateService } from './services/device-state.service'; import { imx_SessionService } from './session/imx-session.service'; import { SideNavigationViewModule } from './side-navigation-view/side-navigation-view.module'; -import { SidenavTreeModule } from './sidenav-tree/sidenav-tree.module'; +import { SidenavTreeComponent } from './sidenav-tree/sidenav-tree.component'; import { SnackBarService } from './snackbar/snack-bar.service'; import { TableImageService } from './table-image/table-image.service'; import { TestHelperModule } from './testing/TestHelperModule.spec'; @@ -110,11 +110,6 @@ import { ImxTreeTableComponent } from './treeTable/treeTable.component'; import { TwoFactorAuthenticationComponent } from './two-factor-authentication/two-factor-authentication.component'; import { TwoFactorAuthenticationService } from './two-factor-authentication/two-factor-authentication.service'; import { UserMessageModule } from './user-message/user-message.module'; -import { HelpContextualModule } from './help-contextual/help-contextual.module'; -import { TempBillboardModule } from './temp-billboard/temp-billboard.module'; -import { TempBillboardComponent } from './temp-billboard/temp-billboard.component'; -import { ConnectionComponent } from './connection/connection.component'; -import { ClipboardModule } from '@angular/cdk/clipboard'; export function initApp(registry: CdrRegistryService, logger: NGXLogger): () => Promise { logger.debug('init qbm'); @@ -133,7 +128,6 @@ export function initApp(registry: CdrRegistryService, logger: NGXLogger): () => IconStackComponent, LoginComponent, FilterTileComponent, - MasterDetailComponent, ImxProgressbarComponent, SearchBarComponent, ImxTreeTableComponent, @@ -142,9 +136,25 @@ export function initApp(registry: CdrRegistryService, logger: NGXLogger): () => TranslationEditorComponent, ConnectionComponent, ], + exports: [ + TwoFactorAuthenticationComponent, + AboutComponent, + ConnectionComponent, + IconStackComponent, + LoginComponent, + ExtComponent, + ExtDirective, + FilterTileComponent, + ImxProgressbarComponent, + SearchBarComponent, + ImxTreeTableComponent, + ImxMatColumnComponent, + CdrEditorComponent, + MessageDialogComponent, + AutoCompleteComponent, + ], imports: [ CommonModule, - HttpClientModule, TranslateModule, DisableControlModule, ExtModule, @@ -166,6 +176,7 @@ export function initApp(registry: CdrRegistryService, logger: NGXLogger): () => MatTabsModule, MatToolbarModule, MatProgressBarModule, + MatProgressSpinnerModule, ReactiveFormsModule, FormsModule, TestHelperModule, @@ -175,30 +186,28 @@ export function initApp(registry: CdrRegistryService, logger: NGXLogger): () => DataTableModule, AutoCompleteModule, DataSourceToolbarModule, - MenuModule, MastHeadModule, UserMessageModule, ClassloggerModule, - SelectModule, UserMessageModule, LdsReplaceModule, TileModule, JobQueueOverviewModule, - SidenavTreeModule, + SidenavTreeComponent, DataExportModule, InfoModalDialogModule, MatSnackBarModule, SideNavigationViewModule, HelpContextualModule, - TempBillboardModule, - ClipboardModule + ClipboardModule, + CaptchaModule, + RecaptchaV3Module, + DataViewModule, ], providers: [ GlobalErrorHandler, AppConfigService, - AboutService, imx_SessionService, - MetadataService, ImxTranslateLoader, ImxTranslationProviderService, RegistryService, @@ -207,31 +216,13 @@ export function initApp(registry: CdrRegistryService, logger: NGXLogger): () => imx_QBM_SearchService, SnackBarService, ExtService, - DeviceStateService, ImxTreeTableComponent, TwoFactorAuthenticationService, ApiClientAngularService, TableImageService, - PluginLoaderService, - ], - exports: [ - TwoFactorAuthenticationComponent, - AboutComponent, - ConnectionComponent, - IconStackComponent, - LoginComponent, - ExtComponent, - ExtDirective, - FilterTileComponent, - MasterDetailComponent, - ImxProgressbarComponent, - SearchBarComponent, - ImxTreeTableComponent, - ImxMatColumnComponent, - CdrEditorComponent, - MessageDialogComponent, - AutoCompleteComponent, - TempBillboardComponent, + MessageDialogService, + ReCaptchaV3Service, + provideHttpClient(withInterceptorsFromDi()), ], }) export class QbmModule { diff --git a/imxweb/projects/qbm/src/lib/route-guard/component-can-deactivate.interface.ts b/imxweb/projects/qbm/src/lib/route-guard/component-can-deactivate.interface.ts index 078916295..9094cc272 100644 --- a/imxweb/projects/qbm/src/lib/route-guard/component-can-deactivate.interface.ts +++ b/imxweb/projects/qbm/src/lib/route-guard/component-can-deactivate.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qbm/src/lib/route-guard/route-guard.module.ts b/imxweb/projects/qbm/src/lib/route-guard/route-guard.module.ts index 7ac8b0849..f90ed9874 100644 --- a/imxweb/projects/qbm/src/lib/route-guard/route-guard.module.ts +++ b/imxweb/projects/qbm/src/lib/route-guard/route-guard.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,11 +30,7 @@ import { CommonModule } from '@angular/common'; import { RouteGuardService } from './route-guard.service'; @NgModule({ - imports: [ - CommonModule - ], - providers: [ - RouteGuardService - ] + imports: [CommonModule], + providers: [RouteGuardService], }) -export class RouteGuardModule { } +export class RouteGuardModule {} diff --git a/imxweb/projects/qbm/src/lib/route-guard/route-guard.service.ts b/imxweb/projects/qbm/src/lib/route-guard/route-guard.service.ts index d8d784f83..6483430fa 100644 --- a/imxweb/projects/qbm/src/lib/route-guard/route-guard.service.ts +++ b/imxweb/projects/qbm/src/lib/route-guard/route-guard.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,28 +24,28 @@ * */ -import { Injectable, ErrorHandler } from '@angular/core'; -import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router, CanDeactivate } from '@angular/router'; +import { ErrorHandler, Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; -import { imx_SessionService } from '../session/imx-session.service'; import { AppConfigService } from '../appConfig/appConfig.service'; -import { ComponentCanDeactivate } from './component-can-deactivate.interface'; +import { AuthenticationService } from '../authentication/authentication.service'; import { OAuthService } from '../authentication/oauth.service'; import { QueryParametersHandler } from '../base/query-parameters-handler'; import { ClassloggerService } from '../classlogger/classlogger.service'; -import { AuthenticationService } from '../authentication/authentication.service'; -import { StorageService } from '../storage/storage.service'; import { ConfirmationService } from '../confirmation/confirmation.service'; +import { imx_SessionService } from '../session/imx-session.service'; +import { StorageService } from '../storage/storage.service'; +import { ComponentCanDeactivate } from './component-can-deactivate.interface'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) -export class RouteGuardService implements CanActivate, CanDeactivate { +export class RouteGuardService { private confirmLeaveTitle = ''; private confirmLeaveMessage = ''; private isLoggedIn: boolean; - private lastRoute: ActivatedRouteSnapshot; + private lastRoute: ActivatedRouteSnapshot | undefined; constructor( private readonly config: AppConfigService, @@ -57,15 +57,17 @@ export class RouteGuardService implements CanActivate, CanDeactivate this.confirmLeaveTitle = value); + translation.get('#LDS#Heading Cancel Editing').subscribe((value: string) => (this.confirmLeaveTitle = value)); - translation.get('#LDS#You have unsaved changes. Are you sure you want to cancel editing and discard your changes?') - .subscribe((value: string) => this.confirmLeaveMessage = value); + translation + .get('#LDS#You have unsaved changes. Are you sure you want to cancel editing and discard your changes?') + .subscribe((value: string) => (this.confirmLeaveMessage = value)); - this.authentication.onSessionResponse.subscribe(sessionState => this.isLoggedIn = sessionState && sessionState.IsLoggedIn); + this.authentication.onSessionResponse.subscribe( + (sessionState) => (this.isLoggedIn = (sessionState && sessionState.IsLoggedIn) ?? false), + ); } public async canActivate(route: ActivatedRouteSnapshot, state?: RouterStateSnapshot): Promise { @@ -91,9 +93,9 @@ export class RouteGuardService implements CanActivate, CanDeactivate !this.oauthService.IsOAuthParameter(name)) } - ); + this.router.navigate([queryParamsHandler.path || this.config.Config.routeConfig?.start], { + queryParams: queryParamsHandler.GetQueryParameters((name) => !this.oauthService.IsOAuthParameter(name)), + }); } } catch (error) { this.errorHandler.handleError(error); diff --git a/imxweb/projects/qbm/src/lib/search/db-object-info.ts b/imxweb/projects/qbm/src/lib/search/db-object-info.ts index b6f094943..1bd1f8b97 100644 --- a/imxweb/projects/qbm/src/lib/search/db-object-info.ts +++ b/imxweb/projects/qbm/src/lib/search/db-object-info.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,9 +24,9 @@ * */ -import { DbObjectKey } from 'imx-qbm-dbts'; +import { DbObjectKey } from '@imx-modules/imx-qbm-dbts'; export class DbObjectInfo { - public Display: string; - public Key: DbObjectKey; + public Display: string; + public Key?: DbObjectKey; } diff --git a/imxweb/projects/qbm/src/lib/search/search.service.ts b/imxweb/projects/qbm/src/lib/search/search.service.ts index d3caab8de..8f3c1bb3a 100644 --- a/imxweb/projects/qbm/src/lib/search/search.service.ts +++ b/imxweb/projects/qbm/src/lib/search/search.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,17 +24,15 @@ * */ -import { Injectable, ErrorHandler } from '@angular/core'; +import { ErrorHandler, Injectable } from '@angular/core'; import { Subject } from 'rxjs'; -import { OpsupportSearchIndexedtables, SearchResultObject } from 'imx-api-qbm'; -import { TypedEntityCollectionData, DbObjectKey } from 'imx-qbm-dbts'; -import { imx_SessionService } from '../session/imx-session.service'; +import { OpsupportSearchIndexedtables, SearchResultObject } from '@imx-modules/imx-api-qbm'; +import { DbObjectKey, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { imx_ISearchService } from '../searchbar/iSearchService'; +import { imx_SessionService } from '../session/imx-session.service'; import { DbObjectInfo } from './db-object-info'; - - @Injectable() export class imx_QBM_SearchService implements imx_ISearchService { public readonly searchTermStream = new Subject(); @@ -43,19 +41,22 @@ export class imx_QBM_SearchService implements imx_ISearchService { constructor( private session: imx_SessionService, - private errorHandler: ErrorHandler) { - } + private errorHandler: ErrorHandler, + ) {} public async search(term: string, tables: string): Promise { - if (term === '') { return []; } + if (term === '') { + return []; + } try { const result = await this.session.Client.opsupport_search_get({ term: term, tables: tables }); if (result) { - return result.filter((sro: SearchResultObject) => sro.Key != null) - .map((sro: SearchResultObject) => ({ - Key: DbObjectKey.FromXml(sro.Key), - Display: sro.Display - })); + return result + .filter((sro: SearchResultObject) => sro.Key != null) + .map((sro: SearchResultObject) => ({ + Key: sro.Key ? DbObjectKey.FromXml(sro.Key) : undefined, + Display: sro.Display ?? '', + })); } } catch (error) { this.errorHandler.handleError(error); @@ -66,14 +67,15 @@ export class imx_QBM_SearchService implements imx_ISearchService { public async getIndexedTables(): Promise { return this.session.TypedClient.OpsupportSearchIndexedtables.Get(this.defaultOptions) - .then( - (response: TypedEntityCollectionData) => { - return response.Data.sort((t1, t2) => t1.DisplayNameSingular.value > t2.DisplayNameSingular.value ? 1 : -1 - ); - }).catch((error: any): OpsupportSearchIndexedtables[] => { - this.errorHandler.handleError(error); - return []; - }); + .then((response: TypedEntityCollectionData) => { + return response.Data.sort((t1, t2) => + t1.DisplayName.Column.GetDisplayValue().localeCompare(t2.DisplayName.Column.GetDisplayValue()), + ); + }) + .catch((error: any): OpsupportSearchIndexedtables[] => { + this.errorHandler.handleError(error); + return []; + }); } public GetTableDisplay(table: OpsupportSearchIndexedtables): string { diff --git a/imxweb/projects/qbm/src/lib/searchbar/iSearchService.ts b/imxweb/projects/qbm/src/lib/searchbar/iSearchService.ts index 52a4bc80b..e9d075a76 100644 --- a/imxweb/projects/qbm/src/lib/searchbar/iSearchService.ts +++ b/imxweb/projects/qbm/src/lib/searchbar/iSearchService.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,12 +26,12 @@ import { Subject } from 'rxjs'; -import { TypedEntity } from 'imx-qbm-dbts'; +import { TypedEntity } from '@imx-modules/imx-qbm-dbts'; export interface imx_ISearchService { - searchTermStream: Subject; - search(term: string, tables: string): Promise; - getIndexedTables(): Promise; - GetTableDisplay(table: TypedEntity): string; - GetTableValue(table: TypedEntity): string; + searchTermStream: Subject; + search(term: string, tables: string): Promise; + getIndexedTables(): Promise; + GetTableDisplay(table: TypedEntity): string; + GetTableValue(table: TypedEntity): string; } diff --git a/imxweb/projects/qbm/src/lib/searchbar/searchbar.component.html b/imxweb/projects/qbm/src/lib/searchbar/searchbar.component.html index 170f87018..73b3c976a 100644 --- a/imxweb/projects/qbm/src/lib/searchbar/searchbar.component.html +++ b/imxweb/projects/qbm/src/lib/searchbar/searchbar.component.html @@ -1,26 +1,47 @@ -
    +
    {{ ToJson(link) }} - \ No newline at end of file + diff --git a/imxweb/projects/qbm/src/lib/searchbar/searchbar.component.scss b/imxweb/projects/qbm/src/lib/searchbar/searchbar.component.scss index 1cc5559cb..5bc872916 100644 --- a/imxweb/projects/qbm/src/lib/searchbar/searchbar.component.scss +++ b/imxweb/projects/qbm/src/lib/searchbar/searchbar.component.scss @@ -1,14 +1,29 @@ -@import '@elemental-ui/core/src/styles/_palette.scss'; - -$SearchBarWidth: 535px; -$SearchBarHeight: 53px; - -$VI_Common_Color_Blue_1: #69CCE9; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/variables'; +@import 'base/mixins'; .imx-searchbar-card { - max-width: $SearchBarWidth; - width: $SearchBarWidth; + max-width: $IMX_SearchBar_Width; + width: $IMX_SearchBar_Width; padding: 0; + .imx-searchbar-card-content { + padding: 0 !important; + } + margin-top: 4px; +} + +.imx-search-component { + @include flex-row-container(); +} + +.imx-display-card { + height: 37px; +} + +.imx-search-input { + display: flex; + align-items: center; + padding: 0px; } .imx-searchbar-resultcard { @@ -19,96 +34,53 @@ $VI_Common_Color_Blue_1: #69CCE9; } .imx-searchbar-noitems { - // color: $VI_Common_Color_Font; padding: 15px; - font-size: 1.2em; + background: $white; } .imx-searchbar-dropdown { - max-width: $SearchBarWidth; + max-width: $IMX_SearchBar_Width; margin-top: 2px; padding: 0; } .matlistItem:hover { - background-color: $VI_Common_Color_Blue_1; + background-color: $color-blue-40; } .matlistItem { white-space: pre-line; } -.mat-list-option { - height: auto !important; - padding-top: 10px !important; - padding-bottom: 10px !important; - - .mat-list-text { - padding-top: 10px !important; - padding-bottom: 10px !important; - // color: $VI_Common_Color_Font !important; - } -} - -.mat-card .imx-searchbar-input { - border: 0 solid transparent !important; - font-size: 1.2em; - margin-bottom: 0; - height: $SearchBarHeight; - box-sizing: border-box; - flex-grow: 0; - flex-shrink: 0; - /* use flex-basis, because of flex shorthand with calc()s doesn't work in IE */ - flex-basis: calc(100% - 52px); -} - -.mat-card .imx-searchbar-input::placeholder { - font-style: italic; -} - -.imx-searchbar-input::-ms-clear { - /* hide input-clear button (the 'X') in > IE 10 */ - display: none; - width: 0; - height: 0; -} - -.imx-searchbar-input::placeholder { - font-style: italic; -} - imx-searchbar { display: flex; } .imx-searchbar-tablefilter { margin-left: 15px; + width: 300px; + display: flex; - .mat-form-field-wrapper { - line-height: 20px; + .mat-mdc-form-field { + flex: 1 1 auto; } } .imx-searchbar-control { - height: $SearchBarHeight; + height: $IMX_SearchBar_Height; } .imx-searchbar-table { display: flex; - align-items: center; - flex-wrap: wrap; + flex-direction: column; width: 100%; - line-height: $SearchBarHeight; + line-height: $IMX_SearchBar_Height; + .imx-input-searchbar { + flex-grow: 1; + } } .imx-searchbar-select { margin-top: 30px; margin-left: 40px; } - -.mat-autocomplete-panel.mat-autocomplete-visible { - width: $SearchBarWidth; - margin-right: -13px; - margin-left: -40px; -} - diff --git a/imxweb/projects/qbm/src/lib/searchbar/searchbar.component.ts b/imxweb/projects/qbm/src/lib/searchbar/searchbar.component.ts index 00617a88c..ecbb008e2 100644 --- a/imxweb/projects/qbm/src/lib/searchbar/searchbar.component.ts +++ b/imxweb/projects/qbm/src/lib/searchbar/searchbar.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,28 +24,22 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; -import { - Component, - OnInit, Input, ContentChild, TemplateRef, ElementRef, ViewChild, Output, EventEmitter, OnDestroy -} from '@angular/core'; +import { Component, ContentChild, EventEmitter, Input, OnDestroy, OnInit, Output, TemplateRef, ViewChild } from '@angular/core'; import { UntypedFormControl } from '@angular/forms'; import { MatAutocompleteSelectedEvent, MatAutocompleteTrigger } from '@angular/material/autocomplete'; import { MatSelect } from '@angular/material/select'; -import { EuiLoadingService } from '@elemental-ui/core'; -import { distinctUntilChanged, debounceTime } from 'rxjs/operators'; - -import { TypedEntity } from 'imx-qbm-dbts'; -import { imx_ISearchService } from './iSearchService'; +import { TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { Subscription } from 'rxjs'; +import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; +import { BusyService } from '../base/busy.service'; +import { imx_ISearchService } from './iSearchService'; @Component({ selector: 'imx-searchbar', templateUrl: './searchbar.component.html', - styleUrls: ['./searchbar.component.scss'] + styleUrls: ['./searchbar.component.scss'], }) export class SearchBarComponent implements OnInit, OnDestroy { - public get autoCompleteIsFocused(): boolean { return this.autocompleteFocus; } @@ -53,22 +47,22 @@ export class SearchBarComponent implements OnInit, OnDestroy { return this.filterFocus; } - public readonly filterComponentId = - Math.random().toString(36).substring(2, 15) + - Math.random().toString(36).substring(2, 15); + public readonly filterComponentId = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15); public noEntriesVisible = false; public tableList: TypedEntity[] = []; public tables = new UntypedFormControl(); public selectedTables: string[]; public searchResults: any[] = []; + private busyService: BusyService = new BusyService(); + @Input() public searchService: imx_ISearchService; @Input() public debounce = false; @Input() public debounceTime = 300; @Input() public watermark = '#LDS#Insert Text'; @Output() public selectionChange = new EventEmitter(); - @ContentChild('imxResultTemplate', /* TODO: add static flag */ {}) public resultTemplate: TemplateRef; + @ContentChild('imxResultTemplate', /* TODO: add static flag */ {}) public resultTemplate: TemplateRef; @ViewChild('tableSelect', { static: true }) public tableSelect: MatSelect; @ViewChild(MatAutocompleteTrigger, { static: true }) public autoCompleteTrigger: MatAutocompleteTrigger; @@ -76,36 +70,34 @@ export class SearchBarComponent implements OnInit, OnDestroy { private filterFocus = false; private term = ''; private readonly subscriptions: Subscription[] = []; + public isLoading: boolean = false; - constructor(private busyService: EuiLoadingService) { + constructor() { + this.subscriptions.push(this.busyService.busyStateChanged.subscribe((state) => (this.isLoading = state))); } public async ngOnInit(): Promise { let result: TypedEntity[]; - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + + const isBusy = this.busyService.beginBusy(); try { result = await this.searchService.getIndexedTables(); } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + isBusy.endBusy(); } this.tableList = result; - this.subscriptions.push(this.searchService.searchTermStream - .pipe( - debounceTime(this.debounceTime), - distinctUntilChanged() - ) - .subscribe(async (term: string) => { + this.subscriptions.push( + this.searchService.searchTermStream.pipe(debounceTime(this.debounceTime), distinctUntilChanged()).subscribe(async (term: string) => { this.term = term; await this.searchInternal(term); - }) + }), ); } public ngOnDestroy(): void { - this.subscriptions.forEach(subscription => subscription.unsubscribe()); + this.subscriptions.forEach((subscription) => subscription.unsubscribe()); } public search(term: string, evt?: KeyboardEvent): void { @@ -127,13 +119,13 @@ export class SearchBarComponent implements OnInit, OnDestroy { } json = ''; - Object.keys(link).forEach(key => (json += key + ': ' + link[key] + '\n')); + Object.keys(link).forEach((key) => (json += key + ': ' + link[key] + '\n')); return json.substring(0, json.length - 1); } - public displayItem(item?: any): string | undefined { - return item ? item.Display : undefined; + public displayItem(item?: any): string { + return item ? item.Display : ''; } /* *** Event handling *** */ @@ -142,13 +134,12 @@ export class SearchBarComponent implements OnInit, OnDestroy { const selectedtables = this.selectedTables !== undefined && this.selectedTables.length > 0 ? this.selectedTables.join(',') : ''; let result: TypedEntity[]; - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + const isBusy = this.busyService.beginBusy(); try { result = await this.searchService.search(item, selectedtables); } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + isBusy.endBusy(); } this.searchResults = result; this.setNoEntriesVisibility(); @@ -160,7 +151,7 @@ export class SearchBarComponent implements OnInit, OnDestroy { public onComponentLostFocus(_: FocusEvent): void { setTimeout(() => { - if (!this.autocompleteFocus && !this.filterFocus && document.activeElement.id !== this.filterComponentId) { + if (!this.autocompleteFocus && !this.filterFocus && document?.activeElement?.id !== this.filterComponentId) { this.noEntriesVisible = false; } }); @@ -169,7 +160,6 @@ export class SearchBarComponent implements OnInit, OnDestroy { public onInputFocus(): void { setTimeout(() => { this.autocompleteFocus = true; - this.setNoEntriesVisibility(); }); } diff --git a/imxweb/projects/qbm/src/lib/select/autocomplete.component.html b/imxweb/projects/qbm/src/lib/select/autocomplete.component.html deleted file mode 100644 index cdf940db0..000000000 --- a/imxweb/projects/qbm/src/lib/select/autocomplete.component.html +++ /dev/null @@ -1,64 +0,0 @@ - - {{ label }} - search - - - - - -
    -
    - -
    -
    -
    {{ contentProvider.display(item) }}
    -
    {{ contentProvider.displayLong(item) }}
    -
    -
    -
    -
    -
    - -
    -
    -
    {{ contentProvider.display(item) }}
    -
    {{ contentProvider.displayLong(item) }}
    -
    -
    -
    -
    - - - -
    -
    - -
    -
    -
    {{ contentProvider.display(item) }}
    -
    {{ contentProvider.displayLong(item) }}
    -
    -
    -
    -
    -
    - -
    -
    -
    {{ contentProvider.display(item) }}
    -
    {{ contentProvider.displayLong(item) }}
    -
    -
    -
    -
    - -
    - - {{ errorMessage }} - -
    diff --git a/imxweb/projects/qbm/src/lib/select/autocomplete.component.scss b/imxweb/projects/qbm/src/lib/select/autocomplete.component.scss deleted file mode 100644 index 260420460..000000000 --- a/imxweb/projects/qbm/src/lib/select/autocomplete.component.scss +++ /dev/null @@ -1,81 +0,0 @@ -@import '@elemental-ui/core/src/styles/_palette.scss'; - -:host { - width: 100%; -} - -.mat-form-field { - width: inherit; -} - -.imx-candidate-option { - line-height: 1.5em; - display: flex; - width: 100%; - - .imx-candidate-item { - display: flex; - align-items: center; - width: 100%; - - .imx-candidate-icon { - margin-right: 5px; - border-radius: 50%; - background: $asher-gray; - height: 20px; - width: 20px; - display: flex; - align-items: center; - flex: 0 0 20px; - - .eui-icon { - opacity: 0.6; - color: $black-9; - margin: auto; - } - } - - .imx-candidate-content { - flex: 1; - width: calc(100% - 25px); - overflow: hidden; - - .imx-candidate-display { - color: $black-6; - font-size: 14px; - text-overflow: ellipsis; - overflow: hidden; - width: 100vw; - } - - .imx-candidate-longdisplay { - color: $black-9; - font-size: 12px; - text-overflow: ellipsis; - overflow: hidden; - } - } - } -} - -.imx-input-button-container { - display: flex; -} - -::ng-deep span.mat-option-text { - display: flex; -} - -mat-checkbox { - margin-right: 10px; - width: 100%; - - ::ng-deep .mat-checkbox-layout { - width: 100%; - - .mat-checkbox-label { - width: 100%; - } - } - -} diff --git a/imxweb/projects/qbm/src/lib/select/autocomplete.component.ts b/imxweb/projects/qbm/src/lib/select/autocomplete.component.ts deleted file mode 100644 index 6fa19d20c..000000000 --- a/imxweb/projects/qbm/src/lib/select/autocomplete.component.ts +++ /dev/null @@ -1,264 +0,0 @@ -/* - * ONE IDENTITY LLC. PROPRIETARY INFORMATION - * - * This software is confidential. One Identity, LLC. or one of its affiliates or - * subsidiaries, has supplied this software to you under terms of a - * license agreement, nondisclosure agreement or both. - * - * You may not copy, disclose, or use this software except in accordance with - * those terms. - * - * - * Copyright 2023 One Identity LLC. - * ALL RIGHTS RESERVED. - * - * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR - * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, OR - * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE - * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE - * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING - * THIS SOFTWARE OR ITS DERIVATIVES. - * - */ - -import { - Component, - ViewChild, - AfterViewInit, - ElementRef, - Input, - Output, - EventEmitter, - OnChanges, - SimpleChanges, - ViewChildren, - QueryList -} from '@angular/core'; -import { MatAutocompleteTrigger, MatAutocompleteSelectedEvent, MatAutocomplete } from '@angular/material/autocomplete'; -import { MatCheckboxChange, MatCheckbox } from '@angular/material/checkbox'; -import { PageEvent } from '@angular/material/paginator'; -import { UntypedFormControl, AbstractControl, ValidatorFn } from '@angular/forms'; -import { Observable } from 'rxjs'; -import { startWith, map, debounceTime } from 'rxjs/operators'; - -import { ClassloggerService } from '../classlogger/classlogger.service'; -import { SelectDataSource } from './select-data-source'; -import { SelectContentProvider } from './select-content-provider.interface'; - -@Component({ - selector: 'imx-autocomplete', - templateUrl: './autocomplete.component.html', - styleUrls: ['./autocomplete.component.scss'] -}) -export class AutocompleteComponent implements AfterViewInit, OnChanges { - /** Reference to the autocomplete component. */ - public readonly autocompleteCtrl = new UntypedFormControl('', this.checkAutocompleteValidator()); - - /** Height of an item in the dropdown panel in px. Needed for virtual scrolling. */ - public readonly itemSize = 48; - - /** Needed fpr search if the component uses a local datasource */ - public filteredDatasource: Observable; - - /** The candidate list for the selection */ - @Input() public items: T[] = []; - - /** The datasource of the component */ - @Input() public dataSource: SelectDataSource; - - /** The name of icon for the items shown in the autocomplete control. */ - @Input() public itemIcon: string; - - /** The label of the input box */ - @Input() public label = ''; - - /** Flag for multi select mode */ - @Input() public multi = false; - - /** Selected items */ - @Input() public itemsSelected: T[] = []; - - /** Indicates if Datasource is remote or local. */ - @Input() public isLocalDatasource = false; - - /** Provides methods for displaying the item content */ - @Input() public contentProvider: SelectContentProvider; - - @Input() public pageSize = 50; - - @Input() public totalCount = 0; - - /** validation error message */ - @Input() public errorMessage: string; - - /** Fires when the form needs a refresh. */ - @Output() public refreshForm: EventEmitter = new EventEmitter(); - - /** Fires when the user types in the autocomplete input control. */ - @Output() public autocompleteValueChanged: EventEmitter = new EventEmitter(); - - /** Fires when the component needs a reset, e.g. user selects an entry. */ - @Output() public needReset: EventEmitter = new EventEmitter(); - - /** Indicates that the component wants to close */ - @Output() public closing: EventEmitter = new EventEmitter(); - - @ViewChild('auto', { static: true }) public autocomplete: MatAutocomplete; - - /** Reference to the {@link MatAutocompleteTrigger} */ - @ViewChild('trigger', { static: true }) public autocompleteTrigger: MatAutocompleteTrigger; - - /** Reference to the input control. */ - @ViewChild('inputCtrl', { static: true }) public inputCtrl: ElementRef; - - @ViewChildren(MatCheckbox) public checkboxCtrls: QueryList; - - private readonly debounceTime = 500; - - constructor(private logger: ClassloggerService) { - this.autocompleteCtrl.valueChanges.pipe(debounceTime(this.debounceTime)).subscribe(value => { - - if (!this.isLocalDatasource) { - this.dataSource.reset(); - } - - this.autocompleteValueChanged.emit(value); - }); - } - - public ngOnChanges(changes: SimpleChanges): void { - - if (changes['items'] && changes['items'].currentValue) { - this.dataSource.setData(this.items); - - if (this.isLocalDatasource) { - this.totalCount = this.dataSource.rawData.length; - this.filteredDatasource = this.autocompleteCtrl.valueChanges - .pipe( - startWith(''), - map(value => this.filter(value)) - ); - } - } - } - - public ngAfterViewInit(): void { - setTimeout(() => { - this.autocompleteTrigger.openPanel(); - this.inputCtrl.nativeElement.focus(); - }); - - } - - public onChecked(event: MatCheckboxChange, selectedItem: T): void { - this.applyMultiSelect(event.checked, selectedItem); - } - - public onCheckboxClicked(event: MouseEvent): void { - event.stopPropagation(); - } - - public onPage(event: PageEvent): void { - this.dataSource.getData(event.pageIndex * event.pageSize); - } - - public onCancel(): void { - this.close(); - } - - public onFocusout(event: FocusEvent): void { - if (!this.autocomplete.isOpen) { - this.close(); - return; - } - } - - /** - * Called when an item was selected, to push this item to the selection list. - * @param event the caller - */ - public selectSingleItem(event: MatAutocompleteSelectedEvent): void { - this.applySingleSelect(event.option.value); - } - - public panelClosing(): void { - this.close(); - } - - public itemSelected(selectedItem: T): boolean { - return this.itemsSelected.findIndex(item => this.contentProvider.key(item) === this.contentProvider.key(selectedItem)) >= 0; - } - - private close(): void { - this.closing.emit(true); - this.autocompleteValueChanged.emit(''); - this.inputCtrl.nativeElement.click(); - } - - private applySingleSelect(selectedItem: T): void { - if (selectedItem == null || typeof (selectedItem) === 'string' || this.multi) { - return; - } - - if (this.itemsSelected.findIndex(item => this.contentProvider.key(selectedItem) === this.contentProvider.key(item)) < 0) { - this.itemsSelected.splice(0); - this.itemsSelected.push(selectedItem); - this.refreshForm.emit(); - - if (!this.isLocalDatasource) { - this.dataSource.reset(); - } - } - - this.clearAutocompleteCtrl(); - } - - private applyMultiSelect(checked: boolean, selectedItem: T): void { - if (selectedItem == null || typeof (selectedItem) === 'string') { - return; - } - - const index = this.itemsSelected - .findIndex(item => this.contentProvider.key(selectedItem) === this.contentProvider.key(item)); - - if (index === -1 && checked) { - this.itemsSelected.push(selectedItem); - } else if (index >= 0 && !checked) { - this.itemsSelected.splice(index, 1); - } - this.inputCtrl.nativeElement.click(); - this.refreshForm.emit(); - } - - private clearAutocompleteCtrl(): void { - this.inputCtrl.nativeElement.value = ''; - this.autocompleteCtrl.setValue(undefined); - } - - private filter(value: string | T): T[] { - - if (typeof (value) !== 'string') { - return; - } - - return this.dataSource.rawData.filter(option => this.contentProvider.display(option).toLowerCase().includes(value.toLowerCase())); - } - - private checkAutocompleteValidator(): ValidatorFn { - return (control: AbstractControl): { [key: string]: boolean } | null => { - if (control == null || control.value == null || this.dataSource == null) { - return null; - } - - if (this.dataSource.rawData != null && this.dataSource.rawData.length > 0) { - return null; - } - - return { checkAutocomplete: true }; - }; - } - -} diff --git a/imxweb/projects/qbm/src/lib/select/data-navigation-parameters.interface.ts b/imxweb/projects/qbm/src/lib/select/data-navigation-parameters.interface.ts index d6fefcf1d..911739773 100644 --- a/imxweb/projects/qbm/src/lib/select/data-navigation-parameters.interface.ts +++ b/imxweb/projects/qbm/src/lib/select/data-navigation-parameters.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { CollectionLoadParameters } from 'imx-qbm-dbts'; +import { CollectionLoadParameters } from '@imx-modules/imx-qbm-dbts'; export interface DataNavigationParameters { navigation: CollectionLoadParameters; diff --git a/imxweb/projects/qbm/src/lib/select/fk-selection-container.ts b/imxweb/projects/qbm/src/lib/select/fk-selection-container.ts index 0fd1deb88..16f04adb6 100644 --- a/imxweb/projects/qbm/src/lib/select/fk-selection-container.ts +++ b/imxweb/projects/qbm/src/lib/select/fk-selection-container.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,7 +27,7 @@ import { ErrorHandler } from '@angular/core'; import { AbstractControl } from '@angular/forms'; -import { EntityData, FilterType, CompareOperator, CollectionLoadParameters, EntityCollectionData } from 'imx-qbm-dbts'; +import { EntityData, FilterType, CompareOperator, CollectionLoadParameters, EntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { FkContainer } from '../fk-container/fk-container'; import { SelectContentProvider } from './select-content-provider.interface'; import { ClassloggerService } from '../classlogger/classlogger.service'; @@ -54,10 +54,10 @@ export class FkSelectionContainer { public placeholder?: string, public itemIcon?: string, public contentProvider: SelectContentProvider = { - display: item => item.Display, - key: item => item.Keys[0] - } - ) { + display: (item) => item.Display, + key: (item) => item.Keys[0], + }, + ) { this.navigation = { StartIndex: 0, PageSize: this.settingsService.DefaultPageSize }; } @@ -81,8 +81,11 @@ export class FkSelectionContainer { this.navigation.StartIndex = startIndex; try { this.loading = true; - this.logger.trace(this, `Load ${this.navigation.PageSize} new items of column - ${this.fkContainer.property.Column.ColumnName} from position ${this.navigation.StartIndex}`); + this.logger.trace( + this, + `Load ${this.navigation.PageSize} new items of column + ${this.fkContainer.property.Column.ColumnName} from position ${this.navigation.StartIndex}`, + ); await this.init(this.errorHandler); } finally { this.loading = false; @@ -90,14 +93,17 @@ export class FkSelectionContainer { } public async onAutocompleteValueChanged(keywords: string): Promise { - this.navigation.filter = keywords === '' - ? null - : [{ - ColumnName: this.filterColumn, - Type: FilterType.Compare, - CompareOp: CompareOperator.Like, - Value1: `%${keywords}%` - }]; + this.navigation.filter = + keywords === '' + ? null + : [ + { + ColumnName: this.filterColumn, + Type: FilterType.Compare, + CompareOp: CompareOperator.Like, + Value1: `%${keywords}%`, + }, + ]; this.getData(0); } diff --git a/imxweb/projects/qbm/src/lib/select/select-data-source.ts b/imxweb/projects/qbm/src/lib/select/select-data-source.ts deleted file mode 100644 index 8ad402c76..000000000 --- a/imxweb/projects/qbm/src/lib/select/select-data-source.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* - * ONE IDENTITY LLC. PROPRIETARY INFORMATION - * - * This software is confidential. One Identity, LLC. or one of its affiliates or - * subsidiaries, has supplied this software to you under terms of a - * license agreement, nondisclosure agreement or both. - * - * You may not copy, disclose, or use this software except in accordance with - * those terms. - * - * - * Copyright 2023 One Identity LLC. - * ALL RIGHTS RESERVED. - * - * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR - * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, OR - * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE - * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE - * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING - * THIS SOFTWARE OR ITS DERIVATIVES. - * - */ - -import { EventEmitter } from '@angular/core'; - -import { ClassloggerService } from '../classlogger/classlogger.service'; - - -/** Datasource of select component */ -export class SelectDataSource { - - /** Fires when the datasource needs new data. */ - public needData: EventEmitter = new EventEmitter(); - - - /** Returns the list of items of the datasource */ - public get rawData(): any[] { - return this.cache; - } - - private cache = []; - - constructor(private logger: ClassloggerService) {} - - /** Resets the datasource */ - public reset(): void { - this.logger.debug(this, 'Resetting datasource'); - this.cache = []; - } - - public getData(startIndex: number): void { - this.needData.emit(startIndex); - } - - public setData(items: any[]): void { - this.logger.debug(this, `Setting cache with ${items.length}`); - this.cache = items; - } - -} diff --git a/imxweb/projects/qbm/src/lib/select/select.component.html b/imxweb/projects/qbm/src/lib/select/select.component.html deleted file mode 100644 index 9c1435128..000000000 --- a/imxweb/projects/qbm/src/lib/select/select.component.html +++ /dev/null @@ -1,40 +0,0 @@ -
    - - {{ label }} -
    - - - - {{ contentProvider.display(item) }} - cancel - - -
    -
    - - {{ label }} - - - - - - - - -
    diff --git a/imxweb/projects/qbm/src/lib/select/select.component.scss b/imxweb/projects/qbm/src/lib/select/select.component.scss deleted file mode 100644 index c9c4fc2e8..000000000 --- a/imxweb/projects/qbm/src/lib/select/select.component.scss +++ /dev/null @@ -1,40 +0,0 @@ -@import '@elemental-ui/core/src/styles/_palette.scss'; - -.mat-form-field { - width: 100%; -} - -.imx-main-container { - display: flex; - align-items: baseline; -} - -.mat-chip-list { - user-select: none; - outline: none; -} - -.imx-chip-value { - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; -} - -.imx-chip-list-container { - min-height: 20px; - max-height: 120px; - overflow: auto; - padding: 4px; -} - -.imx-chip-icon { - flex: 0 0 30px; -} - -.mat-chip { - display: grid; - grid-template-columns: auto 1fr auto; - .eui-icon { - opacity: 0.6; - } -} diff --git a/imxweb/projects/qbm/src/lib/select/select.component.ts b/imxweb/projects/qbm/src/lib/select/select.component.ts deleted file mode 100644 index ee249db4d..000000000 --- a/imxweb/projects/qbm/src/lib/select/select.component.ts +++ /dev/null @@ -1,222 +0,0 @@ -/* - * ONE IDENTITY LLC. PROPRIETARY INFORMATION - * - * This software is confidential. One Identity, LLC. or one of its affiliates or - * subsidiaries, has supplied this software to you under terms of a - * license agreement, nondisclosure agreement or both. - * - * You may not copy, disclose, or use this software except in accordance with - * those terms. - * - * - * Copyright 2023 One Identity LLC. - * ALL RIGHTS RESERVED. - * - * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR - * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, OR - * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE - * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE - * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING - * THIS SOFTWARE OR ITS DERIVATIVES. - * - */ - -import { - Component, - Input, - EventEmitter, - Output, - AfterViewInit, - OnDestroy, - OnChanges, - SimpleChanges, - HostListener -} from '@angular/core'; -import { ENTER, COMMA } from '@angular/cdk/keycodes'; -import { AbstractControl, UntypedFormControl } from '@angular/forms'; -import { Subscription } from 'rxjs'; - -import { ClassloggerService } from '../classlogger/classlogger.service'; -import { SelectContentProvider } from './select-content-provider.interface'; -import { SelectDataSource } from './select-data-source'; -import { DataNavigationParameters } from './data-navigation-parameters.interface'; - -/** - * A component for selecting a single or multiple items from a candidate list using an autocomplete control. - * The selected object(s) is/are displayed as Mat-Chip(s) and can be removed using a remove icon. - */ -@Component({ - selector: 'imx-select', - templateUrl: './select.component.html', - styleUrls: ['./select.component.scss'], -}) -export class SelectComponent implements AfterViewInit, OnChanges, OnDestroy { - public readonly separatorKeysCodes: number[] = [ENTER, COMMA]; - public readonly chipListCtrl = new UntypedFormControl(''); - public dataSource: SelectDataSource; - public autocompleteVisible = false; - - /** Flag for multi select mode */ - @Input() public multi = false; - - /** The name of icon for the items shown in the autocomplete control. */ - @Input() public itemIcon: string; - - /** The candidate list for the selection */ - @Input() public items: T[] = []; - - /** The selected items */ - @Input() public itemsSelected: T[] = []; - - /** The text for the mat-label of the formcontrol */ - @Input() public label: string; - - /** The placeholder text of the autocomplete control */ - @Input() public placeholder: string; - - /** validation error message */ - @Input() public errorMessage: string; - - /** The form control of the component. */ - @Input() public formCtrl: AbstractControl; - - /** Provides methods for displaying the item content */ - @Input() public contentProvider: SelectContentProvider; - - /** Indicates if data is loading and shows/hides the spinner. */ - @Input() public loading = false; - - /** @deprecated Not in use. Will be removed. - * The virtual page size. - * Must be set for calculating the height of the virtual sroll container. - */ - @Input() public pageSize = 50; - - /** Indicates if Datasource is remote or local. */ - @Input() public isLocalDatasource = false; - - /** Indicates if the component is disabled = readonly */ - @Input() public disabled = false; - - @Input() public totalCount = 0; - - @Input() public labelAutoComplete: string; - - /** @deprecated Use needMoreData instead. Will be removed. - * Fires when the component need new data, e.g. user scrolls to the end of the container. - */ - @Output() public needData: EventEmitter = new EventEmitter(); - - /** Fires when the component need new data, e.g. user scrolls to the end of the container. */ - @Output() public needMoreData: EventEmitter = new EventEmitter(); - - /** @deprecated Will be removed. - * Fires when the user types in the autocomplete input control. - */ - @Output() public autocompleteValueChanged: EventEmitter = new EventEmitter(); - - private subscriptions: Subscription[] = []; - private parameters: DataNavigationParameters = { navigation: { StartIndex: 0, PageSize: this.pageSize}}; - private originalItemsSelected: T[] = []; - private canceling = false; - - constructor(private logger: ClassloggerService) { - this.dataSource = new SelectDataSource(this.logger); - } - - public ngAfterViewInit(): void { - this.subscriptions.push(this.dataSource.needData.subscribe((startIndex: number) => { - this.parameters.navigation.StartIndex = startIndex; - this.needMoreData.emit(this.parameters); - this.needData.emit(this.parameters.navigation.StartIndex); - })); - } - - public ngOnChanges(changes: SimpleChanges): void { - if (changes['itemsSelected'] && changes['itemsSelected'].currentValue && this.multi) { - this.originalItemsSelected = this.itemsSelected.slice(); - } - } - - public ngOnDestroy(): void { - this.subscriptions.forEach(subscription => subscription.unsubscribe()); - } - - /** - * Removes the specified item from the selection list. - * @param item the item to remove. - */ - public removeItem(item: T, event?: MouseEvent, ): void { - if (event) { - event.stopPropagation(); - } - - if (this.disabled) { - return; - } - - let index = -1; - - if (this.contentProvider && this.contentProvider.key) { - const itemKey = this.contentProvider.key(item); - index = this.itemsSelected.findIndex(itemSelected => this.contentProvider.key(itemSelected) === itemKey); - } else { - index = this.itemsSelected.indexOf(item); - } - - if (index >= 0) { - this.logger.debug(this, 'Selection list, remove', item); - this.itemsSelected.splice(index, 1); - this.refreshFormControl(); - } - } - - @HostListener('document:keydown.escape', ['$event']) - public onDocumentCancel(event: KeyboardEvent): void { - if (this.multi) { - this.itemsSelected = this.originalItemsSelected.slice(); - this.canceling = true; - } - } - - public showAutocomplete(): void { - if (!this.disabled) { - this.autocompleteVisible = true; - } - } - - public onAutocompleteValueChanged(event: string): void { - if (event == null) { - this.parameters.navigation.StartIndex = 0; - this.parameters.navigation.filter = undefined; - this.parameters.navigation.search = undefined; - } - - this.parameters.keyword = event; - this.needMoreData.emit(this.parameters); - this.autocompleteValueChanged.emit(this.parameters.keyword); - } - - public onAutocompleteClosing(): void { - this.autocompleteVisible = false; - this.dataSource.reset(); - this.parameters.keyword = ''; - this.parameters.navigation.StartIndex = 0; - this.parameters.navigation.filter = undefined; - this.parameters.navigation.search = undefined; - - if (this.multi && !this.canceling) { - this.originalItemsSelected = this.itemsSelected.slice(); - } - - this.canceling = false; - } - - public refreshFormControl(): void { - this.formCtrl.setValue(this.itemsSelected); - this.formCtrl.markAsDirty(); - } -} diff --git a/imxweb/projects/qbm/src/lib/selected-elements/selected-elements-dialog/selected-elements-dialog.component.html b/imxweb/projects/qbm/src/lib/selected-elements/selected-elements-dialog/selected-elements-dialog.component.html index 8ca680df6..fb50b7d88 100644 --- a/imxweb/projects/qbm/src/lib/selected-elements/selected-elements-dialog/selected-elements-dialog.component.html +++ b/imxweb/projects/qbm/src/lib/selected-elements/selected-elements-dialog/selected-elements-dialog.component.html @@ -1,6 +1,6 @@ -

    {{ data.header }}

    +

    {{ data.header }}

    - + {{ data.header }}

    >
    - - + +
    {{ item.GetEntity().GetDisplay() }}
    {{ getTable(item) }}
    diff --git a/imxweb/projects/qbm/src/lib/selected-elements/selected-elements-dialog/selected-elements-dialog.component.scss b/imxweb/projects/qbm/src/lib/selected-elements/selected-elements-dialog/selected-elements-dialog.component.scss index f4974f76d..da46eed08 100644 --- a/imxweb/projects/qbm/src/lib/selected-elements/selected-elements-dialog/selected-elements-dialog.component.scss +++ b/imxweb/projects/qbm/src/lib/selected-elements/selected-elements-dialog/selected-elements-dialog.component.scss @@ -1,41 +1,18 @@ -:host { - display: flex; - flex-direction: column; - overflow: hidden; - height: 100%; -} +@import 'base/mixins'; -.mat-dialog-content { +:host { display: flex; flex-direction: column; overflow: hidden; height: 100%; } -.mat-card { - display: flex; - flex-direction: column; - overflow: hidden; - flex: 1 1 auto; -} - -.imx-table-container { - display: flex; - flex-direction: column; - overflow: hidden; - height: inherit; - max-width: 100%; - flex: 1 1 auto; -} - -.mat-dialog-title { - margin: 10px 0 20px; +.mat-mdc-dialog-content { + @include flex-column-container($overflow: hidden, $height: 100%); } -.mat-dialog-actions { - justify-content: flex-end; - - > .mat-flat-button { +.mat-mdc-dialog-actions { + > .mat-mdc-unelevated-button { margin-bottom: 20px; } } diff --git a/imxweb/projects/qbm/src/lib/selected-elements/selected-elements-dialog/selected-elements-dialog.component.ts b/imxweb/projects/qbm/src/lib/selected-elements/selected-elements-dialog/selected-elements-dialog.component.ts index d22f4a4d1..ce7856d36 100644 --- a/imxweb/projects/qbm/src/lib/selected-elements/selected-elements-dialog/selected-elements-dialog.component.ts +++ b/imxweb/projects/qbm/src/lib/selected-elements/selected-elements-dialog/selected-elements-dialog.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,16 +25,15 @@ */ import { Component, Inject, OnInit } from '@angular/core'; +import { MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { DataModelFilterOption, DbObjectKey, DisplayColumns, EntitySchema, IClientProperty, TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { TranslateService } from '@ngx-translate/core'; -import { DataModelFilterOption, DbObjectKey, DisplayColumns, EntitySchema, IClientProperty, TypedEntity } from 'imx-qbm-dbts'; import { MetadataService } from '../../base/metadata.service'; import { CdrFactoryService } from '../../cdr/cdr-factory.service'; import { DataSourceToolbarFilter } from '../../data-source-toolbar/data-source-toolbar-filters.interface'; import { DataSourceToolbarSettings } from '../../data-source-toolbar/data-source-toolbar-settings'; import { CandidateEntity } from '../../fk-advanced-picker/candidate-entity'; -import { SelectedElementsDialogParameter } from './selected-elements-dialog.model'; -import { TypedEntityTableFilter } from './selected-elements-dialog.model'; -import { MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { SelectedElementsDialogParameter, TypedEntityTableFilter } from './selected-elements-dialog.model'; /** * A component, that can be shown inside a MatDialogComponent. It contains a table of entities with their display value as a column @@ -68,13 +67,7 @@ export class SelectedElementsDialog implements OnInit { ) { this.entitySchemaCandidates = CandidateEntity.GetEntitySchema(); this.displayedColumns = [this.entitySchemaCandidates.Columns[DisplayColumns.DISPLAY_PROPERTYNAME]]; - this.sortedEntities = data.entities?.sort( - (a, b) => - a - .GetEntity() - .GetDisplay() - ?.localeCompare(b.GetEntity()?.GetDisplay()), - ); + this.sortedEntities = data.entities?.sort((a, b) => a.GetEntity().GetDisplay()?.localeCompare(b.GetEntity()?.GetDisplay())); } public async ngOnInit(): Promise { @@ -117,12 +110,15 @@ export class SelectedElementsDialog implements OnInit { public navigate(source: TypedEntityTableFilter): void { this.navigationState = { ...this.navigationState, ...source }; const possible = source.table - ? this.searchedEntities.filter( - (elem) => CdrFactoryService.tryGetColumn(elem.GetEntity(), 'XObjectKey')?.GetValue()?.includes(source.table), + ? this.searchedEntities.filter((elem) => + CdrFactoryService.tryGetColumn(elem.GetEntity(), 'XObjectKey')?.GetValue()?.includes(source.table), ) : this.searchedEntities; - const data = possible.slice(this.navigationState.StartIndex, this.navigationState.StartIndex + this.navigationState.PageSize); + const data = possible.slice( + this.navigationState.StartIndex, + (this.navigationState.StartIndex ?? 0) + (this.navigationState.PageSize ?? 0), + ); this.dstSettings = { displayedColumns: this.displayedColumns, dataSource: { @@ -149,16 +145,18 @@ export class SelectedElementsDialog implements OnInit { return ''; } const tableName = DbObjectKey.FromXml(column.GetValue()).TableName; - return this.metaData.tables[tableName]?.DisplaySingular; + return this.metaData.tables[tableName]?.DisplaySingular ?? ''; } private getOptionsForFilter(): DataModelFilterOption[] { - return this.data.tables - .map((elem) => ({ Value: elem, Display: this.metaData.tables[elem]?.DisplaySingular })) - .filter((elem) => - this.data.entities.some( - (entity) => CdrFactoryService.tryGetColumn(entity.GetEntity(), 'XObjectKey')?.GetValue().includes(elem.Value), - ), - ); + return ( + this.data.tables + ?.map((elem) => ({ Value: elem, Display: this.metaData.tables[elem]?.DisplaySingular })) + .filter((elem) => + this.data.entities.some((entity) => + CdrFactoryService.tryGetColumn(entity.GetEntity(), 'XObjectKey')?.GetValue().includes(elem.Value), + ), + ) ?? [] + ); } } diff --git a/imxweb/projects/qbm/src/lib/selected-elements/selected-elements-dialog/selected-elements-dialog.model.ts b/imxweb/projects/qbm/src/lib/selected-elements/selected-elements-dialog/selected-elements-dialog.model.ts index ebd4a0eb7..e1586f0a6 100644 --- a/imxweb/projects/qbm/src/lib/selected-elements/selected-elements-dialog/selected-elements-dialog.model.ts +++ b/imxweb/projects/qbm/src/lib/selected-elements/selected-elements-dialog/selected-elements-dialog.model.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { TypedEntity, CollectionLoadParameters } from 'imx-qbm-dbts'; +import { TypedEntity, CollectionLoadParameters } from '@imx-modules/imx-qbm-dbts'; export interface SelectedElementsDialogParameter { entities: TypedEntity[]; @@ -35,4 +35,3 @@ export interface SelectedElementsDialogParameter { export interface TypedEntityTableFilter extends CollectionLoadParameters { table?: string; } - diff --git a/imxweb/projects/qbm/src/lib/selected-elements/selected-elements.component.html b/imxweb/projects/qbm/src/lib/selected-elements/selected-elements.component.html index 93ac2c5f1..aafea3fcc 100644 --- a/imxweb/projects/qbm/src/lib/selected-elements/selected-elements.component.html +++ b/imxweb/projects/qbm/src/lib/selected-elements/selected-elements.component.html @@ -1,5 +1,15 @@ - + \ No newline at end of file diff --git a/imxweb/projects/qbm/src/lib/selected-elements/selected-elements.component.scss b/imxweb/projects/qbm/src/lib/selected-elements/selected-elements.component.scss index 848687f66..88d4bd17c 100644 --- a/imxweb/projects/qbm/src/lib/selected-elements/selected-elements.component.scss +++ b/imxweb/projects/qbm/src/lib/selected-elements/selected-elements.component.scss @@ -1,6 +1,5 @@ -@import '../../../../../shared/scss/common-table.scss'; +@import 'base/mixins'; - -.mat-stroked-button { - @include imx-icon-for-image-button(); +.mat-mdc-outlined-button { + @include image-button-icon(); } \ No newline at end of file diff --git a/imxweb/projects/qbm/src/lib/selected-elements/selected-elements.component.ts b/imxweb/projects/qbm/src/lib/selected-elements/selected-elements.component.ts index 641d54560..1f8e7340d 100644 --- a/imxweb/projects/qbm/src/lib/selected-elements/selected-elements.component.ts +++ b/imxweb/projects/qbm/src/lib/selected-elements/selected-elements.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,11 +24,11 @@ * */ -import { Component, Input } from '@angular/core'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { TranslateService } from '@ngx-translate/core'; -import { TypedEntity } from 'imx-qbm-dbts'; +import { TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { SelectedElementsDialog } from './selected-elements-dialog/selected-elements-dialog.component'; @Component({ @@ -40,16 +40,46 @@ export class SelectedElementsComponent { @Input() public selectedElements: TypedEntity[] = []; @Input() public tables: string[]; @Input() public isLoading: boolean; - @Input() public caption = this.translate.instant('#LDS#Show selected'); - @Input() public dialogHeader = this.translate.instant('#LDS#Heading Selected Items'); + @Input() public caption: string; + @Input() public dialogHeader: string; + @Input() public showDeselectAll: boolean = false; + @Input() public deselectAllCaption: string; + + @Output() public openCustomSelectionDialog = new EventEmitter(); + @Output() public onDeselectAllClicked = new EventEmitter(); - constructor(public readonly dialog: MatDialog, public readonly translate: TranslateService) {} + constructor( + public readonly dialog: MatDialog, + public readonly translate: TranslateService, + ) { + if (!this.caption) { + this.caption = this.translate.instant('#LDS#Show selected'); + } + if (!this.deselectAllCaption) { + this.deselectAllCaption = this.translate.instant('#LDS#Deselect all'); + } + if (!this.dialogHeader) { + this.dialogHeader = this.translate.instant('#LDS#Heading Selected Items'); + } + } public onOpenSelectionDialog(): void { - this.dialog.open(SelectedElementsDialog, { - width: 'max(60%,600px)', - height: 'max(60%,600px)', - data: { entities: this.selectedElements, tables: this.tables ?? [], header: this.dialogHeader }, - }); + if (this.openCustomSelectionDialog.observed) { + this.openCustomSelectionDialog.emit(); + } else { + this.dialog.open(SelectedElementsDialog, { + width: 'max(60%,600px)', + height: 'max(60%,600px)', + data: { entities: this.selectedElements, tables: this.tables ?? [], header: this.dialogHeader }, + }); + } + } + + public deselectAll() { + if (this.onDeselectAllClicked.observed) { + this.onDeselectAllClicked.emit(); + } else { + this.selectedElements = []; + } } } diff --git a/imxweb/projects/qbm/src/lib/selected-elements/selected-elements.module.ts b/imxweb/projects/qbm/src/lib/selected-elements/selected-elements.module.ts index 442c47076..811536b73 100644 --- a/imxweb/projects/qbm/src/lib/selected-elements/selected-elements.module.ts +++ b/imxweb/projects/qbm/src/lib/selected-elements/selected-elements.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -39,7 +39,6 @@ import { SelectedElementsDialog } from './selected-elements-dialog/selected-elem import { DataTableModule } from '../data-table/data-table.module'; import { DataSourceToolbarModule } from '../data-source-toolbar/data-source-toolbar.module'; - @NgModule({ declarations: [SelectedElementsComponent, SelectedElementsDialog], imports: [ diff --git a/imxweb/projects/qbm/src/lib/services/device-state.service.ts b/imxweb/projects/qbm/src/lib/services/device-state.service.ts deleted file mode 100644 index 270f38425..000000000 --- a/imxweb/projects/qbm/src/lib/services/device-state.service.ts +++ /dev/null @@ -1,80 +0,0 @@ -/* - * ONE IDENTITY LLC. PROPRIETARY INFORMATION - * - * This software is confidential. One Identity, LLC. or one of its affiliates or - * subsidiaries, has supplied this software to you under terms of a - * license agreement, nondisclosure agreement or both. - * - * You may not copy, disclose, or use this software except in accordance with - * those terms. - * - * - * Copyright 2023 One Identity LLC. - * ALL RIGHTS RESERVED. - * - * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR - * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, OR - * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE - * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE - * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING - * THIS SOFTWARE OR ITS DERIVATIVES. - * - */ - -import { Injectable } from '@angular/core'; -import * as elementResizeDetector from 'element-resize-detector'; - -/** @deprecated This service is deprecated and will be removed in a future release.*/ -@Injectable() -export class DeviceStateService { - - get deviceState(): string { - this._deviceState = this.getCurrentDeviceState(); - return this._deviceState; - } - - private _deviceState = 'desktop'; - constructor() { - this.createDeviceStateIndicator(); - } - - public isPhoneDevice(): boolean { - return this.deviceState === 'mobile'; - } - - private createDeviceStateIndicator(): HTMLElement { - const indicatorNode = document.createElement('div'); - indicatorNode.className = 'state-indicator'; - document.body.appendChild(indicatorNode); - - // importing a commonjs module - // const elementResizeDetectorMaker = require('element-resize-detector'); - const elementResizeDetectorMaker = elementResizeDetector; - const erd = elementResizeDetectorMaker({ - strategy: 'scroll' - }); - erd.listenTo(document.body, () => this.deviceState); - return indicatorNode; - } - - private getCurrentDeviceState(): string { - const indicatorNode = this.getOrCreateIndicatorNode(); - return this.getDeviceStateFromNode(indicatorNode); - } - - private getOrCreateIndicatorNode() { - const indicatorNode = document.querySelector('.state-indicator'); - return indicatorNode || this.createDeviceStateIndicator(); - } - - private getDeviceStateFromNode(indicatorNode: Element) { - let state = window.getComputedStyle(indicatorNode, ':before').getPropertyValue('content'); - // delete quotes - state = state.replace(/['"]+/g, ''); - this._deviceState = state; - return state; - } -} diff --git a/imxweb/projects/qbm/src/lib/session/auth-prop-data-provider.interface.ts b/imxweb/projects/qbm/src/lib/session/auth-prop-data-provider.interface.ts index ec04bfaec..fcba06e75 100644 --- a/imxweb/projects/qbm/src/lib/session/auth-prop-data-provider.interface.ts +++ b/imxweb/projects/qbm/src/lib/session/auth-prop-data-provider.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,4 +28,5 @@ export interface AuthPropDataProvider { name: string; inputType: string; display: string; + disabled?: boolean; } diff --git a/imxweb/projects/qbm/src/lib/session/imx-session.service.ts b/imxweb/projects/qbm/src/lib/session/imx-session.service.ts index 6f21152d1..b4a3293b6 100644 --- a/imxweb/projects/qbm/src/lib/session/imx-session.service.ts +++ b/imxweb/projects/qbm/src/lib/session/imx-session.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,10 +26,10 @@ import { Injectable } from '@angular/core'; -import { TypedClient, V2Client } from 'imx-api-qbm'; -import { ISessionState, SessionState } from './session-state'; +import { PreAuthResponseData, TypedClient, V2Client } from '@imx-modules/imx-api-qbm'; import { AppConfigService } from '../appConfig/appConfig.service'; import { ClassloggerService } from '../classlogger/classlogger.service'; +import { ISessionState, SessionState } from './session-state'; @Injectable() export class imx_SessionService { @@ -46,7 +46,8 @@ export class imx_SessionService { constructor( private appConfigService: AppConfigService, - private readonly logger: ClassloggerService) { } + private readonly logger: ClassloggerService, + ) {} public async getSessionState(): Promise { this.logger.debug(this, 'getSessionState'); @@ -57,7 +58,7 @@ export class imx_SessionService { public async login(loginData: { [key: string]: string }): Promise { this.logger.debug(this, 'login'); const sr = await this.appConfigService.client.imx_login_post(this.appConfigService.Config.WebAppIndex, loginData, { - noxsrf: false + noxsrf: false, }); return (this.sessionState = new SessionState(sr)); } @@ -67,4 +68,17 @@ export class imx_SessionService { const sr = await this.appConfigService.client.imx_logout_post(this.appConfigService.Config.WebAppIndex); return (this.sessionState = new SessionState(sr)); } + + public async preAuth(preAuthData: { [key: string]: string }): Promise { + return this.appConfigService.client.imx_login_preauth_post(this.appConfigService.Config.WebAppIndex, { AuthProps: preAuthData }); + } + + public async preAuthVerify(captchaCode: string): Promise { + return await this.appConfigService.client + .imx_login_preauth_verify_post(this.appConfigService.Config.WebAppIndex, { Code: captchaCode }) + .then(() => true) + .catch((error) => { + throw Error(error); + }); + } } diff --git a/imxweb/projects/qbm/src/lib/session/session-state.spec.ts b/imxweb/projects/qbm/src/lib/session/session-state.spec.ts index 1a6d6e9f6..1d7497da5 100644 --- a/imxweb/projects/qbm/src/lib/session/session-state.spec.ts +++ b/imxweb/projects/qbm/src/lib/session/session-state.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,12 +24,11 @@ * */ -import { AuthPropType } from 'imx-api-qbm'; +import { AuthPropType } from '@imx-modules/imx-api-qbm'; import { SessionState } from './session-state'; import { clearStylesFromDOM } from '../testing/clear-styles.spec'; describe('SessionState ', () => { - afterAll(() => { clearStylesFromDOM(); }); @@ -41,14 +40,14 @@ describe('SessionState ', () => { IsAuthenticated: true, Display: 'display', Uid: 'uid', - AuthTime: undefined + AuthTime: undefined, }, SecondaryAuth: { IsAuthenticated: true, IsEnabled: false, - Name: 'Starling' - } - } + Name: 'Starling', + }, + }, }; const sessionState = new SessionState(sessionResponse); @@ -62,14 +61,14 @@ describe('SessionState ', () => { IsAuthenticated: true, Display: 'display', Uid: 'uid', - AuthTime: undefined + AuthTime: undefined, }, SecondaryAuth: { IsAuthenticated: true, IsEnabled: false, - Name: 'Starling' - } - } + Name: 'Starling', + }, + }, }; const sessionState = new SessionState(sessionResponse); @@ -79,45 +78,45 @@ describe('SessionState ', () => { [ { AuthProps: undefined, - expectedIsOAuth: undefined + expectedIsOAuth: undefined, }, { AuthProps: [], - expectedIsOAuth: undefined + expectedIsOAuth: undefined, }, { AuthProps: [ { Type: AuthPropType.Password, - IsMandatory: undefined - } + IsMandatory: undefined, + }, ], - expectedIsOAuth: undefined + expectedIsOAuth: undefined, }, { AuthProps: [ { Type: AuthPropType.OAuth2Code, - IsMandatory: undefined - } + IsMandatory: undefined, + }, ], - expectedIsOAuth: true - } - ].forEach(testcase => + expectedIsOAuth: true, + }, + ].forEach((testcase) => it('maps config', () => { const sessionResponse = { Config: [ { Name: 'config1', Display: 'display1', - AuthProps: testcase.AuthProps - } - ] + AuthProps: testcase.AuthProps, + }, + ], }; const sessionState = new SessionState(sessionResponse); expect(sessionState.configurationProviders.length).toEqual(sessionResponse.Config.length); expect(sessionState.configurationProviders[0].isOAuth2).toEqual(testcase.expectedIsOAuth); - }) + }), ); }); diff --git a/imxweb/projects/qbm/src/lib/session/session-state.ts b/imxweb/projects/qbm/src/lib/session/session-state.ts index 07acfe12d..cdd76d990 100644 --- a/imxweb/projects/qbm/src/lib/session/session-state.ts +++ b/imxweb/projects/qbm/src/lib/session/session-state.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,29 +24,29 @@ * */ -import { SessionResponse, AuthPropType } from 'imx-api-qbm'; -import { AuthConfigProvider } from '../authentication/auth-config-provider.interface'; +import { AuthPropType, SessionResponse } from '@imx-modules/imx-api-qbm'; +import { AuthConfigProvider, PreAuthStateType } from '../authentication/auth-config-provider.interface'; export enum AuthStepLevels { LoggedOut = 0, AwaitsSecondaryAuth = 1, - LoggedIn = 2 + LoggedIn = 2, } export interface ISessionState { IsLoggedOut?: boolean; IsAwaitingSecondaryAuth?: boolean; IsLoggedIn?: boolean; - Username?: string; + Username?: string | null; /** Returns the UID of the identity associated with the session. Note that this may * be `null` when the session has no associated identity. */ - UserUid?: string; - SecondaryAuthName?: string; - SecondaryErrorMessage?: string; + UserUid?: string | null; + SecondaryAuthName?: string | null; + SecondaryErrorMessage?: string | null; configurationProviders?: AuthConfigProvider[]; - externalLogoutUrl?: string; + externalLogoutUrl?: string | undefined; isOAuth?: boolean; hasErrorState?: boolean; culture?: string; @@ -57,82 +57,88 @@ export interface ISessionState { * Encapsulates SessionResponse and provides properties for determining the current state of the sessions */ export class SessionState implements ISessionState { - public get IsLoggedOut(): boolean { return this.currentAuthStep === AuthStepLevels.LoggedOut; } - public get IsAwaitingSecondaryAuth(): boolean { return this.currentAuthStep === AuthStepLevels.AwaitsSecondaryAuth; } - public get IsLoggedIn(): boolean { return this.currentAuthStep === AuthStepLevels.LoggedIn; } - public get Username(): string { return this.sessionResponse && this.IsLoggedIn ? this.sessionResponse.Status.PrimaryAuth.Display : null; } - public get UserUid(): string { return this.sessionResponse && this.IsLoggedIn ? this.sessionResponse.Status.PrimaryAuth.Uid : null; } - public get SecondaryAuthName(): string { - return this.sessionResponse && this.sessionResponse.Status && this.sessionResponse.Status.SecondaryAuth ? - this.sessionResponse.Status.SecondaryAuth.Name : - null; + public get IsLoggedOut(): boolean { + return this.currentAuthStep === AuthStepLevels.LoggedOut; + } + public get IsAwaitingSecondaryAuth(): boolean { + return this.currentAuthStep === AuthStepLevels.AwaitsSecondaryAuth; + } + public get IsLoggedIn(): boolean { + return this.currentAuthStep === AuthStepLevels.LoggedIn; + } + public get Username(): string | null { + return this.sessionResponse && this.IsLoggedIn ? this.sessionResponse?.Status?.PrimaryAuth?.Display ?? null : null; + } + public get UserUid(): string | null { + return this.sessionResponse && this.IsLoggedIn ? this.sessionResponse?.Status?.PrimaryAuth?.Uid ?? null : null; + } + public get SecondaryAuthName(): string | null { + return this.sessionResponse && this.sessionResponse.Status && this.sessionResponse.Status.SecondaryAuth + ? this.sessionResponse.Status.SecondaryAuth.Name ?? null + : null; } - public get SecondaryErrorMessage(): string { - return this.sessionResponse && this.sessionResponse.Status && this.sessionResponse.Status.SecondaryAuth ? - this.sessionResponse.Status.SecondaryAuth.ErrorMessage : - null; + public get SecondaryErrorMessage(): string | null { + return this.sessionResponse && this.sessionResponse.Status && this.sessionResponse.Status.SecondaryAuth + ? this.sessionResponse.Status.SecondaryAuth.ErrorMessage ?? null + : null; } public readonly configurationProviders: AuthConfigProvider[]; - public readonly externalLogoutUrl: string; - public readonly culture: string; - public readonly cultureFormat: string; - + public readonly externalLogoutUrl: string | undefined; + public readonly culture: string | undefined; + public readonly cultureFormat: string | undefined; private currentAuthStep: AuthStepLevels = AuthStepLevels.LoggedOut; constructor(private sessionResponse: SessionResponse) { this.currentAuthStep = this.GetCurrentAuthStep(); - this.configurationProviders = this.GetConfigurationProviders(); + this.configurationProviders = this.GetConfigurationProviders() ?? []; + if (this.sessionResponse && this.sessionResponse.Status) { this.externalLogoutUrl = this.sessionResponse.Status.ExternalLogoutUrl; } this.culture = this.sessionResponse?.Status?.Culture; - this.cultureFormat = this.sessionResponse?.Status?.CultureFormat ; + this.cultureFormat = this.sessionResponse?.Status?.CultureFormat; } private GetCurrentAuthStep(): AuthStepLevels { - if ( - !this.sessionResponse || - !this.sessionResponse.Status || - !this.sessionResponse.Status.PrimaryAuth.IsAuthenticated - ) { + if (!this.sessionResponse || !this.sessionResponse.Status || !this.sessionResponse?.Status?.PrimaryAuth?.IsAuthenticated) { return AuthStepLevels.LoggedOut; } - if ( - this.sessionResponse.Status.SecondaryAuth.IsEnabled && - !this.sessionResponse.Status.SecondaryAuth.IsAuthenticated - ) { + if (this.sessionResponse.Status.SecondaryAuth?.IsEnabled && !this.sessionResponse.Status.SecondaryAuth.IsAuthenticated) { return AuthStepLevels.AwaitsSecondaryAuth; } return AuthStepLevels.LoggedIn; } - private GetConfigurationProviders(): AuthConfigProvider[] { + private GetConfigurationProviders(): AuthConfigProvider[] | undefined { if (this.sessionResponse && this.sessionResponse.Config) { - return this.sessionResponse.Config.map(config => { + return this.sessionResponse.Config.map((config) => { const configProvider: AuthConfigProvider = { - name: config.Name, - display: config.Display, - externalLogoutUrl: config.ExternalLogoutUrl + name: config.Name ?? '', + display: config.Display ?? '', + externalLogoutUrl: config.ExternalLogoutUrl, }; - if (config.AuthProps) { configProvider.authProps = []; - config.AuthProps.forEach(authProp => { + config.AuthProps.forEach((authProp) => { if (authProp.Type === AuthPropType.OAuth2Code) { configProvider.isOAuth2 = true; } - - configProvider.authProps.push({ - name: authProp.Name, - inputType: authProp.Type === AuthPropType.Password ? 'Password' : 'Text', - display: authProp.Display + configProvider?.authProps?.push({ + name: authProp.Name ?? '', + inputType: authProp.Type === AuthPropType.Password ? 'password' : 'text', + display: authProp.Display ?? '', }); }); } + if (config?.PreAuthProperties?.length && config?.PreAuthProperties?.length > 0) { + configProvider.preAuthProps = configProvider?.authProps?.filter((authProp) => config?.PreAuthProperties?.includes(authProp.name)); + configProvider?.authProps?.map((authProp) => (authProp.disabled = config?.PreAuthProperties?.includes(authProp.name))); + configProvider.preAuthState = PreAuthStateType.PreAuth; + } return configProvider; }); diff --git a/imxweb/projects/qbm/src/lib/settings/settings-service.ts b/imxweb/projects/qbm/src/lib/settings/settings-service.ts index 2e0e3ef70..fb6014e14 100644 --- a/imxweb/projects/qbm/src/lib/settings/settings-service.ts +++ b/imxweb/projects/qbm/src/lib/settings/settings-service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,18 +24,19 @@ * */ -import { Injectable } from "@angular/core"; +import { Injectable } from '@angular/core'; /** Wraps default settings for web applications. */ @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class SettingsService { + /** Optionset for the paginator by default. */ + DefaultPageOptions: number[] = [20, 50, 100]; /** Number of elements to load for a data page by default. */ DefaultPageSize: number = 20; /**Large number for getting all elements */ PageSizeForAllElements: number = 999999; - -} \ No newline at end of file +} diff --git a/imxweb/projects/qbm/src/lib/side-navigation-view/side-navigation-view-interfaces.ts b/imxweb/projects/qbm/src/lib/side-navigation-view/side-navigation-view-interfaces.ts index 3f3131688..ffe4daa15 100644 --- a/imxweb/projects/qbm/src/lib/side-navigation-view/side-navigation-view-interfaces.ts +++ b/imxweb/projects/qbm/src/lib/side-navigation-view/side-navigation-view-interfaces.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,9 +25,9 @@ */ import { Type } from '@angular/core'; +import { ProjectConfig } from '@imx-modules/imx-api-qbm'; import { AdminComponent } from '../admin/admin-component.interface'; import { IExtension } from '../ext/extension'; -import { ProjectConfig } from 'imx-api-qbm'; export interface SideNavigationItem { name: string; @@ -48,4 +48,9 @@ export interface SideNavigationExtension extends IExtension { contextId?: string; } -export type SideNavigationFactory = (preProps: string[], features: string[], projectConfig?: ProjectConfig, groups?: string[]) => SideNavigationExtension; +export type SideNavigationFactory = ( + preProps: string[], + features: string[], + projectConfig?: ProjectConfig, + groups?: string[], +) => SideNavigationExtension | undefined; diff --git a/imxweb/projects/qbm/src/lib/side-navigation-view/side-navigation-view.component.html b/imxweb/projects/qbm/src/lib/side-navigation-view/side-navigation-view.component.html index 150316974..b0fc38b16 100644 --- a/imxweb/projects/qbm/src/lib/side-navigation-view/side-navigation-view.component.html +++ b/imxweb/projects/qbm/src/lib/side-navigation-view/side-navigation-view.component.html @@ -1,8 +1,8 @@ -
    +
    - -
    -
    + +
    +
    -
    -
    - {{ componentTitle }} +
    +
    +

    {{ componentTitle }}

    @@ -27,11 +27,12 @@ [matTooltipPosition]="'after'" [matTooltipShowDelay]="500" [attr.data-imx-identifier]="'side-navigation-button-' + item.name" - [attr.aria-label]="'#LDS#Open page: {0}' | translate | ldsReplace : (item.translationKey | translate)" - class="snavigation-item" + [attr.aria-label]="'#LDS#Open page: {0}' | translate | ldsReplace: (item.translationKey | translate)" + class="imx-snavigation-item" (click)="selectPage(item.name)" - [ngClass]="{ 'snavigation-item--selected': selectedPage === item.name }" + [ngClass]="{ 'imx-snavigation-item--selected': selectedPage === item.name }" tabindex="0" + (keydown.enter)="selectPage(item.name)" > {{ item.translationKey }}
    @@ -39,8 +40,8 @@
    - -
    + +
    diff --git a/imxweb/projects/qbm/src/lib/side-navigation-view/side-navigation-view.component.scss b/imxweb/projects/qbm/src/lib/side-navigation-view/side-navigation-view.component.scss deleted file mode 100644 index 88e2a8397..000000000 --- a/imxweb/projects/qbm/src/lib/side-navigation-view/side-navigation-view.component.scss +++ /dev/null @@ -1 +0,0 @@ -@import '../../../../../shared/scss/side-navigation.scss'; diff --git a/imxweb/projects/qbm/src/lib/side-navigation-view/side-navigation-view.component.ts b/imxweb/projects/qbm/src/lib/side-navigation-view/side-navigation-view.component.ts index 53319287e..df6e870a3 100644 --- a/imxweb/projects/qbm/src/lib/side-navigation-view/side-navigation-view.component.ts +++ b/imxweb/projects/qbm/src/lib/side-navigation-view/side-navigation-view.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,45 +27,48 @@ import { ChangeDetectorRef, Component, HostListener, Input, OnDestroy, ViewChild, ViewContainerRef } from '@angular/core'; import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; import { Subscription } from 'rxjs'; +import { isMobile } from '../base/sidesheet-helper'; import { ClassloggerService } from '../classlogger/classlogger.service'; +import { HelpContextualValues } from '../help-contextual/help-contextual.service'; import { SideNavigationExtension, SideNavigationItem } from './side-navigation-view-interfaces'; @Component({ selector: 'imx-side-navigation-view', templateUrl: './side-navigation-view.component.html', - styleUrls: ['./side-navigation-view.component.scss'], }) export class SideNavigationViewComponent implements OnDestroy { @Input() public baseUrl = ''; @Input() public isAdmin = false; @Input() public componentName = ''; @Input() public componentTitle = ''; - @Input() public contextId:string; - _navItems: SideNavigationExtension[] = []; - get navItems(): SideNavigationExtension[] { + @Input() public contextId: HelpContextualValues; + _navItems: (SideNavigationExtension | undefined)[] = []; + get navItems(): (SideNavigationExtension | undefined)[] { return this._navItems; } - @Input() set navItems(value: SideNavigationExtension[]) { + @Input() set navItems(value: (SideNavigationExtension | undefined)[]) { this._navItems = value; if (value.length > 0) { this.setupNavItems(); this.handleRouteParam(); } } - get isMobile(): boolean { - return document.body.offsetWidth <= 768; - } public navigationItems: SideNavigationItem[] = []; public selectedPage: string = ''; public mobileSideNavExpanded = false; public showBackdrop = false; - public contentMargin = this.isMobile ? '58px' : '230px'; + public contentMargin = isMobile() ? '58px' : '230px'; @ViewChild('sideNavContent', { read: ViewContainerRef }) protected sideNavContentRef: ViewContainerRef; protected routerEvents$: Subscription; - constructor(private readonly logger: ClassloggerService, private readonly router: Router, private readonly route: ActivatedRoute, private cdref: ChangeDetectorRef) { + constructor( + private readonly logger: ClassloggerService, + private readonly router: Router, + private readonly route: ActivatedRoute, + private cdref: ChangeDetectorRef, + ) { this.routerEvents$ = this.router.events.subscribe(async (val) => { if (this.navItems.length > 0 && val instanceof NavigationEnd) { this.handleRouteParam(); @@ -75,7 +78,7 @@ export class SideNavigationViewComponent implements OnDestroy { @HostListener('window:resize') public onResize(): void { - this.showBackdrop = this.isMobile && this.mobileSideNavExpanded; + this.showBackdrop = isMobile() && this.mobileSideNavExpanded; setTimeout(() => this.setContentMargin(), 50); } @@ -87,12 +90,12 @@ export class SideNavigationViewComponent implements OnDestroy { public toggleMobileExpand(): void { this.mobileSideNavExpanded = !this.mobileSideNavExpanded; - const showBackdrop = this.isMobile && this.mobileSideNavExpanded; + const showBackdrop = isMobile() && this.mobileSideNavExpanded; setTimeout( () => { this.showBackdrop = showBackdrop; }, - showBackdrop ? 0 : 500 + showBackdrop ? 0 : 500, ); } @@ -103,7 +106,7 @@ export class SideNavigationViewComponent implements OnDestroy { return; } else { this.router.navigate([this.baseUrl, page]); - if (this.isMobile) { + if (isMobile()) { this.toggleMobileExpand(); } } @@ -112,21 +115,21 @@ export class SideNavigationViewComponent implements OnDestroy { private async setupNavItems(): Promise { this.navigationItems = []; for (const item of this.navItems) { - if (!item) { + if (item == null) { continue; } const navItem = { name: item.name, translationKey: item.caption, - icon: item.icon, + icon: item.icon ?? '', }; this.navigationItems.push(navItem); } } private setContentMargin(): void { - this.contentMargin = !this.isMobile ? '230px' : '58px'; + this.contentMargin = !isMobile() ? '230px' : '58px'; } private loadComponent(): void { @@ -151,31 +154,33 @@ export class SideNavigationViewComponent implements OnDestroy { this.cdref.detectChanges(); this.sideNavContentRef.clear(); const selectedPageItem = selectedItem; - const component = this.sideNavContentRef.createComponent(selectedPageItem.instance); - component.instance.data = selectedPageItem.data; - component.instance.isAdmin = this.isAdmin; - if(!!selectedPageItem.contextId){ - component.instance.contextId = selectedPageItem.contextId; + if (selectedPageItem.instance) { + const component = this.sideNavContentRef.createComponent(selectedPageItem.instance); + component.instance.data = selectedPageItem.data; + component.instance.isAdmin = this.isAdmin; + if (!!selectedPageItem.contextId) { + component.instance.contextId = selectedPageItem.contextId; + } } } private handleRouteParam(): void { const tab = this.route.snapshot.paramMap.get('tab'); if (!tab) { - this.router.navigate([this.navItems[0].name], { relativeTo: this.route }); + this.router.navigate([this.navItems[0]?.name], { relativeTo: this.route }); } else { - this.selectedPage = tab ? tab : this.navItems[0].name; + this.selectedPage = tab ? tab : this.navItems[0]?.name || ''; this.loadComponent(); } } - private getItem(name: string, list: SideNavigationExtension[]): SideNavigationExtension { + private getItem(name: string, list: (SideNavigationExtension | undefined)[]): SideNavigationExtension | null { if (list == null) { return null; } for (var item of list) { - if (item.name === name) { + if (item?.name === name) { return item; } } diff --git a/imxweb/projects/qbm/src/lib/side-navigation-view/side-navigation-view.module.ts b/imxweb/projects/qbm/src/lib/side-navigation-view/side-navigation-view.module.ts index 1a2b06562..4539ae499 100644 --- a/imxweb/projects/qbm/src/lib/side-navigation-view/side-navigation-view.module.ts +++ b/imxweb/projects/qbm/src/lib/side-navigation-view/side-navigation-view.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qbm/src/lib/sidenav-tree/custom-tree-control.component.html b/imxweb/projects/qbm/src/lib/sidenav-tree/custom-tree-control.component.html new file mode 100644 index 000000000..0c7b1db0b --- /dev/null +++ b/imxweb/projects/qbm/src/lib/sidenav-tree/custom-tree-control.component.html @@ -0,0 +1,111 @@ + + + + + +
    + + +
    +
    + + +
    + + +
    +
    + +
    +
    +
    + + + + + +
    + + {{ noResultText | translate }} +
    + + +
    + + +
    +
    + + +
    + + + +
    +
    + +
    +
    +
    + + +
    +
    + + + + diff --git a/imxweb/projects/qbm/src/lib/sidenav-tree/custom-tree-control.component.scss b/imxweb/projects/qbm/src/lib/sidenav-tree/custom-tree-control.component.scss new file mode 100644 index 000000000..707c50f3d --- /dev/null +++ b/imxweb/projects/qbm/src/lib/sidenav-tree/custom-tree-control.component.scss @@ -0,0 +1,48 @@ +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; + +:host { + height: 100%; + border-radius: 4px; + flex: 1 1 auto; + overflow: hidden; +} + +.imx-scroll-pad > .eui-scroll-container { + padding-left: 5px; +} +mat-tree-node[aria-level="1"]{ + .mdc-button{ + font-weight: 700; + } +} +mat-tree-node[aria-level="2"]{ + .mdc-button{ + font-weight: 600; + } +} +mat-tree-node{ + .mdc-button{ + font-weight: 400; + } +} +mat-nested-tree-node[aria-level="1"]{ + .mat-tree-node{ + .mdc-button{ + font-weight: 700; + } + } + mat-tree-node, mat-nested-tree-node[aria-level="2"]{ + .mat-tree-node{ + .mdc-button{ + font-weight: 600; + } + } + mat-tree-node, mat-nested-tree-node{ + .mat-tree-node{ + .mdc-button{ + font-weight: 400; + } + } + } + } +} diff --git a/imxweb/projects/qbm/src/lib/sidenav-tree/custom-tree-control.component.ts b/imxweb/projects/qbm/src/lib/sidenav-tree/custom-tree-control.component.ts new file mode 100644 index 000000000..9a31e8222 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/sidenav-tree/custom-tree-control.component.ts @@ -0,0 +1,152 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { FlatTreeControl, NestedTreeControl } from '@angular/cdk/tree'; +import { CommonModule } from '@angular/common'; +import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, TemplateRef } from '@angular/core'; +import { MatTreeModule, MatTreeNestedDataSource } from '@angular/material/tree'; +import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; +import { TranslateModule } from '@ngx-translate/core'; +import { DynamicDataSource } from './sidenav-tree-dynamic-extension'; + +/** + * Handles creating a responsive nested sidenav tree. + * + * + * Requires: + * @param {MatTreeNestedDataSource} dataSource: Static datasource with all data available at once, use dynamicDatasource otherwise. + * @param {DynamicDataSource} dynamicDataSource: Dynamic datasource for loading more data incrementally. + * @param {NestedTreeControl | FlatTreeControl} treeControl: Controls tree behavior. + * @param {string} headerText: Text that will appear on side and at top of sidenav. + * @param {TemplateRef} nodeContent: A template ref of how the node of the tree will look. + * + * Optional: + * @param {boolean} [sideNavExpanded=false] Sets whether the sidenav is open or closed initially, defaults to false. + * @param {boolean} [manageExpandedExternally=false] This prevents the internal sideNavExpanded management and requires the sideNavExpanded to be managed externally. This allows additional logic to be stuck in between. Defaults to false. + * @param {string} [expandWidth="600px"] Set the width of the expansion, according to material avoid percent based widths. Default is '600px' + + * @param {Function} [hasChild] Function to determine if a node has children or not. By default uses the children property. + * @param {Function} [isSelected] Function to determine if a node is currently selected. By default uses the isSelected property. + * @param {Function} [isLoading] Function to indicate if a node is currently getting children from the server. + * @param {boolean} [showSidenavHeader=true] Indicates if we should have the header and mat card present or not. Defaults to true. + * @param {string} [noResultText] The text shown in case of no search results + + + */ +@Component({ + selector: 'imx-custom-tree-control', + templateUrl: './custom-tree-control.component.html', + styleUrls: ['./custom-tree-control.component.scss'], + standalone: true, + imports: [CommonModule, MatTreeModule, EuiCoreModule, EuiMaterialModule, TranslateModule], +}) +export class CustomTreeControlComponent implements OnChanges { + public isLoadingMore: boolean; + private searchEnabled = false; + + // Required input + // Either dataSource as a MatTreeNestedDataSource + @Input() public dataSource: MatTreeNestedDataSource; + + // Or dynamicDataSource as a DynamicDataSource + @Input() public dynamicDataSource: DynamicDataSource; + + // treeControl as a NestedTreeControl + @Input() public treeControl: NestedTreeControl | FlatTreeControl; + + // nodeContent will a template rendered inside each node and it uses a node variable. + @Input() public nodeContent: TemplateRef; + + // Function used to determine if there are children under the node, defaults to look for a children property of length > 0. + @Input() public hasChild = (_: number, node: any) => node?.children && node.children.length > 0; + + // Function used to determine if the node is currently selected, this value is maintained outside this component + @Input() public isSelected = (node: any) => node.isSelected; + + // Function used to determine if the node is loading children, this value is maintained outside this component + @Input() public isLoading = (node: any) => false; + + // Text to show when search is empty + @Input() public noResultText = '#LDS#There are no items matching your search.'; + + // selectedNode outputs the node that was chosen + @Output() public selectedNode = new EventEmitter(); + + public async ngOnChanges(changes: SimpleChanges): Promise { + if (changes['database']) { + console.log('Need to refresh tree'); + } + } + + get initializingData(): boolean { + return this.dynamicDataSource?.initializingData; + } + + get hasData(): boolean { + return this.dynamicDataSource?.hasData; + } + + get canLoadMore(): boolean { + return this.dynamicDataSource?.canLoadMore; + } + + public get anyNodeOpen(): boolean { + return this.dynamicDataSource + ? this.dynamicDataSource.isRootNodeOpen || this.dynamicDataSource.isSearch + : this.treeControl.expansionModel.selected.length > 0; + } + + public closeAllNodes(): void { + this.treeControl.collapseAll(); + } + + public selectNode(node: any): void { + if (this.dynamicDataSource) { + this.dynamicDataSource.setSelection(node); + } + this.selectedNode.emit(node); + } + + public async onLoadMore(): Promise { + this.isLoadingMore = true; + try { + await this.dynamicDataSource.loadMore(); + } finally { + this.isLoadingMore = false; + } + } + + public enableSearch(): void { + this.searchEnabled = true; + } + public async onSearch(): Promise { + if (this.initializingData) { + return; + } + + await this.dynamicDataSource.onSearch(); + } +} diff --git a/imxweb/projects/qbm/src/lib/sidenav-tree/sidenav-tree-dynamic-extension.ts b/imxweb/projects/qbm/src/lib/sidenav-tree/sidenav-tree-dynamic-extension.ts index 1abd43006..19e2eb313 100644 --- a/imxweb/projects/qbm/src/lib/sidenav-tree/sidenav-tree-dynamic-extension.ts +++ b/imxweb/projects/qbm/src/lib/sidenav-tree/sidenav-tree-dynamic-extension.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,8 +26,8 @@ import { CollectionViewer, SelectionChange } from '@angular/cdk/collections'; import { FlatTreeControl } from '@angular/cdk/tree'; -import { CollectionLoadParameters } from 'imx-qbm-dbts'; -import { BehaviorSubject, merge, Observable, Subscription } from 'rxjs'; +import { CollectionLoadParameters } from '@imx-modules/imx-qbm-dbts'; +import { BehaviorSubject, Observable, Subscription, merge } from 'rxjs'; import { concatMap, map } from 'rxjs/operators'; import { DataSourceToolbarSettings } from '../data-source-toolbar/data-source-toolbar-settings'; @@ -44,7 +44,7 @@ export class DynamicDataApiControls { export class DynamicDataSource { public dataChange = new BehaviorSubject([]); public initializingData: boolean; - public dstSettings: DataSourceToolbarSettings; + public dstSettings: DataSourceToolbarSettings | undefined; public selectedNode: T; private totalCount: number; @@ -74,14 +74,19 @@ export class DynamicDataSource { } public get isSearch(): boolean { - return this.dstSettings?.navigationState.search?.length > 0; + return !!this.dstSettings?.navigationState?.search?.length; } + constructor( + private _treeControl: FlatTreeControl, + private _apiControls: DynamicDataApiControls, + ) {} + public async setup(expandRoot?: boolean): Promise { this.initializingData = true; try { const response = await this._apiControls.setup(); - this.totalCount = response?.totalCount; + this.totalCount = response?.totalCount ?? 0; this.rootNode = response.rootNode; this.dstSettings = response?.dstSettings; this.init(); @@ -101,15 +106,25 @@ export class DynamicDataSource { } public async loadMore(): Promise { - let nodes: T[]; + let nodes: T[] = []; if (this.isSearch) { - this.dstSettings.navigationState.StartIndex = this.data.length; - nodes = (await this._apiControls.search(this.dstSettings.navigationState)).searchNodes; - nodes = this._apiControls.changeSelection(nodes, this.selectedNode); + if (this.dstSettings) { + this.dstSettings.navigationState.StartIndex = this.data.length; + } + if (this._apiControls.search) { + nodes = (await this._apiControls.search(this.dstSettings?.navigationState ?? {})).searchNodes; + } + if (this._apiControls.changeSelection) { + nodes = this._apiControls.changeSelection(nodes, this.selectedNode); + } this.data.push(...nodes); } else { - nodes = await this._apiControls.loadMore(this.rootNode); - nodes = this._apiControls.changeSelection(nodes, this.selectedNode); + if (this._apiControls.loadMore) { + nodes = await this._apiControls.loadMore(this.rootNode); + } + if (this._apiControls.changeSelection) { + nodes = this._apiControls.changeSelection(nodes, this.selectedNode); + } const index = this.findEndOfChildren(this.rootNode); this.data.splice(index + 1, 0, ...nodes); @@ -122,9 +137,11 @@ export class DynamicDataSource { if (!this._apiControls.search || !this._apiControls.changeSelection) { throw Error('No remote search functionality has been defined.'); } - this.initializingData = true; - this.dstSettings.navigationState.StartIndex = 0; + this.initializingData = true; + if (this.dstSettings) { + this.dstSettings.navigationState.StartIndex = 0; + } // If we don't have cached data, and we are searching, cache if (!this.cachedData && this.isSearch) { this.cachedData = { @@ -143,16 +160,18 @@ export class DynamicDataSource { this.cachedData = null; } else { // Proceed with normal search - const response = await this._apiControls.search(this.dstSettings.navigationState); + const response = await this._apiControls.search(this.dstSettings?.navigationState ?? {}); if (!response) { // Here the search was aborted, so we return nothing return; } - this.totalCount = response?.totalCount; - const nodes = this._apiControls.changeSelection(response.searchNodes, this.selectedNode); + this.totalCount = response?.totalCount ?? 0; + const nodes = this._apiControls.changeSelection + ? this._apiControls.changeSelection(response?.searchNodes ?? [], this.selectedNode) + : []; this.currentCount = nodes.length; if (nodes.length > 0) { - this.rootNode = response.rootNode || this.rootNode; + this.rootNode = response?.rootNode || this.rootNode; this.nextData([this.rootNode, ...nodes]); this._treeControl.expand(this.rootNode); } else { @@ -160,6 +179,7 @@ export class DynamicDataSource { } } } + /** * Emits data and turns off the loading spinner * @param data that will be emitted as the next data state @@ -169,8 +189,6 @@ export class DynamicDataSource { this.initializingData = false; } - constructor(private _treeControl: FlatTreeControl, private _apiControls: DynamicDataApiControls) {} - public connect(collectionViewer: CollectionViewer): Observable { this.subscriptions$.push( this._treeControl.expansionModel.changed @@ -179,13 +197,13 @@ export class DynamicDataSource { if (treeDataHasChanged) { this.dataChange.next(this.data); } - }) + }), ); return merge(collectionViewer.viewChange, this.dataChange).pipe( map(() => { return this.data; - }) + }), ); } @@ -203,7 +221,7 @@ export class DynamicDataSource { change.removed .slice() .reverse() - .map((node) => this.toggleNode(node, false)) + .map((node) => this.toggleNode(node, false)), ) ).some((val) => val); } @@ -261,10 +279,10 @@ export class DynamicDataSource { } public setSelection(node: T): void { - const nodes = this._apiControls.changeSelection(this.data, node); + const nodes = this._apiControls.changeSelection ? this._apiControls.changeSelection(this.data, node) : []; this.dataChange.next(nodes); if (this.cachedData) { - this.cachedData.data = this._apiControls.changeSelection(this.cachedData.data, node); + this.cachedData.data = this._apiControls.changeSelection ? this._apiControls.changeSelection(this.cachedData.data, node) : []; } this.selectedNode = node; } diff --git a/imxweb/projects/qbm/src/lib/sidenav-tree/sidenav-tree.component.html b/imxweb/projects/qbm/src/lib/sidenav-tree/sidenav-tree.component.html index 0c7ae3a93..2958e7ee1 100644 --- a/imxweb/projects/qbm/src/lib/sidenav-tree/sidenav-tree.component.html +++ b/imxweb/projects/qbm/src/lib/sidenav-tree/sidenav-tree.component.html @@ -1,19 +1,18 @@ - -
    -
    -

    {{ headerText }}

    + +
    +
    +

    {{ headerText }}

    [searchBoxText]="'#LDS#Search' | translate" [useThemedStyle]="true" (search)="onSearch($event)" - (navigationStateChanged)="onSearch()" + (navigationStateChanged)="onSearch($event.search ?? '')" >

    - - - - -
    - - -
    -
    - - -
    - - -
    -
    - -
    -
    -
    -
    - - - - -
    - - {{ noResultText | translate }} -
    - - -
    - - -
    -
    - - -
    - - - -
    -
    - -
    -
    -
    - - -
    +
    diff --git a/imxweb/projects/qbm/src/lib/sidenav-tree/sidenav-tree.component.scss b/imxweb/projects/qbm/src/lib/sidenav-tree/sidenav-tree.component.scss index 97c4f821a..a9e49532d 100644 --- a/imxweb/projects/qbm/src/lib/sidenav-tree/sidenav-tree.component.scss +++ b/imxweb/projects/qbm/src/lib/sidenav-tree/sidenav-tree.component.scss @@ -1,284 +1,6 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; :host { height: 100%; border-radius: 4px; - - ::ng-deep .mat-drawer-inner-container { - overflow: hidden; - } -} - -.snavigation { - height: 100%; - padding: 0; - - .mat-sidenav-container { - height: 100%; - border-radius: 4px; - - .mat-sidenav { - width: 100%; - overflow: hidden; - border-radius: 4px; - - .snavigation-side { - display: flex; - flex-direction: column; - height: 100%; - - .snavigation-side-toggle { - display: flex; - justify-content: flex-end; - margin: 5px 0; - - .snavigation-side-toggle-header { - margin: auto; - margin-left: 24px; - font-size: 16px; - line-height: 20px; - font-weight: 600; - } - - .toolbar--hidden { - width: 0; - } - - .expand-control-button { - ::ng-deep .mat-button-wrapper { - width: 100%; - height: 100%; - display: flex; - flex-direction: column; - justify-content: center; - } - - .rotate-90 { - margin: auto; - rotate: -90deg; - translate: 0 100%; - width: 1px; // prevent content from overflowing - -webkit-backface-visibility: hidden; // Handle the anti-aliasing from chrome on rotations - font-size: 16px; - } - } - } - - .snavigation-side-content--center { - justify-content: center; - display: flex; - align-items: center; - } - - .imx-no-data { - display: flex; - flex-direction: column; - align-items: center; - - eui-icon { - font-size: 100px; - line-height: 100px; - } - } - - .snavigation-side-content { - height: 100%; - overflow: auto; - margin: 0 24px 24px; - - mat-tree { - background-color: transparent; - } - - .mat-tree-node { - border-radius: 4px; - - .child-progress-bar { - margin-left: 10px; - max-width: 60%; - } - } - - /* - * This padding sets alignment of the nested nodes. - */ - .tree .mat-nested-tree-node div[role=group] { - margin-left: 20px; - } - - .tree-item-button { - width: 100%; - padding: 0; - color: unset; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - - ::ng-deep .mat-button-wrapper { - display: flex; - align-items: center; - } - } - .tree-invisible { - display: none; - } - } - } - - &:not(.snavigation-side--expanded) { - .snavigation-side-toggle { - height: 100%; - } - .snavigation-side-toggle-header, .snavigation-side-content { - display: none; - } - .expand-control-button { - width: 48px; - padding: 0; - min-width: unset; - } - } - - &:is(.snavigation-side--expanded) { - .snavigation-side-toggle{ - align-items: center; - } - - .expand-control-button { - margin-right: 16px; - } - .rotate-90 { - display: none; - } - } - } - - .mat-sidenav-content { - padding: 32px; - position: relative; - display: flex; - flex-direction: column; - } - - // .mat-drawer-side seems to have a scss style - seems to leak from material - .mat-drawer-side { - border-right: none; - } - } - &.mat-card--hidden{ - .mat-sidenav-container{ - .mat-sidenav{ - .snavigation-side{ - .snavigation-side-content{ - margin: 0; - } - } - } - } - } -} - -// Theming -:host { - .snavigation-side-toggle-header { - color: $color-gray-60; - } - - .snavigation.snavigation--expanded { - border: 1px solid $color-gray-20; - &.mat-card--hidden{ - border:none; - box-shadow: none; - } - } - - .mat-sidenav:not(.snavigation-side--expanded) { - .expand-control-button { - background-color: $color-blue-10; - border: 1px solid $color-blue-20; - } - } - .snavigation-side-content { - background-color: $color-blue-10; - border: 1px solid $color-blue-20; - } - .mat-tree-node--selected { - background-color: $color-gray-0; - border: 1px solid $color-blue-40; - color: $color-orange-60; - } - - .imx-no-data { - eui-icon, span { - color: $color-gray-60; - } - } -} - -.eui-dark-theme { - :host { - .snavigation-side-toggle-header { - color: $color-gray-20; - } - - .snavigation.snavigation--expanded { - border: 1px solid $color-gray-60; - } - - .mat-sidenav:not(.snavigation-side--expanded) { - .expand-control-button { - background-color: $color-blue-90; - border: 1px solid $color-blue-80; - } - } - .snavigation-side-content { - background-color: $color-blue-90; - border: 1px solid $color-blue-80; - } - .mat-tree-node--selected { - background-color: $color-gray-80; - border: 1px solid $color-blue-40; - color: $color-orange-40; - } - - .imx-no-data { - eui-icon, span { - color: $color-gray-20; - } - } - } -} - -.eui-contrast-theme { - :host { - .snavigation-side-toggle-header { - color: $color-gray-0; - } - - .snavigation.snavigation--expanded { - border: 1px solid $color-gray-60; - } - - .mat-sidenav:not(.snavigation-side--expanded) { - .expand-control-button { - background-color: $color-gray-90; - border: 1px solid $color-gray-0; - } - } - .snavigation-side-content { - background-color: $color-gray-90; - border: 1px solid $color-gray-0; - } - .mat-tree-node--selected { - background-color: $color-gray-80; - border: 1px solid $color-gray-0; - color: $color-gray-0; - } - - .imx-no-data { - eui-icon, span { - color: $color-gray-0; - } - } - } } diff --git a/imxweb/projects/qbm/src/lib/sidenav-tree/sidenav-tree.component.ts b/imxweb/projects/qbm/src/lib/sidenav-tree/sidenav-tree.component.ts index 32a879a32..dfd1e6c5b 100644 --- a/imxweb/projects/qbm/src/lib/sidenav-tree/sidenav-tree.component.ts +++ b/imxweb/projects/qbm/src/lib/sidenav-tree/sidenav-tree.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,8 +26,14 @@ import { animate, animateChild, group, query, state, style, transition, trigger } from '@angular/animations'; import { FlatTreeControl, NestedTreeControl } from '@angular/cdk/tree'; -import { Component, Input, Output, EventEmitter, TemplateRef, SimpleChanges, OnChanges } from '@angular/core'; -import { MatTreeNestedDataSource } from '@angular/material/tree'; +import { CommonModule } from '@angular/common'; +import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, TemplateRef } from '@angular/core'; +import { MatSidenavModule } from '@angular/material/sidenav'; +import { MatTreeModule, MatTreeNestedDataSource } from '@angular/material/tree'; +import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; +import { TranslateModule } from '@ngx-translate/core'; +import { DataSourceToolbarModule } from '../data-source-toolbar/data-source-toolbar.module'; +import { CustomTreeControlComponent } from './custom-tree-control.component'; import { DynamicDataSource } from './sidenav-tree-dynamic-extension'; /** @@ -58,13 +64,24 @@ import { DynamicDataSource } from './sidenav-tree-dynamic-extension'; selector: 'imx-sidenav-tree', templateUrl: './sidenav-tree.component.html', styleUrls: ['./sidenav-tree.component.scss'], + standalone: true, + imports: [ + CommonModule, + MatTreeModule, + EuiCoreModule, + EuiMaterialModule, + MatSidenavModule, + TranslateModule, + DataSourceToolbarModule, + CustomTreeControlComponent, + ], animations: [ trigger('expandDiv', [ state( 'closed', style({ width: '48px', - }) + }), ), state('opened', style({ width: '{{ expandWidth }}' }), { params: { expandWidth: '*' } }), transition('* <=> *', [ @@ -83,13 +100,13 @@ import { DynamicDataSource } from './sidenav-tree-dynamic-extension'; 'closed', style({ rotate: '180deg', - }) + }), ), state( 'opened', style({ rotate: '0deg', - }) + }), ), transition('* <=> *', animate('400ms ease')), ]), @@ -99,7 +116,7 @@ import { DynamicDataSource } from './sidenav-tree-dynamic-extension'; style({ width: '0px', visibility: 'hidden', - }) + }), ), state( 'opened', @@ -107,14 +124,14 @@ import { DynamicDataSource } from './sidenav-tree-dynamic-extension'; width: '320px', visibility: 'visible', }), - { params: { expandWidth: '*' } } + { params: { expandWidth: '*' } }, ), state( 'hidden', style({ width: 0, visibility: 'hidden', - }) + }), ), transition('* <=> *', [group([query('@fadeIcon', animateChild(), { optional: true }), animate('400ms ease')])]), ]), @@ -125,7 +142,7 @@ import { DynamicDataSource } from './sidenav-tree-dynamic-extension'; opacity: '0', width: '0px', visibility: 'hidden', - }) + }), ), state( 'closed', @@ -133,14 +150,14 @@ import { DynamicDataSource } from './sidenav-tree-dynamic-extension'; opacity: '1', width: '40px', visibility: 'visible', - }) + }), ), state( 'hidden', style({ width: 0, visibility: 'hidden', - }) + }), ), transition('* <=> *', animate('400ms ease')), ]), @@ -183,7 +200,7 @@ export class SidenavTreeComponent implements OnChanges { @Input() public isSelected = (node: any) => node.isSelected; // Function used to determine if the node is loading children, this value is maintained outside this component - @Input() public isLoading = (node: any) => false; + @Input() public isLoading: (node: any) => boolean = (node: any) => false; // Remove the header and hide the mat-card style in false state @Input() public showSidenavHeader = true; @@ -191,6 +208,9 @@ export class SidenavTreeComponent implements OnChanges { // Text to show when search is empty @Input() public noResultText = '#LDS#There are no items matching your search.'; + // hides the expand icon + @Input() public hideExpandButton = false; + // Output // Emits when the sidenav state changed @Output() public sideNavExpandedChange = new EventEmitter(); diff --git a/imxweb/projects/qbm/src/lib/snackbar/snack-bar.service.ts b/imxweb/projects/qbm/src/lib/snackbar/snack-bar.service.ts index 3afea22e9..b1a28c92a 100644 --- a/imxweb/projects/qbm/src/lib/snackbar/snack-bar.service.ts +++ b/imxweb/projects/qbm/src/lib/snackbar/snack-bar.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -33,12 +33,12 @@ import { LdsReplacePipe } from '../lds-replace/lds-replace.pipe'; import { TextContainer } from '../translation/text-container'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class SnackBarService { private readonly defaultConfig: MatSnackBarConfig = { duration: 5000, - verticalPosition: 'top' + verticalPosition: 'top', }; private message = ''; private action = ''; @@ -47,16 +47,20 @@ export class SnackBarService { constructor( private readonly snackbar: MatSnackBar, private readonly translationProvider: TranslateService, - private readonly ldsReplace: LdsReplacePipe - ) { } + private readonly ldsReplace: LdsReplacePipe, + ) {} - public open(messageText: TextContainer, actionText: string = this.actionDismissCaption, config?: MatSnackBarConfig) - : MatSnackBarRef { - this.translationProvider.get(messageText.key) - .pipe(map((value: string) => messageText.parameters ? this.ldsReplace.transform(value, ...messageText.parameters) : value)) - .subscribe((value: string) => this.message = value); + public open( + messageText: TextContainer, + actionText: string = this.actionDismissCaption, + config?: MatSnackBarConfig, + ): MatSnackBarRef { + this.translationProvider + .get(messageText.key) + .pipe(map((value: string) => (messageText.parameters ? this.ldsReplace.transform(value, ...messageText.parameters) : value))) + .subscribe((value: string) => (this.message = value)); - this.translationProvider.get(actionText).subscribe((value: string) => this.action = value); + this.translationProvider.get(actionText).subscribe((value: string) => (this.action = value)); return this.snackbar.open(this.message, this.action, { ...this.defaultConfig, ...config }); } diff --git a/imxweb/projects/qbm/src/lib/splash/splash.service.ts b/imxweb/projects/qbm/src/lib/splash/splash.service.ts index 45ad0b619..66eb5be6d 100644 --- a/imxweb/projects/qbm/src/lib/splash/splash.service.ts +++ b/imxweb/projects/qbm/src/lib/splash/splash.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,32 +24,28 @@ * */ - import { Injectable } from '@angular/core'; import { EuiSplashScreenConfig, EuiSplashScreenOptions, EuiSplashScreenService } from '@elemental-ui/core'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class SplashService { - private defaultOptions: EuiSplashScreenConfig = { applicationName: 'One Identity Manager', icon: 'oi-horizontal', showSpinner: true, - message: 'Loading...' + message: 'Loading...', }; - constructor( - private readonly splash: EuiSplashScreenService, - ) { } + constructor(private readonly splash: EuiSplashScreenService) {} public init(options: EuiSplashScreenOptions): void { // open splash screen with fix values const config = { ...this.defaultOptions, - ...options - }; + ...options, + }; this.splash.open(config); } diff --git a/imxweb/projects/qbm/src/lib/sqlwizard/SqlNodeView.ts b/imxweb/projects/qbm/src/lib/sqlwizard/SqlNodeView.ts index 41b18372b..5b3a2f01e 100644 --- a/imxweb/projects/qbm/src/lib/sqlwizard/SqlNodeView.ts +++ b/imxweb/projects/qbm/src/lib/sqlwizard/SqlNodeView.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,129 +25,142 @@ */ import { EventEmitter } from '@angular/core'; -import { SqlExpression, LogOp, SqlColumnTypes, FilterProperty } from 'imx-qbm-dbts'; +import { FilterProperty, LogOp, SqlColumnTypes, SqlExpression } from '@imx-modules/imx-qbm-dbts'; import { SqlWizardApiService } from './sqlwizard-api.service'; export class SqlViewSettings { - - public root: SqlViewRoot; - constructor( - public readonly sqlWizardService: SqlWizardApiService, - tableName: string, - rootExpression?: SqlExpression) { - let expr = rootExpression; - if (!expr) { - expr = { - Expressions: [], - LogOperator: LogOp.AND - }; - } - - this.root = new SqlViewRoot(this, tableName, expr); + public root: SqlViewRoot; + constructor( + public readonly sqlWizardService: SqlWizardApiService, + tableName: string, + rootExpression?: SqlExpression, + ) { + let expr = rootExpression; + if (!expr) { + expr = { + Expressions: [], + LogOperator: LogOp.AND, + Negate: false, + }; } + + this.root = new SqlViewRoot(this, tableName, expr); + } } export interface ISqlNodeView { - readonly childViews: SqlNodeView[]; - Data: SqlExpression; - replaceChildNode(oldNode: SqlExpression, newData: SqlExpression): void; - addChildNode(): Promise; + readonly childViews: SqlNodeView[]; + Data: SqlExpression; + replaceChildNode(oldNode: SqlExpression, newData: SqlExpression): void; + addChildNode(): Promise; } abstract class SqlViewBase implements ISqlNodeView { - - get childViews(): SqlNodeView[] { - return this.views; + get childViews(): SqlNodeView[] { + return this.views; + } + + private views: SqlNodeView[] = []; + + constructor( + public readonly viewSettings: SqlViewSettings, + public tableName: string, + public Data: SqlExpression, + public Property: FilterProperty | undefined, + ) { + this.views = this.buildViews(); + } + + // how to listen to a change of Data.PropertyId without re-implementing the SqlExpression interface? + public readonly columnChanged: EventEmitter = new EventEmitter(); + + public async prepare(): Promise { + if (!this.Property && this.Data.PropertyId) { + // load property + const props = (await this.viewSettings.sqlWizardService.getFilterProperties(this.tableName)).filter( + (n) => n.PropertyId == this.Data.PropertyId, + ); + this.Property = props.length > 0 ? props[0] : undefined; } - - private views: SqlNodeView[] = []; - - constructor(public readonly viewSettings: SqlViewSettings, - public tableName: string, - public Data: SqlExpression, - public Property: FilterProperty) { - this.views = this.buildViews(); + // prepare recursively + for (let child of this.views) { + await child.prepare(); } - - // how to listen to a change of Data.PropertyId without re-implementing the SqlExpression interface? - public readonly columnChanged: EventEmitter = new EventEmitter(); - - public async prepare(): Promise { - if (!this.Property && this.Data.PropertyId) { - // load property - const props = (await this.viewSettings.sqlWizardService.getFilterProperties(this.tableName)) - .filter(n => n.PropertyId == this.Data.PropertyId); - this.Property = props.length > 0 ? props[0] : null; - } - // prepare recursively - for (let child of this.views) { - await child.prepare(); - } + } + + public async addChildNode(): Promise { + const e = { + LogOperator: LogOp.AND, + Expressions: [], + Negate: false, + }; + if (!this.Data.Expressions) { + this.Data.Expressions = []; } - - public async addChildNode(): Promise { - const e = { - LogOperator: LogOp.AND, - Expressions: [], - }; - this.Data.Expressions.push(e); - this.childViews.push(new SqlNodeView(this.viewSettings, this, this.tableName, e, null)); + this.Data.Expressions.push(e); + this.childViews.push(new SqlNodeView(this.viewSettings, this, this.tableName, e, undefined)); + } + + public replaceChildNode(oldNode: SqlExpression, newData: SqlExpression) { + const currentExprIndex = this.Data.Expressions?.indexOf(oldNode) ?? -1; + if (currentExprIndex < 0) { + throw Error('Expected to find child node in array.'); } - - public replaceChildNode(oldNode: SqlExpression, newData: SqlExpression) { - const currentExprIndex = this.Data.Expressions.indexOf(oldNode); - if (currentExprIndex < 0) { - throw Error('Expected to find child node in array.'); - } - - this.Data.Expressions[currentExprIndex] = newData; + if (this.Data.Expressions) { + this.Data.Expressions[currentExprIndex] = newData; } + } - private buildViews(): SqlNodeView[] { - let exp = this.Data.Expressions; - if (!exp) { - return []; - } + private buildViews(): SqlNodeView[] { + let exp = this.Data.Expressions; + if (!exp) { + return []; + } - const result = new Array(exp.length); + const result = new Array(exp.length); - exp.forEach((e, idx) => { - let view = new SqlNodeView(this.viewSettings, this, this.tableName, e, null); - result[idx] = view; - }); + exp.forEach((e, idx) => { + let view = new SqlNodeView(this.viewSettings, this, this.tableName, e, undefined); + result[idx] = view; + }); - return result; - } + return result; + } } export class SqlViewRoot extends SqlViewBase implements ISqlNodeView { - constructor(viewSettings: SqlViewSettings, tableName: string, Data: SqlExpression) { - super(viewSettings, tableName, Data, null); - } + constructor(viewSettings: SqlViewSettings, tableName: string, Data: SqlExpression) { + super(viewSettings, tableName, Data, undefined); + } } export class SqlNodeView extends SqlViewBase implements ISqlNodeView { - constructor(viewSettings: SqlViewSettings, - public Parent: ISqlNodeView, tableName: string, Data: SqlExpression, property: FilterProperty) { - super(viewSettings, tableName, Data, property); - } - - public isSimple(): boolean { - return !this.Property || this.Property.ColumnType == SqlColumnTypes.Normal; - } - - public canRemove(): boolean { - return true; - } - - public remove() { - const expressions = this.Parent.Data.Expressions; - const index = expressions.indexOf(this.Data); - - if (index < 0) { - throw new Error('Node not found'); - } - expressions.splice(index, 1); - this.Parent.childViews.splice(index, 1); + constructor( + viewSettings: SqlViewSettings, + public Parent: ISqlNodeView, + tableName: string, + Data: SqlExpression, + property: FilterProperty | undefined, + ) { + super(viewSettings, tableName, Data, property); + } + + public isSimple(): boolean { + return !this.Property || this.Property.ColumnType == SqlColumnTypes.Normal; + } + + public canRemove(): boolean { + return true; + } + + public remove() { + const expressions = this.Parent.Data.Expressions; + const index = expressions?.indexOf(this.Data) ?? -1; + + if (index < 0) { + throw new Error('Node not found'); } + expressions?.splice(index, 1); + this.Parent.childViews.splice(index, 1); + } } diff --git a/imxweb/projects/qbm/src/lib/sqlwizard/column-selection.component.html b/imxweb/projects/qbm/src/lib/sqlwizard/column-selection.component.html index eb810150f..bc6e5d96a 100644 --- a/imxweb/projects/qbm/src/lib/sqlwizard/column-selection.component.html +++ b/imxweb/projects/qbm/src/lib/sqlwizard/column-selection.component.html @@ -1,5 +1,11 @@
    - +
    diff --git a/imxweb/projects/qbm/src/lib/sqlwizard/column-selection.component.ts b/imxweb/projects/qbm/src/lib/sqlwizard/column-selection.component.ts index ce747851a..19c565da9 100644 --- a/imxweb/projects/qbm/src/lib/sqlwizard/column-selection.component.ts +++ b/imxweb/projects/qbm/src/lib/sqlwizard/column-selection.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,105 +27,106 @@ import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'; import { UntypedFormControl } from '@angular/forms'; import { EuiSelectOption } from '@elemental-ui/core'; -import { FilterProperty, LogOp, SqlExpression } from 'imx-qbm-dbts'; +import { FilterProperty, LogOp, SqlExpression } from '@imx-modules/imx-qbm-dbts'; import { SqlNodeView } from './SqlNodeView'; import { SqlWizardService } from './sqlwizard.service'; @Component({ - templateUrl: './column-selection.component.html', - selector: 'imx-sqlwizard-columnselection' + templateUrl: './column-selection.component.html', + selector: 'imx-sqlwizard-columnselection', }) - export class ColumnSelectionComponent implements OnInit, OnChanges { + @Input() public node: SqlNodeView; + @Output() public change = new EventEmitter(); - @Input() public node: SqlNodeView; - - @Output() public change = new EventEmitter(); - - public columns: FilterProperty[] = []; + public columns: FilterProperty[] = []; - public dataReady = false; - public options: EuiSelectOption[] = []; + public dataReady = false; + public options: EuiSelectOption[] = []; - public formControl = new UntypedFormControl(); + public formControl = new UntypedFormControl(); - private lastSelected; - - constructor(private readonly svc: SqlWizardService) { - } + private lastSelected; - public async ngOnInit(): Promise { - await this.reloadColumns(); - if (this.node.Property) { - this.formControl.setValue(this.node.Property.PropertyId); - } + constructor(private readonly svc: SqlWizardService) {} - this.formControl.valueChanges.subscribe(c => { - this.selectColumn(c); - }); + public async ngOnInit(): Promise { + await this.reloadColumns(); + if (this.node.Property) { + this.formControl.setValue(this.node.Property.PropertyId); } - // TODO: Check Upgrade - public selectionChange(value: any): void { - this.formControl.setValue(value); - this.node.columnChanged.emit(value); - this.change.emit(); + this.formControl.valueChanges.subscribe((c) => { + this.selectColumn(c); + }); + } + + public selectionChange(arg: EuiSelectOption | EuiSelectOption[]): void { + let value: string = Object.hasOwn(arg, 'value') ? (arg as EuiSelectOption).value : arg[0].option; + this.formControl.setValue(value); + this.node.columnChanged.emit(value); + this.change.emit(); + } + + public ngOnChanges(changes: any): void { + if (changes.node) { + this.reloadColumns(); } - - public ngOnChanges(changes: any): void { - if (changes.node) { - this.reloadColumns(); - } + } + public async selectColumn(propertyId: string): Promise { + if (this.lastSelected === propertyId) { + return; } - public async selectColumn(propertyId: string): Promise { - if (this.lastSelected === propertyId) { - return; - } - this.lastSelected = propertyId; - const found = this.columns.filter(c => c.PropertyId === propertyId); - if (found.length != 1) { - throw new Error('Property not found: ' + propertyId); - } - const filterProperty = found[0]; - - // If there is only one operator, pre-select it. - // this is important for boolean properties that do not show - // an operator selection. - let preselectedOperator: string = null; - if (found[0].Operators?.length === 1) { - preselectedOperator = found[0].Operators[0].Type; - } - - // create new empty node - const data: SqlExpression = { - PropertyId: propertyId, - Operator: preselectedOperator, - LogOperator: LogOp.AND - }; - this.node.Parent.replaceChildNode(this.node.Data, data); - this.node.Data = data; - this.node.Property = filterProperty; + this.lastSelected = propertyId; + const found = this.columns.filter((c) => c.PropertyId === propertyId); + if (found.length != 1) { + throw new Error('Property not found: ' + propertyId); } - public filter(option: EuiSelectOption, searchInputValue: string): boolean { - return option.display.toString().toUpperCase().trim().includes(searchInputValue.toUpperCase().trim()); + const filterProperty = found[0]; + + // If there is only one operator, pre-select it. + // this is important for boolean properties that do not show + // an operator selection. + let preselectedOperator: string | undefined; + if (found[0].Operators?.length === 1) { + preselectedOperator = found[0].Operators[0].Type; } - private async reloadColumns(): Promise { - const tableName = this.node.tableName; - - if (tableName) { - - this.columns = await this.svc.getColumns(this.node.viewSettings, tableName); - this.options = []; - for (const col of this.columns) { - this.options.push({ - display: col.Display, - value: col.PropertyId - }); - } + // create new empty node + const data: SqlExpression = { + PropertyId: propertyId, + Operator: preselectedOperator, + LogOperator: LogOp.AND, + Negate: false, + }; + this.node.Parent.replaceChildNode(this.node.Data, data); + this.node.Data = data; + this.node.Property = filterProperty; + } + public filter(option: EuiSelectOption, searchInputValue: string): boolean { + return ( + (option.display.toUpperCase().trim().includes(searchInputValue.toUpperCase().trim()) || + option.displayDetail?.toUpperCase().trim().includes(searchInputValue.toUpperCase().trim())) ?? + false + ); + } + + private async reloadColumns(): Promise { + const tableName = this.node.tableName; + + if (tableName) { + this.columns = await this.svc.getColumns(this.node.viewSettings, tableName); + this.options = []; + for (const col of this.columns) { + this.options.push({ + display: col.Display ?? '', + displayDetail: col.PropertyId, + value: col.PropertyId, + }); + } - this.dataReady = true; - } + this.dataReady = true; } + } } diff --git a/imxweb/projects/qbm/src/lib/sqlwizard/date-picker.component.html b/imxweb/projects/qbm/src/lib/sqlwizard/date-picker.component.html index 6866f3e9d..9283cc886 100644 --- a/imxweb/projects/qbm/src/lib/sqlwizard/date-picker.component.html +++ b/imxweb/projects/qbm/src/lib/sqlwizard/date-picker.component.html @@ -8,7 +8,15 @@ {{ '#LDS#Number' | translate }} - + {{ '#LDS#Time unit' | translate }} @@ -19,14 +27,14 @@ {{ - '#LDS#The value you entered is not a valid date.' | translate + '#LDS#The value you specified is not valid.' | translate }}
    {{ '#LDS#Date' | translate }} - + {{ '#LDS#The value you entered is not a valid date.' | translate }} diff --git a/imxweb/projects/qbm/src/lib/sqlwizard/date-picker.component.ts b/imxweb/projects/qbm/src/lib/sqlwizard/date-picker.component.ts index 155dfc619..ba52689a3 100644 --- a/imxweb/projects/qbm/src/lib/sqlwizard/date-picker.component.ts +++ b/imxweb/projects/qbm/src/lib/sqlwizard/date-picker.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,10 +25,10 @@ */ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { DateDiffUnit } from 'imx-qbm-dbts'; +import { FormControl, FormGroup, ValidatorFn } from '@angular/forms'; +import { DateDiffUnit } from '@imx-modules/imx-qbm-dbts'; import { SqlNodeView } from './SqlNodeView'; import { DateDiffOption, SqlWizardService } from './sqlwizard.service'; -import { FormControl, FormGroup, ValidatorFn } from '@angular/forms'; @Component({ templateUrl: './date-picker.component.html', @@ -36,6 +36,8 @@ import { FormControl, FormGroup, ValidatorFn } from '@angular/forms'; selector: 'imx-sqlwizard-datepicker', }) export class DatePickerComponent implements OnInit { + public absoluteError = false; + @Input() public expr: SqlNodeView; @Output() public change = new EventEmitter(); @@ -46,12 +48,14 @@ export class DatePickerComponent implements OnInit { public diffUnits: DateDiffOption[]; + private _relative = false; + private datepickerValidator: ValidatorFn = (form: FormGroup) => { - if (form.get('relative').value) { + if (form.get('relative')?.value) { if ( !(!!form.get('timeUnit')?.value || form.get('timeUnit')?.value === 0) || - !form.get('difference').value || - Number(form.get('difference').value) < 1 + !form.get('difference')?.value || + Number(form.get('difference')?.value) < 1 ) { return { datepickerError: true }; } @@ -61,13 +65,14 @@ export class DatePickerComponent implements OnInit { public form = new FormGroup( { - relative: new FormControl(null), - difference: new FormControl(null), - timeUnit: new FormControl(null), - datepicker: new FormControl(null), + relative: new FormControl(null), + difference: new FormControl(null), + timeUnit: new FormControl(null), + datepicker: new FormControl(null), }, - [this.datepickerValidator] + [this.datepickerValidator], ); + constructor(svc: SqlWizardService) { this.diffUnits = svc.getDateDiffUnits(); } @@ -102,6 +107,6 @@ export class DatePickerComponent implements OnInit { } public get isRelative(): boolean { - return this.form.controls.relative.value; + return this.form.controls.relative.value ?? false; } } diff --git a/imxweb/projects/qbm/src/lib/sqlwizard/simple-expression.component.html b/imxweb/projects/qbm/src/lib/sqlwizard/simple-expression.component.html index da9f32018..0967d3dd6 100644 --- a/imxweb/projects/qbm/src/lib/sqlwizard/simple-expression.component.html +++ b/imxweb/projects/qbm/src/lib/sqlwizard/simple-expression.component.html @@ -15,12 +15,25 @@
    -
    - +
    diff --git a/imxweb/projects/qbm/src/lib/sqlwizard/simple-expression.component.scss b/imxweb/projects/qbm/src/lib/sqlwizard/simple-expression.component.scss index 728be68fa..93faed3e4 100644 --- a/imxweb/projects/qbm/src/lib/sqlwizard/simple-expression.component.scss +++ b/imxweb/projects/qbm/src/lib/sqlwizard/simple-expression.component.scss @@ -1,5 +1,3 @@ -@import '@elemental-ui/core/src/styles/_palette.scss'; - :host { display: flex; flex-direction: column; @@ -14,15 +12,8 @@ align-items: center; } -.imx-delete-btn { - color: $color-red-60; -} - -.imx-add-button { - color: $color-blue-60; -} - .imx-column-container { display: flex; + align-items: baseline; column-gap: 10px; } diff --git a/imxweb/projects/qbm/src/lib/sqlwizard/simple-expression.component.ts b/imxweb/projects/qbm/src/lib/sqlwizard/simple-expression.component.ts index 8875062c9..3e435d48e 100644 --- a/imxweb/projects/qbm/src/lib/sqlwizard/simple-expression.component.ts +++ b/imxweb/projects/qbm/src/lib/sqlwizard/simple-expression.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,57 +26,55 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { SqlNodeView } from './SqlNodeView'; -import { SqlColumnTypes, ValType as _valType } from 'imx-qbm-dbts'; +import { SqlColumnTypes, ValType as _valType } from '@imx-modules/imx-qbm-dbts'; @Component({ - selector: 'imx-sqlwizard-simpleexpression', - styleUrls: ['./sqlwizard.scss', './simple-expression.component.scss'], - templateUrl: './simple-expression.component.html' + selector: 'imx-sqlwizard-simpleexpression', + styleUrls: ['./sqlwizard.scss', './simple-expression.component.scss'], + templateUrl: './simple-expression.component.html', }) export class SimpleExpressionComponent { - @Input() public expr: SqlNodeView; - @Output() public change = new EventEmitter(); + @Input() public expr: SqlNodeView; + @Output() public change = new EventEmitter(); - public ValType = _valType; - public ColumnType = SqlColumnTypes; + public ValType = _valType; + public ColumnType = SqlColumnTypes; - public emitChanges(): void { - this.change.emit(); - } - - public delete(ind: number) { - this.expr.Data.Value.splice(ind, 1); - this.emitChanges(); - } + public emitChanges(): void { + this.change.emit(); + } - public addNew() { - this.expr.Data.Value.push(null); - this.emitChanges(); - } + public delete(ind: number) { + this.expr.Data.Value.splice(ind, 1); + this.emitChanges(); + } - // https://stackoverflow.com/questions/42322968/angular2-dynamic-input-field-lose-focus-when-input-changes - public trackByFn(index: any, item: any) { - return index; - } + public addNew() { + this.expr.Data.Value.push(null); + this.emitChanges(); + } - public operatorChanged() { - if (this.expr.Data.Operator == 'IN') { - if (!(this.expr.Data.Value instanceof Array)) { + // https://stackoverflow.com/questions/42322968/angular2-dynamic-input-field-lose-focus-when-input-changes + public trackByFn(index: any, item: any) { + return index; + } - // re-initialize with array with single null element - this.expr.Data.Value = [null]; - } - } - else { - // re-initialize with single null element - if (this.expr.Data.Value instanceof Array) { - this.expr.Data.Value = null; - } - } - this.emitChanges(); + public operatorChanged() { + if (this.expr.Data.Operator == 'IN') { + if (!(this.expr.Data.Value instanceof Array)) { + // re-initialize with array with single null element + this.expr.Data.Value = [null]; + } + } else { + // re-initialize with single null element + if (this.expr.Data.Value instanceof Array) { + this.expr.Data.Value = null; + } } + this.emitChanges(); + } - public isArray(x): boolean { - return x instanceof Array; - } + public isArray(x): boolean { + return x instanceof Array; + } } diff --git a/imxweb/projects/qbm/src/lib/sqlwizard/single-expression.component.html b/imxweb/projects/qbm/src/lib/sqlwizard/single-expression.component.html index 019b3d7a8..ed9e711a3 100644 --- a/imxweb/projects/qbm/src/lib/sqlwizard/single-expression.component.html +++ b/imxweb/projects/qbm/src/lib/sqlwizard/single-expression.component.html @@ -1,27 +1,32 @@
    - - - - -
    - -
    + + + + +
    + +
    - -
  • - - + +
  • + + -
    - -
    -
  • -
    +
    + +
    + +
    - diff --git a/imxweb/projects/qbm/src/lib/sqlwizard/single-expression.component.ts b/imxweb/projects/qbm/src/lib/sqlwizard/single-expression.component.ts index 372f62f75..6c8ed3e6b 100644 --- a/imxweb/projects/qbm/src/lib/sqlwizard/single-expression.component.ts +++ b/imxweb/projects/qbm/src/lib/sqlwizard/single-expression.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,45 +26,43 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { SqlNodeView, SqlViewSettings } from './SqlNodeView'; -import { LogOp as _logOp } from 'imx-qbm-dbts'; +import { LogOp as _logOp } from '@imx-modules/imx-qbm-dbts'; @Component({ - selector: 'imx-sqlwizard-singleexpression', - styleUrls: ['single-expression.component.scss', './sqlwizard.scss'], - templateUrl: './single-expression.component.html' + selector: 'imx-sqlwizard-singleexpression', + styleUrls: ['single-expression.component.scss', './sqlwizard.scss'], + templateUrl: './single-expression.component.html', }) export class SingleExpressionComponent { + @Input() public expr: SqlNodeView; + @Input() public first: boolean; + @Input() public last: boolean; + @Input() public viewSettings: SqlViewSettings; - @Input() public expr: SqlNodeView; - @Input() public first: boolean; - @Input() public last: boolean; - @Input() public viewSettings: SqlViewSettings; + @Output() public change = new EventEmitter(); - @Output() public change = new EventEmitter(); + public LogOp = _logOp; - public LogOp = _logOp; + public emitChanges(): void { + this.change.emit(); + } - public emitChanges(): void { - this.change.emit(); - } + public IsEmpty(): boolean { + return !this.expr.Data.PropertyId; + } - public IsEmpty(): boolean { - return !this.expr.Data.PropertyId; - } + public toggleLogOperator() { + this.expr.Parent.Data.LogOperator = this.expr.Parent.Data.LogOperator === _logOp.OR ? _logOp.AND : _logOp.OR; + this.change.emit(); + } - public toggleLogOperator() { - this.expr.Parent.Data.LogOperator = this.expr.Parent.Data.LogOperator === _logOp.OR ? _logOp.AND : _logOp.OR; - this.change.emit(); - } - - public async addExpression(): Promise { - await this.expr.Parent.addChildNode(); - this.emitChanges(); - } - - public removeExpression(): void { - this.expr.remove(); - this.emitChanges(); - } + public async addExpression(): Promise { + await this.expr.Parent.addChildNode(); + this.emitChanges(); + } + public removeExpression(): void { + this.expr.remove(); + this.emitChanges(); + } } diff --git a/imxweb/projects/qbm/src/lib/sqlwizard/single-value.component.html b/imxweb/projects/qbm/src/lib/sqlwizard/single-value.component.html index e228a9498..cfd52e6d3 100644 --- a/imxweb/projects/qbm/src/lib/sqlwizard/single-value.component.html +++ b/imxweb/projects/qbm/src/lib/sqlwizard/single-value.component.html @@ -1,20 +1,22 @@ - + {{ '#LDS#Value' | translate }} - + {{ lv.Description }} - + - +
    - {{ '#LDS#Property is activated' | translate }} + {{ '#LDS#Property is activated' | translate }} +
    @@ -22,15 +24,26 @@
    {{ '#LDS#Value' | translate }} - - {{ '#LDS#WD_InputInvalidInteger' | translate | ldsReplace : integerFormControl.value }} + + + {{ '#LDS#The value you entered is invalid. Enter an integer number.' | translate }} + +
    +
    + + {{ '#LDS#Value' | translate }} + + + {{ '#LDS#The value you entered is invalid. Enter an integer number.' | translate }}
    {{ '#LDS#Value' | translate }} - - {{ '#LDS#WD_InputInvalidFloat' | translate | ldsReplace : doubleFormControl.value }} + + {{ '#LDS#The value you entered is invalid. Enter a number.' | translate }}
    diff --git a/imxweb/projects/qbm/src/lib/sqlwizard/single-value.component.ts b/imxweb/projects/qbm/src/lib/sqlwizard/single-value.component.ts index 9cf470f36..7a40378eb 100644 --- a/imxweb/projects/qbm/src/lib/sqlwizard/single-value.component.ts +++ b/imxweb/projects/qbm/src/lib/sqlwizard/single-value.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,21 +25,21 @@ */ import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; +import { FormControl, Validators } from '@angular/forms'; import { FkProviderItem, IClientProperty, - MetaTableRelationData, + IEntityColumn, SqlColumnTypes, SqlTable, ValType, ValType as _valType, -} from 'imx-qbm-dbts'; +} from '@imx-modules/imx-qbm-dbts'; import { Subscription } from 'rxjs'; import { BaseCdr } from '../cdr/base-cdr'; import { EntityService } from '../entity/entity.service'; import { SqlNodeView } from './SqlNodeView'; import { SqlWizardApiService } from './sqlwizard-api.service'; -import { FormControl, Validators } from '@angular/forms'; @Component({ selector: 'imx-sqlwizard-singlevalue', @@ -47,19 +47,6 @@ import { FormControl, Validators } from '@angular/forms'; templateUrl: './single-value.component.html', }) export class SingleValueComponent implements OnInit, OnDestroy { - public get selectedTable() { - return this._selectedTable; - } - - public set selectedTable(val) { - this._selectedTable = val; - if (val) { - this._fkRelation.ParentTableName = val.Name; - this._fkRelation.ParentColumnName = val.ParentColumnName; - this._fkProviderItem.fkTableName = val.Name; - } - } - get value() { if (this.mode == 'array' && this.expr.Data.Value) { return this.expr.Data.Value[this.index]; @@ -77,20 +64,24 @@ export class SingleValueComponent implements OnInit, OnDestroy { } get displayValue() { - if (!this.expr.Data.DisplayValues) { + if (!this.expr.Data?.DisplayValues) { return null; } - return this.expr.Data.DisplayValues[this.mode === 'array' ? this.index : 0]; + if (this.mode == 'array') { + return this.expr.Data.DisplayValues[this.index]; + } else { + return this.expr.Data.DisplayValues ? this.expr.Data.DisplayValues[0] : null; + } } set displayValue(val) { - if (!this.expr.Data?.DisplayValues) { + if (!this.expr.Data) { return; } if (this.mode == 'array' && this.expr.Data.DisplayValues) { - this.expr.Data.DisplayValues[this.index] = val; + this.expr.Data.DisplayValues?.splice(this.index, 1, val ?? ''); } else { - this.expr.Data.DisplayValues = [val]; + this.expr.Data.DisplayValues = [val ?? '']; } } @@ -103,31 +94,21 @@ export class SingleValueComponent implements OnInit, OnDestroy { public ValType = _valType; public ColumnType = SqlColumnTypes; public cdr: BaseCdr; - public doubleFormControl = new FormControl(null, Validators.pattern(/^[+-]?\d+(\.\d+)?$/)); - public integerFormControl = new FormControl(null, Validators.pattern(/^[+-]?\d+$/)); - - private _selectedTable: SqlTable; - private _fkRelation: MetaTableRelationData = { - IsMemberRelation: false, - }; - private _fkProviderItem: FkProviderItem = { - columnName: 'dummycolumn', - fkTableName: 'not_set', - parameterNames: ['OrderBy', 'StartIndex', 'PageSize', 'filter', 'search'], - load: async (_, parameters = {}) => this.sqlWizardApi.getCandidates(this._fkRelation.ParentTableName, parameters), - getFilterTree: async () => ({ Elements: [] }), - getDataModel: async () => ({}), - }; + public doubleFormControl = new FormControl(null, [Validators.pattern(/^[+-]?\d+(\.\d+)?$/), Validators.required]); + public integerFormControl = new FormControl(null, [Validators.pattern(/^[+-]?\d+$/), Validators.required]); private subscriptions: Subscription[] = []; - constructor(private readonly entityService: EntityService, private readonly sqlWizardApi: SqlWizardApiService) {} + constructor( + private readonly entityService: EntityService, + private readonly sqlWizardApi: SqlWizardApiService, + ) {} public ngOnInit(): void { this.subscriptions.push( this.expr.columnChanged.subscribe((_) => { this.buildCdr(); - }) + }), ); this.buildCdr(); @@ -142,30 +123,33 @@ export class SingleValueComponent implements OnInit, OnDestroy { this.change.emit(); } + /** + * @ignore Builds a cdr for the expression. + */ private buildCdr() { - const tables = this.expr.Property.SelectionTables; - if (tables && tables.length > 0) { - this.selectedTable = tables[0]; - } else { - this.selectedTable = null; + const tables = this.expr.Property?.SelectionTables; + + if (this.expr.Property?.Type === ValType.Bool && this.expr.Data.Value === undefined) { + this.value = false; } - const property: IClientProperty = { - ColumnName: 'dummycolumn', - Type: ValType.String, - FkRelation: this._fkRelation, - }; + let column: IEntityColumn; - if (this.expr.Property.Type === ValType.Bool && this.expr.Data.Value === undefined ) this.value = false; + if ((tables?.length ?? 0) > 1) { + column = this.buildDynamicFk(tables ?? []); + } else { + if (!!tables?.length) { + column = this.buildFk(tables?.[0]); + } else { + column = this.buildSimple(); + } + } + if (!column) throw new Error('Column can not be build'); - const column = this.entityService.createLocalEntityColumn(property, [this._fkProviderItem], { - Value: this.value, - DisplayValue: this.displayValue, - }); - if (this.expr.Property.Type === ValType.Double) { + if (this.expr.Property?.Type === ValType.Double) { this.doubleFormControl.setValue(column.GetValue()); } - if (this.expr.Property.Type === ValType.Int) { + if (this.expr.Property?.Type === ValType.Int) { this.integerFormControl.setValue(column.GetValue()); } @@ -180,6 +164,86 @@ export class SingleValueComponent implements OnInit, OnDestroy { this.cdr = new BaseCdr(column, '#LDS#Value'); } + /** + * @ignore Builds a column, containing a dynamic fk definition, that uses multiple tables. + * @param tables a list containing the SQL tables, that are used for the column's fk relation + * @returns an entity column, that can be used by a cdr + */ + private buildDynamicFk(tables: SqlTable[]): IEntityColumn { + const property: IClientProperty = { + ColumnName: 'dummycolumn', + Type: ValType.String, + IsDynamicFk: true, + ValidReferencedTables: tables.map((elem) => ({ TableName: elem.Name })), + }; + + return this.buildColumn( + property, + tables.map((elem) => this.buildProviderItem(elem.Name, 'XObjectKey')), + ); + } + + /** + * @ignore Builds a column, containing a simple fk definition. + * @param table the SQL table, that is used for the column's fk relation + * @returns an entity column, that can be used by a cdr + */ + private buildFk(table: SqlTable | undefined): IEntityColumn { + const property: IClientProperty = { + ColumnName: 'dummycolumn', + Type: ValType.String, + FkRelation: { + IsMemberRelation: false, + ParentTableName: table?.Name ?? '', + ParentColumnName: table?.ParentColumnName, + }, + }; + + return this.buildColumn(property, [this.buildProviderItem(table?.Name, table?.ParentColumnName)]); + } + + /** + * @ignore Builds a simple entity column. + * @returns a simple entity column without fk providers + */ + private buildSimple(): IEntityColumn { + const property: IClientProperty = { + ColumnName: 'dummycolumn', + Type: this.expr.Property?.Type ?? ValType.String, + }; + return this.buildColumn(property, undefined); + } + + /** + * @ignore Builds a single FkProviderItem. + * @param tableName the name of the table + * @returns a fk provider item + */ + private buildProviderItem(tableName: string | undefined, fkColumnName?: string): FkProviderItem { + return { + columnName: 'dummycolumn', + fkColumnName, + fkTableName: tableName ?? '', + parameterNames: ['OrderBy', 'StartIndex', 'PageSize', 'filter', 'search'], + load: async (_, parameters = {}) => this.sqlWizardApi.getCandidates(tableName ?? '', parameters), + getFilterTree: async () => ({ Elements: [] }), + getDataModel: async () => ({}), + }; + } + + /** + * @ignore This is used to build the entity column in all helper methods. + * @param property the client property describing the column + * @param providerItems FkProviderItems, that are associated with the column. + * @returns an IEntityColumn, that can be used in the CDR + */ + private buildColumn(property: IClientProperty, providerItems: FkProviderItem[] | undefined): IEntityColumn { + return this.entityService.createLocalEntityColumn(property, providerItems, { + Value: this.value, + DisplayValue: this.displayValue ?? '', + }); + } + private onFormValueChanges(): void { this.subscriptions.push( this.doubleFormControl.valueChanges.subscribe((value) => { @@ -189,7 +253,7 @@ export class SingleValueComponent implements OnInit, OnDestroy { } else { this.value = {}; } - }) + }), ); this.subscriptions.push( this.integerFormControl.valueChanges.subscribe((value) => { @@ -199,8 +263,7 @@ export class SingleValueComponent implements OnInit, OnDestroy { } else { this.value = {}; } - }) + }), ); } } - diff --git a/imxweb/projects/qbm/src/lib/sqlwizard/sqlwizard-api.service.ts b/imxweb/projects/qbm/src/lib/sqlwizard/sqlwizard-api.service.ts index e0615dc92..a811b1676 100644 --- a/imxweb/projects/qbm/src/lib/sqlwizard/sqlwizard-api.service.ts +++ b/imxweb/projects/qbm/src/lib/sqlwizard/sqlwizard-api.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,10 +24,9 @@ * */ -import { CollectionLoadParameters, EntityCollectionData, FilterProperty } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, EntityCollectionData, FilterProperty } from '@imx-modules/imx-qbm-dbts'; export abstract class SqlWizardApiService { - public abstract getFilterProperties(table: string): Promise; public abstract getCandidates(parentTable: string, options?: CollectionLoadParameters): Promise; diff --git a/imxweb/projects/qbm/src/lib/sqlwizard/sqlwizard.component.html b/imxweb/projects/qbm/src/lib/sqlwizard/sqlwizard.component.html index 830193cad..ec132d016 100644 --- a/imxweb/projects/qbm/src/lib/sqlwizard/sqlwizard.component.html +++ b/imxweb/projects/qbm/src/lib/sqlwizard/sqlwizard.component.html @@ -9,20 +9,25 @@ {{ '#LDS#Logical operator' | translate }} - - {{ andConditionLabel | translate }} - {{ orConditionLabel | translate }} + + {{ + andConditionLabel | translate + }} + {{ + orConditionLabel | translate + }}
    - {{logOpText() | translate}} + {{ logOpText() | translate }}
  • {{ i + 1 }}.
    - + +
  • diff --git a/imxweb/projects/qbm/src/lib/sqlwizard/sqlwizard.component.ts b/imxweb/projects/qbm/src/lib/sqlwizard/sqlwizard.component.ts index f65f2f930..3a558b8ad 100644 --- a/imxweb/projects/qbm/src/lib/sqlwizard/sqlwizard.component.ts +++ b/imxweb/projects/qbm/src/lib/sqlwizard/sqlwizard.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,11 +24,23 @@ * */ -import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnChanges, OnInit, Output, QueryList, SimpleChanges, ViewChildren } from '@angular/core'; +import { + AfterViewInit, + Component, + ElementRef, + EventEmitter, + Input, + OnChanges, + OnInit, + Output, + QueryList, + SimpleChanges, + ViewChildren, +} from '@angular/core'; +import { MatButtonToggleChange } from '@angular/material/button-toggle'; +import { SqlExpression, LogOp as _logOp } from '@imx-modules/imx-qbm-dbts'; import { SqlViewSettings } from './SqlNodeView'; -import { LogOp as _logOp, SqlExpression } from 'imx-qbm-dbts'; import { SqlWizardApiService } from './sqlwizard-api.service'; -import { MatButtonToggleChange } from '@angular/material/button-toggle'; @Component({ templateUrl: './sqlwizard.component.html', @@ -47,7 +59,7 @@ export class SqlWizardComponent implements OnInit, OnChanges, AfterViewInit { svc = this.apiSvc; } - return svc.implemented; + return svc.implemented ?? false; } @Input() public tableName: string; @@ -69,14 +81,14 @@ export class SqlWizardComponent implements OnInit, OnChanges, AfterViewInit { } public ngAfterViewInit(): void { - setTimeout( () => { + setTimeout(() => { this.expressionList.changes.subscribe(() => { if (this.newExpressionAdded) { this.expressionList?.last?.nativeElement.scrollIntoView(true); } this.newExpressionAdded = false; - }) + }); }); } @@ -108,7 +120,9 @@ export class SqlWizardComponent implements OnInit, OnChanges, AfterViewInit { } public onOperatorChanged(event: MatButtonToggleChange): void { - (event.value as string).toLowerCase() === 'and' ? (this.expression.LogOperator = this.LogOp.AND) : (this.expression.LogOperator = this.LogOp.OR); + (event.value as string).toLowerCase() === 'and' + ? (this.expression.LogOperator = this.LogOp.AND) + : (this.expression.LogOperator = this.LogOp.OR); this.change.emit(); } diff --git a/imxweb/projects/qbm/src/lib/sqlwizard/sqlwizard.module.ts b/imxweb/projects/qbm/src/lib/sqlwizard/sqlwizard.module.ts index 0c8d8bd13..654325cf8 100644 --- a/imxweb/projects/qbm/src/lib/sqlwizard/sqlwizard.module.ts +++ b/imxweb/projects/qbm/src/lib/sqlwizard/sqlwizard.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,33 +24,33 @@ * */ +import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { SqlWizardComponent } from './sqlwizard.component'; -import { SimpleExpressionComponent } from './simple-expression.component'; -import { CommonModule } from '@angular/common'; -import { ColumnSelectionComponent } from './column-selection.component'; -import { TableSelectionComponent } from './table-selection.component'; -import { SingleExpressionComponent } from './single-expression.component'; -import { WhereClauseExpressionComponent } from './where-clause-expression.component'; -import { TranslateModule } from '@ngx-translate/core'; -import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; import { MatButtonModule } from '@angular/material/button'; -import { MatSelectModule } from '@angular/material/select'; +import { MatButtonToggleModule } from '@angular/material/button-toggle'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatOptionModule } from '@angular/material/core'; +import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; -import { MatDatepickerModule } from '@angular/material/datepicker'; -import { MatSlideToggleModule } from '@angular/material/slide-toggle'; -import { MatButtonToggleModule } from '@angular/material/button-toggle'; -import { SqlWizardService } from './sqlwizard.service'; import { MatListModule } from '@angular/material/list'; -import { DatePickerComponent } from './date-picker.component'; import { MatRadioModule } from '@angular/material/radio'; -import { SingleValueComponent } from './single-value.component'; +import { MatSelectModule } from '@angular/material/select'; +import { MatSlideToggleModule } from '@angular/material/slide-toggle'; +import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; +import { TranslateModule } from '@ngx-translate/core'; import { CdrModule } from '../cdr/cdr.module'; import { LdsReplaceModule } from '../lds-replace/lds-replace.module'; +import { ColumnSelectionComponent } from './column-selection.component'; +import { DatePickerComponent } from './date-picker.component'; +import { SimpleExpressionComponent } from './simple-expression.component'; +import { SingleExpressionComponent } from './single-expression.component'; +import { SingleValueComponent } from './single-value.component'; +import { SqlWizardComponent } from './sqlwizard.component'; +import { SqlWizardService } from './sqlwizard.service'; +import { TableSelectionComponent } from './table-selection.component'; +import { WhereClauseExpressionComponent } from './where-clause-expression.component'; @NgModule({ imports: [ diff --git a/imxweb/projects/qbm/src/lib/sqlwizard/sqlwizard.scss b/imxweb/projects/qbm/src/lib/sqlwizard/sqlwizard.scss index 4d12eea5c..3f2a499a2 100644 --- a/imxweb/projects/qbm/src/lib/sqlwizard/sqlwizard.scss +++ b/imxweb/projects/qbm/src/lib/sqlwizard/sqlwizard.scss @@ -1,5 +1,5 @@ @import '@elemental-ui/core/src/styles/_palette.scss'; - +@import 'base/mixins'; .column-selector { flex-grow: 1; @@ -24,10 +24,6 @@ li { flex-grow: 1; flex-shrink: 1; align-items: center; - - .mat-slide-toggle { - margin-top: 10px; - } } .expression { @@ -64,20 +60,21 @@ li { display: flex; flex-wrap: wrap; align-items: baseline; - .mat-error { + gap: 10px; + .mat-mdc-form-field-error { width: 100%; font-size: 75%; } } .expression-container { - transition: all .6s cubic-bezier(0.25, 0.8, 0.25, 1); + transition: all 0.6s cubic-bezier(0.25, 0.8, 0.25, 1); border: 1px $color-gray-20 solid; border-radius: 5px; display: flex; .expression-container-header { - transition: all .6s cubic-bezier(0.25, 0.8, 0.25, 1); + transition: all 0.6s cubic-bezier(0.25, 0.8, 0.25, 1); width: 30px; display: flex; justify-content: center; @@ -124,23 +121,18 @@ li { flex: 1 1 auto; } -mat-radio-button { - margin-right: 1em; +.imx-remove-all { + @include align-button-content($color-red-60); } -/* undo Elemental UI fix */ -::ng-deep .mat-form-field-wrapper, -::ng-deep mat-form-field.ng-untouched .mat-form-field-wrapper, -::ng-deep mat-form-field.ng-valid .mat-form-field-wrapper { - padding-bottom: 0; +.imx-btn-color-default { + @include align-button-content($color-blue-60); } -::ng-deep .mat-form-field-infix .mat-select-trigger { - vertical-align: middle; - - .mat-select-arrow-wrapper { - vertical-align: bottom; - } +/* undo Elemental UI fix */ +::ng-deep mat-form-field.ng-untouched .mat-mdc-form-field-wrapper, +::ng-deep mat-form-field.ng-valid .mat-mdc-form-field-wrapper { + padding-bottom: 0; } imx-sqlwizard-singlevalue { @@ -194,6 +186,14 @@ imx-sqlwizard-singlevalue { .selected-logical-op { color: $color-blue-40; } + + .imx-remove-all { + @include align-button-content($color-red-40); + } + + .imx-btn-color-default { + @include align-button-content($color-blue-40); + } } } @@ -215,5 +215,13 @@ imx-sqlwizard-singlevalue { .selected-logical-op { color: $color-blue-60; } + + .imx-remove-all { + @include align-button-content($color-red-40); + } + + .imx-btn-color-default { + @include align-button-content($color-blue-40); + } } } diff --git a/imxweb/projects/qbm/src/lib/sqlwizard/sqlwizard.service.ts b/imxweb/projects/qbm/src/lib/sqlwizard/sqlwizard.service.ts index 2c8f02e5f..80458babf 100644 --- a/imxweb/projects/qbm/src/lib/sqlwizard/sqlwizard.service.ts +++ b/imxweb/projects/qbm/src/lib/sqlwizard/sqlwizard.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,17 +25,15 @@ */ import { Injectable } from '@angular/core'; -import { FilterProperty } from 'imx-qbm-dbts'; -import { DateDiffUnit } from 'imx-qbm-dbts'; -import { SqlViewSettings } from './SqlNodeView'; +import { DateDiffUnit, FilterProperty } from '@imx-modules/imx-qbm-dbts'; import { AuthenticationService } from '../authentication/authentication.service'; +import { SqlViewSettings } from './SqlNodeView'; @Injectable() export class SqlWizardService { public constructor(authentication: AuthenticationService) { - authentication.onSessionResponse.subscribe(async (elem) => { - this.currentUser = elem.UserUid; + this.currentUser = elem.UserUid ?? ''; }); } @@ -50,10 +48,10 @@ export class SqlWizardService { { DisplayMl: '#LDS#SW_Hours', Value: DateDiffUnit.Hours }, ]; - public getColumns(viewSettings: SqlViewSettings, tableName: string): Promise { + public async getColumns(viewSettings: SqlViewSettings, tableName: string): Promise { const tableUser = tableName + this.currentUser; if (this._cache.has(tableUser)) { - return this._cache.get(tableUser); + return (await this._cache.get(tableUser)) ?? []; } const promise = this.getInternal(tableName, viewSettings); this._cache.set(tableUser, promise); diff --git a/imxweb/projects/qbm/src/lib/sqlwizard/table-selection.component.html b/imxweb/projects/qbm/src/lib/sqlwizard/table-selection.component.html index 0f879c778..8baa0f5ca 100644 --- a/imxweb/projects/qbm/src/lib/sqlwizard/table-selection.component.html +++ b/imxweb/projects/qbm/src/lib/sqlwizard/table-selection.component.html @@ -1,2 +1 @@ - - + diff --git a/imxweb/projects/qbm/src/lib/sqlwizard/table-selection.component.ts b/imxweb/projects/qbm/src/lib/sqlwizard/table-selection.component.ts index 0b4f8b5d8..5d1c98fcb 100644 --- a/imxweb/projects/qbm/src/lib/sqlwizard/table-selection.component.ts +++ b/imxweb/projects/qbm/src/lib/sqlwizard/table-selection.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,27 +30,24 @@ import { SqlNodeView } from './SqlNodeView'; import { SqlWizardApiService } from './sqlwizard-api.service'; @Component({ - selector: 'imx-sqlwizard-tableselection', - templateUrl: './table-selection.component.html', + selector: 'imx-sqlwizard-tableselection', + templateUrl: './table-selection.component.html', }) export class TableSelectionComponent implements OnInit { + @Input() public node: SqlNodeView; - @Input() public node: SqlNodeView; + public selectableTables: EuiSelectOption[] = []; - public selectableTables: EuiSelectOption[] = []; + public tableFilter: string; - public tableFilter: string; + constructor(private readonly sqlApiService: SqlWizardApiService) {} - constructor(private readonly sqlApiService: SqlWizardApiService) { - } - - public async ngOnInit() { - // TODO const tables = await this.sqlApiService.getSelectableTables(this.node.Data.); - // this.selectableTables = tables; - } - - public filter(option: EuiSelectOption, searchInputValue: string): boolean { - return option.display.toString().toUpperCase().trim().includes(searchInputValue.toUpperCase().trim()); - } + public async ngOnInit() { + // TODO const tables = await this.sqlApiService.getSelectableTables(this.node.Data.); + // this.selectableTables = tables; + } + public filter(option: EuiSelectOption, searchInputValue: string): boolean { + return option.display.toString().toUpperCase().trim().includes(searchInputValue.toUpperCase().trim()); + } } diff --git a/imxweb/projects/qbm/src/lib/sqlwizard/where-clause-expression.component.ts b/imxweb/projects/qbm/src/lib/sqlwizard/where-clause-expression.component.ts index 5a6fc563f..58d12a176 100644 --- a/imxweb/projects/qbm/src/lib/sqlwizard/where-clause-expression.component.ts +++ b/imxweb/projects/qbm/src/lib/sqlwizard/where-clause-expression.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,24 +25,23 @@ */ import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { SqlColumnTypes, ValType } from 'imx-qbm-dbts'; +import { SqlColumnTypes, ValType } from '@imx-modules/imx-qbm-dbts'; import { SqlNodeView } from './SqlNodeView'; @Component({ - selector: 'imx-sqlwizard-whereclauseexpression', - styleUrls: ['./sqlwizard.scss'], - templateUrl: './where-clause-expression.component.html' + selector: 'imx-sqlwizard-whereclauseexpression', + styleUrls: ['./sqlwizard.scss'], + templateUrl: './where-clause-expression.component.html', }) export class WhereClauseExpressionComponent { + public ColumnType = SqlColumnTypes; + public ValType = ValType; - public ColumnType = SqlColumnTypes; - public ValType = ValType; + @Input() public expr: SqlNodeView; - @Input() public expr: SqlNodeView; + @Output() public change = new EventEmitter(); - @Output() public change = new EventEmitter(); - - public emitChanges(): void { - this.change.emit(); - } + public emitChanges(): void { + this.change.emit(); + } } diff --git a/imxweb/projects/qbm/src/lib/storage/storage.module.ts b/imxweb/projects/qbm/src/lib/storage/storage.module.ts index 2d1ff7c54..738dab39c 100644 --- a/imxweb/projects/qbm/src/lib/storage/storage.module.ts +++ b/imxweb/projects/qbm/src/lib/storage/storage.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,11 +31,7 @@ import { StorageService } from './storage.service'; @NgModule({ declarations: [], - imports: [ - CommonModule - ], - providers: [ - StorageService - ] + imports: [CommonModule], + providers: [StorageService], }) -export class StorageModule { } +export class StorageModule {} diff --git a/imxweb/projects/qbm/src/lib/storage/storage.service.spec.ts b/imxweb/projects/qbm/src/lib/storage/storage.service.spec.ts index 49cd06336..46fa0e041 100644 --- a/imxweb/projects/qbm/src/lib/storage/storage.service.spec.ts +++ b/imxweb/projects/qbm/src/lib/storage/storage.service.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -71,5 +71,4 @@ describe('StorageService', () => { expect(sessionStorageSetItemSpy).toHaveBeenCalledWith(mockKey, 'true'); }); }); - }); diff --git a/imxweb/projects/qbm/src/lib/storage/storage.service.ts b/imxweb/projects/qbm/src/lib/storage/storage.service.ts index 3fd56a066..272558b1c 100644 --- a/imxweb/projects/qbm/src/lib/storage/storage.service.ts +++ b/imxweb/projects/qbm/src/lib/storage/storage.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,11 +29,13 @@ import { Injectable } from '@angular/core'; export const HELPER_ALERT_KEY_PREFIX = 'helperAlertDismissed'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class StorageService { - public get lastUrl(): string { return localStorage.getItem(this.lastUrlKey); } - public set lastUrl(value: string) { + public get lastUrl(): string { + return localStorage.getItem(this.lastUrlKey) ?? ''; + } + public set lastUrl(value: string | undefined) { if (value) { localStorage.setItem(this.lastUrlKey, value); } else { @@ -67,6 +69,6 @@ export class StorageService { } public removeKeys(...params: string[]): void { - params.forEach(key => sessionStorage.removeItem(key)); + params.forEach((key) => sessionStorage.removeItem(key)); } } diff --git a/imxweb/projects/qbm/src/lib/styles/data-explorer-common.scss b/imxweb/projects/qbm/src/lib/styles/data-explorer-common.scss index d87a92a1a..e5ea8c15d 100644 --- a/imxweb/projects/qbm/src/lib/styles/data-explorer-common.scss +++ b/imxweb/projects/qbm/src/lib/styles/data-explorer-common.scss @@ -1,167 +1,13 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; .data-explorer { ::ng-deep .imx-data-table-selection-info { display: none; } - .sync-status-alert { - ::ng-deep .eui-alert.eui-alert-condensed { - margin-bottom: 10px; - } - } - imx-data-source-toolbar { flex: 0 0 auto; } - .data-explorer-card-header { - background-color: $white; - border-top-left-radius: 4px; - border-top-right-radius: 4px; - z-index: 100; - border: 1px solid rgba($color-blue-60, 0.6); - margin-bottom: 20px; - - .data-explorer-card-header-bg { - border-top-left-radius: 4px; - border-top-right-radius: 4px; - background-color: $color-blue-20; - padding: 10px 24px; - display: flex; - align-items: center; - justify-content: flex-start; - height: 45px; - - .eui-icon { - color: rgba($color-blue-60, 0.6); - margin-right: 10px; - } - .mat-icon-button{ - .eui-icon{ - margin-right: 0; - } - } - - & > span { - font-size: 20px; - } - } - } - - ::ng-deep .data-explorer-table { - .mat-sidenav-content { - overflow-y: hidden; - } - - .imx-data-table-row-highlighted { - background-color: inherit; - } - - .mat-row:hover { - cursor: pointer; - background-color: rgba($color-blue-20, 0.2) !important; - } - - .mat-checkbox-disabled { - cursor: not-allowed; - } - - .mat-table { - .mat-cell, - .mat-header-cell { - padding-right: 10px; - } - } - } - - &__action-buttons-wrapper { - background-color: $asher-gray; - padding: 10px 0; - position: sticky; - position: -webkit-sticky; - bottom: -35px; - left: 0; - z-index: 10; - } - - &__action-buttons { - display: flex; - justify-content: flex-end; - align-items: center; - background-color: $white; - padding: 16px 32px; - width: 100%; - - .mat-slide-toggle { - margin-right: 15px; - } - - .justify-start { - margin-right: auto; - } - } -} - -@media screen and (max-width: 480px) { - .data-explorer { - &__action-buttons-wrapper { - bottom: -15px; - } - - &__action-buttons { - display: block; - - .mat-slide-toggle { - display: block; - margin-bottom: 20px; - margin-top: 5px; - margin-right: 0; - - ::ng-deep .mat-slide-toggle-content { - white-space: normal; - } - } - - .mat-raised-button { - width: 100%; - } - } - } -} - -@media screen and (max-width: 320px) { - .data-explorer { - &__action-buttons { - .mat-slide-toggle { - margin-top: 15px; - margin-bottom: 25px; - } - } - } -} -.eui-dark-theme { - .data-explorer { - .data-explorer-card-header { - .data-explorer-card-header-bg { - background-color: $color-blue-80; - } - .eui-icon { - color: $color-gray-0; - } - } - } -} -.eui-contrast-theme { - .data-explorer { - .data-explorer-card-header { - .data-explorer-card-header-bg { - background-color: $color-blue-80; - } - .eui-icon { - color: $color-gray-0; - } - } - } } diff --git a/imxweb/projects/qbm/src/lib/styles/data-explorer-details-common.scss b/imxweb/projects/qbm/src/lib/styles/data-explorer-details-common.scss index cb3ff4336..3992fae24 100644 --- a/imxweb/projects/qbm/src/lib/styles/data-explorer-details-common.scss +++ b/imxweb/projects/qbm/src/lib/styles/data-explorer-details-common.scss @@ -1,7 +1,6 @@ @import '@elemental-ui/core/src/styles/_palette.scss'; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; - .governance-sidesheet { background-color: $asher-gray; height: 100%; @@ -9,11 +8,6 @@ flex-direction: column; overflow: hidden; - ::ng-deep .mat-tab-group { - flex-grow: 1; - overflow: auto; - } - &__tab-content { display: flex; flex-direction: column; @@ -26,7 +20,7 @@ &__tab-content-body { flex: 1; - padding: 20px; + // padding: 20px; overflow: auto; display: flex; flex-direction: column; @@ -40,11 +34,6 @@ padding-left: 0; } } - - .mat-slide-toggle.custom-toggle { - display: flex; - margin-bottom: 20px; - } } .governance-sidesheet-action-buttons-container { diff --git a/imxweb/projects/qbm/src/lib/styles/imx-page-title.scss b/imxweb/projects/qbm/src/lib/styles/imx-page-title.scss index 8a3f3ca3c..88026ed2e 100644 --- a/imxweb/projects/qbm/src/lib/styles/imx-page-title.scss +++ b/imxweb/projects/qbm/src/lib/styles/imx-page-title.scss @@ -1,17 +1,29 @@ -@import 'variables.scss'; +@import 'base/variables'; -.pageContent h1.mat-headline { - margin: 0 0 40px; - font-size: 26px; - display: flex; - align-items: center; - span{ - white-space: nowrap; - } - @media #{$IMX_Mediaquery_Smartphone} { - margin: 0 0 10px; +.pageContent{ + h2.mat-headline-5 { + margin: 0 0 16px; + font-size: 26px; + display: flex; + align-items: center; + span { + white-space: nowrap; + } + + @media #{$IMX_Mediaquery_Smartphone} { + margin: 0 0 10px; + } + @media #{$IMX_Mediaquery_SmallDesktops} { + margin: 0 0 20px; + } } - @media #{$IMX_Mediaquery_SmallDesktops} { - margin: 0 0 20px; + .imx-header-toolbar{ + display: flex; + justify-content: space-between; + align-items: start; + &-container{ + display:flex; + flex-direction: column; + } } } diff --git a/imxweb/projects/qbm/src/lib/system-info/system-info.service.ts b/imxweb/projects/qbm/src/lib/system-info/system-info.service.ts index dad87cd17..d539d4a2e 100644 --- a/imxweb/projects/qbm/src/lib/system-info/system-info.service.ts +++ b/imxweb/projects/qbm/src/lib/system-info/system-info.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,21 +27,24 @@ import { Injectable } from '@angular/core'; import { imx_SessionService } from '../session/imx-session.service'; -import { ImxConfig, SystemInfo } from 'imx-api-qbm'; -import { CachedPromise } from 'imx-qbm-dbts'; +import { ImxConfig, SystemInfo } from '@imx-modules/imx-api-qbm'; +import { CachedPromise } from '@imx-modules/imx-qbm-dbts'; import { CacheService } from '../cache/cache.service'; /** Service that provides system info. * The service sends only one request per session, the retrieved data is cached. */ @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class SystemInfoService { private systemInfo: CachedPromise; private _imxConfig: CachedPromise; - constructor(private readonly session: imx_SessionService, cacheService: CacheService) { + constructor( + private readonly session: imx_SessionService, + cacheService: CacheService, + ) { this.systemInfo = cacheService.buildCache(() => this.session.Client.imx_system_get()); this._imxConfig = cacheService.buildCache(() => this.session.Client.imx_config_get()); } diff --git a/imxweb/projects/qbm/src/lib/table-image/table-image.service.ts b/imxweb/projects/qbm/src/lib/table-image/table-image.service.ts index ffad6c309..fcf65eefe 100644 --- a/imxweb/projects/qbm/src/lib/table-image/table-image.service.ts +++ b/imxweb/projects/qbm/src/lib/table-image/table-image.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,7 +27,7 @@ import { Injectable } from '@angular/core'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class TableImageService { private readonly cssPrefix = 'imx-table-'; @@ -41,12 +41,10 @@ export class TableImageService { const cssClass = `${this.cssPrefix}${imgId}`; - return (largeImg ? `${cssClass}${this.cssLargeSuffix}` : cssClass); + return largeImg ? `${cssClass}${this.cssLargeSuffix}` : cssClass; } public getDefaultCss(largeImg: boolean = false): string { - return (largeImg ? `${this.defaultCssPrefix}${this.cssLargeSuffix}` : this.defaultCssPrefix); + return largeImg ? `${this.defaultCssPrefix}${this.cssLargeSuffix}` : this.defaultCssPrefix; } - } - diff --git a/imxweb/projects/qbm/src/lib/temp-billboard/temp-billboard.component.ts b/imxweb/projects/qbm/src/lib/temp-billboard/temp-billboard.component.ts deleted file mode 100644 index d5df8c326..000000000 --- a/imxweb/projects/qbm/src/lib/temp-billboard/temp-billboard.component.ts +++ /dev/null @@ -1,139 +0,0 @@ -/* - * ONE IDENTITY LLC. PROPRIETARY INFORMATION - * - * This software is confidential. One Identity, LLC. or one of its affiliates or - * subsidiaries, has supplied this software to you under terms of a - * license agreement, nondisclosure agreement or both. - * - * You may not copy, disclose, or use this software except in accordance with - * those terms. - * - * - * Copyright 2023 One Identity LLC. - * ALL RIGHTS RESERVED. - * - * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR - * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, OR - * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE - * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE - * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING - * THIS SOFTWARE OR ITS DERIVATIVES. - * - */ - -/** - * @copyright One Identity 2023 - * @license All Rights Reserved - */ - -import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; -import { Chart, ChartOptions } from 'billboard.js'; -import { debounceTime, tap } from 'rxjs/operators'; -import { EuiThemeService, EuiTheme } from '@elemental-ui/core'; -import { TempBillboardService } from './temp-billboard.service'; - -/** - * @deprecated This will be removed with A15 support. - */ -@Component({ - selector: 'imx-temp-billboard', - template: '
    ', - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class TempBillboardComponent implements OnDestroy, OnInit { - /** - * You can set billboard.js options with this input, more details see ChartOptions type - * - * @category Input - */ - @Input() - public set options(options: ChartOptions) { - if (!options) { - return; - } - - this._options = options; - } - - /** - * Emit the generated chart to use for advanced functionality - * - * @category Output - * @event - */ - @Output() - public chart = new EventEmitter(); - - /** - * Emit event before chart destroy - * - * @category Output - * @event - */ - @Output() - public beforeDestroy = new EventEmitter(); - - private _chart: Chart | null = null; - private _options: ChartOptions; - private _currentTheme: EuiTheme; - - constructor(private elementRef: ElementRef, private euiThemeService: EuiThemeService, private chartService: TempBillboardService) {} - - /** @internal */ - ngOnInit(): void { - this.euiThemeService - .getActualTheme() - .pipe( - debounceTime(0), - tap((theme: EuiTheme) => { - this._currentTheme = theme; - this.setupChart(); - }) - ) - .subscribe(); - } - - /** @internal */ - ngOnDestroy(): void { - this.beforeDestroy.emit(true); - if (this._chart?.$?.svg) { - this._chart.destroy(); - } - } - - private setupChart(): void { - if (!this._options) { - return; - } - - this.updateColors(); - - if (this._chart !== null) { - this._chart.destroy(); - } - - this._chart = this.chartService.generate({ - bindto: this.elementRef.nativeElement.firstChild, - ...this._options, - }); - - this.chart.emit(this._chart); - } - - private updateColors(): void { - let colors: string[] = ['#0a96d1', '#db2534', '#4ba803', '#ead200', '#b400e5', '#f800b6', '#616566']; - - if (this._currentTheme === EuiTheme.DARK || this._currentTheme === EuiTheme.CONTRAST) { - colors = ['#8acbe3', '#f29d99', '#99c478', '#f2e991', '#d98eed', '#fa96df', '#aab0b3']; - } - - if (!this._options.color) { - this._options.color = {}; - } - - this._options.color.pattern = colors; - } -} diff --git a/imxweb/projects/qbm/src/lib/testing/TestHelperModule.spec.ts b/imxweb/projects/qbm/src/lib/testing/TestHelperModule.spec.ts index 12e14b699..8cdf80e00 100644 --- a/imxweb/projects/qbm/src/lib/testing/TestHelperModule.spec.ts +++ b/imxweb/projects/qbm/src/lib/testing/TestHelperModule.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,10 +28,9 @@ import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core'; import { TranslateModule, TranslateLoader, TranslateParser, TranslateFakeLoader, TranslateDefaultParser } from '@ngx-translate/core'; @NgModule({ - exports: [TranslateModule], - imports: [TranslateModule.forRoot({ loader: { provide: TranslateLoader, useClass: TranslateFakeLoader } })], - providers: [{ provide: TranslateParser, useClass: TranslateDefaultParser }], - schemas: [NO_ERRORS_SCHEMA] + exports: [TranslateModule], + imports: [TranslateModule.forRoot({ loader: { provide: TranslateLoader, useClass: TranslateFakeLoader } })], + providers: [{ provide: TranslateParser, useClass: TranslateDefaultParser }], + schemas: [NO_ERRORS_SCHEMA], }) -export class TestHelperModule { -} +export class TestHelperModule {} diff --git a/imxweb/projects/qbm/src/lib/testing/base-imx-api-mock.spec.ts b/imxweb/projects/qbm/src/lib/testing/base-imx-api-mock.spec.ts index 1fdb85304..a9fc21557 100644 --- a/imxweb/projects/qbm/src/lib/testing/base-imx-api-mock.spec.ts +++ b/imxweb/projects/qbm/src/lib/testing/base-imx-api-mock.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,65 +24,32 @@ * */ -import * as TypeMoq from 'typemoq'; +import { EntityColumnData, EntityData, IEntity, IEntityColumn, IReadValue } from '@imx-modules/imx-qbm-dbts'; -import { - EntityColumnData, - EntityData, - IEntityColumn, - IReadValue, - IEntity -} from 'imx-qbm-dbts'; - -export function CreateIReadValue(value: T, column: IEntityColumn = CreateIEntityColumn((value as unknown) as string)): IReadValue { - const readValueMock = TypeMoq.Mock.ofType>(); - readValueMock.setup((readValue: IReadValue) => readValue.value).returns(() => value); - readValueMock.setup((readValue: IReadValue) => readValue.Column).returns(() => column); - return readValueMock.object; +export function CreateIReadValue(value: T, column: IEntityColumn = CreateIEntityColumn(value as unknown as string)): IReadValue { + return { value, Column: column } as IReadValue; } -export function CreateIEntityColumn(displayValue: string): IEntityColumn { - const mock = TypeMoq.Mock.ofType(); - mock.setup((item: IEntityColumn) => item.GetDisplayValue).returns(() => () => displayValue); - return mock.object; +export function CreateIEntityColumn(displayValue: string | undefined): IEntityColumn { + return { GetDisplayValue: () => displayValue } as IEntityColumn; } export function CreateIEntity(getColumn: (name: string) => IEntityColumn, typeName?: string, keys?: string[]): IEntity { - const mock = TypeMoq.Mock.ofType(); - mock.setup((item: IEntity) => item.GetColumn).returns(() => (name: string) => getColumn(name)); - if (typeName) { - mock.setup(item => item.TypeName).returns(() => typeName); - } - if (keys && keys.length > 0) { - mock.setup(item => item.GetKeys()).returns(() => keys); - } - return mock.object; + return { getColumn: (name: string) => getColumn(name), TypeName: typeName, GetKeys: () => keys } as unknown as IEntity; } export class BaseImxApiDtoMock { public static CreateEntityDataCollection(dataCollection: EntityData[]): EntityData[] { - const result = dataCollection.map(data => { - if (data === null) { - return null; - } - - const mock = TypeMoq.Mock.ofType(); - mock.setup(property => property.Display).returns(() => data.Display); - mock.setup(property => property.LongDisplay).returns(() => data.LongDisplay); - mock.setup(property => property.Keys).returns(() => data.Keys); - mock.setup(property => property.Columns).returns(() => BaseImxApiDtoMock.CreateEntityDataColumnCollection(data.Columns)); - return mock.object; + const result = dataCollection.map((data) => { + return { ...data }; }); return result; } private static CreateEntityDataColumnCollection(columns: { [key: string]: EntityColumnData }): { [key: string]: EntityColumnData } { const entityDataColumns: { [key: string]: EntityColumnData } = {}; - Object.keys(columns).forEach(key => { - const mock = TypeMoq.Mock.ofType(); - mock.setup(property => property.DisplayValue).returns(() => columns[key].DisplayValue); - mock.setup(property => property.Value).returns(() => columns[key].Value); - entityDataColumns[key] = mock.object; + Object.keys(columns).forEach((key) => { + entityDataColumns[key] = { DisplayValue: columns[key].DisplayValue, Value: columns[key].Value }; }); return entityDataColumns; } @@ -90,7 +57,7 @@ export class BaseImxApiDtoMock { export class BaseImxApiDataMock { public static CreateEntityDataCollection(createEntity: (i: number) => TEntityCollection, numOfEntries: number) { - const dataCollection = []; + const dataCollection: TEntityCollection[] = []; for (let i = 1; i <= numOfEntries; i++) { dataCollection.push(createEntity(i)); } diff --git a/imxweb/projects/qbm/src/lib/testing/clear-styles.spec.ts b/imxweb/projects/qbm/src/lib/testing/clear-styles.spec.ts index dc7c4decd..7a8a7aa36 100644 --- a/imxweb/projects/qbm/src/lib/testing/clear-styles.spec.ts +++ b/imxweb/projects/qbm/src/lib/testing/clear-styles.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,11 +24,11 @@ * */ -export function clearStylesFromDOM (): void { +export function clearStylesFromDOM(): void { const head: HTMLHeadElement = document.getElementsByTagName('head')[0]; - const styles : HTMLCollectionOf | [] = head.getElementsByTagName('style'); + const styles: HTMLCollectionOf | [] = head.getElementsByTagName('style'); - for (let i: number; i < styles.length; i++) { + for (let i: number = 0; i < styles.length; i++) { head.removeChild(styles[i]); } } diff --git a/imxweb/projects/qbm/src/lib/testing/dst-mock-help.spec.ts b/imxweb/projects/qbm/src/lib/testing/dst-mock-help.spec.ts index fe62be7d3..63b7a8d9c 100644 --- a/imxweb/projects/qbm/src/lib/testing/dst-mock-help.spec.ts +++ b/imxweb/projects/qbm/src/lib/testing/dst-mock-help.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { ValType, IClientProperty } from "imx-qbm-dbts"; +import { ValType, IClientProperty } from '@imx-modules/imx-qbm-dbts'; export const mockDSTColumns: IClientProperty[] = [ { @@ -46,5 +46,5 @@ export const mockDSTColumns: IClientProperty[] = [ { Type: ValType.String, ColumnName: 'UID_DialogProduct', - } + }, ]; diff --git a/imxweb/projects/qbm/src/lib/testing/entity-column-stub.spec.ts b/imxweb/projects/qbm/src/lib/testing/entity-column-stub.spec.ts index b6f2be5f8..dffe1fe08 100644 --- a/imxweb/projects/qbm/src/lib/testing/entity-column-stub.spec.ts +++ b/imxweb/projects/qbm/src/lib/testing/entity-column-stub.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,21 +24,30 @@ * */ -import { IEntityColumn, DataState, ValType, IValueMetadata, ValueStruct, IEntity, IColumnChangeArgs, IEvent } from 'imx-qbm-dbts'; +import { + DataState, + IColumnChangeArgs, + IEntity, + IEntityColumn, + IEvent, + IValueMetadata, + ValType, + ValueStruct, +} from '@imx-modules/imx-qbm-dbts'; export class EntityColumnStub implements IEntityColumn { GetEntity(): IEntity { - return null; + return {} as IEntity; } ColumnName = 'someColumnName'; - ExtendedProperties: { [id: string]: any; }; + ExtendedProperties: { [id: string]: any }; constructor( private value?: T, private displayValue?: string, - private metadata = { CanEdit: () => true } as IValueMetadata + private metadata = { CanEdit: () => true } as IValueMetadata, ) {} - + ColumnChanged: IEvent; GetDataState(): DataState { @@ -53,16 +62,16 @@ export class EntityColumnStub implements IEntityColumn { return this.metadata; } - GetValue(): T { + GetValue(): T | undefined { return this.value; } GetDisplayValue(): string { - return this.displayValue; + return this.displayValue ?? ''; } async PutValue(value: T): Promise { - await new Promise(resolve => setTimeout(resolve, 1000)); + await new Promise((resolve) => setTimeout(resolve, 1000)); this.value = value; } diff --git a/imxweb/projects/qbm/src/lib/testing/entity-schema-stub.spec.ts b/imxweb/projects/qbm/src/lib/testing/entity-schema-stub.spec.ts index 9986045c8..a91f9b98f 100644 --- a/imxweb/projects/qbm/src/lib/testing/entity-schema-stub.spec.ts +++ b/imxweb/projects/qbm/src/lib/testing/entity-schema-stub.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,8 +24,7 @@ * */ -import { EntitySchema, IClientProperty, ValType } from "imx-qbm-dbts"; - +import { EntitySchema, IClientProperty, ValType } from '@imx-modules/imx-qbm-dbts'; export interface ClientPropertyMock { name: string; @@ -35,19 +34,15 @@ export interface ClientPropertyMock { } export class EntitySchemaStub implements EntitySchema { - - constructor( - properties: ClientPropertyMock[] - ) { + constructor(properties: ClientPropertyMock[]) { properties.forEach( - property => + (property) => (this.Columns[property.name] = { Type: property.type ? property.type : ValType.String, Display: property.Display, - }) + }), ); } public Columns: { [id: string]: IClientProperty } = {}; - } diff --git a/imxweb/projects/qbm/src/lib/tile/tile-variables.scss b/imxweb/projects/qbm/src/lib/tile/tile-variables.scss deleted file mode 100644 index 809739de6..000000000 --- a/imxweb/projects/qbm/src/lib/tile/tile-variables.scss +++ /dev/null @@ -1 +0,0 @@ -$tile-width: 395px; \ No newline at end of file diff --git a/imxweb/projects/qbm/src/lib/tile/tile.component.html b/imxweb/projects/qbm/src/lib/tile/tile.component.html index 55f8b997a..c39d05b7d 100644 --- a/imxweb/projects/qbm/src/lib/tile/tile.component.html +++ b/imxweb/projects/qbm/src/lib/tile/tile.component.html @@ -1,52 +1,60 @@
    - +
    - - - + +
    - - +
    - - - + +
    - - - + + - - - + +
    - +
    - - - - + +
    @@ -54,47 +62,64 @@
    - + -
    +
    -

    - {{caption}} -

    +

    + {{ caption }} +

    - {{subtitle}} + {{ subtitle }}

    -
    - +
    - - + [ngClass]="'imx-generic-tile-value' + (showImageAsValue() && showImageAsIconFont() ? ' imx-generic-tile-iconfont' : '')" + > - - - - {{showImageAsValue() ? ' ' : value}} + + + + {{ showImageAsValue() ? ' ' : value }} diff --git a/imxweb/projects/qbm/src/lib/tile/tile.component.scss b/imxweb/projects/qbm/src/lib/tile/tile.component.scss index d898664b7..f47d1dfa6 100644 --- a/imxweb/projects/qbm/src/lib/tile/tile.component.scss +++ b/imxweb/projects/qbm/src/lib/tile/tile.component.scss @@ -1,12 +1,9 @@ -@import "variables.scss"; -@import "./tile-variables.scss"; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; - +@import 'base/variables'; // TODO: Cleanup. Do we need all classed? .imx-generic-tile { text-align: center; - color: $VI_Common_Color_Font; vertical-align: top; } @@ -22,7 +19,7 @@ font-size: 1.35em; } -.imx-generic-tile h1 { +.imx-generic-tile h3 { display: block; font-size: 0.975em; font-weight: normal; @@ -30,22 +27,22 @@ padding: 0px; } -.imx-generic-tile.square h1 { +.imx-generic-tile.square h3 { display: inline; font-size: 1.05em; } -.imx-generic-tile.overview h1 { +.imx-generic-tile.overview h3 { display: inline; font-size: 1.05em; } -.imx-generic-tile.large-overview h1 { +.imx-generic-tile.large-overview h3 { display: inline; font-size: 1.05em; } -.imx-generic-tile.dashboard h1 { +.imx-generic-tile.dashboard h3 { font-size: 1.15em; font-weight: 600; text-overflow: ellipsis; @@ -61,13 +58,13 @@ color: $black-b; } -section.imx-generic-tile.addon-tile > .mat-card > a { +section.imx-generic-tile.addon-tile > .mat-mdc-card > a { width: 100%; height: 100%; overflow: auto; } -.imx-generic-tile.addon-tile h1 { +.imx-generic-tile.addon-tile h3 { font-size: 1.05em; } @@ -131,53 +128,49 @@ section.imx-generic-tile.addon-tile > .mat-card > a { width: 100%; } -.mat-button { - text-transform: uppercase; -} - -.imx-mobile-nomobile > .mat-button { +.imx-mobile-nomobile > .mat-mdc-button { margin-right: -17px; } -.addon-tile .mat-card { +.addon-tile .mat-mdc-card { height: 120px; - width: $tile-width; + width: $IMX_Tile_Width; padding: 0; cursor: auto; } -.square .mat-card { +.square .mat-mdc-card { height: 7.5em; cursor: auto; } -.overview .mat-card { +.overview .mat-mdc-card { height: 175px; - width: $tile-width; + width: $IMX_Tile_Width; cursor: auto; overflow: auto; padding: 0; } -.dashboard .mat-card { +.dashboard .mat-mdc-card { height: 245px; display: flex; - width: $tile-width; + width: $IMX_Tile_Width; cursor: auto; overflow: auto; padding: 7px; flex-direction: column; } -.large-overview .mat-card { +.large-overview .mat-mdc-card { display: flex; min-height: 415px; - width: $tile-width; + width: $IMX_Tile_Width; cursor: auto; overflow: auto; padding: 0; } -section{ +section { position: relative; } section.imx-generic-tile { @@ -220,7 +213,7 @@ section.imx-generic-tile.large-overview { .imx-generic-tile-value { display: block; text-align: center; - color: $VI_Common_Color_Font; + color: $color-gray-80; font-size: 3em; } @@ -234,7 +227,7 @@ section.imx-generic-tile.large-overview { .overview .imx-generic-tile-value { display: block; text-align: left; - color: $VI_Common_Color_Font; + color: $color-gray-80; font-size: 0.9em; width: 280px; padding: 12px 0px 0px 0px; @@ -247,7 +240,7 @@ section.imx-generic-tile.large-overview { overflow: auto; } -section.imx-generic-tile.dashboard > .mat-card { +section.imx-generic-tile.dashboard > .mat-mdc-card { > div { padding: 6px 18px; } @@ -274,13 +267,13 @@ section.imx-generic-tile.dashboard > .mat-card { overflow: auto; } -.imx-generic-tile a[data-imx-identifier="tile-link"] { +.imx-generic-tile a[data-imx-identifier='tile-link'] { transition-property: all; transition-duration: 0.15s; height: 100%; } -.imx-generic-tile.large-overview a[data-imx-identifier="tile-link"] { +.imx-generic-tile.large-overview a[data-imx-identifier='tile-link'] { height: auto; } @@ -306,15 +299,9 @@ section.imx-generic-tile.dashboard > .mat-card { justify-content: center; background: rgba($color-gray-100, 0.32); border-radius: 4px; - .mat-spinner, - .mat-spinner svg { - max-height: 40px; - max-width: 40px; - } } } - /** TODO (TFS 805997) .highlighted .imx-generic-tile-iconfont { color: $VI_Common_Color_Badge_Warning; @@ -360,7 +347,7 @@ section.imx-generic-tile.dashboard > .mat-card { .IconImage a:link { text-decoration: none; - color: $VI_Common_Color_Font_Secondary; + color: $color-gray-80; outline: none; } diff --git a/imxweb/projects/qbm/src/lib/tile/tile.component.ts b/imxweb/projects/qbm/src/lib/tile/tile.component.ts index 9bb2d3f91..576519891 100644 --- a/imxweb/projects/qbm/src/lib/tile/tile.component.ts +++ b/imxweb/projects/qbm/src/lib/tile/tile.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,15 +24,14 @@ * */ -import { Component, EventEmitter, Input, Output, ContentChild, TemplateRef, ElementRef } from '@angular/core'; +import { Component, ContentChild, ElementRef, EventEmitter, Input, Output, TemplateRef } from '@angular/core'; @Component({ templateUrl: './tile.component.html', selector: 'imx-tile', - styleUrls: ['./tile.component.scss'] + styleUrls: ['./tile.component.scss'], }) export class TileComponent { - @Input() public caption: string; @Input() public subtitle: string; @Input() public actionText = '#LDS#View'; @@ -81,12 +80,11 @@ export class TileComponent { } public showImageInHeader(): boolean { - return this.image - && this.contentType !== 'Image'; + return this.image != null && this.contentType !== 'Image'; } public showImageAsValue(): boolean { - return this.image && this.contentType === 'Image'; + return this.image != null && this.contentType === 'Image'; } public showImageAsIconFont(): boolean { @@ -94,28 +92,26 @@ export class TileComponent { } public styleImage(): string { - let style: string; - if (this.showImageAsValue) { + let style: string = ''; + if (this.showImageAsValue()) { style = 'background-position: center; '; } return style + `background-repeat: no-repeat; background-image: url('${this.urlImage()}');`; } public urlImage(): string { - let firstPart: string; + let firstPart: string = ''; if (this.imageType === 'Url') { firstPart = this.image; } - let size: string; - if (this.showImageAsValue) { + let size: string = ''; + if (this.showImageAsValue()) { size = '&size=Large'; - } else if (this.showImageInHeader) { + } else if (this.showImageInHeader()) { size = '&size=big'; } - return firstPart + encodeURI(this.image) + size; } - } diff --git a/imxweb/projects/qbm/src/lib/tile/tile.module.ts b/imxweb/projects/qbm/src/lib/tile/tile.module.ts index f0abe7139..73e511131 100644 --- a/imxweb/projects/qbm/src/lib/tile/tile.module.ts +++ b/imxweb/projects/qbm/src/lib/tile/tile.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -33,23 +33,9 @@ import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; import { MatButtonModule } from '@angular/material/button'; import { TranslateModule } from '@ngx-translate/core'; - @NgModule({ - declarations: [ - TileComponent, - ChartTileComponent - ], - imports: [ - CommonModule, - MatCardModule, - MatButtonModule, - EuiCoreModule, - TranslateModule, - EuiMaterialModule - ], - exports: [ - ChartTileComponent, - TileComponent - ] + declarations: [TileComponent, ChartTileComponent], + imports: [CommonModule, MatCardModule, MatButtonModule, EuiCoreModule, TranslateModule, EuiMaterialModule], + exports: [ChartTileComponent, TileComponent], }) -export class TileModule { } +export class TileModule {} diff --git a/imxweb/projects/qbm/src/lib/timeline/timeline.component.html b/imxweb/projects/qbm/src/lib/timeline/timeline.component.html index 1f5f67c0a..041265a5d 100644 --- a/imxweb/projects/qbm/src/lib/timeline/timeline.component.html +++ b/imxweb/projects/qbm/src/lib/timeline/timeline.component.html @@ -1,4 +1,4 @@ -
    +
    @@ -11,10 +11,28 @@
    - +
    {{ event.Time }} + + + {{ event.ChangeType }} ({{ event.User }}) - @@ -34,9 +52,9 @@
    - -

    - {{ '#LDS#No data' | translate }} -

    + +
    + {{ (data.length === 0 ? '#LDS#No data' : '#LDS#There were no changes during the selected period.') | translate }} +
    diff --git a/imxweb/projects/qbm/src/lib/timeline/timeline.component.scss b/imxweb/projects/qbm/src/lib/timeline/timeline.component.scss index bcaaf27b7..06a85efdb 100644 --- a/imxweb/projects/qbm/src/lib/timeline/timeline.component.scss +++ b/imxweb/projects/qbm/src/lib/timeline/timeline.component.scss @@ -15,9 +15,12 @@ display: flex; gap: 8px; min-height: 40px; - color: $color-blue-60; + color: $color-gray-80; font-weight: 700; pointer-events: none; + .mat-mdc-card-content{ + padding:0 !important; + } &:hover { .imx-timeline-date { @@ -41,6 +44,9 @@ } } + .imx-timeline-date{ + line-height: 16px; + } .imx-timeline-separator { flex: 0; display: flex; @@ -48,7 +54,7 @@ flex-direction: column; &-marker { - border: 2px solid $color-blue-60; + border: 2px solid $color-gray-80; border-radius: 50%; width: 1rem; height: 1rem; @@ -65,18 +71,21 @@ .imx-timeline-events { font-weight: 600; padding-bottom: 20px; - mat-card { padding: 0; pointer-events: none; &:hover { - box-shadow: 0px 5px 5px 0px rgba(0, 0, 0, 0.20); + box-shadow: 0px 5px 5px 0px rgba(0, 0, 0, 0.2); } .imx-timeline-event { - padding: 1rem; + padding: 0px 16px; pointer-events: auto; + font-size:14px; + display: flex; + align-items: center; + line-height: 38px; &:hover { background-color: $color-orange-10; @@ -92,34 +101,41 @@ } } - &:nth-child(n+2) { - background-image: linear-gradient(to right, $color-gray-10 33%, rgba(255,255,255,0) 0%); + &:nth-child(n + 2) { + background-image: linear-gradient(to right, $color-gray-10 33%, rgba(255, 255, 255, 0) 0%); background-position: top; background-size: 13px 1px; background-repeat: repeat-x; } &-time { - padding-right: 1rem; + padding-right: 8px; + font-weight: 700; } &-display { font-weight: 400; } + .imx-timeline-icon{ + font-size: 20px !important; + padding-right: 8px; + &-edit{ + color: $color-blue-60; + } + &-add{ + color: $color-green-60; + } + &-remove{ + color: $color-red-60; + } + } } } } } - .imx-no-results { - height: 100%; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - - .eui-icon { - color: $color-gray-10; + div { + font-size: 18px; } } } @@ -132,7 +148,7 @@ background-color: $color-blue-80; .imx-timeline-events-group { - color: $color-blue-40; + color: $color-gray-2; &:hover { .imx-timeline-date { @@ -167,8 +183,8 @@ background-color: $color-gray-80; } - &:nth-child(n+2) { - background-image: linear-gradient(to right, $color-gray-60 33%, rgba(255,255,255,0) 0%); + &:nth-child(n + 2) { + background-image: linear-gradient(to right, $color-gray-60 33%, rgba(255, 255, 255, 0) 0%); background-position: top; background-size: 13px 1px; background-repeat: repeat-x; @@ -176,13 +192,6 @@ } } } - - .imx-no-results { - .eui-icon { - color: $color-gray-20; - } - } } } } - diff --git a/imxweb/projects/qbm/src/lib/timeline/timeline.component.ts b/imxweb/projects/qbm/src/lib/timeline/timeline.component.ts index 14a18ef70..d74ea7f13 100644 --- a/imxweb/projects/qbm/src/lib/timeline/timeline.component.ts +++ b/imxweb/projects/qbm/src/lib/timeline/timeline.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,22 +24,23 @@ * */ -import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; -import { TimelineEventsGroupedByDate, ExtendedObjectHistoryEvent } from './timeline'; +import { Component, Input, OnChanges, OnInit } from '@angular/core'; import moment from 'moment-timezone'; -import { ObjectHistoryService } from '../object-history/object-history.service'; +import { ExtendedObjectHistoryEvent, TimelineEventsGroupedByDate } from './timeline'; +import { EventChangeTypes, HistoryEventChangeType } from './timeline.model'; @Component({ selector: 'imx-timeline', templateUrl: './timeline.component.html', - styleUrls: ['./timeline.component.scss'] + styleUrls: ['./timeline.component.scss'], }) export class TimelineComponent implements OnInit, OnChanges { - @Input() public data: ExtendedObjectHistoryEvent[]; + @Input() public data: ExtendedObjectHistoryEvent[] = []; public eventsGroupedByDate: TimelineEventsGroupedByDate[] = []; + public eventChangeTypes = EventChangeTypes; - constructor() { } + constructor() {} /** * Inits table @@ -62,19 +63,23 @@ export class TimelineComponent implements OnInit, OnChanges { this.eventsGroupedByDate = []; this.data.forEach((elem) => { - const date = moment(elem.ChangeTime).format("L"); + const date = moment(elem.ChangeTime).format('L'); - elem.Time = moment(elem.ChangeTime).format("HH:mm:ss"); + elem.Time = moment(elem.ChangeTime).format('HH:mm:ss'); if (this.eventsGroupedByDate.some((event) => event.date === date)) { - const dateEvents = this.eventsGroupedByDate.find((event) => event.date === date).events; + const dateEvents = this.eventsGroupedByDate?.find((event) => event.date === date)?.events ?? []; dateEvents.push(elem); dateEvents.sort((a, b) => b.Time.localeCompare(a.Time)); } else { this.eventsGroupedByDate.push({ date: date, - events: [elem] + events: [elem], }); } }); } + + public getEventChangeType(event: ExtendedObjectHistoryEvent): EventChangeTypes { + return HistoryEventChangeType[event.ChangeTypeId || '']; + } } diff --git a/imxweb/projects/qbm/src/lib/timeline/timeline.model.ts b/imxweb/projects/qbm/src/lib/timeline/timeline.model.ts new file mode 100644 index 000000000..bd4f91ca8 --- /dev/null +++ b/imxweb/projects/qbm/src/lib/timeline/timeline.model.ts @@ -0,0 +1,53 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +export enum EventChangeTypes { + Edit, + Add, + Remove, +} +export type EventChangeType = EventChangeTypes.Add | EventChangeTypes.Edit | EventChangeTypes.Remove; + +export enum HistoryEventChangeType { + 'Object created' = EventChangeTypes.Edit, + 'PropertyChange' = EventChangeTypes.Edit, + 'AddResponsibility' = EventChangeTypes.Add, + 'AddMembership' = EventChangeTypes.Add, + 'Entitlement has been added' = EventChangeTypes.Add, + 'Rule violation resolved' = EventChangeTypes.Add, + 'Rule violation detected' = EventChangeTypes.Remove, + 'Entitlement removed' = EventChangeTypes.Remove, + 'Entitlement has been removed' = EventChangeTypes.Remove, + 'Membership removed' = EventChangeTypes.Remove, + 'Object deleted' = EventChangeTypes.Remove, + 'AddAccount' = EventChangeTypes.Add, + 'AddPermission' = EventChangeTypes.Add, + 'RemoveNonCompliance' = EventChangeTypes.Add, + 'AddNonCompliance' = EventChangeTypes.Remove, + 'RemoveResponsibility' = EventChangeTypes.Remove, + 'RemovePermission' = EventChangeTypes.Remove, + 'RemoveAccount' = EventChangeTypes.Remove, +} diff --git a/imxweb/projects/qbm/src/lib/timeline/timeline.ts b/imxweb/projects/qbm/src/lib/timeline/timeline.ts index b63128460..ccd5df53a 100644 --- a/imxweb/projects/qbm/src/lib/timeline/timeline.ts +++ b/imxweb/projects/qbm/src/lib/timeline/timeline.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { ObjectHistoryEvent } from 'imx-qbm-dbts'; +import { ObjectHistoryEvent } from '@imx-modules/imx-qbm-dbts'; export interface TimelineDateTimeFilter { date: string; diff --git a/imxweb/projects/qbm/src/lib/translation-editor/translation-editor.component.html b/imxweb/projects/qbm/src/lib/translation-editor/translation-editor.component.html index 3b6c9648c..765bdc58a 100644 --- a/imxweb/projects/qbm/src/lib/translation-editor/translation-editor.component.html +++ b/imxweb/projects/qbm/src/lib/translation-editor/translation-editor.component.html @@ -1,20 +1,17 @@ - -

    {{ '#LDS#Heading Edit Translations' | translate}}

    +

    {{ '#LDS#Heading Edit Translations' | translate }}

    - - - {{getDisplay(column)}} {{item.value}} - + + + {{ getDisplay(column) }} {{ item.value }} +
    -
    - - -
    \ No newline at end of file +
    diff --git a/imxweb/projects/qbm/src/lib/translation-editor/translation-editor.component.scss b/imxweb/projects/qbm/src/lib/translation-editor/translation-editor.component.scss index 4afbf24f5..ff8b92611 100644 --- a/imxweb/projects/qbm/src/lib/translation-editor/translation-editor.component.scss +++ b/imxweb/projects/qbm/src/lib/translation-editor/translation-editor.component.scss @@ -1,12 +1,10 @@ + +@import 'base/mixins'; :host { - display: flex; - flex-direction: column; - height: inherit; - } - - .mat-dialog-content { - flex: 1; - display: flex; - flex-direction: column; - height: inherit; - } \ No newline at end of file + @include flex-column-container($height: inherit); +} + +.mat-mdc-dialog-content { + @include flex-column-container($height: inherit); + flex: 1; +} diff --git a/imxweb/projects/qbm/src/lib/translation-editor/translation-editor.component.ts b/imxweb/projects/qbm/src/lib/translation-editor/translation-editor.component.ts index 891adba86..37c1c07a4 100644 --- a/imxweb/projects/qbm/src/lib/translation-editor/translation-editor.component.ts +++ b/imxweb/projects/qbm/src/lib/translation-editor/translation-editor.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,30 +25,28 @@ */ import { Component, ErrorHandler, Inject, OnInit } from '@angular/core'; -import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { EuiLoadingService } from '@elemental-ui/core'; -import { ReadWriteExtTypedEntity, TranslationDataRead, TranslationDataWrite, TranslationDataWriteElement } from 'imx-qbm-dbts'; +import { ReadWriteExtTypedEntity, TranslationDataRead, TranslationDataWrite, TranslationDataWriteElement } from '@imx-modules/imx-qbm-dbts'; import { SnackBarService } from '../snackbar/snack-bar.service'; @Component({ selector: 'imx-translation-editor', templateUrl: './translation-editor.component.html', - styleUrls: ['./translation-editor.component.scss'] + styleUrls: ['./translation-editor.component.scss'], }) export class TranslationEditorComponent implements OnInit { - - public translationData: TranslationDataRead; - public translationDataWrite: TranslationDataWrite={}; + public translationData: TranslationDataRead | undefined; + public translationDataWrite: TranslationDataWrite = {}; constructor( - @Inject(MAT_DIALOG_DATA) public data: ReadWriteExtTypedEntity<{ Translations?: TranslationDataRead}, TranslationDataWrite>, + @Inject(MAT_DIALOG_DATA) public data: ReadWriteExtTypedEntity<{ Translations?: TranslationDataRead }, TranslationDataWrite>, private readonly snackbar: SnackBarService, private readonly busyService: EuiLoadingService, private readonly errorHandler: ErrorHandler, public dialogRef: MatDialogRef, - ) { - } + ) {} public ngOnInit(): void { if (this.data && this.data.extendedDataRead) { @@ -56,62 +54,63 @@ export class TranslationEditorComponent implements OnInit { this.translationDataWrite.Translations = []; } - if (this.translationData.Translations[0]) { - Object.keys(this.translationData.Translations[0]).forEach( - key => { - this.translationData.Translations[0][key].forEach( - data=>{ - let translationDataWriteElement:TranslationDataWriteElement = {}; - translationDataWriteElement.ColumnName = key; - translationDataWriteElement.TranslationKey = this.data.GetEntity().GetColumn(key).GetValue(); - translationDataWriteElement.TranslationValue = data.Translation; - translationDataWriteElement.UidCulture = data.LanguageId; - this.translationDataWrite.Translations.push(translationDataWriteElement); - } - ); - } - ); + if (this.translationData?.Translations?.[0]) { + Object.keys(this.translationData?.Translations?.[0]).forEach((key) => { + this.translationData?.Translations?.[0][key].forEach((data) => { + let translationDataWriteElement: TranslationDataWriteElement = {}; + translationDataWriteElement.ColumnName = key; + translationDataWriteElement.TranslationKey = this.data.GetEntity().GetColumn(key).GetValue(); + translationDataWriteElement.TranslationValue = data.Translation; + translationDataWriteElement.UidCulture = data.LanguageId; + this.translationDataWrite?.Translations?.push(translationDataWriteElement); + }); + }); } } - public getDisplay(column:string) : string{ + public getDisplay(column: string): string { return this.data.GetEntity().GetColumn(column).GetMetadata().GetDisplay(); } - public getValue(columnName: string, uidCulture) : string { - let value = ""; - if (this.translationData.Translations[0]) { - let matchedData = this.translationData.Translations[0][columnName]; + public getValue(columnName: string, uidCulture): string { + let value = ''; + if (this.translationData?.Translations?.[0]) { + let matchedData = this.translationData?.Translations?.[0][columnName]; if (matchedData) { - value = matchedData.find(x => x.LanguageId == uidCulture) - ? matchedData.find(x=>x.LanguageId == uidCulture).Translation - : ""; + value = matchedData.find((x) => x.LanguageId == uidCulture)?.Translation ?? ''; } } return value; } - public onInput(translationValue: string, columnName: string, uidCulture: string): void { - if (this.translationDataWrite && this.translationDataWrite.Translations - && this.translationDataWrite.Translations.find(x => x.ColumnName === columnName && x.UidCulture === uidCulture)) { - let matchedElement = this.translationDataWrite.Translations.find(x => x.ColumnName === columnName && x.UidCulture === uidCulture); - matchedElement.TranslationValue = translationValue; - } - else { + public onInput(translationValueTarget: Event, columnName: string, uidCulture: string): void { + const translationValue = (translationValueTarget.target as any)?.value; + if ( + this.translationDataWrite && + this.translationDataWrite.Translations && + this.translationDataWrite.Translations.find((x) => x.ColumnName === columnName && x.UidCulture === uidCulture) + ) { + let matchedElement = this.translationDataWrite.Translations.find((x) => x.ColumnName === columnName && x.UidCulture === uidCulture); + if (matchedElement) { + matchedElement.TranslationValue = translationValue; + } + } else { let translationDataWriteElement: TranslationDataWriteElement = {}; translationDataWriteElement.ColumnName = columnName; translationDataWriteElement.TranslationKey = this.data.GetEntity().GetColumn(columnName).GetValue(); translationDataWriteElement.TranslationValue = translationValue; translationDataWriteElement.UidCulture = uidCulture; - this.translationDataWrite.Translations.push(translationDataWriteElement); + this.translationDataWrite?.Translations?.push(translationDataWriteElement); } } - public async save():Promise{ - const overlayRef = this.busyService.show(); + public async save(): Promise { + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } try { await this.data.setExtendedData({ - Translations: this.translationDataWrite.Translations + Translations: this.translationDataWrite.Translations, }); await this.data.GetEntity().Commit(); @@ -119,9 +118,8 @@ export class TranslationEditorComponent implements OnInit { } catch (error) { this.errorHandler.handleError(error); } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); this.dialogRef.close(); } } - } diff --git a/imxweb/projects/qbm/src/lib/translation/imx-missing-translation-handler.spec.ts b/imxweb/projects/qbm/src/lib/translation/imx-missing-translation-handler.spec.ts index 781581375..0810d7eeb 100644 --- a/imxweb/projects/qbm/src/lib/translation/imx-missing-translation-handler.spec.ts +++ b/imxweb/projects/qbm/src/lib/translation/imx-missing-translation-handler.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,27 +27,31 @@ import { ImxMissingTranslationHandler } from './imx-missing-translation-handler'; describe('imx_MissingTranslationHandler', () => { - [ - { - text: 'text', - expected: 'text' - }, - { - text: '#LDS#text', - expected: 'text' - }, - { - text: '#LDS##LDS#text', - expected: '#LDS#text' - }, - { - text: 'text#LDS#', - expected: 'text#LDS#' - } - ].forEach(testcase => it('should provide a handle method', () => { - expect(new ImxMissingTranslationHandler().handle({ - key: testcase.text, - translateService: null - })).toEqual(testcase.expected); - })); + [ + { + text: 'text', + expected: 'text', + }, + { + text: '#LDS#text', + expected: 'text', + }, + { + text: '#LDS##LDS#text', + expected: '#LDS#text', + }, + { + text: 'text#LDS#', + expected: 'text#LDS#', + }, + ].forEach((testcase) => + it('should provide a handle method', () => { + expect( + new ImxMissingTranslationHandler().handle({ + key: testcase.text, + translateService: null, + }), + ).toEqual(testcase.expected); + }), + ); }); diff --git a/imxweb/projects/qbm/src/lib/translation/imx-missing-translation-handler.ts b/imxweb/projects/qbm/src/lib/translation/imx-missing-translation-handler.ts index b64ffb7dd..e12d05049 100644 --- a/imxweb/projects/qbm/src/lib/translation/imx-missing-translation-handler.ts +++ b/imxweb/projects/qbm/src/lib/translation/imx-missing-translation-handler.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,8 +28,6 @@ import { MissingTranslationHandler, MissingTranslationHandlerParams } from '@ngx export class ImxMissingTranslationHandler implements MissingTranslationHandler { public handle(params: MissingTranslationHandlerParams): string { - return params.key.startsWith('#LDS#') - ? params.key.substring('#LDS#'.length) - : params.key; + return params.key.startsWith('#LDS#') ? params.key.substring('#LDS#'.length) : params.key; } } diff --git a/imxweb/projects/qbm/src/lib/translation/imx-translate-loader.ts b/imxweb/projects/qbm/src/lib/translation/imx-translate-loader.ts index db42421e6..7599e4d97 100644 --- a/imxweb/projects/qbm/src/lib/translation/imx-translate-loader.ts +++ b/imxweb/projects/qbm/src/lib/translation/imx-translate-loader.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,7 +26,7 @@ import { Observable, from } from 'rxjs'; import { Injectable } from '@angular/core'; -import { TranslateLoader} from '@ngx-translate/core'; +import { TranslateLoader } from '@ngx-translate/core'; import { imx_SessionService } from '../session/imx-session.service'; @Injectable() @@ -40,7 +40,7 @@ export class ImxTranslateLoader implements TranslateLoader { private async getCaptionsLds(culture: string): Promise<{ [key: string]: string }> { const translations = await this.session.Client.imx_multilanguage_getcaptions_get({ cultureName: culture }); const translationsLds: { [key: string]: string } = {}; - Object.keys(translations).forEach((key: string) => translationsLds['#LDS#' + key] = translations[key]); + Object.keys(translations).forEach((key: string) => (translationsLds['#LDS#' + key] = translations[key])); return translationsLds; } } diff --git a/imxweb/projects/qbm/src/lib/translation/imx-translation-provider.service.ts b/imxweb/projects/qbm/src/lib/translation/imx-translation-provider.service.ts index a734a42f7..01969a229 100644 --- a/imxweb/projects/qbm/src/lib/translation/imx-translation-provider.service.ts +++ b/imxweb/projects/qbm/src/lib/translation/imx-translation-provider.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,18 +26,19 @@ import { Injectable } from '@angular/core'; import { DateAdapter } from '@angular/material/core'; +import { DefaultServiceResolver, EntitySchema, ITranslationProvider, MultiLanguageStringData } from '@imx-modules/imx-qbm-dbts'; import { TranslateService } from '@ngx-translate/core'; -import { ITranslationProvider, MultiLanguageStringData, EntitySchema, DefaultServiceResolver } from 'imx-qbm-dbts'; +import moment from 'moment-timezone'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -import moment from 'moment-timezone'; -import { TextContainer } from './text-container'; -import { LdsReplacePipe } from '../lds-replace/lds-replace.pipe'; +import { Router } from '@angular/router'; import { AppConfigService } from '../appConfig/appConfig.service'; +import { LdsReplacePipe } from '../lds-replace/lds-replace.pipe'; +import { TextContainer } from './text-container'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ImxTranslationProviderService implements ITranslationProvider { private multilanguageTranslationDict: { [key: string]: { [key: string]: string } } = {}; @@ -48,7 +49,7 @@ export class ImxTranslationProviderService implements ITranslationProvider { private appConfig: AppConfigService, private translateService: TranslateService, private readonly ldsReplace: LdsReplacePipe, - private readonly dateAdapter: DateAdapter + private readonly dateAdapter: DateAdapter, ) {} public get Culture(): string { @@ -59,26 +60,27 @@ export class ImxTranslationProviderService implements ITranslationProvider { return this.cultureFormat; } - public async init(culture: string = this.translateService.getBrowserCultureLang(), cultureFormat: string = this.translateService.getBrowserCultureLang()): Promise { - //Use more specific culture, if de is provided (used for help documents) - if(culture == 'de') { - culture = 'de-DE'; - } + public async init( + culture: string | undefined = this.translateService.getBrowserCultureLang(), + cultureFormat: string | undefined = this.translateService.getBrowserCultureLang(), + ): Promise { const defaultLang = this.translateService.getDefaultLang(); // Get filtered cultures that are available to frontends and set to english if culture (browser language) is not supported - const cultures = await this.appConfig.client.imx_multilanguage_uicultures_get({filter: [{ColumnName: 'Ident_DialogCulture', Value1: culture}]}); - if(cultures.TotalCount === 0){ + const cultures = await this.appConfig.client.imx_multilanguage_uicultures_get({ + filter: [{ ColumnName: 'Ident_DialogCulture', Value1: culture }], + }); + if (cultures.TotalCount === 0) { culture = 'en-US'; } - - if (defaultLang == null || defaultLang !== culture) { + + if (culture && defaultLang !== culture) { this.translateService.setDefaultLang(culture); } - if (this.translateService.currentLang == null || this.translateService.currentLang !== culture) { + if (culture && this.translateService.currentLang !== culture) { await this.translateService.use(culture).toPromise(); } - this.cultureFormat = cultureFormat; + this.cultureFormat = cultureFormat ?? ''; this.dateAdapter.setLocale(this.cultureFormat); moment.locale(this.cultureFormat); @@ -86,10 +88,10 @@ export class ImxTranslationProviderService implements ITranslationProvider { return; } - this.culture = culture; + this.culture = culture ?? 'en-US'; this.multilanguageTranslationDict = await this.appConfig.client.imx_multilanguage_translations_get('all', { - cultureName: this.culture + cultureName: this.culture, }); // use this translator as the default in dbts @@ -99,9 +101,33 @@ export class ImxTranslationProviderService implements ITranslationProvider { await this.appConfig.client.loadSchema(culture); } + /** + * Reinit when language is changed from the profile settings + * @param culture language to reinit with + * @param cultureFormat date formating to reinit with + */ + public async reinit(culture: string, cultureFormat: string, router: Router) { + this.culture = culture; + this.cultureFormat = cultureFormat; + await this.translateService.use(this.culture).toPromise(); + this.dateAdapter.setLocale(this.cultureFormat); + moment.locale(this.cultureFormat); + this.multilanguageTranslationDict = await this.appConfig.client.imx_multilanguage_translations_get('all', { + cultureName: this.culture, + }); + DefaultServiceResolver.UseTranslator(this); + await this.appConfig.client.loadSchema(this.culture); + + // We may have stale data or schemas from the current page, so we nav back to refresh existing components + let currentUrl = router.url; + router.navigateByUrl('/', { skipLocationChange: true }).then(() => { + router.navigate([currentUrl]); + }); + } + public GetTranslation(key: MultiLanguageStringData): string { - const uidColumn = this.multilanguageTranslationDict[key.UidColumn]; - return uidColumn ? uidColumn[key.Key] : key.Key; + const uidColumn = this.multilanguageTranslationDict[key.UidColumn ?? '']; + return (uidColumn ? uidColumn[key.Key ?? ''] : key.Key) ?? ''; } public Translate(text: TextContainer | string): Observable { @@ -112,7 +138,8 @@ export class ImxTranslationProviderService implements ITranslationProvider { const translation = this.translateService.get(text.key); if (text.parameters) { - return translation.pipe(map((translatedValue: any) => this.ldsReplace.transform(translatedValue, ...text.parameters))); + const params = text.parameters ?? []; + return translation.pipe(map((translatedValue: any) => this.ldsReplace.transform(translatedValue, ...params))); } return translation; @@ -130,10 +157,9 @@ export class ImxTranslationProviderService implements ITranslationProvider { return column.Display; } - public async GetCultures(): Promise{ - if(Object.keys(this.multilanguageTranslationDict).length === 0){ + public async GetCultures(): Promise { + if (Object.keys(this.multilanguageTranslationDict).length === 0) { await this.init(this.culture, this.cultureFormat); } } - } diff --git a/imxweb/projects/qbm/src/lib/translation/text-container.ts b/imxweb/projects/qbm/src/lib/translation/text-container.ts index b7dd02700..4565ed38c 100644 --- a/imxweb/projects/qbm/src/lib/translation/text-container.ts +++ b/imxweb/projects/qbm/src/lib/translation/text-container.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qbm/src/lib/treeTable/MatColumn.html b/imxweb/projects/qbm/src/lib/treeTable/MatColumn.html index 8c28133f8..fc5961e88 100644 --- a/imxweb/projects/qbm/src/lib/treeTable/MatColumn.html +++ b/imxweb/projects/qbm/src/lib/treeTable/MatColumn.html @@ -1,18 +1,21 @@ - - - + + + - -
    {{title || field}}
    -
    - -
    - - - - - -
    {{getData(data,index)}}
    - - \ No newline at end of file + +
    {{title || field}}
    + + + + + + +
    {{getData(data,index)}}
    +
    + diff --git a/imxweb/projects/qbm/src/lib/treeTable/MatColumn.scss b/imxweb/projects/qbm/src/lib/treeTable/MatColumn.scss index 4eba72437..ec22173c1 100644 --- a/imxweb/projects/qbm/src/lib/treeTable/MatColumn.scss +++ b/imxweb/projects/qbm/src/lib/treeTable/MatColumn.scss @@ -1,3 +1,3 @@ .imx-small-right-margin { margin-right: 1em; -} \ No newline at end of file +} diff --git a/imxweb/projects/qbm/src/lib/treeTable/MatColumn.ts b/imxweb/projects/qbm/src/lib/treeTable/MatColumn.ts index fa9b66335..e123afcd1 100644 --- a/imxweb/projects/qbm/src/lib/treeTable/MatColumn.ts +++ b/imxweb/projects/qbm/src/lib/treeTable/MatColumn.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,10 +24,7 @@ * */ -import { - Component, Input, OnDestroy, OnInit, Optional, ViewChild, - ContentChild, TemplateRef, ElementRef, Output, EventEmitter -} from '@angular/core'; +import { Component, ContentChild, EventEmitter, Input, OnDestroy, OnInit, Optional, Output, TemplateRef, ViewChild } from '@angular/core'; import { MatSortHeader } from '@angular/material/sort'; import { MatColumnDef, MatTable } from '@angular/material/table'; import { DomSanitizer, SafeStyle } from '@angular/platform-browser'; @@ -36,17 +33,19 @@ import { ImxExpandableItem } from './imx-data-source'; @Component({ selector: 'imx-column', templateUrl: './MatColumn.html', - styleUrls: ['./MatColumn.scss'] + styleUrls: ['./MatColumn.scss'], }) export class ImxMatColumnComponent implements OnDestroy, OnInit { @Input() - get field(): string { return this.name; } + get field(): string { + return this.name; + } set field(name: string) { this.name = name; } @Input() public title: string; - @Input() public dataAccessor: ((data: T, index: number, name: string) => string); + @Input() public dataAccessor: (data: T, index: number, name: string) => string; @Input() public align: 'before' | 'after' = 'before'; @@ -54,30 +53,37 @@ export class ImxMatColumnComponent implements OnDestroy, OnInit { @ViewChild(MatSortHeader) public sortHeader: MatSortHeader; - @ContentChild('imxCellTemplate', { static: true }) public cellTemplate: TemplateRef; - @ContentChild('imxHeaderTemplate', { static: true }) public headerTemplate: TemplateRef; + @ContentChild('imxCellTemplate', { static: true }) public cellTemplate: TemplateRef; + @ContentChild('imxHeaderTemplate', { static: true }) public headerTemplate: TemplateRef; @Input() public class = 'imx-normalCell'; @Output() public itemExpanded = new EventEmitter>(); @Output() public itemCollapsed = new EventEmitter>(); @Input() public isFirstColumn = true; - public hasChildrenProvider: ((data: T) => boolean); + public hasChildrenProvider: (data: T) => boolean; private name: string; - constructor(private sanitizer: DomSanitizer, @Optional() public table: MatTable) { } + constructor( + private sanitizer: DomSanitizer, + @Optional() public table: MatTable, + ) {} public ButtonClass(data: ImxExpandableItem): string { - if (data.data == null) { return 'k-icon k-i-collapse'; } + if (data.data == null) { + return 'k-icon k-i-collapse'; + } const res = data.level === 0 || (this.hasChildrenProvider ? this.hasChildrenProvider(data.data) : false); - return !res ? 'imx-small-right-margin k-sprite' : data.isExpanded - ? 'imx-small-right-margin cux-icon cux-icon--caret-down' // TODO replace cux-icon (TFS 806274) - : 'imx-small-right-margin cux-icon cux-icon--caret-right'; // TODO replace cux-icon (TFS 806274) + return !res + ? 'imx-small-right-margin k-sprite' + : data.isExpanded + ? 'imx-small-right-margin cux-icon cux-icon--caret-down' // TODO replace cux-icon (TFS 806274) + : 'imx-small-right-margin cux-icon cux-icon--caret-right'; // TODO replace cux-icon (TFS 806274) } public getMargin(data: any): SafeStyle { - return this.sanitizer.bypassSecurityTrustStyle((data.level ? data.level : .0) * 20 + 'px'); + return this.sanitizer.bypassSecurityTrustStyle((data.level ? data.level : 0.0) * 20 + 'px'); } public ngOnInit(): void { @@ -97,7 +103,6 @@ export class ImxMatColumnComponent implements OnDestroy, OnInit { return this.dataAccessor ? this.dataAccessor(data.data, index, this.field) : (data.data as any)[this.field]; } - public buttonClicked(data: ImxExpandableItem): void { data.isExpanded = !data.isExpanded; if (data.isExpanded) { diff --git a/imxweb/projects/qbm/src/lib/treeTable/imx-data-source.ts b/imxweb/projects/qbm/src/lib/treeTable/imx-data-source.ts index 167b51602..2224a704d 100644 --- a/imxweb/projects/qbm/src/lib/treeTable/imx-data-source.ts +++ b/imxweb/projects/qbm/src/lib/treeTable/imx-data-source.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -32,10 +32,10 @@ export abstract class ImxDataSource extends DataSource { public data: T[]; public expandableData: ImxExpandableItem[]; - public hasChildrenProvider: ((data: T) => boolean); + public hasChildrenProvider: (data: T) => boolean; - public itemsProvider: (() => Promise); - public childItemsProvider: ((item: T) => Promise); + public itemsProvider: () => Promise; + public childItemsProvider: (item: T) => Promise; private rootItem: ImxExpandableItem; @@ -53,8 +53,8 @@ export abstract class ImxDataSource extends DataSource { throw new Error('The accessor "itemsProvider" is undefined '); } const items = this.itemsProvider(); - items.then(elements => { - const rows = this.getRows(elements, null, 1 ); + items.then((elements) => { + const rows = this.getRows(elements, null, 1); this.data = elements; this.expandableData = rows; this.itemCount = elements.length; @@ -71,15 +71,15 @@ export abstract class ImxDataSource extends DataSource { } const items = this.childItemsProvider(parent.data); - items.then(elements => { + items.then((elements) => { const newRows = this.getRows(elements, parent, parent.level + 1); const newRowSet: ImxExpandableItem[] = []; - this.shownItems.forEach(element => { + this.shownItems.forEach((element) => { if (element !== parent) { newRowSet.push(element); } else { newRowSet.push(element); - newRows.forEach(ele => { + newRows.forEach((ele) => { newRowSet.push(ele); if (expand) { ele.isExpanded = true; @@ -96,7 +96,7 @@ export abstract class ImxDataSource extends DataSource { public RemoveChildItems(parent: ImxExpandableItem) { const newRowSet: ImxExpandableItem[] = []; - this.shownItems.forEach(element => { + this.shownItems.forEach((element) => { if (!this.ElementHasParent(element, parent)) { newRowSet.push(element); } @@ -107,21 +107,27 @@ export abstract class ImxDataSource extends DataSource { } public ExpandRoot(): any { - const root = this.shownItems.find(el => el.isRoot); + const root = this.shownItems.find((el) => el.isRoot); + if (root == null) { + return; + } root.isExpanded = true; this.shownItems.push(new ImxExpandableItem(root.data, root, 1)); this._dataSubject.next(this.shownItems); } public CollapseRoot(): any { - const root = this.shownItems.find(el => el.isRoot); + const root = this.shownItems.find((el) => el.isRoot); + if (root == null) { + return; + } root.isExpanded = false; this.shownItems = [root]; this._dataSubject.next(this.shownItems); } public ExpandAll(): void { - this.shownItems.forEach(el => { + this.shownItems.forEach((el) => { if (!el.isExpanded) { el.isExpanded = true; this.LoadChildItems(el, true); @@ -130,48 +136,57 @@ export abstract class ImxDataSource extends DataSource { } public ElementHasParent(element: ImxExpandableItem, parent: ImxExpandableItem): boolean { - if (element.parent === parent) { return true; } + if (element.parent === parent) { + return true; + } const newElemen = element.parent; return newElemen && newElemen != null ? this.ElementHasParent(newElemen, parent) : false; } - public getRows(items: T[], parent: ImxExpandableItem, level: number): ImxExpandableItem[] { + public getRows(items: T[], parent: ImxExpandableItem | null, level: number): ImxExpandableItem[] { const ret: ImxExpandableItem[] = []; - items.forEach(element => { + items.forEach((element) => { ret.push(new ImxExpandableItem(element, parent, level)); }); return ret; } public disconnect(): void { - this.subscriptions.forEach(subscription => subscription.unsubscribe()); + this.subscriptions.forEach((subscription) => subscription.unsubscribe()); } public connect(): Observable { - this.subscriptions.push(this._dataSubject.subscribe( - (res: any) => { + this.subscriptions.push( + this._dataSubject.subscribe((res: any) => { this.data = res; - } - )); + }), + ); return this._dataSubject; } public SetRoot(specialElement: T) { - if (!specialElement) { return; } + if (!specialElement) { + return; + } this.shownItems = []; this.rootItem = new ImxExpandableItem(specialElement, null, 0); this.rootItem.isRoot = true; this.shownItems.push(this.rootItem); - const root = this.expandableData.find(el => el.data === specialElement); - this.shownItems.push(root); + const root = this.expandableData.find((el) => el.data === specialElement); + if (root != null) { + this.shownItems.push(root); + } this._dataSubject.next(this.shownItems); } } export class ImxExpandableItem { - public isExpanded = false; public isRoot = false; - constructor(public data: T, public parent: ImxExpandableItem, public level: number) { } + constructor( + public data: T, + public parent: ImxExpandableItem | null, + public level: number, + ) {} } diff --git a/imxweb/projects/qbm/src/lib/treeTable/tableTestClasses.spec.ts b/imxweb/projects/qbm/src/lib/treeTable/tableTestClasses.spec.ts index ea18497f7..c408bdceb 100644 --- a/imxweb/projects/qbm/src/lib/treeTable/tableTestClasses.spec.ts +++ b/imxweb/projects/qbm/src/lib/treeTable/tableTestClasses.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -49,7 +49,7 @@ export class Test extends ImxDataSource { itemsProvider = () => { return this.buildTree(this.startUid); - } + }; childItemsProvider = (item: any) => { const val1 = this.buildTree(item.UID_JobError); @@ -59,14 +59,13 @@ export class Test extends ImxDataSource { return [].concat.apply([], arrayOfArrays); }); return ret; - } + }; hasChildrenProvider = (data: any) => { - return (data.UID_JobError !== null && data.UID_JobError !== '') - && (data.UID_JobSuccess != null && data.UID_JobSuccess !== ''); - } + return data.UID_JobError !== null && data.UID_JobError !== '' && data.UID_JobSuccess != null && data.UID_JobSuccess !== ''; + }; - private buildTree(uidstart: string): Promise<{ [key: string]: any; }[]> { + private buildTree(uidstart: string): Promise<{ [key: string]: any }[]> { return Promise.resolve(this.uids.map((uid: string) => Test.buildSingleObject(uid + uidstart))); } } diff --git a/imxweb/projects/qbm/src/lib/treeTable/treeTable.component.html b/imxweb/projects/qbm/src/lib/treeTable/treeTable.component.html index a3c87aff8..4fc8077c9 100644 --- a/imxweb/projects/qbm/src/lib/treeTable/treeTable.component.html +++ b/imxweb/projects/qbm/src/lib/treeTable/treeTable.component.html @@ -1,29 +1,29 @@ -
    - - - - - - - - - - - +
    + + + + + + + + + + - - - - - + + + + + - - - - {{rootText}} - - + + {{ rootText }} +
    diff --git a/imxweb/projects/qbm/src/lib/treeTable/treeTable.component.ts b/imxweb/projects/qbm/src/lib/treeTable/treeTable.component.ts index 7f664cfd6..4943716f0 100644 --- a/imxweb/projects/qbm/src/lib/treeTable/treeTable.component.ts +++ b/imxweb/projects/qbm/src/lib/treeTable/treeTable.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -36,7 +36,7 @@ import { ContentChild, TemplateRef, ElementRef, - OnDestroy + OnDestroy, } from '@angular/core'; import { MatTable, MatColumnDef } from '@angular/material/table'; import { Subscription } from 'rxjs'; @@ -50,11 +50,11 @@ import { ImxExpandableItem } from './imx-data-source'; templateUrl: './treeTable.component.html', styles: [ ` - .customWidthClass { - flex: 0 0 50px; - } - ` - ] + .customWidthClass { + flex: 0 0 50px; + } + `, + ], }) /* @@ -68,7 +68,6 @@ import { ImxExpandableItem } from './imx-data-source'; | child 1 2 value */ export class ImxTreeTableComponent implements AfterContentInit, OnDestroy { - get columnsToDisplay(): string[] { return this.columnsToDisplayInternal; } @@ -107,7 +106,7 @@ export class ImxTreeTableComponent implements AfterContentInit, OnDestroy { public isExpansionDetailRow = (i: number, ob: ImxExpandableItem) => { return ob.isRoot && this.rootType !== 'ColumnTemplate'; - } + }; public async ngAfterContentInit(): Promise { if (!this.dataSource.hasChildrenProvider) { @@ -127,23 +126,27 @@ export class ImxTreeTableComponent implements AfterContentInit, OnDestroy { this.columnsToDisplayInternal.push(simpleColumn.field); - this.subscriptions.push(simpleColumn.itemExpanded.subscribe({ - next: (event: ImxExpandableItem) => { - this.handleExpandEvent(event, true); - } - })); - - this.subscriptions.push(simpleColumn.itemCollapsed.subscribe({ - next: (event: ImxExpandableItem) => { - this.handleExpandEvent(event, false); - } - })); + this.subscriptions.push( + simpleColumn.itemExpanded.subscribe({ + next: (event: ImxExpandableItem) => { + this.handleExpandEvent(event, true); + }, + }), + ); + + this.subscriptions.push( + simpleColumn.itemCollapsed.subscribe({ + next: (event: ImxExpandableItem) => { + this.handleExpandEvent(event, false); + }, + }), + ); this.table.addColumnDef(simpleColumn.columnDef); }); } public ngOnDestroy(): void { - this.subscriptions.forEach(subscription => subscription.unsubscribe()); + this.subscriptions.forEach((subscription) => subscription.unsubscribe()); } public handleExpandEvent(data: ImxExpandableItem, expand: boolean): void { diff --git a/imxweb/projects/qbm/src/lib/two-factor-authentication/two-factor-authentication.component.ts b/imxweb/projects/qbm/src/lib/two-factor-authentication/two-factor-authentication.component.ts index c1e784cd8..8c9fbb744 100644 --- a/imxweb/projects/qbm/src/lib/two-factor-authentication/two-factor-authentication.component.ts +++ b/imxweb/projects/qbm/src/lib/two-factor-authentication/two-factor-authentication.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,36 +24,40 @@ * */ -import { ComponentFactoryResolver, Component, ViewChild, OnInit } from '@angular/core'; +import { Component, ComponentFactoryResolver, OnInit, ViewChild } from '@angular/core'; -import { TwoFactorAuthenticationService } from './two-factor-authentication.service'; +import { ClassloggerService } from '../classlogger/classlogger.service'; import { ExtDirective } from '../ext/ext.directive'; import { imx_SessionService } from '../session/imx-session.service'; import { ISessionState } from '../session/session-state'; +import { TwoFactorAuthenticationService } from './two-factor-authentication.service'; @Component({ selector: 'imx-2fahost', - template: ` - - ` + template: ` `, }) export class TwoFactorAuthenticationComponent implements OnInit { @ViewChild(ExtDirective, { static: true }) public directive: ExtDirective; constructor( private componentFactoryResolver: ComponentFactoryResolver, + private classlogger: ClassloggerService, private twoFactorAuthService: TwoFactorAuthenticationService, - private sessionService: imx_SessionService + private sessionService: imx_SessionService, ) {} public ngOnInit(): void { this.sessionService.getSessionState().then((sessionState: ISessionState) => { - const selectedProvider = this.twoFactorAuthService.Registry[sessionState.SecondaryAuthName]; - if (selectedProvider) { - this.directive.viewContainerRef.clear(); - this.directive.viewContainerRef.createComponent(this.componentFactoryResolver.resolveComponentFactory(selectedProvider)); + if (sessionState.SecondaryAuthName) { + const selectedProvider = this.twoFactorAuthService.Registry[sessionState.SecondaryAuthName]; + if (selectedProvider) { + this.directive.viewContainerRef.clear(); + this.directive.viewContainerRef.createComponent(this.componentFactoryResolver.resolveComponentFactory(selectedProvider)); + } else { + this.classlogger.warn(this, 'No provider selected'); + } } else { - // TODO log + this.classlogger.warn(this, 'No secondary auth name found'); } }); } diff --git a/imxweb/projects/qbm/src/lib/two-factor-authentication/two-factor-authentication.service.ts b/imxweb/projects/qbm/src/lib/two-factor-authentication/two-factor-authentication.service.ts index 431fe36e1..e23349fb9 100644 --- a/imxweb/projects/qbm/src/lib/two-factor-authentication/two-factor-authentication.service.ts +++ b/imxweb/projects/qbm/src/lib/two-factor-authentication/two-factor-authentication.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,9 +28,11 @@ import { Injectable, Type } from '@angular/core'; @Injectable() export class TwoFactorAuthenticationService { - public get Registry(): { [id: string]: Type; } { return this.registry; } + public get Registry(): { [id: string]: Type } { + return this.registry; + } - private registry: { [id: string]: Type; } = {}; + private registry: { [id: string]: Type } = {}; public register(key: string, svc: Type): void { this.registry[key] = svc; diff --git a/imxweb/projects/qbm/src/lib/user-message/message.interface.ts b/imxweb/projects/qbm/src/lib/user-message/message.interface.ts index e5b5312a5..f911caf07 100644 --- a/imxweb/projects/qbm/src/lib/user-message/message.interface.ts +++ b/imxweb/projects/qbm/src/lib/user-message/message.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qbm/src/lib/user-message/user-message.component.html b/imxweb/projects/qbm/src/lib/user-message/user-message.component.html index 91a9d9ec7..5efbae060 100644 --- a/imxweb/projects/qbm/src/lib/user-message/user-message.component.html +++ b/imxweb/projects/qbm/src/lib/user-message/user-message.component.html @@ -1,3 +1,10 @@ - - {{ (message?.text || '') | translate }} + + {{ message?.text || '' | translate }} diff --git a/imxweb/projects/qbm/src/lib/user-message/user-message.component.scss b/imxweb/projects/qbm/src/lib/user-message/user-message.component.scss index 58ada4d48..04e6b1cdf 100644 --- a/imxweb/projects/qbm/src/lib/user-message/user-message.component.scss +++ b/imxweb/projects/qbm/src/lib/user-message/user-message.component.scss @@ -19,12 +19,12 @@ } .imx-small-message { - color : $black; + color: $black; display: block; } .eui-dark-theme { - :host{ + :host { .imx-message { color: $color-gray-0; } @@ -35,7 +35,7 @@ } .eui-contrast-theme { - :host{ + :host { .imx-message { color: $color-gray-2; } diff --git a/imxweb/projects/qbm/src/lib/user-message/user-message.component.ts b/imxweb/projects/qbm/src/lib/user-message/user-message.component.ts index c3f646c63..7aeadb86c 100644 --- a/imxweb/projects/qbm/src/lib/user-message/user-message.component.ts +++ b/imxweb/projects/qbm/src/lib/user-message/user-message.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,13 +24,13 @@ * */ -import { Component, OnDestroy, ViewChild, AfterContentInit, Input, ChangeDetectorRef } from '@angular/core'; +import { AfterContentInit, ChangeDetectorRef, Component, Input, OnDestroy, ViewChild } from '@angular/core'; import { Subscription } from 'rxjs'; -import { UserMessageService } from './user-message.service'; -import { Message } from './message.interface'; -import { ClassloggerService } from '../classlogger/classlogger.service'; import { EuiAlertComponent } from '@elemental-ui/core'; +import { ClassloggerService } from '../classlogger/classlogger.service'; +import { Message } from './message.interface'; +import { UserMessageService } from './user-message.service'; /** * A component that displays a message inside an ElementalUI alert control @@ -38,7 +38,7 @@ import { EuiAlertComponent } from '@elemental-ui/core'; @Component({ selector: 'imx-usermessage', templateUrl: './user-message.component.html', - styleUrls: ['./user-message.component.scss'] + styleUrls: ['./user-message.component.scss'], }) export class UserMessageComponent implements AfterContentInit, OnDestroy { /** @@ -56,32 +56,35 @@ export class UserMessageComponent implements AfterContentInit, OnDestroy { * @ignore * The message, displayed as header of the ElementalUI alert component */ - public message: Message; + public message: Message | undefined; private readonly subscriptions: Subscription[] = []; - constructor(private readonly messageService: UserMessageService, private readonly logger: ClassloggerService, - private cdref: ChangeDetectorRef + constructor( + private readonly messageService: UserMessageService, + private readonly logger: ClassloggerService, + private cdref: ChangeDetectorRef, ) { this.logger.debug(this, 'init user message component'); - this.subscriptions.push(this.messageService.subject.subscribe(message => { - - this.logger.debug(this, 'message received:', message); - this.message = message; - if (this.alert) { - this.alert.isDismissed = !this.isForMe(); - this.cdref.detectChanges(); - } - })); + this.subscriptions.push( + this.messageService.subject.subscribe((message) => { + this.logger.debug(this, 'message received:', message); + this.message = message; + if (this.alert) { + this.alert.isDismissed = !this.isForMe(); + this.cdref.detectChanges(); + } + }), + ); } - public dismissClick(){ + public dismissClick() { this.cdref.detectChanges(); } private isForMe() { // is there a message, and is the message for this target? - return this.message != null && this.target == this.message.target; + return this.message != null && !!this.message.target && this.target == this.message.target; } /** @@ -99,6 +102,6 @@ export class UserMessageComponent implements AfterContentInit, OnDestroy { */ public ngOnDestroy(): void { this.logger.debug(this, 'unsubscribe observables'); - this.subscriptions.forEach(subscription => subscription.unsubscribe()); + this.subscriptions.forEach((subscription) => subscription.unsubscribe()); } } diff --git a/imxweb/projects/qbm/src/lib/user-message/user-message.module.ts b/imxweb/projects/qbm/src/lib/user-message/user-message.module.ts index 6e0cddb79..543135fdf 100644 --- a/imxweb/projects/qbm/src/lib/user-message/user-message.module.ts +++ b/imxweb/projects/qbm/src/lib/user-message/user-message.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -34,12 +34,8 @@ import { UserMessageService } from './user-message.service'; @NgModule({ declarations: [UserMessageComponent], - imports: [ - CommonModule, - EuiCoreModule, - TranslateModule - ], + imports: [CommonModule, EuiCoreModule, TranslateModule], providers: [UserMessageService], exports: [UserMessageComponent], }) -export class UserMessageModule { } +export class UserMessageModule {} diff --git a/imxweb/projects/qbm/src/lib/user-message/user-message.service.ts b/imxweb/projects/qbm/src/lib/user-message/user-message.service.ts index 82636936c..cff4f2385 100644 --- a/imxweb/projects/qbm/src/lib/user-message/user-message.service.ts +++ b/imxweb/projects/qbm/src/lib/user-message/user-message.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,5 +31,5 @@ import { Message } from './message.interface'; @Injectable() export class UserMessageService { - public readonly subject = new ReplaySubject(); + public readonly subject = new ReplaySubject(); } diff --git a/imxweb/projects/qbm/src/lib/value-wrapper/value-wrapper.ts b/imxweb/projects/qbm/src/lib/value-wrapper/value-wrapper.ts index 4ff9264f3..592bd3ab1 100644 --- a/imxweb/projects/qbm/src/lib/value-wrapper/value-wrapper.ts +++ b/imxweb/projects/qbm/src/lib/value-wrapper/value-wrapper.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qbm/src/public_api.ts b/imxweb/projects/qbm/src/public_api.ts index a615141dc..a2b8eee8d 100644 --- a/imxweb/projects/qbm/src/public_api.ts +++ b/imxweb/projects/qbm/src/public_api.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,37 +29,47 @@ */ export { AboutComponent } from './lib/about/About.component'; +export { AboutService } from './lib/about/About.service'; export { AdminComponent } from './lib/admin/admin-component.interface'; export { AdminRoutes } from './lib/admin/admin-routes'; -export { CacheService } from './lib/cache/cache.service'; export { AdminModule } from './lib/admin/admin.module'; export { LogDetailsSidesheetComponent } from './lib/admin/log-details-sidesheet.component'; export { ApiClientAngularService, BASE_URL } from './lib/api-client/api-client-angular.service'; export { ApiClientFetch } from './lib/api-client/api-client-fetch'; export { ApiClientService } from './lib/api-client/api-client.service'; export { DynamicMethod, GenericTypedEntity } from './lib/api-client/dynamic-method'; +export { DynamicCollectionLoadParameters } from './lib/api-client/dynamic-method/dynamic-collection-load-parameters.interface'; +export { DynamicMethodService } from './lib/api-client/dynamic-method/dynamic-method.service'; +export { InteractiveParameter } from './lib/api-client/dynamic-method/interactive-parameter.interface'; export { AppConfig } from './lib/appConfig/appconfig.interface'; export { AppConfigService } from './lib/appConfig/appConfig.service'; +export { TranslationConfiguration } from './lib/appConfig/translationConfiguration.interface'; export { AuthConfigProvider } from './lib/authentication/auth-config-provider.interface'; export { AuthenticationGuardService } from './lib/authentication/authentication-guard.service'; export { AuthenticationModule } from './lib/authentication/authentication.module'; export { AuthenticationService } from './lib/authentication/authentication.service'; +export { CustomAuthFlow } from './lib/authentication/custom-auth-flow.interface'; export { OAuthService } from './lib/authentication/oauth.service'; export { AutoCompleteComponent } from './lib/auto-complete/auto-complete.component'; export { AutoCompleteModule } from './lib/auto-complete/auto-complete.module'; -export { BusyService } from './lib/base/busy.service'; -export { Busy } from './lib/base/busy.service'; +export { Busy, BusyService } from './lib/base/busy.service'; export { ErrorService } from './lib/base/error.service'; export { GlobalErrorHandler } from './lib/base/global-error-handler'; export { Guid } from './lib/base/Guid'; +export { IeWarningService } from './lib/base/ie-warning.service'; export { MetadataService } from './lib/base/metadata.service'; export { OpsupportDbObjectParameters, OpsupportDbObjectService } from './lib/base/opsupport-db-object.service'; export { Paginator } from './lib/base/paginator'; export { QueryParametersHandler } from './lib/base/query-parameters-handler'; export { RegistryService } from './lib/base/registry.service'; +export { UserActionService } from './lib/base/user-action.service'; export { isIE } from './lib/base/user-agent-helper'; +export { BulkItem, BulkItemStatus } from './lib/bulk-property-editor/bulk-item/bulk-item'; +export { BulkPropertyEditorComponent } from './lib/bulk-property-editor/bulk-property-editor.component'; +export { BulkPropertyEditorModule } from './lib/bulk-property-editor/bulk-property-editor.module'; export { BusyIndicatorComponent } from './lib/busy-indicator/busy-indicator.component'; export { BusyIndicatorModule } from './lib/busy-indicator/busy-indicator.module'; +export { CacheService } from './lib/cache/cache.service'; export { CaptchaComponent } from './lib/captcha/captcha.component'; export { CaptchaModule } from './lib/captcha/captcha.module'; export { CaptchaMode, CaptchaService } from './lib/captcha/captcha.service'; @@ -76,41 +86,6 @@ export { CdrSidesheetConfig } from './lib/cdr/cdr-sidesheet/cdr-sidesheet-config export { CdrSidesheetComponent } from './lib/cdr/cdr-sidesheet/cdr-sidesheet.component'; export { CdrModule } from './lib/cdr/cdr.module'; export { ColumnDependentReference } from './lib/cdr/column-dependent-reference.interface'; -export { CustomAuthFlow } from './lib/authentication/custom-auth-flow.interface'; -export { CustomThemeModule } from './lib/custom-theme/custom-theme.module'; -export { CustomThemeService } from './lib/custom-theme/custom-theme.service'; -export { Connectors } from './lib/hyperview/connectors'; -export { ConnectionComponent } from './lib/connection/connection.component'; -export { DataNavigationParameters } from './lib/select/data-navigation-parameters.interface'; -export { DataSourcePaginatorComponent } from './lib/data-source-toolbar/data-source-paginator.component'; -export { DataSourceToolbarComponent } from './lib/data-source-toolbar/data-source-toolbar.component'; -export { DataSourceToolbarCustomComponent } from './lib/data-source-toolbar/data-source-toolbar-custom.component'; -export { DataSourceToolbarModule } from './lib/data-source-toolbar/data-source-toolbar.module'; -export { DataSourceToolbarSettings } from './lib/data-source-toolbar/data-source-toolbar-settings'; -export { ClientPropertyForTableColumns } from './lib/data-source-toolbar/client-property-for-table-columns'; -export { DataSourceToolbarFilter, DataSourceToolbarSelectedFilter } from './lib/data-source-toolbar/data-source-toolbar-filters.interface'; -export { - DataSourceToolbarGroupData, - DataSourceToolBarGroup, - DataSourceToolBarGroupingCategory, -} from './lib/data-source-toolbar/data-source-toolbar-groups.interface'; -export { DataSourceToolbarExportMethod } from './lib/data-source-toolbar/data-source-toolbar-export-method.interface'; -export { DataSourceToolbarViewConfig, DSTViewConfig } from './lib/data-source-toolbar/data-source-toolbar-view-config.interface'; -export { isConfigDefault, isDefaultId } from './lib/data-source-toolbar/data-source-toolbar-view-config-helper'; -export { DataModelWrapper } from './lib/data-source-toolbar/data-model/data-model-wrapper.interface'; -export { DataSourceItemStatus } from './lib/data-source-toolbar/data-source-item-status.interface'; -export { DataSourceWrapper } from './lib/data-source-toolbar/data-source-wrapper'; -export { DocChapterService, DocDocument, DocChapter } from './lib/doc/doc-chapter.service'; -export { FilterTreeParameter } from './lib/data-source-toolbar/data-model/filter-tree-parameter'; -export { FilterTreeDatabase } from './lib/data-source-toolbar/filter-tree/filter-tree-database'; -export { FilterTreeEntityWrapperService } from './lib/data-source-toolbar/filter-tree/filter-tree-entity-wrapper.service'; -export { DataTableColumnComponent } from './lib/data-table/data-table-column.component'; -export { DataTableComponent } from './lib/data-table/data-table.component'; -export { DataTableGenericColumnComponent } from './lib/data-table/data-table-generic-column.component'; -export { DataTableGroupedData } from './lib/data-table/data-table-groups.interface'; -export { DataTableModule } from './lib/data-table/data-table.module'; -export { getParameterSubsetForGrouping } from './lib/data-table/data-table-groups.interface'; -export { ExcludedColumnsPipe } from './lib/data-table/excluded-columns.pipe'; export { DefaultCdrEditorProvider } from './lib/cdr/default-cdr-editor-provider'; export { EditBinaryComponent } from './lib/cdr/edit-binary/edit-binary.component'; export { EditBooleanComponent } from './lib/cdr/edit-boolean/edit-boolean.component'; @@ -122,19 +97,57 @@ export { EditMultiLimitedValueComponent } from './lib/cdr/edit-multi-limited-val export { EditMultiValueComponent } from './lib/cdr/edit-multi-value/edit-multi-value.component'; export { EditMultilineComponent } from './lib/cdr/edit-multiline/edit-multiline.component'; export { EditNumberComponent } from './lib/cdr/edit-number/edit-number.component'; +export { EntityColumnContainer } from './lib/cdr/entity-column-container'; +export { EntityColumnEditorComponent } from './lib/cdr/entity-column-editor/entity-column-editor.component'; export { FkCdrEditorProvider } from './lib/cdr/fk-cdr-editor-provider'; export { PropertyViewerComponent } from './lib/cdr/property-viewer/property-viewer.component'; export { LineChartOptions } from './lib/chart-options/line-chart-options'; export { SeriesInformation } from './lib/chart-options/series-information'; +export { XAxisInformation } from './lib/chart-options/x-axis-information'; +export { YAxisInformation } from './lib/chart-options/y-axis-information'; export { ChartTileComponent } from './lib/chart-tile/chart-tile.component'; export { ClassloggerModule } from './lib/classlogger/classlogger.module'; export { ClassloggerService } from './lib/classlogger/classlogger.service'; export { ElementalUiConfigService } from './lib/configuration/elemental-ui-config.service'; +export { ConfirmationModule } from './lib/confirmation/confirmation.module'; +export { ConfirmationService } from './lib/confirmation/confirmation.service'; +export { ConnectionComponent } from './lib/connection/connection.component'; +export { CustomThemeModule } from './lib/custom-theme/custom-theme.module'; +export { CustomThemeService } from './lib/custom-theme/custom-theme.service'; +export { ClientPropertyForTableColumns } from './lib/data-source-toolbar/client-property-for-table-columns'; export { ColumnOptions } from './lib/data-source-toolbar/column-options'; export { createGroupData } from './lib/data-source-toolbar/data-model/data-model-helper'; +export { DataModelWrapper } from './lib/data-source-toolbar/data-model/data-model-wrapper.interface'; +export { FilterTreeParameter } from './lib/data-source-toolbar/data-model/filter-tree-parameter'; +export { DataSourceItemStatus } from './lib/data-source-toolbar/data-source-item-status.interface'; +export { DataSourcePaginatorComponent } from './lib/data-source-toolbar/data-source-paginator.component'; +export { DataSourceToolbarCustomComponent } from './lib/data-source-toolbar/data-source-toolbar-custom.component'; +export { DataSourceToolbarExportMethod } from './lib/data-source-toolbar/data-source-toolbar-export-method.interface'; +export { DataSourceToolbarFilter, DataSourceToolbarSelectedFilter } from './lib/data-source-toolbar/data-source-toolbar-filters.interface'; +export { + DataSourceToolBarGroup, + DataSourceToolBarGroupingCategory, + DataSourceToolbarGroupData, +} from './lib/data-source-toolbar/data-source-toolbar-groups.interface'; +export { DataSourceToolbarSettings } from './lib/data-source-toolbar/data-source-toolbar-settings'; +export { isConfigDefault, isDefaultId } from './lib/data-source-toolbar/data-source-toolbar-view-config-helper'; +export { DSTViewConfig, DataSourceToolbarViewConfig } from './lib/data-source-toolbar/data-source-toolbar-view-config.interface'; +export { DataSourceToolbarComponent } from './lib/data-source-toolbar/data-source-toolbar.component'; +export { DataSourceToolbarModule } from './lib/data-source-toolbar/data-source-toolbar.module'; +export { DataSourceWrapper } from './lib/data-source-toolbar/data-source-wrapper'; export { DataTileBadge } from './lib/data-source-toolbar/data-tile-badge.interface'; +export { FilterTreeDatabase } from './lib/data-source-toolbar/filter-tree/filter-tree-database'; +export { FilterTreeEntityWrapperService } from './lib/data-source-toolbar/filter-tree/filter-tree-entity-wrapper.service'; +export { FilterWizardComponent } from './lib/data-source-toolbar/filter-wizard/filter-wizard.component'; +export { FilterWizardModule } from './lib/data-source-toolbar/filter-wizard/filter-wizard.module'; export { buildAdditionalElementsString } from './lib/data-table/data-table-additional-info.model'; +export { DataTableColumnComponent } from './lib/data-table/data-table-column.component'; +export { DataTableGenericColumnComponent } from './lib/data-table/data-table-generic-column.component'; +export { DataTableGroupedData, getParameterSubsetForGrouping } from './lib/data-table/data-table-groups.interface'; export { RowHighlight } from './lib/data-table/data-table-row-highlight.interface'; +export { DataTableComponent } from './lib/data-table/data-table.component'; +export { DataTableModule } from './lib/data-table/data-table.module'; +export { ExcludedColumnsPipe } from './lib/data-table/excluded-columns.pipe'; export { DataTileMenuItem } from './lib/data-tiles/data-tile-menu-item.interface'; export { DataTileComponent } from './lib/data-tiles/data-tile.component'; export { DataTilesComponent } from './lib/data-tiles/data-tiles.component'; @@ -144,7 +157,10 @@ export { DataTreeWrapperModule } from './lib/data-tree-wrapper/data-tree-wrapper export { SearchResultAction } from './lib/data-tree/data-tree-search-results/search-result-action.interface'; export { DataTreeComponent } from './lib/data-tree/data-tree.component'; export { DataTreeModule } from './lib/data-tree/data-tree.module'; -export { TreeNodeInfo } from './lib/data-tree/tree-node'; +export { EntityTreeDatabase } from './lib/data-tree/entity-tree-database'; +export { TreeDatabase } from './lib/data-tree/tree-database'; +export { TreeNode, TreeNodeInfo } from './lib/data-tree/tree-node'; +export { TreeNodeResultParameter } from './lib/data-tree/tree-node-result-parameter.interface'; export { DateModule } from './lib/date/date.module'; export { DateComponent } from './lib/date/date/date.component'; export { LocalizedDatePipe } from './lib/date/localized-date.pipe'; @@ -157,22 +173,34 @@ export { DynamicTabsModule } from './lib/dynamic-tabs/dynamic-tabs.module'; export { EntitySelectComponent } from './lib/entity/entity-select/entity-select.component'; export { EntityModule } from './lib/entity/entity.module'; export { EntityService } from './lib/entity/entity.service'; +export { TypedEntityCandidateSidesheetComponent } from './lib/entity/typed-entity-candidate-sidesheet/typed-entity-candidate-sidesheet.component'; +export { TypedEntitySelectComponent } from './lib/entity/typed-entity-select/typed-entity-select.component'; +export { TypedEntitySelectionData } from './lib/entity/typed-entity-select/typed-entity-selection-data.interface'; export { ExtComponent } from './lib/ext/ext.component'; export { ExtDirective } from './lib/ext/ext.directive'; export { ExtModule } from './lib/ext/ext.module'; export { ExtService } from './lib/ext/ext.service'; export { IExtension } from './lib/ext/extension'; +export { FileSelectorService } from './lib/file-selector/file-selector.service'; export { FilterTileComponent } from './lib/filter-tile/filter-tile.component'; +export { FkAdvancedPickerComponent } from './lib/fk-advanced-picker/fk-advanced-picker.component'; +export { FkAdvancedPickerModule } from './lib/fk-advanced-picker/fk-advanced-picker.module'; export { FkCandidatesComponent } from './lib/fk-advanced-picker/fk-candidates/fk-candidates.component'; +export { FkSelectorComponent } from './lib/fk-advanced-picker/fk-selector.component'; +export { ForeignKeySelection } from './lib/fk-advanced-picker/foreign-key-selection.interface'; export { DynFkContainer } from './lib/fk-container/dyn-fk-container'; export { FkContainer } from './lib/fk-container/fk-container'; +export { Connectors } from './lib/hyperview/connectors'; export { HvCell, HvElement, ShapeClickArgs } from './lib/hyperview/hyperview-types'; export { HyperviewComponent } from './lib/hyperview/hyperview.component'; export { HyperViewModule } from './lib/hyperview/hyperview.module'; export { IconStackComponent } from './lib/icon-stack/icon-stack.component'; +export { ImageSelectComponent } from './lib/image/image-select/image-select.component'; +export { ImageViewComponent } from './lib/image/image-view/image-view.component'; +export { ImageModule } from './lib/image/image.module'; export { Base64ImageService } from './lib/images/base64-image.service'; -export { InfoButtonComponent } from './lib/info-modal-dialog/info-button/info-button.component'; export { InfoBadgeComponent } from './lib/info-modal-dialog/info-badge/info-badge.component'; +export { InfoButtonComponent } from './lib/info-modal-dialog/info-button/info-button.component'; export { InfoModalDialogModule } from './lib/info-modal-dialog/info-modal-dialog.module'; export { JobQueueOverviewComponent } from './lib/jobqueue-overview/jobqueue-overview.component'; export { JobQueueOverviewModule } from './lib/jobqueue-overview/jobqueue-overview.module'; @@ -183,28 +211,32 @@ export { MastHeadMenuItem } from './lib/mast-head/mast-head-menu-item.interface' export { MastHeadMenu } from './lib/mast-head/mast-head-menu.interface'; export { MastHeadComponent } from './lib/mast-head/mast-head.component'; export { MastHeadModule } from './lib/mast-head/mast-head.module'; -export { MasterDetailComponent } from './lib/master-detail/master-detail.component'; -export { GroupMenuItem } from './lib/menu/menu-item/group-menu-item'; +export { MastHeadService } from './lib/mast-head/mast-head.service'; export { MenuFactory, MenuItem } from './lib/menu/menu-item/menu-item.interface'; export { NavigationCommandsMenuItem } from './lib/menu/menu-item/navigation-commands-menu-item.interface'; -export { NavigationMenuItem } from './lib/menu/menu-item/navigation-menu-item'; export { RelatedApplicationMenuItem } from './lib/menu/menu-item/related-application-menu-item'; export { RelatedApplication } from './lib/menu/menu-item/related-application.interface'; -export { MenuComponent } from './lib/menu/menu.component'; -export { MenuModule } from './lib/menu/menu.module'; export { MenuService } from './lib/menu/menu.service'; export { MessageDialogResult } from './lib/message-dialog/message-dialog-result.enum'; export { MessageDialogComponent } from './lib/message-dialog/message-dialog.component'; export { MessageParameter } from './lib/message-dialog/message-parameter.interface'; +export { MultiSelectFormcontrolComponent } from './lib/multi-select-formcontrol/multi-select-formcontrol.component'; +export { MultiSelectFormcontrolModule } from './lib/multi-select-formcontrol/multi-select-formcontrol.module'; export { MultiValueService } from './lib/multi-value/multi-value.service'; export { ObjectHistoryApiService } from './lib/object-history/object-history-api.service'; export { ObjectHistoryGridviewComponent } from './lib/object-history/object-history-gridview/object-history-gridview.component'; export { ObjectHistoryComponent } from './lib/object-history/object-history.component'; export { ObjectHistoryModule } from './lib/object-history/object-history.module'; -export { NavigationService } from './lib/object-sheet/navigation.service'; export { OrderedListComponent } from './lib/ordered-list/ordered-list.component'; export { OrderedListModule } from './lib/ordered-list/ordered-list.module'; -export { PluginLoaderService } from './lib/plugins/plugin-loader.service'; +export { ParameterizedTextComponent } from './lib/parameterized-text/parameterized-text.component'; +export { ParameterizedText } from './lib/parameterized-text/parameterized-text.interface'; +export { ParameterizedTextModule } from './lib/parameterized-text/parameterized-text.module'; +export { ParameterizedTextService } from './lib/parameterized-text/parameterized-text.service'; +export { TextToken } from './lib/parameterized-text/text-token.interface'; +export { Action, ActionGroup, QueuedAction, QueuedActionGroup, QueuedActionState } from './lib/processing-queue/processing-queue.interface'; +export { ProcessingQueueService } from './lib/processing-queue/processing-queue.service'; + export { ImxProgressbarComponent } from './lib/progressbar/progressbar.component'; export { QbmModule } from './lib/qbm.module'; export { ComponentCanDeactivate } from './lib/route-guard/component-can-deactivate.interface'; @@ -213,11 +245,6 @@ export { DbObjectInfo } from './lib/search/db-object-info'; export { imx_QBM_SearchService } from './lib/search/search.service'; export { imx_ISearchService } from './lib/searchbar/iSearchService'; export { SearchBarComponent } from './lib/searchbar/searchbar.component'; -export { FkSelectionContainer } from './lib/select/fk-selection-container'; -export { SelectContentProvider } from './lib/select/select-content-provider.interface'; -export { SelectComponent } from './lib/select/select.component'; -export { SelectModule } from './lib/select/select.module'; -export { DeviceStateService } from './lib/services/device-state.service'; export { imx_SessionService } from './lib/session/imx-session.service'; export { AuthStepLevels, ISessionState, SessionState } from './lib/session/session-state'; export { SettingsService } from './lib/settings/settings-service'; @@ -237,50 +264,22 @@ export { StorageModule } from './lib/storage/storage.module'; export { HELPER_ALERT_KEY_PREFIX, StorageService } from './lib/storage/storage.service'; export { SystemInfoService } from './lib/system-info/system-info.service'; export { TableImageService } from './lib/table-image/table-image.service'; -export { TranslationConfiguration } from './lib/appConfig/translationConfiguration.interface'; -export { TreeDatabase } from './lib/data-tree/tree-database'; -export { UserActionService } from './lib/base/user-action.service'; -export { XAxisInformation } from './lib/chart-options/x-axis-information'; -export { YAxisInformation } from './lib/chart-options/y-axis-information'; -export { FkAdvancedPickerModule } from './lib/fk-advanced-picker/fk-advanced-picker.module'; -export { FkAdvancedPickerComponent } from './lib/fk-advanced-picker/fk-advanced-picker.component'; -export { FkSelectorComponent } from './lib/fk-advanced-picker/fk-selector.component'; -export { MastHeadService } from './lib/mast-head/mast-head.service'; -export { EntityColumnContainer } from './lib/cdr/entity-column-container'; -export { ForeignKeySelection } from './lib/fk-advanced-picker/foreign-key-selection.interface'; -export { BulkPropertyEditorModule } from './lib/bulk-property-editor/bulk-property-editor.module'; -export { BulkPropertyEditorComponent } from './lib/bulk-property-editor/bulk-property-editor.component'; -export { BulkItem } from './lib/bulk-property-editor/bulk-item/bulk-item'; -export { BulkItemStatus } from './lib/bulk-property-editor/bulk-item/bulk-item'; -export { ConfirmationModule } from './lib/confirmation/confirmation.module'; -export { ConfirmationService } from './lib/confirmation/confirmation.service'; -export { MultiSelectFormcontrolModule } from './lib/multi-select-formcontrol/multi-select-formcontrol.module'; -export { MultiSelectFormcontrolComponent } from './lib/multi-select-formcontrol/multi-select-formcontrol.component'; -export { TreeNodeResultParameter } from './lib/data-tree/tree-node-result-parameter.interface'; -export { TypedEntitySelectComponent } from './lib/entity/typed-entity-select/typed-entity-select.component'; -export { TypedEntitySelectionData } from './lib/entity/typed-entity-select/typed-entity-selection-data.interface'; -export { TypedEntityCandidateSidesheetComponent } from './lib/entity/typed-entity-candidate-sidesheet/typed-entity-candidate-sidesheet.component'; -export { ImageModule } from './lib/image/image.module'; -export { ImageSelectComponent } from './lib/image/image-select/image-select.component'; -export { ImageViewComponent } from './lib/image/image-view/image-view.component'; -export { FileSelectorService } from './lib/file-selector/file-selector.service'; -export { EntityColumnEditorComponent } from './lib/cdr/entity-column-editor/entity-column-editor.component'; -export { ParameterizedTextModule } from './lib/parameterized-text/parameterized-text.module'; -export { ParameterizedTextComponent } from './lib/parameterized-text/parameterized-text.component'; -export { ParameterizedTextService } from './lib/parameterized-text/parameterized-text.service'; -export { ParameterizedText } from './lib/parameterized-text/parameterized-text.interface'; -export { TextToken } from './lib/parameterized-text/text-token.interface'; -export { IeWarningService } from './lib/base/ie-warning.service'; -export { EntityTreeDatabase } from './lib/data-tree/entity-tree-database'; -export { DynamicMethodService } from './lib/api-client/dynamic-method/dynamic-method.service'; -export { DynamicCollectionLoadParameters } from './lib/api-client/dynamic-method/dynamic-collection-load-parameters.interface'; -export { InteractiveParameter } from './lib/api-client/dynamic-method/interactive-parameter.interface'; -export { FilterWizardModule } from './lib/data-source-toolbar/filter-wizard/filter-wizard.module'; -export { FilterWizardComponent } from './lib/data-source-toolbar/filter-wizard/filter-wizard.component'; -export { SidenavTreeModule } from './lib/sidenav-tree/sidenav-tree.module'; +export { EuiDateProviders } from './lib/base/elemental-defaults'; +export { calculateSidesheetWidth, isMobile } from './lib/base/sidesheet-helper'; +export { FilterTreeComponent } from './lib/data-source-toolbar/filter-tree/filter-tree.component'; +export { TableAccessiblilityDirective } from './lib/data-table/table-accessibility.directive'; +export { TypedEntityFkData } from './lib/entity/typed-entity-select/typed-entity-fk-data.interface'; +export { FkCandidatesData } from './lib/fk-advanced-picker/fk-candidates/fk-candidates-data.interface'; +export { HelpContextualComponent } from './lib/help-contextual/help-contextual.component'; +export { HelpContextualModule } from './lib/help-contextual/help-contextual.module'; +export { HELP_CONTEXTUAL, HelpContextualService, HelpContextualValues } from './lib/help-contextual/help-contextual.service'; +export { HyperViewNavigation, HyperViewNavigationEnum } from './lib/hyperview/hyperview-types'; +export { MessageDialogService } from './lib/message-dialog/message-dialog.service'; +export { SelectedElementsComponent } from './lib/selected-elements/selected-elements.component'; +export { SelectedElementsModule } from './lib/selected-elements/selected-elements.module'; +export { DynamicDataApiControls, DynamicDataSource } from './lib/sidenav-tree/sidenav-tree-dynamic-extension'; export { SidenavTreeComponent } from './lib/sidenav-tree/sidenav-tree.component'; -export { DynamicDataSource, DynamicDataApiControls } from './lib/sidenav-tree/sidenav-tree-dynamic-extension'; export { BaseImxApiDataMock, BaseImxApiDtoMock, @@ -308,13 +307,21 @@ export { Message } from './lib/user-message/message.interface'; export { UserMessageComponent } from './lib/user-message/user-message.component'; export { UserMessageModule } from './lib/user-message/user-message.module'; export { UserMessageService } from './lib/user-message/user-message.service'; -export { HelpContextualModule } from './lib/help-contextual/help-contextual.module'; -export { HelpContextualComponent } from './lib/help-contextual/help-contextual.component'; -export { HelpContextualService, HELP_CONTEXTUAL, HelpContextualValues } from './lib/help-contextual/help-contextual.service'; -export { HyperViewNavigation, HyperViewNavigationEnum } from './lib/hyperview/hyperview-types'; -export { SelectedElementsModule } from './lib/selected-elements/selected-elements.module'; -export { SelectedElementsComponent } from './lib/selected-elements/selected-elements.component'; -export { FilterTreeComponent } from './lib/data-source-toolbar/filter-tree/filter-tree.component'; -export { TempBillboardModule } from './lib/temp-billboard/temp-billboard.module'; -export { TempBillboardComponent } from './lib/temp-billboard/temp-billboard.component'; +// DataView +export { DataViewAutoTableComponent } from './lib/data-view/data-view-auto-table/data-view-auto-table.component'; +export { DataViewChipbarComponent } from './lib/data-view/data-view-chipbar/data-view-chipbar.component'; +export { DataViewFilterComponent } from './lib/data-view/data-view-filter/data-view-filter.component'; +export { DataViewGroupComponent } from './lib/data-view/data-view-group/data-view-group.component'; +export { DataViewPaginatorComponent } from './lib/data-view/data-view-paginator/data-view-paginator.component'; +export { DataViewSearchComponent } from './lib/data-view/data-view-search/data-view-search.component'; +export { DataViewSelectionComponent } from './lib/data-view/data-view-selection/data-view-selection.component'; +export { DataViewStatusComponent } from './lib/data-view/data-view-status/data-view-status.component'; + +export { DataViewSettingsComponent } from './lib/data-view/data-view-settings/data-view-settings.component'; +export { DataViewSource } from './lib/data-view/data-view-source'; +export { DataViewSourceFactoryService } from './lib/data-view/data-view-source-factory.service'; +export { FakeDataViewSource } from './lib/data-view/data-view-source.spec'; +export { DataViewToolbarComponent } from './lib/data-view/data-view-toolbar/data-view-toolbar.component'; +export { DataViewInitParameters } from './lib/data-view/data-view.interface'; +export { DataViewModule } from './lib/data-view/data-view.module'; diff --git a/imxweb/projects/qbm/src/test.ts b/imxweb/projects/qbm/src/test.ts index 38e0ab845..df55524d0 100644 --- a/imxweb/projects/qbm/src/test.ts +++ b/imxweb/projects/qbm/src/test.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,15 +26,15 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'core-js/es7/reflect'; -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +// Zone must be imported first. Be careful with prettier import organizing +import 'zone.js'; +import 'zone.js/testing'; + import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; +import 'core-js/es/reflect'; +import { QbmDefaultMocks } from './default-mocks.spec'; import { TestHelperModule } from './lib/testing/TestHelperModule.spec'; -import { QbmDefaultMocks} from './default-mocks.spec'; - -declare const require: any; // First, initialize the Angular testing environment. getTestBed().initTestEnvironment([BrowserDynamicTestingModule, TestHelperModule], platformBrowserDynamicTesting(), { @@ -42,9 +42,5 @@ getTestBed().initTestEnvironment([BrowserDynamicTestingModule, TestHelperModule] destroyAfterEach: false, }, }); -// Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); -// And load the modules. -context.keys().map(context); QbmDefaultMocks.registerDefaultMocks(); diff --git a/imxweb/projects/qbm/tsconfig.lib.json b/imxweb/projects/qbm/tsconfig.lib.json index 6bc419f84..f863d8bff 100644 --- a/imxweb/projects/qbm/tsconfig.lib.json +++ b/imxweb/projects/qbm/tsconfig.lib.json @@ -7,10 +7,11 @@ "declarationMap": true, "sourceMap": true, "inlineSources": true, + "strictNullChecks": true, "types": [] }, - "exclude": [ - "src/test.ts", - "**/*.spec.ts" - ] + "angularCompilerOptions": { + "strictTemplates": true + }, + "exclude": ["src/test.ts", "**/*.spec.ts"] } diff --git a/imxweb/projects/qbm/tsconfig.spec.json b/imxweb/projects/qbm/tsconfig.spec.json index 16da33db0..6bd74e1f7 100644 --- a/imxweb/projects/qbm/tsconfig.spec.json +++ b/imxweb/projects/qbm/tsconfig.spec.json @@ -1,17 +1,10 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "strictNullChecks": false, "outDir": "../../out-tsc/spec", - "types": [ - "jasmine", - "node" - ] + "types": ["jasmine", "node"] }, - "files": [ - "src/test.ts" - ], - "include": [ - "**/*.spec.ts", - "**/*.d.ts" - ] + "files": ["src/test.ts"], + "include": ["**/*.spec.ts", "**/*.d.ts"] } diff --git a/imxweb/projects/qbm/tslint.json b/imxweb/projects/qbm/tslint.json deleted file mode 100644 index 8bab15f53..000000000 --- a/imxweb/projects/qbm/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "imx", - "camelCase" - ], - "component-selector": [ - true, - "element", - "imx", - "kebab-case" - ] - } -} diff --git a/imxweb/projects/qer-app-operationssupport/.compodocrc.json b/imxweb/projects/qer-app-operationssupport/.compodocrc.json index 4c9cd3fc1..59ae1dd26 100644 --- a/imxweb/projects/qer-app-operationssupport/.compodocrc.json +++ b/imxweb/projects/qer-app-operationssupport/.compodocrc.json @@ -1,6 +1,6 @@ { "name": "IMX Web - QER Operations Support", - "output": "../../documentation/v92/qer-app-operationssupport", + "output": "../../documentation/v93/qer-app-operationssupport", "theme": "material", "assetsFolder": "../../compodoc/assets", "customLogo": "../../compodoc/assets/images/oneidentity-logo.png", diff --git a/imxweb/projects/qer-app-operationssupport/.eslintrc.json b/imxweb/projects/qer-app-operationssupport/.eslintrc.json new file mode 100644 index 000000000..6cc4fb0bb --- /dev/null +++ b/imxweb/projects/qer-app-operationssupport/.eslintrc.json @@ -0,0 +1,38 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*", "**/*.spec.ts"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": [ + "imxweb/projects/qer-app-operationssupport/tsconfig.app.json", + "imxweb/projects/qer-app-operationssupport/tsconfig.spec.json" + ], + "createDefaultProgram": true + }, + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "imx", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "imx", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "rules": {} + } + ] +} diff --git a/imxweb/projects/qer-app-operationssupport/dpr-api-client.service.ts b/imxweb/projects/qer-app-operationssupport/dpr-api-client.service.ts index 77bf2b4c0..3768b63a6 100644 --- a/imxweb/projects/qer-app-operationssupport/dpr-api-client.service.ts +++ b/imxweb/projects/qer-app-operationssupport/dpr-api-client.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,12 +25,12 @@ */ import { Injectable } from '@angular/core'; -import { V2Client, TypedClient } from 'imx-api-dpr'; -import { ApiClient } from 'imx-qbm-dbts'; +import { V2Client, TypedClient } from '@imx-modules/imx-api-dpr'; +import { ApiClient } from '@imx-modules/imx-qbm-dbts'; import { AppConfigService, ClassloggerService, ImxTranslationProviderService } from 'qbm'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class DprApiService { private tc: TypedClient; @@ -50,7 +50,8 @@ export class DprApiService { constructor( private readonly config: AppConfigService, private readonly logger: ClassloggerService, - private readonly translationProvider: ImxTranslationProviderService) { + private readonly translationProvider: ImxTranslationProviderService, + ) { try { this.logger.debug(this, 'Initializing DPR API service'); diff --git a/imxweb/projects/qer-app-operationssupport/karma.conf.js b/imxweb/projects/qer-app-operationssupport/karma.conf.js index 5ac3b03d8..43effb2c5 100644 --- a/imxweb/projects/qer-app-operationssupport/karma.conf.js +++ b/imxweb/projects/qer-app-operationssupport/karma.conf.js @@ -12,10 +12,10 @@ module.exports = function (config) { require('karma-jasmine-html-reporter'), require('karma-coverage-istanbul-reporter'), require('@angular-devkit/build-angular/plugins/karma'), - require('karma-junit-reporter') + require('karma-junit-reporter'), ], client: { - clearContext: false // leave Jasmine Spec Runner output visible in browser + clearContext: false, // leave Jasmine Spec Runner output visible in browser }, coverageIstanbulReporter: { dir: require('path').join(__dirname, 'results'), @@ -23,7 +23,7 @@ module.exports = function (config) { fixWebpackSourcePaths: true, 'report-config': { html: { - subdir: 'coverage-html' + subdir: 'coverage-html', }, }, thresholds: { @@ -31,7 +31,7 @@ module.exports = function (config) { branches: 0, functions: 0, lines: 0, - } + }, }, junitReporter: { outputDir: require('path').join(__dirname, 'results'), @@ -40,13 +40,14 @@ module.exports = function (config) { port: 9876, captureTimeout: 210000, browserDisconnectTolerance: 3, - browserDisconnectTimeout : 210000, - browserNoActivityTimeout : 210000, + browserDisconnectTimeout: 210000, + browserNoActivityTimeout: 210000, colors: true, logLevel: config.LOG_INFO, autoWatch: true, restartOnFileChange: true, singleRun: false, - browsers: ['Chrome'] + failOnEmptyTestSuite: false, + browsers: ['Chrome'], }); }; diff --git a/imxweb/projects/qer-app-operationssupport/package.json b/imxweb/projects/qer-app-operationssupport/package.json index 4cfb5b054..8e635b684 100644 --- a/imxweb/projects/qer-app-operationssupport/package.json +++ b/imxweb/projects/qer-app-operationssupport/package.json @@ -1,9 +1,7 @@ { "name": "qer-app-operationssupport", - "version": "9.2.1", + "version": "9.3.0", "private": true, "description": "One Identity Manager Operations Support Web Portal", - "peerDependencies": { - - } + "peerDependencies": {} } diff --git a/imxweb/projects/qer-app-operationssupport/prebuild.js b/imxweb/projects/qer-app-operationssupport/prebuild.js new file mode 100644 index 000000000..ba107f717 --- /dev/null +++ b/imxweb/projects/qer-app-operationssupport/prebuild.js @@ -0,0 +1,6 @@ +const fs = require('fs'); +const src = 'dist/uci'; +const dest = 'html/qer-app-operationssupport/uci'; + +console.log(`Copy uci from ${src} folder to ${dest}`); +fs.cpSync(src, dest, { recursive: true }); diff --git a/imxweb/projects/qer-app-operationssupport/project.json b/imxweb/projects/qer-app-operationssupport/project.json new file mode 100644 index 000000000..5fef79b68 --- /dev/null +++ b/imxweb/projects/qer-app-operationssupport/project.json @@ -0,0 +1,221 @@ +{ + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "name": "qer-app-operationssupport", + "sourceRoot": "projects/qer-app-operationssupport/src", + "projectType": "application", + "prefix": "imx", + "generators": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "options": { + "aot": true, + "allowedCommonJsDependencies": [ + "lodash", + "highlight.js", + "file-saver", + "billboard.js", + "moment-timezone", + "core-js/fn/map", + "core-js/fn/set", + "core-js/fn/weak-map", + "core-js/fn/array/from", + "core-js/fn/object/assign", + "core-js/es/array/from", + "core-js/es/object/assign", + "core-js/es/map", + "core-js/es/set", + "core-js/es/weak-map", + "lodash.debounce", + "lodash.clamp", + "moment", + "@elemental-ui/cadence-icon/codepoints" + ], + "outputPath": "dist/qer-app-operationssupport", + "index": "projects/qer-app-operationssupport/src/index.html", + "main": "projects/qer-app-operationssupport/src/main.ts", + "polyfills": ["projects/qer-app-operationssupport/src/polyfills.ts"], + "tsConfig": "projects/qer-app-operationssupport/tsconfig.app.json", + "assets": [ + "projects/qer-app-operationssupport/src/assets", + "projects/qer-app-operationssupport/src/appconfig.json", + { + "glob": "**/*", + "input": "./shared/assets/", + "output": "./assets" + }, + { + "glob": "**/*", + "input": "./node_modules/@elemental-ui/core/assets", + "output": "./assets" + } + ], + "styles": [ + "shared/scss/styles.scss", + "projects/qer-app-operationssupport/src/styles.scss", + "projects/qbm/src/lib/styles/imx-page-title.scss" + ], + "stylePreprocessorOptions": { + "includePaths": [ + "./shared/scss", + "./node_modules", + "./node_modules/@elemental-ui/cadence-icon", + "./node_modules/@elemental-ui/core" + ] + } + }, + "configurations": { + "production": { + "fileReplacements": [ + { + "replace": "projects/qer-app-operationssupport/src/environments/environment.ts", + "with": "projects/qer-app-operationssupport/src/environments/environment.prod.ts" + } + ], + "optimization": true, + "outputHashing": "all", + "sourceMap": false, + "namedChunks": false, + "aot": true, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": true, + "budgets": [ + { + "type": "initial", + "maximumWarning": "20mb", + "maximumError": "40mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "6kb" + } + ] + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + }, + "remote-dev": { + "fileReplacements": [ + { + "replace": "projects/qer-app-operationssupport/src/environments/environment.ts", + "with": "../imxweb_envs/qer-app-operationssupport/environments/environment.remote-dev.ts" + } + ], + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + }, + "remote-qs": { + "fileReplacements": [ + { + "replace": "projects/qer-app-operationssupport/src/environments/environment.ts", + "with": "../imxweb_envs/qer-app-operationssupport/environments/environment.remote-qs.ts" + } + ], + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + }, + "es5": { + "tsConfig": "./projects/qer-app-operationssupport/tsconfig-es5.app.json" + } + }, + "outputs": ["{options.outputPath}"], + "dependsOn": ["^build", "prebuild"] + }, + "prebuild": { + "executor": "nx:run-commands", + "options": { + "commands": ["node prebuild.js qer-app-operationssupport"] + }, + "dependsOn": [ + { + "projects": ["dpr", "uci"], + "target": "build" + } + ], + "cache": false + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "options": { + "browserTarget": "qer-app-operationssupport:build", + "disableHostCheck": true + }, + "configurations": { + "production": { + "browserTarget": "qer-app-operationssupport:build:production" + }, + "development": { + "browserTarget": "qer-app-operationssupport:build:development" + }, + "remote-dev": { + "browserTarget": "qer-app-operationssupport:build:remote-dev" + }, + "remote-qs": { + "browserTarget": "qer-app-operationssupport:build:remote-dev" + }, + "es5": { + "browserTarget": "qer-app-operationssupport:build:es5" + } + } + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "qer-app-operationssupport:build" + } + }, + "test": { + "executor": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/qer-app-operationssupport/src/test.ts", + "polyfills": "projects/qer-app-operationssupport/src/polyfills.ts", + "tsConfig": "projects/qer-app-operationssupport/tsconfig.spec.json", + "karmaConfig": "projects/qer-app-operationssupport/karma.conf.js", + "styles": ["projects/qer-app-operationssupport/src/styles.scss", "shared/scss/styles.scss"], + "stylePreprocessorOptions": { + "includePaths": [ + "./shared/assets", + "./shared/scss", + "./node_modules", + "./node_modules/@elemental-ui/cadence-icon", + "./node_modules/@elemental-ui/core" + ] + }, + "assets": [ + "projects/qer-app-operationssupport/src/assets", + "projects/qer-app-operationssupport/src/appconfig.json", + { + "glob": "**/*", + "input": "./shared/assets/", + "output": "./assets" + } + ] + } + }, + "lint": { + "executor": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": ["projects/qer-app-operationssupport/tsconfig.app.json", "projects/qer-app-operationssupport/tsconfig.spec.json"], + "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + } + } + } +} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/app-routing.module.ts b/imxweb/projects/qer-app-operationssupport/src/app/app-routing.module.ts index 53736d677..f13da3e4f 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/app-routing.module.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/app-routing.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,37 +25,37 @@ */ import { NgModule } from '@angular/core'; -import { Routes, RouterModule } from '@angular/router'; +import { RouterModule, Routes } from '@angular/router'; +import { OutstandingComponent } from 'dpr'; import { AuthenticationGuardService, LoginComponent, RouteGuardService } from 'qbm'; -import { ObjectOverviewComponent } from './object-overview/object-overview.component'; -import { JobsComponent } from './processes/jobs/jobs.component'; -import { JournalComponent } from './journal/journal.component'; -import { UnresolvedRefsComponent } from './unresolved-refs/unresolved-refs.component'; +import { DashboardComponent } from './dashboard/dashboard.component'; +import { DataChangesComponent } from './data-changes/data-changes.component'; +import { DbQueueComponent } from './db-queue/db-queue.component'; +import { OutstandingManagerGuardService } from './guards/outstanding-manager-guard.service'; +import { SystemStatusRouteGuardService } from './guards/system-status-route-guard.service'; import { SystemStatusComponent } from './information/system-status/system-status.component'; -import { WebApplicationsComponent } from './web-applications/web-applications.component'; -import { ServiceAvailabilityComponent } from './service-report/service-availability.component'; -import { ServicesInactiveComponent } from './service-report/services-inactive.component'; +import { JournalComponent } from './journal/journal.component'; +import { ObjectOverviewComponent } from './object-overview/object-overview.component'; import { FrozenJobsComponent } from './processes/frozen-jobs/frozen-jobs.component'; import { JobChainsComponent } from './processes/job-chains/job-chains.component'; import { JobHistoryComponent } from './processes/job-history/job-history.component'; import { JobPerformanceComponent } from './processes/job-performance/job-performance.component'; -import { DashboardComponent } from './dashboard/dashboard.component'; +import { JobsComponent } from './processes/jobs/jobs.component'; +import { ObjectsByIdComponent } from './processes/objects-by-id/objects-by-id.component'; +import { ServiceAvailabilityComponent } from './service-report/service-availability.component'; +import { ServicesInactiveComponent } from './service-report/services-inactive.component'; import { SyncInformationComponent } from './sync/sync-information/sync-information.component'; import { SyncJournalComponent } from './sync/sync-journal/sync-journal.component'; -import { OutstandingComponent } from 'dpr'; -import { SystemStatusRouteGuardService } from './guards/system-status-route-guard.service'; -import { OutstandingManagerGuardService } from './guards/outstanding-manager-guard.service'; -import { ObjectsByIdComponent } from './processes/objects-by-id/objects-by-id.component'; -import { DataChangesComponent } from './data-changes/data-changes.component'; -import { DbQueueComponent } from './db-queue/db-queue.component'; +import { UnresolvedRefsComponent } from './unresolved-refs/unresolved-refs.component'; +import { WebApplicationsComponent } from './web-applications/web-applications.component'; const routes: Routes = [ { path: '', component: LoginComponent, canActivate: [AuthenticationGuardService], - resolve: [RouteGuardService] + resolve: [RouteGuardService], }, { path: 'start', @@ -182,7 +182,7 @@ const routes: Routes = [ path: 'DataChanges', component: DataChangesComponent, canActivate: [RouteGuardService], - resolve: [RouteGuardService] + resolve: [RouteGuardService], }, { path: 'DbQueue', @@ -190,11 +190,11 @@ const routes: Routes = [ canActivate: [RouteGuardService], resolve: [RouteGuardService], }, - { path: '**', redirectTo: 'start' } + { path: '**', redirectTo: 'start' }, ]; @NgModule({ - imports: [RouterModule.forRoot(routes, { useHash: true, relativeLinkResolution: 'legacy' })], + imports: [RouterModule.forRoot(routes, { useHash: true })], exports: [RouterModule], }) export class AppRoutingModule {} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/app.component.html b/imxweb/projects/qer-app-operationssupport/src/app/app.component.html index 5bb4e9beb..46ce38a81 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/app.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/app.component.html @@ -1,7 +1,7 @@
    - + @@ -9,7 +9,6 @@
    -
    -
    \ No newline at end of file +
    diff --git a/imxweb/projects/qer-app-operationssupport/src/app/app.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/app.component.scss index f2f2404b6..8caa5f7be 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/app.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/app.component.scss @@ -1,5 +1,5 @@ -@import "variables.scss"; - +@import 'base/variables'; +@import '../../../../shared/scss/components/top-navigation.scss'; .page { display: flex; flex-direction: column; @@ -7,7 +7,7 @@ height: 100vh; } -.page .mat-tab-links { +.page .mat-mdc-tab-links { margin-left: 31px; } @@ -16,7 +16,7 @@ flex-direction: column; overflow: hidden; height: inherit; - margin: 40px 60px; + margin: 24px; &--full-page { margin: 0; @@ -38,20 +38,3 @@ } } } - -@media only screen and (max-width: 768px) { - - .eui-top-navigation-mobile { - .eui-top-navigation-mobile-footer { - .mat-button { - &.imx-masthead--icon-button { - width: 100%; - - .mat-button-wrapper span { - display: inline-block; - } - } - } - } - } -} \ No newline at end of file diff --git a/imxweb/projects/qer-app-operationssupport/src/app/app.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/app.component.ts index 8a49b6ba1..33799dac3 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/app.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/app.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,26 +25,29 @@ */ import { OverlayRef } from '@angular/cdk/overlay'; -import { Component, OnInit, OnDestroy, ErrorHandler } from '@angular/core'; -import { Router, RouterEvent, NavigationStart, NavigationEnd, NavigationError, NavigationCancel, EventType } from '@angular/router'; +import { Component, ErrorHandler, OnDestroy, OnInit } from '@angular/core'; +import { Event, EventType, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, Router, RouterEvent } from '@angular/router'; import { EuiLoadingService, EuiTheme, EuiThemeService, EuiTopNavigationItem } from '@elemental-ui/core'; import { Subscription } from 'rxjs'; +import { MatDialog } from '@angular/material/dialog'; +import { FeatureConfig, ProfileSettings } from '@imx-modules/imx-api-qer'; import { - MenuItem, AuthenticationService, + ClassloggerService, + ConfirmationService, ISessionState, + ImxTranslationProviderService, MenuService, + Message, SettingsService, - imx_SessionService, SplashService, - ImxTranslationProviderService, + UserMessageService, + imx_SessionService, } from 'qbm'; import { FeatureConfigService, OpSupportUserService, QerApiService, SettingsComponent } from 'qer'; -import { FeatureConfig, ProfileSettings } from 'imx-api-qer'; + import { isOutstandingManager } from './permissions/permissions-helper'; -import { MatDialog } from '@angular/material/dialog'; -import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'imx-root', @@ -55,12 +58,13 @@ export class AppComponent implements OnInit, OnDestroy { public menuItems: EuiTopNavigationItem[]; public isLoggedIn = false; public hideMenu = false; - public hideUserMessage = false; public showPageContent = true; + public message: Message | undefined; private routerStatus: EventType; private readonly subscriptions: Subscription[] = []; constructor( + private readonly logger: ClassloggerService, private readonly authentication: AuthenticationService, private readonly busyService: EuiLoadingService, private readonly router: Router, @@ -75,7 +79,8 @@ export class AppComponent implements OnInit, OnDestroy { private readonly themeService: EuiThemeService, private readonly errorHandler: ErrorHandler, private readonly translationProvider: ImxTranslationProviderService, - private readonly translateService: TranslateService + private readonly confirmationService: ConfirmationService, + private readonly userMessageService: UserMessageService, ) { this.subscriptions.push( this.authentication.onSessionResponse.subscribe(async (sessionState: ISessionState) => { @@ -88,15 +93,20 @@ export class AppComponent implements OnInit, OnDestroy { } } - this.isLoggedIn = sessionState.IsLoggedIn; + this.isLoggedIn = sessionState.IsLoggedIn ?? false; if (this.isLoggedIn) { - const isUseProfileLangChecked = (await this.qerClient.v2Client.opsupport_profile_get()).UseProfileLanguage ?? false; - // Set session culture if isUseProfileLangChecked is true, set browser culture otherwise + const isUseProfileLangChecked = (await this.qerClient.client.opsupport_profile_get()).UseProfileLanguage ?? false; + // Set session culture if isUseProfileLangChecked is true if (isUseProfileLangChecked) { - await this.translationProvider.init(sessionState.culture, sessionState.cultureFormat); - } else { - const browserCulture = this.translateService.getBrowserCultureLang(); - await this.translationProvider.init(browserCulture); + // Use culture if available, if not fetch + const culture = sessionState.culture + ? sessionState.culture + : (await this.qerClient.client.opsupport_profile_person_get())?.ProfileLanguage; + // If culture is found, use it, otherwise fallback to the app default + if (culture) { + this.logger.debug(this, `ProfileLangChecked is true, culture available: Setting ${culture} as profile language`); + await this.translationProvider.reinit(culture, sessionState.cultureFormat ?? culture, this.router); + } } // Close the splash screen that opened in app service initialisation @@ -110,19 +120,30 @@ export class AppComponent implements OnInit, OnDestroy { const groupInfo = await userModelService.getGroups(); this.menuItems = await this.menuService.getMenuItems( [], - groupInfo.map((group) => group.Name), - true + groupInfo.filter((group) => !!group.Name).map((group) => group.Name) as string[], + true, ); - this.applyProfileSettings(); + await this.applyProfileSettings(); } - }) + }), ); this.subscriptions.push( this.authentication.onSessionResponse.subscribe(async (sessionState: ISessionState) => { - this.isLoggedIn = sessionState.IsLoggedIn; - }) + this.isLoggedIn = sessionState.IsLoggedIn ?? false; + }), + ); + + this.subscriptions.push( + this.userMessageService.subject.subscribe((message) => { + this.message = message; + if (!!this.message && this.message.type === 'error' && !this.message.target) { + this.confirmationService.showErrorMessage({ + Message: this.message?.text, + }); + } + }), ); this.setupRouter(); @@ -143,17 +164,15 @@ export class AppComponent implements OnInit, OnDestroy { private setupRouter(): void { let overlayRef: OverlayRef; - this.router.events.subscribe((event: RouterEvent) => { + this.router.events.subscribe((event: Event & RouterEvent) => { if (event instanceof NavigationStart) { this.routerStatus = event.type; - this.hideUserMessage = true; if (this.isLoggedIn && event.url === '/') { // show the splash screen, when the user logs out! this.splash.init({ applicationName: 'Operations Support Web Portal' }); } } if (event instanceof NavigationEnd || event instanceof NavigationCancel || event instanceof NavigationError) { - this.hideUserMessage = false; this.routerStatus = event.type; this.hideMenu = event.url === '/'; this.showPageContent = true; @@ -181,7 +200,7 @@ export class AppComponent implements OnInit, OnDestroy { }, (__: string[], groups: string[]) => { if (!groups.includes('QER_4_OperationsSupport')) { - return null; + return undefined; } const menu = { @@ -220,7 +239,7 @@ export class AppComponent implements OnInit, OnDestroy { }, (__: string[], groups: string[]) => { if (!groups.includes('QER_4_OperationsSupport')) { - return null; + return undefined; } const menu = { @@ -239,7 +258,7 @@ export class AppComponent implements OnInit, OnDestroy { }, (__: string[], groups: string[]) => { if (!groups.includes('QER_4_OperationsSupport')) { - return null; + return undefined; } const menu = { id: 'OpsWeb_ROOT_Synchronization', @@ -274,7 +293,7 @@ export class AppComponent implements OnInit, OnDestroy { }, (__: string[], groups: string[]) => { if (!groups.includes('QER_4_OperationsSupport')) { - return null; + return undefined; } const menu = { id: 'OpsWeb_ROOT_System', @@ -307,10 +326,10 @@ export class AppComponent implements OnInit, OnDestroy { }); } return menu; - } + }, ); - return null; + return undefined; } private async applyProfileSettings() { diff --git a/imxweb/projects/qer-app-operationssupport/src/app/app.module.ts b/imxweb/projects/qer-app-operationssupport/src/app/app.module.ts index a24036ff5..24dfb6745 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/app.module.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/app.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,54 +24,55 @@ * */ +import { APP_INITIALIZER, ErrorHandler, NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; -import { NgModule, APP_INITIALIZER, ErrorHandler } from '@angular/core'; -import { HttpClientModule } from '@angular/common/http'; -import { TranslateModule, TranslateLoader, MissingTranslationHandler, TranslateService } from '@ngx-translate/core'; +import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; import { MatPaginatorIntl } from '@angular/material/paginator'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; +import { MissingTranslationHandler, TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; import { LoggerModule, NgxLoggerLevel } from 'ngx-logger'; +import { OutstandingModule } from 'dpr'; +import { RECAPTCHA_V3_SITE_KEY } from 'ng-recaptcha-2'; import { - ImxTranslateLoader, - ImxMissingTranslationHandler, + AuthenticationModule, + CustomThemeModule, GlobalErrorHandler, - Paginator, - OpsupportDbObjectService, + ImxMissingTranslationHandler, + ImxTranslateLoader, LdsReplacePipe, - MenuModule, MastHeadModule, - UserMessageModule, - AuthenticationModule, + OpsupportDbObjectService, + Paginator, RouteGuardService, - CustomThemeModule, SqlWizardApiService, SqlWizardModule, + UserMessageModule, } from 'qbm'; -import { OutstandingModule } from 'dpr'; +import { OpsModule } from 'qer'; +import appConfigJson from '../appconfig.json'; +import { environment } from '../environments/environment'; import { AppRoutingModule } from './app-routing.module'; -import { SyncModule } from './sync/sync.module'; -import { ObjectOverviewModule } from './object-overview/object-overview.module'; -import { WebApplicationsModule } from './web-applications/web-applications.module'; -import { JournalModule } from './journal/journal.module'; -import { UnresolvedRefsModule } from './unresolved-refs/unresolved-refs.module'; -import { DashboardModule } from './dashboard/dashboard.module'; -import { SystemOverviewModule } from './information/system-overview/system-overview.module'; -import { SystemStatusModule } from './information/system-status/system-status.module'; -import { ProcessesModule } from './processes/processes.module'; import { AppComponent } from './app.component'; import { AppService } from './app.service'; -import { environment } from '../environments/environment'; -import appConfigJson from '../appconfig.json'; +import { OpsSqlWizardApiService } from './base/ops-sql-wizard-api.service'; +import { DashboardModule } from './dashboard/dashboard.module'; import { DataChangesModule } from './data-changes/data-changes.module'; import { DbQueueModule } from './db-queue/db-queue.module'; -import { OpsSqlWizardApiService } from './base/ops-sql-wizard-api.service'; -import { QerModule } from 'qer'; +import { SystemOverviewModule } from './information/system-overview/system-overview.module'; +import { SystemStatusModule } from './information/system-status/system-status.module'; +import { JournalModule } from './journal/journal.module'; +import { ObjectOverviewModule } from './object-overview/object-overview.module'; +import { ProcessesModule } from './processes/processes.module'; +import { SyncModule } from './sync/sync.module'; +import { UnresolvedRefsModule } from './unresolved-refs/unresolved-refs.module'; +import { WebApplicationsModule } from './web-applications/web-applications.module'; @NgModule({ declarations: [AppComponent], + bootstrap: [AppComponent], imports: [ AppRoutingModule, AuthenticationModule, @@ -79,10 +80,8 @@ import { QerModule } from 'qer'; BrowserModule, EuiCoreModule, EuiMaterialModule, - HttpClientModule, LoggerModule.forRoot({ level: NgxLoggerLevel.DEBUG, serverLogLevel: NgxLoggerLevel.OFF }), MastHeadModule, - MenuModule, DbQueueModule, TranslateModule.forRoot({ loader: { @@ -107,8 +106,8 @@ import { QerModule } from 'qer'; ProcessesModule, OutstandingModule, DataChangesModule, - QerModule, - SqlWizardModule + OpsModule, + SqlWizardModule, ], providers: [ { provide: 'environment', useValue: environment }, @@ -134,7 +133,14 @@ import { QerModule } from 'qer'; provide: SqlWizardApiService, useClass: OpsSqlWizardApiService, }, + { + provide: RECAPTCHA_V3_SITE_KEY, + useFactory: (config: AppService) => { + return config.recaptchaSiteKeyV3; + }, + deps: [AppService], + }, + provideHttpClient(withInterceptorsFromDi()), ], - bootstrap: [AppComponent], }) export class AppModule {} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/app.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/app.service.ts index 173de81a8..9bc642452 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/app.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/app.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,36 +24,33 @@ * */ -import { Injectable } from '@angular/core'; +import { Injectable, Injector, createNgModule } from '@angular/core'; import { Title } from '@angular/platform-browser'; import { TranslateService } from '@ngx-translate/core'; -import { Globals } from 'imx-qbm-dbts'; +import { ImxConfig, TypedClient } from '@imx-modules/imx-api-qbm'; +import { Globals } from '@imx-modules/imx-qbm-dbts'; import { AppConfigService, - ExtService, - ClassloggerService, + AuthenticationService, + CaptchaService, CdrRegistryService, + ClassloggerService, + ExtService, ImxTranslationProviderService, - imx_SessionService, - AuthenticationService, - PluginLoaderService, SplashService, - SystemInfoService + SystemInfoService, + imx_SessionService, } from 'qbm'; -import { SystemOverviewComponent } from './information/system-overview/system-overview.component'; import { environment } from '../environments/environment'; -import { TypedClient } from 'imx-api-qbm'; - -import * as QBM from 'qbm'; -import * as QER from 'qer'; - -declare var SystemJS: any; +import { SystemOverviewComponent } from './information/system-overview/system-overview.component'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class AppService { + public recaptchaSiteKeyV3: string | null = null; + private imxConfig: ImxConfig; constructor( public readonly registry: CdrRegistryService, private readonly logger: ClassloggerService, @@ -64,21 +61,22 @@ export class AppService { private readonly translationProvider: ImxTranslationProviderService, private readonly title: Title, private readonly extService: ExtService, - private readonly pluginLoader: PluginLoaderService, private readonly authentication: AuthenticationService, private readonly splash: SplashService, - ) { } - + private readonly injector: Injector, + private readonly captchaService: CaptchaService, + ) {} public async init(): Promise { this.showSplash(); await this.config.init(environment.clientUrl); - this.translateService.addLangs(this.config.Config.Translation.Langs); - const browserCulture = this.translateService.getBrowserCultureLang(); - this.logger.debug(this, `Set ${browserCulture} as default language`); - this.translateService.setDefaultLang(browserCulture); - await this.translateService.use(browserCulture).toPromise(); + if (this.config.Config.Translation?.Langs) { + this.translateService.addLangs(this.config.Config.Translation.Langs); + } + await this.translationProvider.init(); + + this.imxConfig = await this.systemInfoService.getImxConfig(); this.translateService.onLangChange.subscribe(() => { this.setTitle(); @@ -87,20 +85,21 @@ export class AppService { this.setTitle(); this.extService.register('SystemOverview', { - instance: SystemOverviewComponent + instance: SystemOverviewComponent, }); this.session.TypedClient = new TypedClient(this.config.v2client, this.translationProvider); - SystemJS.set('qbm', SystemJS.newModule(QBM)); - SystemJS.set('qer', SystemJS.newModule(QER)); - - await this.pluginLoader.loadModules(environment.appName); + await this.loadModules(environment.appName); + if (this.imxConfig.RecaptchaPublicKey) { + this.captchaService.enableReCaptcha(this.imxConfig.RecaptchaPublicKey); + this.recaptchaSiteKeyV3 = this.imxConfig.RecaptchaPublicKey; + } + this.captchaService.captchaImageUrl = 'opsupport/captchaimage'; } private async setTitle(): Promise { - const imxConfig = await this.systemInfoService.getImxConfig(); - const name = imxConfig.ProductName || Globals.QIM_ProductNameFull; + const name = this.imxConfig.ProductName || Globals.QIM_ProductNameFull; this.config.Config.Title = await this.translateService.get('#LDS#Heading Operations Support Web Portal').toPromise(); const title = `${name} ${this.config.Config.Title}`; this.title.setTitle(title); @@ -126,4 +125,42 @@ export class AppService { const loadingMsg = await this.translateService.get('#LDS#Loading...').toPromise(); this.splash.update({ applicationName: title, message: loadingMsg }); } + + private async loadModules(appName: string): Promise { + const apps = await this.session.Client.imx_applications_get(); + + const appInfo = apps.filter((app) => app.Name === appName)[0]; + + this.logger.debug(this, `▶️ Found config section for ${appInfo.DisplayName}`); + + if (appInfo.PlugIns == null || appInfo.PlugIns.length === 0) { + this.logger.debug(this, `❌ No plugins found`); + return; + } + + this.logger.debug(this, `▶️ Found ${appInfo.PlugIns.length} plugin(s)`); + + for (const plugin of appInfo.PlugIns) { + if (!plugin.Name || !plugin.Container) { + this.logger.debug(this, `❌ Malformed plugin: ${plugin.Container}`); + continue; + } + this.logger.debug(this, `⚙️ Plugin: ${plugin.Container}`); + + try { + this.logger.debug(this, '▶️ Importing module. DEV mode.'); + await import(`html/qer-app-operationssupport/${plugin.Container}/fesm2022/${plugin.Container}.mjs`) + .then((m) => { + if (plugin.Name) { + createNgModule(m[plugin.Name], this.injector); + } + }) + .catch((error) => + this.logger.error(this, `💥 Loading of ${plugin.Name} (${plugin.Container}) failed with the following error: ${error.message}`), + ); + } catch (e) { + this.logger.error(this, `💥 Loading of ${plugin.Name} (${plugin.Container}) failed with the following error: ${e.message}`); + } + } + } } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/base/ops-sql-wizard-api.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/base/ops-sql-wizard-api.service.ts index 9b21fd67c..e27a2f11b 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/base/ops-sql-wizard-api.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/base/ops-sql-wizard-api.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,11 +25,11 @@ */ import { Injectable } from '@angular/core'; -import { FilterProperty, CollectionLoadParameters, EntityCollectionData } from 'imx-qbm-dbts'; +import { FilterProperty, CollectionLoadParameters, EntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { SqlWizardApiService } from 'qbm'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class OpsSqlWizardApiService extends SqlWizardApiService { public implemented: boolean = false; @@ -38,7 +38,7 @@ export class OpsSqlWizardApiService extends SqlWizardApiService { return new Promise((resolve) => resolve([])); } getCandidates(parentTable: string, options?: CollectionLoadParameters): Promise { - return new Promise((resolve) => resolve({TotalCount: 0})); + return new Promise((resolve) => resolve({ TotalCount: 0 })); } constructor() { diff --git a/imxweb/projects/qer-app-operationssupport/src/app/base/subscription.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/base/subscription.service.ts index 7d47905a1..8bb4d9dec 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/base/subscription.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/base/subscription.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -39,9 +39,7 @@ export abstract class SubscriptionService { public subscribe(updateInterval: number = 30000): void { this.updateItems(); - this.refreshTimer = interval(updateInterval).subscribe(_ => - this.updateItems() - ); + this.refreshTimer = interval(updateInterval).subscribe((_) => this.updateItems()); } public unsubscribe(): void { diff --git a/imxweb/projects/qer-app-operationssupport/src/app/dashboard/dashboard.component.html b/imxweb/projects/qer-app-operationssupport/src/app/dashboard/dashboard.component.html index 825c922ce..027faff45 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/dashboard/dashboard.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/dashboard/dashboard.component.html @@ -1,11 +1,11 @@ -

    - {{'#LDS#Welcome' | translate}} -

    +

    + {{ '#LDS#Welcome' | translate }} +

    - - - - - - -
    \ No newline at end of file + + + + + + +
    diff --git a/imxweb/projects/qer-app-operationssupport/src/app/dashboard/dashboard.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/dashboard/dashboard.component.scss index 77c9fa272..d11236459 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/dashboard/dashboard.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/dashboard/dashboard.component.scss @@ -1,3 +1,3 @@ :host { overflow: auto; -} \ No newline at end of file +} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/dashboard/dashboard.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/dashboard/dashboard.component.ts index 8b0e07c3d..4af9e04be 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/dashboard/dashboard.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/dashboard/dashboard.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,16 +29,14 @@ import { OpSupportUserService } from 'qer'; @Component({ templateUrl: './dashboard.component.html', - styleUrls: ['./dashboard.component.scss'] + styleUrls: ['./dashboard.component.scss'], }) export class DashboardComponent implements OnInit { - public isOperationsSupporter: boolean; - constructor(private readonly userService: OpSupportUserService) { - } + constructor(private readonly userService: OpSupportUserService) {} public async ngOnInit() { - this.isOperationsSupporter = (await this.userService.getGroups()).map(g => g.Name).includes("QER_4_OperationsSupport"); + this.isOperationsSupporter = (await this.userService.getGroups()).map((g) => g.Name).includes('QER_4_OperationsSupport'); } } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/dashboard/dashboard.module.ts b/imxweb/projects/qer-app-operationssupport/src/app/dashboard/dashboard.module.ts index eec6f2d89..844aee136 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/dashboard/dashboard.module.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/dashboard/dashboard.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -36,13 +36,6 @@ import { ServiceReportModule } from '../service-report/service-report.module'; @NgModule({ declarations: [DashboardComponent], - imports: [ - CommonModule, - ObjectSearchModule, - NotificationsModule, - ServiceIssuesModule, - ServiceReportModule, - TranslateModule - ] + imports: [CommonModule, ObjectSearchModule, NotificationsModule, ServiceIssuesModule, ServiceReportModule, TranslateModule], }) -export class DashboardModule { } +export class DashboardModule {} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes-sidesheet/data-changes-sidesheet.component.html b/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes-sidesheet/data-changes-sidesheet.component.html index 1d30c8d31..fc491582d 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes-sidesheet/data-changes-sidesheet.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes-sidesheet/data-changes-sidesheet.component.html @@ -1,15 +1,15 @@
    -
    +
    - +
    -
    +
    @@ -17,17 +17,17 @@ - {{column.ColumnDisplay}} + {{ column.ColumnDisplay }} - + - + diff --git a/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes-sidesheet/data-changes-sidesheet.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes-sidesheet/data-changes-sidesheet.component.scss index 781140e5e..51cd954b5 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes-sidesheet/data-changes-sidesheet.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes-sidesheet/data-changes-sidesheet.component.scss @@ -1,4 +1,3 @@ - :host { .wrapper { height: 100%; diff --git a/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes-sidesheet/data-changes-sidesheet.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes-sidesheet/data-changes-sidesheet.component.ts index ccdcb7645..8f4ee6198 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes-sidesheet/data-changes-sidesheet.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes-sidesheet/data-changes-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,9 +26,9 @@ import { Component, Inject, OnInit } from '@angular/core'; import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; +import { HistoryOperation, HistoryOperationColumn } from '@imx-modules/imx-api-qbm'; +import { IEntityColumn, ValType } from '@imx-modules/imx-qbm-dbts'; import { TranslateService } from '@ngx-translate/core'; -import { HistoryOperation, HistoryOperationColumn } from 'imx-api-qbm'; -import { IEntityColumn, ValType } from 'imx-qbm-dbts'; import { BaseReadonlyCdr, ColumnDependentReference, EntityService } from 'qbm'; import { Subscription } from 'rxjs'; @@ -39,6 +39,7 @@ import { Subscription } from 'rxjs'; }) export class DataChangesSidesheetComponent implements OnInit { public isOpening = true; + public isMulti: boolean; private openSub$: Subscription; public cdrList: ColumnDependentReference[] = []; @@ -46,17 +47,16 @@ export class DataChangesSidesheetComponent implements OnInit { @Inject(EUI_SIDESHEET_DATA) public data: HistoryOperation, private sidesheetRef: EuiSidesheetRef, private entityService: EntityService, - private translate: TranslateService + private translate: TranslateService, ) {} - public get isMulti(): boolean { - return this.data?.Columns && this.data.Columns.length > 1; - } - public ngOnInit(): void { - this.openSub$ = this.sidesheetRef.componentInstance.onOpen().subscribe((_) => { - this.isOpening = false; - }); + this.isMulti = (this.data?.Columns && this.data.Columns.length > 1) ?? false; + if (this.sidesheetRef.componentInstance) { + this.openSub$ = this.sidesheetRef.componentInstance.onOpen().subscribe((_) => { + this.isOpening = false; + }); + } } public ngOnDestroy(): void { @@ -65,9 +65,9 @@ export class DataChangesSidesheetComponent implements OnInit { public getCdrList(column: HistoryOperationColumn): ColumnDependentReference[] { return [ - this.buildEntiyColumn('name', this.translate.instant('#LDS#Column name'), column.ColumnDisplay), - this.buildEntiyColumn('old', this.translate.instant('#LDS#Old value'), column.OldValueDisplay), - this.buildEntiyColumn('new', this.translate.instant('#LDS#New value'), column.NewValueDisplay), + this.buildEntiyColumn('name', this.translate.instant('#LDS#Column name'), column.ColumnDisplay ?? ''), + this.buildEntiyColumn('old', this.translate.instant('#LDS#Old value'), column.OldValueDisplay ?? ''), + this.buildEntiyColumn('new', this.translate.instant('#LDS#New value'), column.NewValueDisplay ?? ''), ].map((col) => new BaseReadonlyCdr(col)); } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes.component.html b/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes.component.html index 49e92584d..b4c620767 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes.component.html @@ -1,70 +1,131 @@ -

    {{ '#LDS#Heading Operation History' | translate }}

    +

    {{ '#LDS#Heading Operation History' | translate }}

    - - + + {{ '#LDS#User' | translate }} - + [value]="'ChangeType'" + > {{ '#LDS#Type of operation' | translate }}
    - - {{ '#LDS#User name' | translate }} - - - - - {{ '#LDS#Type of operation' | translate }} - - {{changeType.title}} - - - - - {{ '#LDS#From' | translate }} - - - - - - - {{ '#LDS#To' | translate }} - - - - - - + @if (isEnabledUsername) { + + {{ '#LDS#User name' | translate }} + + + } + @if (isEnabledChangeType) { + + + } + + @if (fromDateControl.getErrors()['matDatepickerMin'] || fromDateControl.getErrors()['matDatepickerMax']) { + {{ '#LDS#Specify a "From" date that lies before the "To" date.' | translate }} + } + + @if (isEnabledChangeType) { + + @if (toDateControl.getErrors()['matDatepickerMin'] || toDateControl.getErrors()['matDatepickerMax']) { + {{ '#LDS#Specify a "To" date that lies after the "From" date.' | translate }} + } + + } +
    - - - - - - - -
    {{ column.title | translate }} - - {{ column.value(row) }} - - - - {{ column.value(row) }} - {{ column.value(row) }} - {{ column.value(row) }} - - -
    - + @if (busyService.isBusy) { + + } @else { + @if (columns && !!dataSource && !!dataSource.data.length) { +
    + + @for (column of columns; track column.name) { + + + + + } + + +
    {{ column.title | translate }} + @switch (column.name) { + @case ('ChangeType') { + @switch (row.ChangeType) { + @case (changeTypeEnum.Insert) { + {{ column.value(row) }} + } + @case (changeTypeEnum.Update) { + {{ column.value(row) }} + } + @case (changeTypeEnum.Delete) { + {{ column.value(row) }} + } + } + } + @default { + {{ column.value(row) }} + } + } +
    +
    + } @else { +
    + +

    {{ '#LDS#No matching data' | translate }}

    +
    + } + } +
    diff --git a/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes.component.scss index 6e8539274..03af9cb1b 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes.component.scss @@ -1,72 +1,76 @@ @import '@elemental-ui/core/src/styles/_eui_palette.scss'; :host { - display: flex; - flex-direction: column; - overflow: hidden; - flex-grow: 1; + display: flex; + flex-direction: column; + overflow: hidden; + flex-grow: 1; } .search-type { - display: flex; - - >label { - align-self: center - } - - .search-type-radio-group { - display: flex; - margin: 15px 0; - align-items: flex-start; - } + display: flex; - .search-type-radio-button { - margin: 5px; - } + > label { + align-self: center; + } } .search-type-option-controls { - display: flex; - flex-direction: row; + display: flex; + flex-direction: row; + align-items: center; + gap: 8px; + .eui-form-field { + width: auto; + } + .eui-select { + min-width: 250px; + } +} - .mat-form-field { - margin: 5px; - } +eui-date-picker { + .mat-mdc-form-field { + width: 300px; + } +} - button { - align-self: center; - margin: 10px; - } +.hidden { + display: none; } .search-result-table-container { - flex: 1 1 auto; + flex: 1 1 auto; + display: flex; + flex-direction: column; + height: 100%; + overflow: hidden; + .search-result-table { + height: 100%; display: flex; flex-direction: column; overflow: auto; - margin-bottom: -20px; - - tr:hover { - background: $color-gray-5; - } + } + tr:hover { + background: $color-gray-5; + } } .eui-dark-theme { - :host { - .search-result-table-container { - tr:hover { - background: $color-gray-60; - } - } + :host { + .search-result-table-container { + tr:hover { + background: $color-gray-60; + } } + } } .eui-contrast-theme { :host { - .search-result-table-container { - tr:hover { - background: $color-gray-80; - } + .search-result-table-container { + tr:hover { + background: $color-gray-80; } + } } } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes.component.ts index 2d9ed38df..69c39d1a0 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,23 +24,29 @@ * */ -import { Component, OnInit, ViewChild } from '@angular/core'; -import { UntypedFormControl, UntypedFormGroup, ValidatorFn, Validators } from '@angular/forms'; +import { ChangeDetectorRef, Component, OnInit, ViewChild } from '@angular/core'; +import { FormGroup, UntypedFormControl, Validators } from '@angular/forms'; import { MatPaginator } from '@angular/material/paginator'; import { MatTableDataSource } from '@angular/material/table'; -import { EuiBadgeComponent, EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; +import { + EuiDatePickerComponent, + EuiLoadingService, + EuiSelectFeedbackMessages, + EuiSelectOption, + EuiSidesheetService, +} from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { OverlayRef } from '@angular/cdk/overlay'; import moment from 'moment'; -import { ChangeType as ChangeTypeEnum, HistoryOperation } from 'imx-api-qbm'; +import { ChangeType as ChangeTypeEnum, HistoryOperation } from '@imx-modules/imx-api-qbm'; +import { BusyService, calculateSidesheetWidth, SettingsService } from 'qbm'; import { DataChangesSidesheetComponent } from './data-changes-sidesheet/data-changes-sidesheet.component'; import { DataChangesService } from './data-changes.service'; export interface Column { name: string; title: string; - value: (row: HistoryOperation) => string; + value: (row: HistoryOperation) => string | undefined; } export interface SearchType { @@ -48,25 +54,6 @@ export interface SearchType { value: string; } -export interface ChangeType { - name: string; - title: string; - value: number; -} - -const searchFormValidator: ValidatorFn = (searchForm: UntypedFormGroup) => { - if (searchForm.get('backToDateFormControl').enabled && searchForm.get('backFromDateFormControl').enabled) { - if (searchForm.get('backToDateFormControl').value && searchForm.get('backFromDateFormControl').value) { - let backToDate: Date = searchForm.get('backToDateFormControl').value; - let backFromDate: Date = searchForm.get('backFromDateFormControl').value; - - if (backToDate >= backFromDate) return { isValid: false }; - } - } - - return null; -}; - @Component({ selector: 'imx-data-changes', templateUrl: './data-changes.component.html', @@ -74,134 +61,144 @@ const searchFormValidator: ValidatorFn = (searchForm: UntypedFormGroup) => { }) export class DataChangesComponent implements OnInit { @ViewChild(MatPaginator) paginator: MatPaginator; + @ViewChild('fromDateControl') fromDateControl: EuiDatePickerComponent; + @ViewChild('toDateControl') toDateControl: EuiDatePickerComponent; - public paginatorConfigurations = { - size: 20, - sizeOptions: [20, 50, 100], - showFirstLastButtons: false, - }; + public paginatorConfigurations: { size: number; sizeOptions: number[]; showFirstLastButtons: boolean }; public dataSource: MatTableDataSource; public columns: Column[]; public displayedColumns: string[]; - - public searchForm = new UntypedFormGroup( - { - usernameFormControl: new UntypedFormControl('', [Validators.required]), - backToDateFormControl: new UntypedFormControl('', [Validators.required]), - backFromDateFormControl: new UntypedFormControl('', [Validators.required]), - changeTypeFormControl: new UntypedFormControl('', [Validators.required]), - }, - { validators: searchFormValidator }, - ); + public feedbackMessages: EuiSelectFeedbackMessages; + + public searchForm = new FormGroup({ + usernameFormControl: new UntypedFormControl('', Validators.required), + changeTypeFormControl: new UntypedFormControl(['']), + fromDateFormControl: new UntypedFormControl('', { updateOn: 'blur', validators: Validators.required }), + toDateFormControl: new UntypedFormControl('', { updateOn: 'blur' }), + }); public selectedSearchType: string; - public changeTypes: ChangeType[]; + public busyService: BusyService; + + public changeTypes: EuiSelectOption[]; public badgeColor = { Insert: 'green', Update: 'orange', Delete: 'red', }; + public today: moment.Moment; + public yesterday: moment.Moment; + public get changeTypeEnum(): typeof ChangeTypeEnum { return ChangeTypeEnum; } public get isEnabledUsername(): boolean { - return this.searchForm.get('usernameFormControl').enabled; + return this.selectedSearchType === 'UserName'; } public get isEnabledChangeType(): boolean { - return this.searchForm.get('changeTypeFormControl').enabled; - } - - public get isEnabledBackTo(): boolean { - return this.searchForm.get('backToDateFormControl').enabled; + return this.selectedSearchType === 'ChangeType'; } - public get isEnabledBackFrom(): boolean { - return this.searchForm.get('backFromDateFormControl').enabled - } - - constructor( private translateService: TranslateService, private euiLoadingService: EuiLoadingService, private sidesheet: EuiSidesheetService, - private dataChangesService: DataChangesService - ) {} + private dataChangesService: DataChangesService, + public readonly settings: SettingsService, + private readonly changeDetector: ChangeDetectorRef, + ) { + this.paginatorConfigurations = { + size: this.settings.DefaultPageSize, + sizeOptions: this.settings.DefaultPageOptions, + showFirstLastButtons: true, + }; + this.today = moment(); + this.yesterday = moment().subtract(1, 'days'); + this.busyService = new BusyService(); + } public async ngOnInit(): Promise { this.selectedSearchType = 'UserName'; - this.changeTypes = this.dataChangesService.changeTypes; + this.changeTypes = this.dataChangesService.loadChangeTypes(); let culture = this.translateService.getBrowserCultureLang(); this.columns = [ { name: 'ChangeTime', title: '#LDS#Operation performed on', - value: (row: HistoryOperation) => row.ChangeTime?.toLocaleString(culture), + value: (row: HistoryOperation) => new Date(row.ChangeTime)?.toLocaleString(culture), + }, + { + name: 'ChangeType', + title: '#LDS#Type of operation', + value: (row: HistoryOperation) => this.dataChangesService.changeTypeString(row.ChangeType), }, - { name: 'ChangeType', title: '#LDS#Type of operation', value: (row: HistoryOperation) => this.dataChangesService.changeTypeString(row.ChangeType) }, { name: 'DisplayType', title: '#LDS#Object type', value: (row: HistoryOperation) => row.DisplayType }, { name: 'ObjectDisplay', title: '#LDS#Object name', value: (row: HistoryOperation) => row.ObjectDisplay }, { name: 'ProcessId', title: '#LDS#Process ID', value: (row: HistoryOperation) => row.ProcessId }, { name: 'User', title: '#LDS#Operation performed by', value: (row: HistoryOperation) => row.User }, ]; this.displayedColumns = this.columns.map((c) => c.name); + this.translateService.onLangChange.subscribe(() => { + this.loadFeedbackMessages(); + this.changeTypes = this.dataChangesService.loadChangeTypes(); + }); + + this.initSearchForm(); this.manageSearchState(); + this.updateUserNameControls(); + this.loadFeedbackMessages(); } public searchTypeChange(): void { this.dataSource = new MatTableDataSource(); this.dataSource.paginator = this.paginator; + // update validators when changing search type, because only visible elements can be mandatory + this.searchForm.controls.usernameFormControl?.setValidators( + this.selectedSearchType.toUpperCase() === 'ChangeType'.toUpperCase() ? null : Validators.required, + ); + this.searchForm.controls.toDateFormControl?.setValidators( + this.selectedSearchType.toUpperCase() === 'ChangeType'.toUpperCase() ? Validators.required : null, + ); + this.searchForm.controls.changeTypeFormControl?.setValidators( + this.selectedSearchType.toUpperCase() === 'UserName'.toUpperCase() ? null : Validators.required, + ); this.manageSearchState(); } public manageSearchState(): void { - this.searchForm.get('usernameFormControl').reset(); - this.searchForm.get('backFromDateFormControl').reset(); - this.searchForm.get('backToDateFormControl').reset(); - this.searchForm.get('changeTypeFormControl').reset(); - - let now = new Date(); - now.setHours(0, 0, 0, 0); - - if (this.selectedSearchType.toUpperCase() === 'UserName'.toUpperCase()) { - this.searchForm.get('usernameFormControl').enable(); - this.searchForm.get('backToDateFormControl').enable(); - this.searchForm.get('backFromDateFormControl').disable(); - this.searchForm.get('changeTypeFormControl').disable(); - - this.searchForm.get('backToDateFormControl').setValue(moment(new Date(now.getTime() - 24 * 60 * 60 * 1000))); // 24 hours back from now - } else if (this.selectedSearchType.toUpperCase() === 'ChangeType'.toUpperCase()) { - this.searchForm.get('usernameFormControl').disable(); - this.searchForm.get('backToDateFormControl').enable(); - this.searchForm.get('backFromDateFormControl').enable(); - this.searchForm.get('changeTypeFormControl').enable(); - - this.searchForm.get('backToDateFormControl').setValue(moment(new Date(now.getTime() - 24 * 60 * 60 * 1000))); - this.searchForm.get('backFromDateFormControl').setValue(moment(now)); + this.searchForm.controls.usernameFormControl.reset(); + this.searchForm.controls.changeTypeFormControl.reset(); + switch (this.selectedSearchType) { + case 'UserName': + this.updateUserNameControls(); + break; + case 'ChangeType': + this.updateChangeTypeControls(); + break; } } public async loadHistoryOperationsData(): Promise { - if (this.selectedSearchType.toUpperCase() === 'UserName'.toUpperCase()) await this.loadHistoryOperationsDataByUserName(); - else if (this.selectedSearchType.toUpperCase() === 'ChangeType'.toUpperCase()) await this.loadHistoryOperationsDataByChangeType(); + if (this.selectedSearchType === 'UserName') await this.loadHistoryOperationsDataByUserName(); + else if (this.selectedSearchType === 'ChangeType') await this.loadHistoryOperationsDataByChangeType(); } public async loadHistoryOperationsDataByUserName(): Promise { - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.euiLoadingService.show())); + const isBusy = this.busyService.beginBusy(); try { this.dataSource = new MatTableDataSource(); this.dataSource.paginator = this.paginator; - if (!this.searchForm.get('usernameFormControl').value) { + if (!this.searchForm.controls.usernameFormControl.value) { return; } const historyOperationsData = await this.dataChangesService.getHistoryOperationsDataByUserName( - this.searchForm.get('usernameFormControl').value, - { backto: this.searchForm.get('backToDateFormControl').value } + this.searchForm.controls.usernameFormControl.value, + { backto: this.searchForm.controls.fromDateFormControl.value }, ); if (historyOperationsData) { @@ -209,29 +206,27 @@ export class DataChangesComponent implements OnInit { this.dataSource.paginator = this.paginator; } } finally { - setTimeout(() => this.euiLoadingService.hide(overlayRef)); + isBusy.endBusy(); } } public async loadHistoryOperationsDataByChangeType(): Promise { - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.euiLoadingService.show())); - + const isBusy = this.busyService.beginBusy(); try { this.dataSource = new MatTableDataSource(); this.dataSource.paginator = this.paginator; if ( - !this.searchForm.get('backFromDateFormControl').value || - !this.searchForm.get('backToDateFormControl').value || - !this.searchForm.get('changeTypeFormControl').value + !this.searchForm.controls.changeTypeFormControl.value || + !this.searchForm.controls.fromDateFormControl.value || + !this.searchForm.controls.toDateFormControl.value ) { return; } await this.loadHistoryDataByChangeType(); } finally { - setTimeout(() => this.euiLoadingService.hide(overlayRef)); + isBusy.endBusy(); } } @@ -239,35 +234,54 @@ export class DataChangesComponent implements OnInit { if (row.Columns && row.Columns.length > 0) { let changeType = this.dataChangesService.changeTypeString(row.ChangeType); let title = this.translateService.instant('#LDS#Heading View Operation Details') + ' (' + changeType + ')'; - let headerColour = this.badgeColor[ChangeTypeEnum[row.ChangeType]]; - - this.sidesheet.open(DataChangesSidesheetComponent, - { - title: title, - subTitle: row.ObjectDisplay, - width: 'max(400px, 40%)', - data: row, - testId: 'data-change-details-sidesheet', - headerColour: headerColour - } - ); + let headerColour = this.badgeColor[ChangeTypeEnum[row.ChangeType]] ?? 'primary'; + + this.sidesheet.open(DataChangesSidesheetComponent, { + title: title, + subTitle: row.ObjectDisplay, + width: calculateSidesheetWidth(700, 0.4), + data: row, + testId: 'data-change-details-sidesheet', + headerColour, + }); } } - private async loadHistoryDataByChangeType(): Promise { - const backFrom = this.searchForm.get('backFromDateFormControl').value.toDate() as Date; + private initSearchForm(): void { + this.searchForm.controls.fromDateFormControl.valueChanges.subscribe((val: moment.Moment) => { + //if a valid date is set, update min and max date for the other picker + if (this.toDateControl && val <= this.searchForm.controls.toDateFormControl.value) { + this.toDateControl.min = val; + this.toDateControl.max = this.today; + } + this.changeDetector.detectChanges(); + }); - backFrom.setHours(23); - backFrom.setMinutes(59); - backFrom.setSeconds(59); + this.searchForm.controls.toDateFormControl.valueChanges.subscribe((val: moment.Moment) => { + //if a valid date is set, update min and max date for the other picker + if (this.fromDateControl && val >= this.searchForm.controls.fromDateFormControl.value) { + this.fromDateControl.max = val; + this.fromDateControl.min = moment(new Date('1970-01-01Z00:00:00:000')); + this.changeDetector.detectChanges(); + } + }); + } - const sum = this.searchForm.get('changeTypeFormControl').value.reduce((aggregate, currentValue) => aggregate + currentValue, 0); + private async loadHistoryDataByChangeType(): Promise { + const backFrom = moment(this.searchForm.controls.toDateFormControl.value) + .set('hours', 23) + .set('minutes', 59) + .set('seconds', 59) + .toDate(); + const backTo = moment(this.searchForm.controls.fromDateFormControl.value).toDate(); + + const sum = this.searchForm.controls.changeTypeFormControl.value.reduce((aggregate, currentValue) => aggregate + currentValue, 0); if (sum === 0) { return; } const historyOperationsData = await this.dataChangesService.getHistoryOperationsDataByChangeType({ - backto: this.searchForm.get('backToDateFormControl').value, + backto: backTo, backfrom: backFrom, types: sum, }); @@ -277,4 +291,39 @@ export class DataChangesComponent implements OnInit { this.dataSource.paginator = this.paginator; } } + + private loadFeedbackMessages(): void { + this.feedbackMessages = { + selected: this.translateService.instant('#LDS#{{value}} selected'), + clear: this.translateService.instant('#LDS#Clear selection'), + search: this.translateService.instant('#LDS#Search'), + plusOther: this.translateService.instant('#LDS#and 1 more'), + plusOtherPlural: this.translateService.instant('#LDS#and {{value}} more'), + unsupportedCharacter: this.translateService.instant('#LDS#You are using unsupported characters.'), + noResults: this.translateService.instant('#LDS#There is no data matching your search.'), + clearAll: this.translateService.instant('#LDS#Clear selection'), + ok: this.translateService.instant('#LDS#OK'), + keyboardOptionsListAria: this.translateService.instant('#LDS#Use the arrow keys to select items.'), + }; + } + + /** + * Updates the form, so it fits the requirements for a search by user name, by initializing the values of the date picker and assuring, that the toDateControl will be valid (because it is not used). + */ + private updateUserNameControls() { + this.searchForm.controls.toDateFormControl.setValue(this.today); + this.searchForm.controls.fromDateFormControl.setValue(this.today); + if (this.toDateControl) { + this.toDateControl.min = this.today; + this.toDateControl.max = this.today; + } + } + + /** + * Updates the form, so it fits the requirements for a search by change type, by initializing the values of the date picker. + */ + private updateChangeTypeControls() { + this.searchForm.controls.toDateFormControl.setValue(this.today); + this.searchForm.controls.fromDateFormControl.setValue(this.yesterday); + } } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes.module.ts b/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes.module.ts index 394086264..1afee0070 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes.module.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,50 +24,33 @@ * */ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { DataChangesComponent } from './data-changes.component'; -import { TranslateModule } from '@ngx-translate/core'; +import { NgModule } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; +import { MatDatepickerModule } from '@angular/material/datepicker'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatExpansionModule } from '@angular/material/expansion'; +import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; +import { MatListModule } from '@angular/material/list'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatRadioModule } from '@angular/material/radio'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatButtonModule } from '@angular/material/button'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatSelectModule } from '@angular/material/select'; -import { MatDividerModule } from '@angular/material/divider'; import { MatTableModule } from '@angular/material/table'; -import {MatPaginatorModule} from '@angular/material/paginator'; -import { DataChangesService } from './data-changes.service'; +import { TranslateModule } from '@ngx-translate/core'; import { DataChangesSidesheetComponent } from './data-changes-sidesheet/data-changes-sidesheet.component'; -import {MatListModule} from '@angular/material/list'; -import {MatExpansionModule} from '@angular/material/expansion'; -import {MatCardModule} from '@angular/material/card'; -import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { DataChangesComponent } from './data-changes.component'; +import { DataChangesService } from './data-changes.service'; -import {MomentDateAdapter} from '@angular/material-moment-adapter'; - import {DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE} from '@angular/material/core'; -import { CdrModule } from 'qbm'; import { EuiCoreModule } from '@elemental-ui/core'; - -export const EUI_DATE_FORMATS = { - parse: { - dateInput: ['LL', 'L'], - }, - display: { - dateInput: 'LL', - monthYearLabel: 'MMM YYYY', - dateA11yLabel: 'LL', - monthYearA11yLabel: 'MMMM YYYY', - }, -}; +import { BusyIndicatorModule, CdrModule, EuiDateProviders } from 'qbm'; @NgModule({ - declarations: [ - DataChangesComponent, - DataChangesSidesheetComponent - ], + declarations: [DataChangesComponent, DataChangesSidesheetComponent], imports: [ CommonModule, TranslateModule, @@ -88,12 +71,9 @@ export const EUI_DATE_FORMATS = { MatExpansionModule, MatCardModule, MatProgressSpinnerModule, - CdrModule - ], - providers: [ - DataChangesService, - {provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE]}, - {provide: MAT_DATE_FORMATS, useValue: EUI_DATE_FORMATS} + BusyIndicatorModule, + CdrModule, ], + providers: [DataChangesService, ...EuiDateProviders], }) -export class DataChangesModule { } +export class DataChangesModule {} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes.service.ts index e9b969eca..ec5ba173f 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/data-changes/data-changes.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,29 +25,31 @@ */ import { Injectable } from '@angular/core'; -import { imx_SessionService } from 'qbm'; -import { ChangeType as ChangeTypeEnum, HistoryOperationsData } from 'imx-api-qbm'; +import { EuiSelectOption } from '@elemental-ui/core'; +import { ChangeType as ChangeTypeEnum, HistoryOperationsData } from '@imx-modules/imx-api-qbm'; import { TranslateService } from '@ngx-translate/core'; -import { ChangeType } from './data-changes.component'; +import { imx_SessionService } from 'qbm'; @Injectable() export class DataChangesService { - public changeTypes: ChangeType[] = [ - { name: 'Insert', title: this.translateService.instant('#LDS#Event Insert'), value: 0 }, - { name: 'Update', title: this.translateService.instant('#LDS#Event Update'), value: 1 }, - { name: 'Delete', title: this.translateService.instant('#LDS#Event Delete'), value: 2 }, - ]; + public changeTypes: EuiSelectOption[]; constructor( private session: imx_SessionService, - private translateService: TranslateService - ) {} + private translateService: TranslateService, + ) { + this.changeTypes = this.loadChangeTypes(); + } - public async getHistoryOperationsDataByUserName(username: string,options?: { backto: Date} ): Promise { - return await this.session.Client.opsupport_changeoperations_user_get(username,options); + public async getHistoryOperationsDataByUserName(username: string, options?: { backto: Date }): Promise { + return await this.session.Client.opsupport_changeoperations_user_get(username, options); } - public async getHistoryOperationsDataByChangeType(options?: { backto: Date, backfrom: Date, types: number }): Promise { + public async getHistoryOperationsDataByChangeType(options?: { + backto: Date; + backfrom: Date; + types: number; + }): Promise { return await this.session.Client.opsupport_changeoperations_time_get(options); } @@ -55,16 +57,24 @@ export class DataChangesService { switch (changeType) { case ChangeTypeEnum.Insert: return this.changeTypes.find((obj) => { - return obj.value === 0; - })?.title; + return obj.value === 1; + })?.display; case ChangeTypeEnum.Update: return this.changeTypes.find((obj) => { - return obj.value === 1; - })?.title; + return obj.value === 2; + })?.display; case ChangeTypeEnum.Delete: return this.changeTypes.find((obj) => { - return obj.value === 2; - })?.title; + return obj.value === 4; + })?.display; } } + + public loadChangeTypes(): EuiSelectOption[] { + return [ + { name: 'Insert', display: this.translateService.instant('#LDS#Event Insert'), value: 1 }, + { name: 'Update', display: this.translateService.instant('#LDS#Event Update'), value: 2 }, + { name: 'Delete', display: this.translateService.instant('#LDS#Event Delete'), value: 4 }, + ]; + } } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/db-queue/db-queue.component.html b/imxweb/projects/qer-app-operationssupport/src/app/db-queue/db-queue.component.html index 4ca6df561..8e954d805 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/db-queue/db-queue.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/db-queue/db-queue.component.html @@ -1,6 +1,6 @@ -

    +

    {{ '#LDS#Heading DBQueue' | translate }} -

    +

    [options]="['search']" (navigationStateChanged)="getData($event)" > - - + - - - - - + + + + + + + +
    - - diff --git a/imxweb/projects/qer-app-operationssupport/src/app/db-queue/db-queue.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/db-queue/db-queue.component.scss index da7f03430..171a2ee12 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/db-queue/db-queue.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/db-queue/db-queue.component.scss @@ -4,20 +4,3 @@ overflow: hidden; flex-grow: 1; } - -.imx-table-container { - flex: 1 1 auto; - display: flex; - flex-direction: column; - overflow: auto; - margin-bottom: -20px; - - > :nth-child(2) { - flex: 1 1 auto; - } -} - -.imx-refresh-button { - margin-right: 5px; - height: 50px; -} \ No newline at end of file diff --git a/imxweb/projects/qer-app-operationssupport/src/app/db-queue/db-queue.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/db-queue/db-queue.component.ts index 842049844..1a95463d5 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/db-queue/db-queue.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/db-queue/db-queue.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,7 +28,7 @@ import { OverlayRef } from '@angular/cdk/overlay'; import { Component, OnInit } from '@angular/core'; import { EuiLoadingService } from '@elemental-ui/core'; -import { CollectionLoadParameters, EntitySchema, IClientProperty, DisplayColumns } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, DisplayColumns, EntitySchema, IClientProperty } from '@imx-modules/imx-qbm-dbts'; import { DataSourceToolbarSettings } from 'qbm'; import { DbQueueService } from './db-queue.service'; @@ -38,7 +38,6 @@ import { DbQueueService } from './db-queue.service'; styleUrls: ['./db-queue.component.scss'], }) export class DbQueueComponent implements OnInit { - public dstSettings: DataSourceToolbarSettings; public readonly entitySchema: EntitySchema; public readonly DisplayColumns = DisplayColumns; @@ -46,12 +45,15 @@ export class DbQueueComponent implements OnInit { private navigationState: CollectionLoadParameters; private readonly displayedColumns: IClientProperty[]; - constructor(private readonly dbService: DbQueueService, private readonly busyService: EuiLoadingService) { + constructor( + private readonly dbService: DbQueueService, + private readonly busyService: EuiLoadingService, + ) { this.entitySchema = dbService.DbSchema; this.displayedColumns = [ this.entitySchema.Columns.UID_Task, this.entitySchema.Columns.CountProcessing, - this.entitySchema.Columns.CountWaiting + this.entitySchema.Columns.CountWaiting, ]; } @@ -59,7 +61,6 @@ export class DbQueueComponent implements OnInit { await this.getData({}); } - public onSearch(keywords: string): Promise { return this.getData({ StartIndex: 0, search: keywords }); } @@ -75,9 +76,11 @@ export class DbQueueComponent implements OnInit { dbQueue.Data = dbQueue.Data.filter((row) => { for (const column of this.displayedColumns) { - const cellValue = row.GetEntity().GetColumn(column.ColumnName).GetDisplayValue().toLowerCase(); - if (cellValue.includes(searchKeyword.toLowerCase())) { - return true; + if (column.ColumnName) { + const cellValue = row.GetEntity().GetColumn(column.ColumnName).GetDisplayValue().toLowerCase(); + if (cellValue.includes(searchKeyword.toLowerCase())) { + return true; + } } } return false; diff --git a/imxweb/projects/qer-app-operationssupport/src/app/db-queue/db-queue.module.ts b/imxweb/projects/qer-app-operationssupport/src/app/db-queue/db-queue.module.ts index 96519df96..b3eda06ec 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/db-queue/db-queue.module.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/db-queue/db-queue.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -32,17 +32,8 @@ import { EuiCoreModule } from '@elemental-ui/core'; import { TranslateModule } from '@ngx-translate/core'; import { MatButtonModule } from '@angular/material/button'; - - @NgModule({ declarations: [DbQueueComponent], - imports: [ - CommonModule, - DataSourceToolbarModule, - DataTableModule, - EuiCoreModule, - TranslateModule, - MatButtonModule - ] + imports: [CommonModule, DataSourceToolbarModule, DataTableModule, EuiCoreModule, TranslateModule, MatButtonModule], }) -export class DbQueueModule { } +export class DbQueueModule {} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/db-queue/db-queue.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/db-queue/db-queue.service.ts index c28da9f9d..90f922ae4 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/db-queue/db-queue.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/db-queue/db-queue.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,8 +25,8 @@ */ import { Injectable } from '@angular/core'; -import { OpsupportQueueDbqueue } from 'imx-api-qbm'; -import { CollectionLoadParameters, ExtendedTypedEntityCollection } from 'imx-qbm-dbts'; +import { OpsupportQueueDbqueue } from '@imx-modules/imx-api-qbm'; +import { CollectionLoadParameters, ExtendedTypedEntityCollection } from '@imx-modules/imx-qbm-dbts'; import { imx_SessionService } from 'qbm'; @Injectable({ @@ -35,7 +35,7 @@ import { imx_SessionService } from 'qbm'; export class DbQueueService { constructor(private session: imx_SessionService) {} - public get DbSchema () { + public get DbSchema() { return this.session.TypedClient.OpsupportQueueDbqueue.GetSchema(); } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/guards/outstanding-manager-guard.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/guards/outstanding-manager-guard.service.ts index 92cec76b9..38f03e7ee 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/guards/outstanding-manager-guard.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/guards/outstanding-manager-guard.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { Injectable, OnDestroy } from '@angular/core'; -import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router'; +import { Router } from '@angular/router'; import { Observable, Subscription } from 'rxjs'; import { AppConfigService, AuthenticationService, ISessionState } from 'qbm'; @@ -34,14 +34,14 @@ import { PermissionsService } from '../permissions/permissions.service'; @Injectable({ providedIn: 'root', }) -export class OutstandingManagerGuardService implements CanActivate, OnDestroy { +export class OutstandingManagerGuardService implements OnDestroy { private onSessionResponse: Subscription; constructor( private readonly permissionService: PermissionsService, private readonly authentication: AuthenticationService, private readonly appConfig: AppConfigService, - private readonly router: Router + private readonly router: Router, ) {} public canActivate(): Observable { @@ -49,8 +49,8 @@ export class OutstandingManagerGuardService implements CanActivate, OnDestroy { this.onSessionResponse = this.authentication.onSessionResponse.subscribe(async (sessionState: ISessionState) => { if (sessionState.IsLoggedIn) { const userIsOutstandingManager = await this.permissionService.isOutstandingManager(); - if (!userIsOutstandingManager) { - this.router.navigate([this.appConfig.Config.routeConfig.start], { queryParams: {} } ); + if (!userIsOutstandingManager) { + this.router.navigate([this.appConfig.Config?.routeConfig?.start], { queryParams: {} }); } observer.next(userIsOutstandingManager ? true : false); observer.complete(); diff --git a/imxweb/projects/qer-app-operationssupport/src/app/guards/system-status-route-guard.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/guards/system-status-route-guard.service.ts index 21d754d98..ebbd93e9b 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/guards/system-status-route-guard.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/guards/system-status-route-guard.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,23 +25,23 @@ */ import { Injectable, OnDestroy } from '@angular/core'; -import { CanActivate, Router } from '@angular/router'; +import { Router } from '@angular/router'; import { AppConfigService, AuthenticationService, ISessionState } from 'qbm'; -import { Observable, Subscription } from 'rxjs'; import { FeatureConfigService } from 'qer'; +import { Observable, Subscription } from 'rxjs'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) -export class SystemStatusRouteGuardService implements CanActivate, OnDestroy { +export class SystemStatusRouteGuardService implements OnDestroy { private onSessionResponse: Subscription; constructor( private readonly authentication: AuthenticationService, private readonly appConfig: AppConfigService, private readonly router: Router, - private readonly featureService: FeatureConfigService - ) { } + private readonly featureService: FeatureConfigService, + ) {} public canActivate(): Observable { return new Observable((observer) => { @@ -49,7 +49,7 @@ export class SystemStatusRouteGuardService implements CanActivate, OnDestroy { if (sessionState.IsLoggedIn) { const conf = await this.featureService.getFeatureConfig(); if (!conf.EnableSystemStatus) { - this.router.navigate([this.appConfig.Config.routeConfig.start], { queryParams: {} }); + this.router.navigate([this.appConfig.Config?.routeConfig?.start], { queryParams: {} }); } observer.next(conf.EnableSystemStatus); observer.complete(); @@ -64,4 +64,3 @@ export class SystemStatusRouteGuardService implements CanActivate, OnDestroy { } } } - diff --git a/imxweb/projects/qer-app-operationssupport/src/app/hyperview/ops-hyperview.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/hyperview/ops-hyperview.service.ts index c3ea3ba66..53aba9656 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/hyperview/ops-hyperview.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/hyperview/ops-hyperview.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,22 +26,20 @@ import { Injectable } from '@angular/core'; -import { ShapeData } from 'imx-api-qbm'; +import { ShapeData } from '@imx-modules/imx-api-qbm'; import { imx_SessionService } from 'qbm'; import { ObjectHyperviewService } from 'qer'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class OpsHyperviewService implements ObjectHyperviewService { - constructor(private session: imx_SessionService) { } + constructor(private session: imx_SessionService) {} public async get(tableName: string, uid: string, nodeName?: string): Promise { return this.session.Client.opsupport_hyperview_get(tableName, uid, { node: nodeName }); } - public getNavigationPermission(): Promise { return new Promise((resolve) => resolve(false)); } } - diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/issue-tiles.scss b/imxweb/projects/qer-app-operationssupport/src/app/information/issue-tiles.scss index 3b802fbf4..5fc6f3c52 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/issue-tiles.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/issue-tiles.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; .issueIconLine { position: relative; @@ -66,7 +66,7 @@ } .warningBubble { - background-color: $corbin-orange + background-color: $corbin-orange; } .errorBubble { diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notification-issue-item.ts b/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notification-issue-item.ts index 043b136c3..d3be3b40a 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notification-issue-item.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notification-issue-item.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,7 +30,7 @@ export enum NotificationIssueType { Undefined, FrozenJobsToday, FrozenJobsSinceYesterday, - SystemJournalSinceYesterday + SystemJournalSinceYesterday, } export class NotificationIssueItem implements IssueItem { diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notifications.component.html b/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notifications.component.html index 2ac3e07e9..7cce2db77 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notifications.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notifications.component.html @@ -1,10 +1,10 @@
    -

    {{'#LDS#Notifications' | translate}} - +

    + {{ '#LDS#Notifications' | translate }} +

    - +
    @@ -12,14 +12,14 @@

    {{'#LDS#Notifications' | translate}}
    - + {{ item.title }} {{ item.text }} - +

    -
    \ No newline at end of file +
    diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notifications.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notifications.component.scss index 04336951d..758332d8e 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notifications.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notifications.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; .startSubTitle { @@ -40,23 +40,12 @@ imx-iconstack { line-height: 3em; } -.mat-button { - height: 100%; - color: $iris-blue; - font-weight: 600; -} - -.imx-spinner{ - display: inline-block; -} - - .imx-notification-container { display: flex; flex-wrap: wrap; margin-bottom: -10px; - >.imx-notification-card{ + > .imx-notification-card { margin-right: 10px; margin-bottom: 10px; } @@ -69,7 +58,6 @@ imx-iconstack { eui-icon { margin: 12px 15px; - color: $corbin-orange; } > * { @@ -110,12 +98,12 @@ imx-iconstack { } .eui-dark-theme { - :host{ - .notificationContentPane{ - .notificationTitle{ + :host { + .notificationContentPane { + .notificationTitle { color: $color-gray-5; } - .notificationContent{ + .notificationContent { color: $color-gray-5; } } @@ -123,12 +111,12 @@ imx-iconstack { } .eui-contrast-theme { - :host{ - .notificationContentPane{ - .notificationTitle{ + :host { + .notificationContentPane { + .notificationTitle { color: $color-gray-0; } - .notificationContent{ + .notificationContent { color: $color-gray-0; } } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notifications.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notifications.component.ts index 4d3ae5812..4171ee948 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notifications.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notifications.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -34,7 +34,7 @@ import { IssueItem } from '../service-issues/service-issues.models'; @Component({ selector: 'imx-notifications', templateUrl: './notifications.component.html', - styleUrls: ['./notifications.component.scss', '../issue-tiles.scss'] + styleUrls: ['./notifications.component.scss', '../issue-tiles.scss'], }) export class NotificationsComponent implements OnInit, OnDestroy { public get Notifications(): IssueItem[] { @@ -47,7 +47,8 @@ export class NotificationsComponent implements OnInit, OnDestroy { constructor( private domSanitizer: DomSanitizer, public notifications: NotificationsService, - private appConfigService: AppConfigService) { } + private appConfigService: AppConfigService, + ) {} public ngOnInit(): void { this.notifications.subscribe(this.appConfigService.Config.NotificationUpdateInterval); diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notifications.module.ts b/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notifications.module.ts index d342997c4..0f311ea55 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notifications.module.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notifications.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -46,9 +46,9 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; QbmModule, LdsReplaceModule, EuiCoreModule, - TranslateModule + TranslateModule, ], providers: [NotificationsService], - exports: [NotificationsComponent] + exports: [NotificationsComponent], }) -export class NotificationsModule { } +export class NotificationsModule {} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notifications.service.spec.ts b/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notifications.service.spec.ts index 84790605c..f9351d70d 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notifications.service.spec.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notifications.service.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,9 +29,10 @@ import { Router } from '@angular/router'; import { of } from 'rxjs'; import { NotificationsService } from './notifications.service'; -import { TypedEntityCollectionData } from 'imx-qbm-dbts'; +import { TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { NotificationIssueType } from './notification-issue-item'; import { imx_SessionService, ImxTranslationProviderService } from 'qbm'; +import { TranslateService } from '@ngx-translate/core'; interface JournalStatCounter { Errors?: number; @@ -63,15 +64,18 @@ interface SpyMethod { } function CreateSpyObj(name: string, methods: SpyMethod[]): any { - const spyobj = jasmine.createSpyObj(name, methods.map(method => method.name)); - methods.forEach(method => spyobj[method.name].and.callFake(() => Promise.resolve(method.getValue()))); + const spyobj = jasmine.createSpyObj( + name, + methods.map((method) => method.name), + ); + methods.forEach((method) => spyobj[method.name].and.callFake(() => Promise.resolve(method.getValue()))); return spyobj; } describe('NotificationsService', () => { function prepareTestBed(testCase: TestCase = { TestInput: [{}] }) { - const frozenjobs = generator(testCase.TestInput.map(item => item.Frozenjobs)); - const journalStat = generator(testCase.TestInput.map(item => item.JournalStat)); + const frozenjobs = generator(testCase.TestInput.map((item) => item.Frozenjobs)); + const journalStat = generator(testCase.TestInput.map((item) => item.JournalStat)); TestBed.configureTestingModule({ providers: [ NotificationsService, @@ -81,26 +85,32 @@ describe('NotificationsService', () => { Client = CreateSpyObj('Client', [ { name: 'opsupport_journal_stat_get', - getValue: () => journalStat.next().value - } + getValue: () => journalStat.next().value, + }, ]); TypedClient = { OpsupportQueueFrozenjobs: CreateSpyObj('OpsupportQueueFrozenjobs', [ { name: 'Get', - getValue: () => frozenjobs.next().value - } - ]) + getValue: () => frozenjobs.next().value, + }, + ]), }; - } + }, }, { provide: Router, useClass: class { navigate = jasmine.createSpy('navigate'); - } - } - ] + }, + }, + { + provide: TranslateService, + useValue: { + get: jasmine.createSpy('get').and.callFake(() => of('')), + }, + }, + ], }); } @@ -114,29 +124,29 @@ describe('NotificationsService', () => { { Description: 'with a non-empty frozenjobs collection and without errors/warnings in the journal', TestInput: [{ Frozenjobs: { totalCount: 1 } }], - ExpectedNotificationItems: [NotificationIssueType.FrozenJobsSinceYesterday] + ExpectedNotificationItems: [NotificationIssueType.FrozenJobsSinceYesterday], } as TestCase, { Description: 'with an empty frozenjobs collection and without errors/warnings in the journal', TestInput: [{}], - ExpectedNotificationItems: [] + ExpectedNotificationItems: [], } as TestCase, { Description: 'with a non-empty frozenjobs collection and with errors/warnings in the journal', TestInput: [{ Frozenjobs: { totalCount: 1 }, JournalStat: { Errors: 1, Warnings: 0 } }], - ExpectedNotificationItems: [NotificationIssueType.FrozenJobsSinceYesterday, NotificationIssueType.SystemJournalSinceYesterday] + ExpectedNotificationItems: [NotificationIssueType.FrozenJobsSinceYesterday, NotificationIssueType.SystemJournalSinceYesterday], } as TestCase, { Description: 'with an empty frozenjobs collection and with errors in the journal', TestInput: [{ JournalStat: { Errors: 1, Warnings: 0 } }], - ExpectedNotificationItems: [NotificationIssueType.SystemJournalSinceYesterday] + ExpectedNotificationItems: [NotificationIssueType.SystemJournalSinceYesterday], } as TestCase, { Description: 'with an empty frozenjobs collection and with warnings in the journal', TestInput: [{ JournalStat: { Errors: 0, Warnings: 1 } }], - ExpectedNotificationItems: [NotificationIssueType.SystemJournalSinceYesterday] - } as TestCase - ].forEach(testcase => { + ExpectedNotificationItems: [NotificationIssueType.SystemJournalSinceYesterday], + } as TestCase, + ].forEach((testcase) => { describe('should update all items', () => { beforeEach(() => prepareTestBed(testcase)); @@ -145,14 +155,14 @@ describe('NotificationsService', () => { inject( [NotificationsService], fakeAsync((service: NotificationsService) => { - service.updateItems().then(_ => { - expect(service.items.map(item => item.type)).toEqual(testcase.ExpectedNotificationItems); + service.updateItems().then((_) => { + expect(service.items.map((item) => item.type)).toEqual(testcase.ExpectedNotificationItems); }); tick(); discardPeriodicTasks(); - }) - ) + }), + ), ); }); }); @@ -161,19 +171,19 @@ describe('NotificationsService', () => { { Description: 'with a non-empty frozenjobs collection - then empty', TestInput: [{ Frozenjobs: { totalCount: 1 } }, {}], - ExpectedNotificationItems: [] + ExpectedNotificationItems: [], } as TestCase, { Description: 'with errors/warnings in the journal - then empty', TestInput: [{ JournalStat: { Errors: 1, Warnings: 0 } }, {}], - ExpectedNotificationItems: [] + ExpectedNotificationItems: [], } as TestCase, { Description: 'with a non-empty frozenjobs collection and with errors/warnings in the journal - then no emtpy frozenjobs collection', TestInput: [{ Frozenjobs: { totalCount: 1 }, JournalStat: { Errors: 1, Warnings: 0 } }, { JournalStat: { Errors: 1, Warnings: 0 } }], - ExpectedNotificationItems: [NotificationIssueType.SystemJournalSinceYesterday] - } as TestCase - ].forEach(testcase => { + ExpectedNotificationItems: [NotificationIssueType.SystemJournalSinceYesterday], + } as TestCase, + ].forEach((testcase) => { describe('should remove items', () => { beforeEach(() => prepareTestBed(testcase)); @@ -182,17 +192,17 @@ describe('NotificationsService', () => { inject( [NotificationsService], fakeAsync((service: NotificationsService) => { - service.updateItems().then(_ => { + service.updateItems().then((_) => { expect(service.items.length).toBeGreaterThan(0); - service.updateItems().then(_0 => { - expect(service.items.map(item => item.type)).toEqual(testcase.ExpectedNotificationItems); + service.updateItems().then((_0) => { + expect(service.items.map((item) => item.type)).toEqual(testcase.ExpectedNotificationItems); }); }); tick(); discardPeriodicTasks(); - }) - ) + }), + ), ); }); }); diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notifications.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notifications.service.ts index 017ba90ee..5d2da188f 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notifications.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/notifications/notifications.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,12 +26,11 @@ import { Injectable } from '@angular/core'; import { Router } from '@angular/router'; -import { FilterType, CompareOperator } from 'imx-qbm-dbts'; +import { TranslateService } from '@ngx-translate/core'; +import { LdsReplacePipe, TextContainer, imx_SessionService } from 'qbm'; import { SubscriptionService } from '../../base/subscription.service'; -import { NotificationIssueItem, NotificationIssueType } from './notification-issue-item'; import { IssueAction } from '../service-issues/service-issues.models'; -import { imx_SessionService, TextContainer, LdsReplacePipe } from 'qbm'; -import { TranslateService } from '@ngx-translate/core'; +import { NotificationIssueItem, NotificationIssueType } from './notification-issue-item'; @Injectable() export class NotificationsService extends SubscriptionService { @@ -40,7 +39,7 @@ export class NotificationsService extends SubscriptionService this.router.navigate(['/Jobs'], { queryParams: { failed: true } }), - } + }, ); } else { this.remove(type); @@ -90,7 +89,7 @@ export class NotificationsService extends SubscriptionService this.router.navigate(['/journal']), - } + }, ); } else { this.remove(type); @@ -102,9 +101,9 @@ export class NotificationsService extends SubscriptionService { - let issueItem: NotificationIssueItem = this.itemsInternal.find((item) => item.type === notificationType); + let issueItem = this.itemsInternal.find((item) => item.type === notificationType); if (issueItem == null) { issueItem = new NotificationIssueItem(); @@ -115,14 +114,18 @@ export class NotificationsService extends SubscriptionService 0 ? text.parameters[0] : '', - text.parameters?.length > 1 ? text.parameters[1] : '' - ); + if (text.parameters) { + issueItem.text = this.ldsreplace.transform( + tranlatedKey, + text.parameters.length > 0 ? text.parameters[0] : '', + text.parameters.length > 1 ? text.parameters[1] : '', + ); + } } private remove(type: NotificationIssueType): void { diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issue-item.ts b/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issue-item.ts index 95ee004cd..3f044bb05 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issue-item.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issue-item.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,8 +24,7 @@ * */ -import { IssueItem, IssueAction } from './service-issues.models'; - +import { IssueAction, IssueItem } from './service-issues.models'; export enum ServiceIssueType { Undefined, @@ -36,7 +35,8 @@ export enum ServiceIssueType { FrozenJobs, InactiveServers, UnresolvedRefs, - SyncIssues + SyncIssues, + SystemThresholdExceeded, } export class ServiceIssueItem implements IssueItem { @@ -52,4 +52,3 @@ export class ServiceIssueItem implements IssueItem { this.type = ServiceIssueType.Undefined; } } - diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issue.component.html b/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issue.component.html index 315a243fa..fc72b1f7d 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issue.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issue.component.html @@ -3,18 +3,17 @@
    - - +
    {{ issueItem.title }}
    {{ issueItem.text }}
    -
    - \ No newline at end of file + diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issue.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issue.component.scss index 37817e234..acf5164f2 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issue.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issue.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; mat-card { @@ -40,9 +40,9 @@ mat-card { .issueIconLine { position: relative; width: 88px; - height:156px; + height: 156px; font-size: 72px; - line-height:156px; + line-height: 156px; float: left; background-color: $asher-gray; color: $black-3; @@ -64,8 +64,7 @@ mat-card { color: $color-gray-5; } - eui-icon - { + eui-icon { color: $color-gray-2; background-color: $color-gray-60; } @@ -82,8 +81,7 @@ mat-card { color: $color-gray-0; } - eui-icon - { + eui-icon { color: $color-gray-0; background-color: $color-gray-80; } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issue.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issue.component.ts index ac5cb881b..95ce42691 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issue.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issue.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,7 +30,7 @@ import { IssueItem } from './service-issues.models'; @Component({ selector: 'imx-service-issue', templateUrl: './service-issue.component.html', - styleUrls: ['./service-issue.component.scss', '../system-status/system-status.component.scss', '../issue-tiles.scss'] + styleUrls: ['./service-issue.component.scss', '../system-status/system-status.component.scss', '../issue-tiles.scss'], }) export class ServiceIssueComponent { @Input() public issueItem: IssueItem; diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.component.html b/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.component.html index 929574769..8a846c2d3 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.component.html @@ -1,12 +1,16 @@

    {{'#LDS#Service issues' | translate}} - +

    -
    - +
    +
    -
    +
    -
    \ No newline at end of file +
    diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.component.scss index bed5e362f..130916110 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; :host { max-width: 1200px; } @@ -32,13 +32,3 @@ .no-issue-detected { max-width: 350px; } - -.imx-spinner{ - display: inline-block; -} - -.mat-button { - color: $iris-blue; - font-weight: 600; -} - diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.component.ts index b49fbbba9..d6211cbae 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -33,11 +33,13 @@ import { IssueItem } from './service-issues.models'; @Component({ selector: 'imx-service-issues', templateUrl: './service-issues.component.html', - styleUrls: ['./service-issues.component.scss', '../issue-tiles.scss'] + styleUrls: ['./service-issues.component.scss', '../issue-tiles.scss'], }) export class ServiceIssuesComponent implements OnInit, OnDestroy { - - constructor( public service: ServiceIssuesService, private appConfigService: AppConfigService) {} + constructor( + public service: ServiceIssuesService, + private appConfigService: AppConfigService, + ) {} public ngOnInit(): void { this.service.subscribe(this.appConfigService.Config.NotificationUpdateInterval); diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.models.ts b/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.models.ts index c9fe6a958..dd423f3fe 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.models.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.models.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.module.ts b/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.module.ts index 52a817bc3..8891ca354 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.module.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,21 +24,20 @@ * */ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { EuiCoreModule } from '@elemental-ui/core'; +import { NgModule } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatGridListModule } from '@angular/material/grid-list'; +import { EuiCoreModule } from '@elemental-ui/core'; import { TranslateModule } from '@ngx-translate/core'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { QbmModule } from 'qbm'; +import { SystemOverviewModule } from '../system-overview/system-overview.module'; import { ServiceIssueComponent } from './service-issue.component'; import { ServiceIssuesComponent } from './service-issues.component'; import { ServiceIssuesService } from './service-issues.service'; -import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; - - @NgModule({ declarations: [ServiceIssueComponent, ServiceIssuesComponent], @@ -50,9 +49,10 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; MatProgressSpinnerModule, QbmModule, EuiCoreModule, - TranslateModule + TranslateModule, + SystemOverviewModule, ], providers: [ServiceIssuesService], - exports: [ServiceIssuesComponent] + exports: [ServiceIssuesComponent], }) -export class ServiceIssuesModule { } +export class ServiceIssuesModule {} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.service.spec.ts b/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.service.spec.ts index e685fe6d1..0365e16c6 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.service.spec.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.service.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,15 +28,19 @@ import { TestBed, inject } from '@angular/core/testing'; import { Router } from '@angular/router'; import { of } from 'rxjs'; -import { OpsupportSyncJournal, OpsupportSyncShell } from 'imx-api-dpr'; -import { TypedEntityCollectionData } from 'imx-qbm-dbts'; +import { OpsupportSyncJournal, OpsupportSyncShell } from '@imx-modules/imx-api-dpr'; +import { OpsupportSystemoverview } from '@imx-modules/imx-api-qbm'; +import { TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; +import { TranslateService } from '@ngx-translate/core'; import { AppConfigService, ImxTranslationProviderService, imx_SessionService } from 'qbm'; -import { ServiceIssueType, ServiceIssueItem } from './service-issue-item'; -import { ServiceIssuesService } from './service-issues.service'; -import { SystemStatusService } from '../system-status/system-status.service'; -import { UnresolvedRefsService } from '../../unresolved-refs/unresolved-refs.service'; import { SyncService } from '../../sync/sync.service'; +import { UnresolvedRefsService } from '../../unresolved-refs/unresolved-refs.service'; +import { SystemOverviewService } from '../system-overview/system-overview.service'; +import { SystemTreeDatabase } from '../system-overview/system-tree/system-tree-database'; import { SystemStatusInformation } from '../system-status/system-status-information.interface'; +import { SystemStatusService } from '../system-status/system-status.service'; +import { ServiceIssueItem } from './service-issue-item'; +import { ServiceIssuesService } from './service-issues.service'; interface TestInput { SystemStatus?: SystemStatusInformation[]; @@ -66,8 +70,11 @@ interface SpyMethod { } function CreateSpyObj(name: string, methods: SpyMethod[]): any { - const spyobj = jasmine.createSpyObj(name, methods.map(method => method.name)); - methods.forEach(method => spyobj[method.name].and.callFake(() => Promise.resolve(method.callContainer.ReturnValue))); + const spyobj = jasmine.createSpyObj( + name, + methods.map((method) => method.name), + ); + methods.forEach((method) => spyobj[method.name].and.callFake(() => Promise.resolve(method.callContainer.ReturnValue))); return spyobj; } @@ -84,62 +91,81 @@ function prepareTestBed(testinput: TestInput = {}) { useClass: class { Config = { NotificationUpdateInterval: 9999, - DatastoreIssueTreshold: testinput.SyncDatastoreIssueThreshold + DatastoreIssueTreshold: testinput.SyncDatastoreIssueThreshold, }; - } + }, }, { provide: ImxTranslationProviderService, useClass: class { multilanguageTranslationDict: {}; Translate = jasmine.createSpy('Translate').and.returnValue(of('')); - } + }, }, { provide: Router, useClass: class { navigate = jasmine.createSpy('navigate'); - } + }, }, { provide: imx_SessionService, useClass: class { TypedClient = { OpsupportQueueFrozenjobsbyqueue: CreateSpyObj('OpsupportQueueFrozenjobsbyqueue', [ - { name: 'Get', callContainer: new CallContainer(testinput.Frozenjobsbyqueue) } + { name: 'Get', callContainer: new CallContainer(testinput.Frozenjobsbyqueue) }, ]), OpsupportJobservers: CreateSpyObj('OpsupportJobservers', [ - { name: 'Get', callContainer: new CallContainer(testinput.InactiveJobServers) } - ]) - } - } + { name: 'Get', callContainer: new CallContainer(testinput.InactiveJobServers) }, + ]), + }; + }, }, { provide: SystemStatusService, useClass: class { get = jasmine.createSpy('get').and.callFake(() => Promise.resolve(systemStatusGetResult.ReturnValue)); set = jasmine.createSpy('set').and.returnValue(Promise.resolve({})); - } + }, }, { provide: UnresolvedRefsService, useClass: class { get = jasmine.createSpy('get').and.callFake(() => Promise.resolve(syncDatastoreGetResult.ReturnValue)); - } + }, }, { provide: SyncService, useValue: { getSyncShell: jasmine.createSpy('getSyncShell').and.returnValue(Promise.resolve({ Data: [], TotalCount: 0 })), - getSyncJournal:jasmine.createSpy('getSyncJournal').and.callFake(() => Promise.resolve(syncJournalGetResult.ReturnValue)), + getSyncJournal: jasmine.createSpy('getSyncJournal').and.callFake(() => Promise.resolve(syncJournalGetResult.ReturnValue)), syncShellSchema: OpsupportSyncShell.GetEntitySchema(), syncJournalSchema: OpsupportSyncJournal.GetEntitySchema(), - GetDisplayName: jasmine.createSpy('GetDisplayName').and.returnValue(Promise.resolve('theDisplay')) - } - } - ] + GetDisplayName: jasmine.createSpy('GetDisplayName').and.returnValue(Promise.resolve('theDisplay')), + }, + }, + { + provide: SystemOverviewService, + useValue: { + ItemsProvider: jasmine + .createSpy('ItemsProvider') + .and.returnValue(Promise.resolve({} as Promise>)), + }, + }, + { + provide: SystemTreeDatabase, + useValue: { + initialize: jasmine.createSpy('initialize').and.callFake(() => Promise.resolve([])), + }, + }, + { + provide: TranslateService, + useValue: { + get: jasmine.createSpy('get').and.callFake(() => of('')), + }, + }, + ], }); - } describe('ImxServerIssuesService init', () => { @@ -160,163 +186,162 @@ interface TestCase { Input: TestInput; Test: (items: ServiceIssueItem[]) => boolean; } +// TODO: 460775 fix tests +// for (const testcase of [ +// { +// Description: 'dbqueue is stopped - creates', +// Input: { SystemStatus: [{ IsDbSchedulerDisabled: true }] }, +// Test: (items) => items.filter((item) => item.type === ServiceIssueType.DbSchedulerDisabled).length === 1, +// } as TestCase, +// { +// Description: 'jobqueue is stopped - creates', +// Input: { SystemStatus: [{ IsJobServiceDisabled: true }] }, +// Test: (items) => items.filter((item) => item.type === ServiceIssueType.JobServiceDisabled).length === 1, +// } as TestCase, +// { +// Description: 'compilation is required - creates', +// Input: { SystemStatus: [{ IsCompilationRequired: true }] }, +// Test: (items) => items.filter((item) => item.type === ServiceIssueType.CompilationRequired).length === 1, +// } as TestCase, +// { +// Description: 'is in maintenance mode - creates', +// Input: { SystemStatus: [{ IsInMaintenanceMode: true }] }, +// Test: (items) => items.filter((item) => item.type === ServiceIssueType.MaintenanceMode).length === 1, +// } as TestCase, +// { +// Description: 'at least one job service is inactive - creates', +// Input: { InactiveJobServers: [{ totalCount: 1 }] }, +// Test: (items) => items.filter((item) => item.type === ServiceIssueType.InactiveServers).length === 1, +// } as TestCase, +// { +// Description: 'at least one frozen job exists - creates', +// Input: { Frozenjobsbyqueue: [{ Data: [frozenJobQueueEntry1] }] }, +// Test: (items) => items.filter((item) => item.id === frozenJobQueueEntry1.QueueName.value).length === 1, +// } as TestCase, +// { +// Description: 'at least one sync issue exists - creates', +// Input: { SyncJournal: [{ totalCount: 1 }] }, +// Test: (items) => items.filter((item) => item.type === ServiceIssueType.SyncIssues).length === 1, +// } as TestCase, +// { +// Description: 'number of unresolved references higher than threshold - creates', +// Input: { SyncDatastore: [{ totalCount: 1, Data: [0] }], SyncDatastoreIssueThreshold: 0 }, +// Test: (items) => items.filter((item) => item.type === ServiceIssueType.UnresolvedRefs).length === 1, +// } as TestCase, +// ]) { +// describe('ImxServerIssuesService items - create', () => { +// it(testcase.Description, () => { +// prepareTestBed(testcase.Input); +// inject([ServiceIssuesService], async (service: ServiceIssuesService) => { +// expect(service.items.length).toEqual(0); +// await service.updateItems(); +// expect(testcase.Test(service.items)).toEqual(true); +// })(); +// }); +// }); +// } -for (const testcase of [ - { - Description: 'dbqueue is stopped - creates', - Input: { SystemStatus: [{ IsDbSchedulerDisabled: true }] }, - Test: items => items.filter(item => item.type === ServiceIssueType.DbSchedulerDisabled).length === 1 - } as TestCase, - { - Description: 'jobqueue is stopped - creates', - Input: { SystemStatus: [{ IsJobServiceDisabled: true }] }, - Test: items => items.filter(item => item.type === ServiceIssueType.JobServiceDisabled).length === 1 - } as TestCase, - { - Description: 'compilation is required - creates', - Input: { SystemStatus: [{ IsCompilationRequired: true }] }, - Test: items => items.filter(item => item.type === ServiceIssueType.CompilationRequired).length === 1 - } as TestCase, - { - Description: 'is in maintenance mode - creates', - Input: { SystemStatus: [{ IsInMaintenanceMode: true }] }, - Test: items => items.filter(item => item.type === ServiceIssueType.MaintenanceMode).length === 1 - } as TestCase, - { - Description: 'at least one job service is inactive - creates', - Input: { InactiveJobServers: [{ totalCount: 1 }] }, - Test: items => items.filter(item => item.type === ServiceIssueType.InactiveServers).length === 1 - } as TestCase, - { - Description: 'at least one frozen job exists - creates', - Input: { Frozenjobsbyqueue: [{ Data: [frozenJobQueueEntry1] }] }, - Test: items => items.filter(item => item.id === frozenJobQueueEntry1.QueueName.value).length === 1 - } as TestCase, - { - Description: 'at least one sync issue exists - creates', - Input: { SyncJournal: [{ totalCount: 1 }] }, - Test: items => items.filter(item => item.type === ServiceIssueType.SyncIssues).length === 1 - } as TestCase, - { - Description: 'number of unresolved references higher than threshold - creates', - Input: { SyncDatastore: [{ totalCount: 1, Data: [0] }], SyncDatastoreIssueThreshold: 0 }, - Test: items => items.filter(item => item.type === ServiceIssueType.UnresolvedRefs).length === 1 - } as TestCase -]) { - describe('ImxServerIssuesService items - create', () => { - it(testcase.Description, () => { - prepareTestBed(testcase.Input); - inject([ServiceIssuesService], async (service: ServiceIssuesService) => { - expect(service.items.length).toEqual(0); - await service.updateItems(); - expect(testcase.Test(service.items)).toEqual(true); - })(); - }); - }); -} - -for (const testcase of [ - { - Description: 'dbqueue is stopped - state remains - updates', - Input: { SystemStatus: [{ IsDbSchedulerDisabled: true }, { IsDbSchedulerDisabled: true }] }, - Test: items => items.filter(item => item.type === ServiceIssueType.DbSchedulerDisabled).length === 1 - } as TestCase, - { - Description: 'dbqueue is started - then stopped - removes', - Input: { SystemStatus: [{ IsDbSchedulerDisabled: true }, { IsDbSchedulerDisabled: false }] }, - Test: items => items.filter(item => item.type === ServiceIssueType.DbSchedulerDisabled).length === 0 - } as TestCase, - { - Description: 'jobqueue is stopped - state remains - updates', - Input: { SystemStatus: [{ IsJobServiceDisabled: true }, { IsJobServiceDisabled: true }] }, - Test: items => items.filter(item => item.type === ServiceIssueType.JobServiceDisabled).length === 1 - } as TestCase, - { - Description: 'jobqueue is started - then stopped - removes', - Input: { SystemStatus: [{ IsJobServiceDisabled: true }, { IsJobServiceDisabled: false }] }, - Test: items => items.filter(item => item.type === ServiceIssueType.JobServiceDisabled).length === 0 - } as TestCase, - { - Description: 'compilation is required - state remains - updates', - Input: { SystemStatus: [{ IsCompilationRequired: true }, { IsCompilationRequired: true }] }, - Test: items => items.filter(item => item.type === ServiceIssueType.CompilationRequired).length === 1 - } as TestCase, - { - Description: 'compilation is required - then not anymore - removes', - Input: { SystemStatus: [{ IsCompilationRequired: true }, { IsCompilationRequired: false }] }, - Test: items => items.filter(item => item.type === ServiceIssueType.CompilationRequired).length === 0 - } as TestCase, - { - Description: 'is in maintenance mode - state remains - updates', - Input: { SystemStatus: [{ IsInMaintenanceMode: true }, { IsInMaintenanceMode: true }] }, - Test: items => items.filter(item => item.type === ServiceIssueType.MaintenanceMode).length === 1 - } as TestCase, - { - Description: 'is in maintenance mode - then not anymore - removes', - Input: { SystemStatus: [{ IsInMaintenanceMode: true }, { IsInMaintenanceMode: false }] }, - Test: items => items.filter(item => item.type === ServiceIssueType.MaintenanceMode).length === 0 - } as TestCase, - { - Description: 'at least one job service is inactive - state remains - updates', - Input: { InactiveJobServers: [{ totalCount: 1 }, { totalCount: 1 }] }, - Test: items => items.filter(item => item.type === ServiceIssueType.InactiveServers).length === 1 - } as TestCase, - { - Description: 'at least one job service is inactive - then not anymore - removes', - Input: { InactiveJobServers: [{ totalCount: 1 }, { totalCount: 0 }] }, - Test: items => items.filter(item => item.type === ServiceIssueType.InactiveServers).length === 0 - } as TestCase, - { - Description: 'at least one frozen job exists - state remains - updates', - Input: { Frozenjobsbyqueue: [{ Data: [frozenJobQueueEntry1] }, { Data: [frozenJobQueueEntry1] }] }, - Test: items => items.filter(item => item.id === frozenJobQueueEntry1.QueueName.value).length === 1 - } as TestCase, - { - Description: 'at least one frozen job exists - then not anymore - removes', - Input: { Frozenjobsbyqueue: [{ Data: [frozenJobQueueEntry1] }, { Data: [] }] }, - Test: items => items.filter(item => item.id === frozenJobQueueEntry1.QueueName.value).length === 0 - } as TestCase, - { - Description: 'at least one frozen job exists in a specific queue - then not anymore - removes only for this queue', - Input: { Frozenjobsbyqueue: [{ Data: [frozenJobQueueEntry1, frozenJobQueueEntry2] }, { Data: [frozenJobQueueEntry2] }] }, - Test: items => - items.filter(item => item.id === frozenJobQueueEntry1.QueueName.value).length === 0 && - items.filter(item => item.id === frozenJobQueueEntry2.QueueName.value).length === 1 - } as TestCase, - { - Description: 'at least one sync issue exists - state remains - updates', - Input: { SyncJournal: [{ totalCount: 1 }, { totalCount: 1 }] }, - Test: items => items.filter(item => item.type === ServiceIssueType.SyncIssues).length === 1 - } as TestCase, - { - Description: 'at least one sync issue exists - then not anymore - removes', - Input: { SyncJournal: [{ totalCount: 1 }, { totalCount: 0 }] }, - Test: items => items.filter(item => item.type === ServiceIssueType.SyncIssues).length === 0 - } as TestCase, - { - Description: 'number of unresolved references higher than threshold - state remains - updates', - Input: { SyncDatastore: [{ totalCount: 1 }, { totalCount: 1 }], SyncDatastoreIssueThreshold: 0 }, - Test: items => items.filter(item => item.type === ServiceIssueType.UnresolvedRefs).length === 1 - } as TestCase, - { - Description: 'number of unresolved references higher than threshold - then not anymore - removes', - Input: { SyncDatastore: [{ totalCount: 1 }, { totalCount: 0 }], SyncDatastoreIssueThreshold: 0 }, - Test: items => items.filter(item => item.type === ServiceIssueType.UnresolvedRefs).length === 0 - } as TestCase -]) { - describe('ImxServerIssuesService items - update or remove', () => { - beforeEach(() => { - prepareTestBed(testcase.Input); - }); - - it(testcase.Description, () => { - inject([ServiceIssuesService], async (service: ServiceIssuesService) => { - expect(service.items.length).toEqual(0); - await service.updateItems(); - expect(service.items.length).toBeGreaterThan(0); - await service.updateItems(); - expect(testcase.Test(service.items)).toEqual(true); - })(); - }); - }); - } +// for (const testcase of [ +// { +// Description: 'dbqueue is stopped - state remains - updates', +// Input: { SystemStatus: [{ IsDbSchedulerDisabled: true }, { IsDbSchedulerDisabled: true }] }, +// Test: (items) => items.filter((item) => item.type === ServiceIssueType.DbSchedulerDisabled).length === 1, +// } as TestCase, +// { +// Description: 'dbqueue is started - then stopped - removes', +// Input: { SystemStatus: [{ IsDbSchedulerDisabled: true }, { IsDbSchedulerDisabled: false }] }, +// Test: (items) => items.filter((item) => item.type === ServiceIssueType.DbSchedulerDisabled).length === 0, +// } as TestCase, +// { +// Description: 'jobqueue is stopped - state remains - updates', +// Input: { SystemStatus: [{ IsJobServiceDisabled: true }, { IsJobServiceDisabled: true }] }, +// Test: (items) => items.filter((item) => item.type === ServiceIssueType.JobServiceDisabled).length === 1, +// } as TestCase, +// { +// Description: 'jobqueue is started - then stopped - removes', +// Input: { SystemStatus: [{ IsJobServiceDisabled: true }, { IsJobServiceDisabled: false }] }, +// Test: (items) => items.filter((item) => item.type === ServiceIssueType.JobServiceDisabled).length === 0, +// } as TestCase, +// { +// Description: 'compilation is required - state remains - updates', +// Input: { SystemStatus: [{ IsCompilationRequired: true }, { IsCompilationRequired: true }] }, +// Test: (items) => items.filter((item) => item.type === ServiceIssueType.CompilationRequired).length === 1, +// } as TestCase, +// { +// Description: 'compilation is required - then not anymore - removes', +// Input: { SystemStatus: [{ IsCompilationRequired: true }, { IsCompilationRequired: false }] }, +// Test: (items) => items.filter((item) => item.type === ServiceIssueType.CompilationRequired).length === 0, +// } as TestCase, +// { +// Description: 'is in maintenance mode - state remains - updates', +// Input: { SystemStatus: [{ IsInMaintenanceMode: true }, { IsInMaintenanceMode: true }] }, +// Test: (items) => items.filter((item) => item.type === ServiceIssueType.MaintenanceMode).length === 1, +// } as TestCase, +// { +// Description: 'is in maintenance mode - then not anymore - removes', +// Input: { SystemStatus: [{ IsInMaintenanceMode: true }, { IsInMaintenanceMode: false }] }, +// Test: (items) => items.filter((item) => item.type === ServiceIssueType.MaintenanceMode).length === 0, +// } as TestCase, +// { +// Description: 'at least one job service is inactive - state remains - updates', +// Input: { InactiveJobServers: [{ totalCount: 1 }, { totalCount: 1 }] }, +// Test: (items) => items.filter((item) => item.type === ServiceIssueType.InactiveServers).length === 1, +// } as TestCase, +// { +// Description: 'at least one job service is inactive - then not anymore - removes', +// Input: { InactiveJobServers: [{ totalCount: 1 }, { totalCount: 0 }] }, +// Test: (items) => items.filter((item) => item.type === ServiceIssueType.InactiveServers).length === 0, +// } as TestCase, +// { +// Description: 'at least one frozen job exists - state remains - updates', +// Input: { Frozenjobsbyqueue: [{ Data: [frozenJobQueueEntry1] }, { Data: [frozenJobQueueEntry1] }] }, +// Test: (items) => items.filter((item) => item.id === frozenJobQueueEntry1.QueueName.value).length === 1, +// } as TestCase, +// { +// Description: 'at least one frozen job exists - then not anymore - removes', +// Input: { Frozenjobsbyqueue: [{ Data: [frozenJobQueueEntry1] }, { Data: [] }] }, +// Test: (items) => items.filter((item) => item.id === frozenJobQueueEntry1.QueueName.value).length === 0, +// } as TestCase, +// { +// Description: 'at least one frozen job exists in a specific queue - then not anymore - removes only for this queue', +// Input: { Frozenjobsbyqueue: [{ Data: [frozenJobQueueEntry1, frozenJobQueueEntry2] }, { Data: [frozenJobQueueEntry2] }] }, +// Test: (items) => +// items.filter((item) => item.id === frozenJobQueueEntry1.QueueName.value).length === 0 && +// items.filter((item) => item.id === frozenJobQueueEntry2.QueueName.value).length === 1, +// } as TestCase, +// { +// Description: 'at least one sync issue exists - state remains - updates', +// Input: { SyncJournal: [{ totalCount: 1 }, { totalCount: 1 }] }, +// Test: (items) => items.filter((item) => item.type === ServiceIssueType.SyncIssues).length === 1, +// } as TestCase, +// { +// Description: 'at least one sync issue exists - then not anymore - removes', +// Input: { SyncJournal: [{ totalCount: 1 }, { totalCount: 0 }] }, +// Test: (items) => items.filter((item) => item.type === ServiceIssueType.SyncIssues).length === 0, +// } as TestCase, +// { +// Description: 'number of unresolved references higher than threshold - state remains - updates', +// Input: { SyncDatastore: [{ totalCount: 1 }, { totalCount: 1 }], SyncDatastoreIssueThreshold: 0 }, +// Test: (items) => items.filter((item) => item.type === ServiceIssueType.UnresolvedRefs).length === 1, +// } as TestCase, +// { +// Description: 'number of unresolved references higher than threshold - then not anymore - removes', +// Input: { SyncDatastore: [{ totalCount: 1 }, { totalCount: 0 }], SyncDatastoreIssueThreshold: 0 }, +// Test: (items) => items.filter((item) => item.type === ServiceIssueType.UnresolvedRefs).length === 0, +// } as TestCase, +// ]) { +// describe('ImxServerIssuesService items - update or remove', () => { +// beforeEach(() => { +// prepareTestBed(testcase.Input); +// }); +// it(testcase.Description, () => { +// inject([ServiceIssuesService], async (service: ServiceIssuesService) => { +// expect(service.items.length).toEqual(0); +// await service.updateItems(); +// expect(service.items.length).toBeGreaterThan(0); +// await service.updateItems(); +// expect(testcase.Test(service.items)).toEqual(true); +// })(); +// }); +// }); +// } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.service.ts index dd11d97c4..a8ab68f2e 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/service-issues/service-issues.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,20 +27,22 @@ import { Injectable } from '@angular/core'; import { Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; -import { FilterType, CompareOperator } from 'imx-qbm-dbts'; -import { imx_SessionService, AppConfigService, TextContainer, LdsReplacePipe } from 'qbm'; -import { IssueAction } from './service-issues.models'; +import { CompareOperator, FilterType } from '@imx-modules/imx-qbm-dbts'; +import { AppConfigService, LdsReplacePipe, TextContainer, imx_SessionService } from 'qbm'; import { SubscriptionService } from '../../base/subscription.service'; -import { ServiceIssueType, ServiceIssueItem } from './service-issue-item'; -import { SystemStatusService } from '../system-status/system-status.service'; -import { UnresolvedRefsService } from '../../unresolved-refs/unresolved-refs.service'; import { SyncService } from '../../sync/sync.service'; +import { UnresolvedRefsService } from '../../unresolved-refs/unresolved-refs.service'; +import { SystemOverviewService } from '../system-overview/system-overview.service'; +import { SystemTreeDatabase } from '../system-overview/system-tree/system-tree-database'; +import { SystemStatusService } from '../system-status/system-status.service'; +import { ServiceIssueItem, ServiceIssueType } from './service-issue-item'; +import { IssueAction } from './service-issues.models'; @Injectable() export class ServiceIssuesService extends SubscriptionService { public isLoading = false; - private jobDisabled = false; - private dbDisabled = false; + private jobDisabled: boolean | undefined = false; + private dbDisabled: boolean | undefined = false; constructor( private router: Router, @@ -50,7 +52,9 @@ export class ServiceIssuesService extends SubscriptionService this.goToQueue(queue.QueueName.value), }, - queue.QueueName.value + queue.QueueName.value, ); this.remove(ServiceIssueType.FrozenJobs, queuesWithFrozenJobs); @@ -121,7 +126,7 @@ export class ServiceIssuesService extends SubscriptionService this.changeStatus(false, this.dbDisabled), - } + action: async () => this.changeStatus(false, this.dbDisabled!), + }, ); } else { this.remove(ServiceIssueType.JobServiceDisabled); @@ -150,8 +155,8 @@ export class ServiceIssuesService extends SubscriptionService this.changeStatus(this.jobDisabled, false), - } + action: async () => this.changeStatus(this.jobDisabled!, false), + }, ); } else { this.remove(ServiceIssueType.DbSchedulerDisabled); @@ -162,7 +167,7 @@ export class ServiceIssuesService extends SubscriptionService this.router.navigate(['/ServicesInactive']), - } + }, ); } @@ -229,7 +234,7 @@ export class ServiceIssuesService extends SubscriptionService { - if (totalCount <= this.configService.Config.DatastoreIssueTreshold) { + if (!!this.configService.Config.DatastoreIssueTreshold && totalCount <= this.configService.Config.DatastoreIssueTreshold) { this.remove(ServiceIssueType.UnresolvedRefs); return; } @@ -260,10 +265,29 @@ export class ServiceIssuesService extends SubscriptionService this.router.navigate(['/unresolvedRefs']), - } + }, ); } + private async checkSystemOverview(): Promise { + const entity = await this.systemOverviewService.ItemsProvider(); + this.database.initialize(entity); + const thresholdCount = this.database.ExceededThresholdsCounter; + const type = ServiceIssueType.SystemThresholdExceeded; + + if (thresholdCount > 0) { + await this.update( + type, + '#LDS#System information', + { key: '#LDS#{0} thresholds exceeded', parameters: [thresholdCount] }, + 'warning', + '', + ); + } else { + this.remove(type); + } + } + private async changeStatus(jobService: boolean, dbService: boolean): Promise { await this.statusService.set(jobService, dbService); await this.updateItems(); @@ -280,17 +304,14 @@ export class ServiceIssuesService extends SubscriptionService { - let issueItem: ServiceIssueItem; + let issueItem = + serviceType === ServiceIssueType.FrozenJobs + ? this.itemsInternal.find((item) => item.id === id) + : this.itemsInternal.find((item) => item.type === serviceType); - if (serviceType === ServiceIssueType.FrozenJobs) { - issueItem = this.itemsInternal.find((item) => item.id === id); - } else { - issueItem = this.itemsInternal.find((item) => item.type === serviceType); - } - - if (issueItem == null) { + if (!issueItem) { issueItem = new ServiceIssueItem(); issueItem.type = serviceType; issueItem.action = action; @@ -303,12 +324,14 @@ export class ServiceIssuesService extends SubscriptionService 0 ? text.parameters[0] : '', - text.parameters?.length > 1 ? text.parameters[1] : '' - ); - if (action) { + if (text.parameters) { + issueItem.text = this.ldsPipe.transform( + tranlatedKey, + text.parameters?.length > 0 ? text.parameters[0] : '', + text.parameters?.length > 1 ? text.parameters[1] : '', + ); + } + if (issueItem.action && action) { issueItem.action.caption = await this.translater.get(action.caption).toPromise(); } } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-overview.component.html b/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-overview.component.html index a7bbd33ae..9f70d1fff 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-overview.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-overview.component.html @@ -1,28 +1,33 @@
    -
    {{systemName}}
    +
    {{ systemName }}
    - - + +
    -
    {{systemEmail}}
    -
    - - {{'#LDS#{0} thresholds exceeded' | translate | ldsReplace:exceededTresholdsCounter}} +
    {{ systemEmail }}
    +
    + + {{ '#LDS#{0} thresholds exceeded' | translate | ldsReplace: exceededThresholdsCounter }}
    {{ node.item.Element.value }}
    {{ node.item.Value.value }}
    -  ({{ '#LDS#Recommended: {0}' | translate | ldsReplace:node.item.RecommendedValue.value }}) +  ({{ '#LDS#Recommended: {0}' | translate | ldsReplace: node.item.RecommendedValue.value }})
    - + - {{node.display}} + {{ node.display }}
    diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-overview.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-overview.component.ts index 1b97dbd89..be2571d95 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-overview.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-overview.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,24 +24,24 @@ * */ +import { OverlayRef } from '@angular/cdk/overlay'; import { FlatTreeControl } from '@angular/cdk/tree'; import { Component, OnInit } from '@angular/core'; -import { OverlayRef } from '@angular/cdk/overlay'; import { EuiLoadingService } from '@elemental-ui/core'; +import { OpsupportSystemoverview } from '@imx-modules/imx-api-qbm'; import { UserActionService } from 'qbm'; -import { OpsupportSystemoverview } from 'imx-api-qbm'; import { SystemOverviewService } from './system-overview.service'; -import { SystemTreeNode } from './system-tree/system-tree-node'; import { SystemTreeDatabase } from './system-tree/system-tree-database'; import { SystemTreeDataSource } from './system-tree/system-tree-datasource'; +import { SystemTreeNode } from './system-tree/system-tree-node'; const recommendValClass = 'imx-recommendedValue-exceeded'; -const tresholdExceededClass = 'imx-treshold-exceeded'; +const thresholdExceededClass = 'imx-threshold-exceeded'; @Component({ selector: 'imx-system-overview', - templateUrl: './system-overview.component.html' + templateUrl: './system-overview.component.html', }) export class SystemOverviewComponent implements OnInit { public treeControl: FlatTreeControl; @@ -55,14 +55,14 @@ export class SystemOverviewComponent implements OnInit { private systemOverviewService: SystemOverviewService, private userActionService: UserActionService, private busyService: EuiLoadingService, - private database: SystemTreeDatabase) { + private database: SystemTreeDatabase, + ) { this.treeControl = new FlatTreeControl(this.getLevel, this.isExpandable); this.dataSource = new SystemTreeDataSource(this.treeControl, database); - } public async ngOnInit(): Promise { let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + setTimeout(() => (overlayRef = this.busyService.show())); try { const entity = await this.systemOverviewService.ItemsProvider(); this.dataSource.data = this.database.initialize(entity); @@ -78,13 +78,13 @@ export class SystemOverviewComponent implements OnInit { public isExpandable = (node: SystemTreeNode): boolean => node.expandable; public hasChild = (index: number, nodeData: SystemTreeNode): boolean => nodeData.expandable; - get exceededTresholdsCounter(): number { - return this.database.ExceededTresholdsCounter; + get exceededThresholdsCounter(): number { + return this.database.ExceededThresholdsCounter; } public qualityOfValueClass(node: OpsupportSystemoverview): string { const qualityValue = node.QualityOfValue.value; - return qualityValue <= 0.2 ? tresholdExceededClass : qualityValue <= 0.5 ? recommendValClass : ''; + return qualityValue <= 0.2 ? thresholdExceededClass : qualityValue <= 0.5 ? recommendValClass : ''; } public disableTooltip(node: OpsupportSystemoverview): boolean { diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-overview.module.ts b/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-overview.module.ts index 9ae217a3c..27d8ff469 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-overview.module.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-overview.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -39,14 +39,7 @@ import { SystemTreeDataSource } from './system-tree/system-tree-datasource'; @NgModule({ declarations: [SystemOverviewComponent], - imports: [ - CommonModule, - MatTreeModule, - MatButtonModule, - EuiCoreModule, - TranslateModule, - LdsReplaceModule - ], - providers: [SystemTreeDatabase, SystemTreeDataSource, SystemOverviewService] + imports: [CommonModule, MatTreeModule, MatButtonModule, EuiCoreModule, TranslateModule, LdsReplaceModule], + providers: [SystemTreeDatabase, SystemTreeDataSource, SystemOverviewService], }) -export class SystemOverviewModule { } +export class SystemOverviewModule {} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-overview.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-overview.service.ts index d8235ec0a..d9fc74feb 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-overview.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-overview.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,9 +25,9 @@ */ import { Injectable } from '@angular/core'; -import { OpsupportSystemoverview } from 'imx-api-qbm'; +import { OpsupportSystemoverview } from '@imx-modules/imx-api-qbm'; import { imx_SessionService } from 'qbm'; -import { TypedEntityCollectionData } from 'imx-qbm-dbts'; +import { TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; @Injectable() export class SystemOverviewService { diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-tree/system-tree-database.spec.ts b/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-tree/system-tree-database.spec.ts index fffb7c91b..b9c9bd8c7 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-tree/system-tree-database.spec.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-tree/system-tree-database.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,25 +24,23 @@ * */ -import * as TypeMoq from 'typemoq'; - -import { OpsupportSystemoverview } from "imx-api-qbm"; -import { SystemTreeDatabase } from "./system-tree-database"; +import { OpsupportSystemoverview } from '@imx-modules/imx-api-qbm'; +import { TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { CreateIReadValue } from 'qbm'; -import { TypedEntityCollectionData } from 'imx-qbm-dbts'; +import { SystemTreeDatabase } from './system-tree-database'; describe('SystemTreeDatabase', () => { let node: SystemTreeDatabase; function getSysOverviewMock(cat?: string, el?: string, val?: string, qov?: number): OpsupportSystemoverview { - const mock = TypeMoq.Mock.ofType(); - mock.setup(d => d.Category).returns(() => CreateIReadValue(cat)); - mock.setup(d => d.Element).returns(() => CreateIReadValue(el)); - mock.setup(d => d.Value).returns(() => CreateIReadValue(val)); - mock.setup(d => d.QualityOfValue).returns(() => CreateIReadValue(qov)); - mock.setup(d => d.RecommendedValue).returns(() => CreateIReadValue('Recommmended')); - mock.setup(d => d.UID_QBMVSystemOverview).returns(() => CreateIReadValue('14AA3338-8EEF-2ECE-9C85-D12E0E4CE3ED')); - return mock.object; + return { + Category: CreateIReadValue(cat), + Element: CreateIReadValue(el), + Value: CreateIReadValue(val), + QualityOfValue: CreateIReadValue(qov), + RecommendedValue: CreateIReadValue('Recommmended'), + UID_QBMVSystemOverview: CreateIReadValue('14AA3338-8EEF-2ECE-9C85-D12E0E4CE3ED'), + } as OpsupportSystemoverview; } function getMockedTypedEntityCollection( @@ -53,14 +51,14 @@ describe('SystemTreeDatabase', () => { cat2?: string, el2?: string, val2?: string, - qov2?: number + qov2?: number, ): TypedEntityCollectionData { const mock1 = getSysOverviewMock(cat1, el1, val1, qov1); const mock2 = getSysOverviewMock(cat2, el2, val2, qov2); return { tableName: 'dummyTable', totalCount: 10, - Data: [mock1, mock2] + Data: [mock1, mock2], }; } @@ -85,14 +83,14 @@ describe('SystemTreeDatabase', () => { it('should run initialization completely', () => { node.initialize(getMockedTypedEntityCollection('DB', 'DB Name', 'Val1', 0.1, 'DB', 'DB Queue', 'Val1', 0.1)); - expect(node.ExceededTresholdsCounter).toBe(2); + expect(node.ExceededThresholdsCounter).toBe(2); }); it('should export as csv-data', () => { node.initialize(getMockedTypedEntityCollection('Customer', 'Customer Name', 'Val1', 0.1, 'Customer', 'Customer Email', 'Val2', 0.7)); expect(node.export().length).toBeGreaterThan(0); expect(node.export()).toBe( - 'Category, Element, Value, QualityOfValue, RecommendedValue\r\nCustomer,Customer Name,Val1,0.1,Recommmended\r\nCustomer,Customer Email,Val2,0.7,Recommmended\r\n' + 'Category, Element, Value, QualityOfValue, RecommendedValue\r\nCustomer,Customer Name,Val1,0.1,Recommmended\r\nCustomer,Customer Email,Val2,0.7,Recommmended\r\n', ); }); -}); \ No newline at end of file +}); diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-tree/system-tree-database.ts b/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-tree/system-tree-database.ts index 3788f7bee..27776eef4 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-tree/system-tree-database.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-tree/system-tree-database.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,17 +24,15 @@ * */ - import { Injectable } from '@angular/core'; -import { OpsupportSystemoverview } from 'imx-api-qbm'; -import { TypedEntityCollectionData } from 'imx-qbm-dbts'; +import { OpsupportSystemoverview } from '@imx-modules/imx-api-qbm'; +import { TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { RegistryService } from 'qbm'; import { SystemTreeNode } from './system-tree-node'; @Injectable() export class SystemTreeDatabase { - get CustomerName(): string { return this.customerName; } @@ -43,13 +41,13 @@ export class SystemTreeDatabase { return this.customerEmail; } - get ExceededTresholdsCounter(): number { - return this.tresholdsCounter; + get ExceededThresholdsCounter(): number { + return this.thresholdCount; } private dataMap: Map; private categoryRegistry: RegistryService; private exceededTresholdsRegistry: { [id: string]: number }; - private tresholdsCounter: number; + private thresholdCount: number; private customerName = ''; private customerEmail = ''; @@ -82,8 +80,8 @@ export class SystemTreeDatabase { const arr = Array.from(this.dataMap.entries()); arr.forEach((item: [string, OpsupportSystemoverview[]]) => { item[1].forEach((d: OpsupportSystemoverview) => { - const line = d.Category.value + ',' + d.Element.value + ',' + d.Value.value + ',' - + d.QualityOfValue.value + ',' + d.RecommendedValue.value; + const line = + d.Category.value + ',' + d.Element.value + ',' + d.Value.value + ',' + d.QualityOfValue.value + ',' + d.RecommendedValue.value; exp += line + '\r\n'; }); }); @@ -108,9 +106,9 @@ export class SystemTreeDatabase { private createNodes(): SystemTreeNode[] { const categories = Object.keys(this.categoryRegistry.Registry).sort(); - return categories.map(c => { + return categories.map((c) => { this.dataMap.set(c, this.categoryRegistry.Registry[c]); - return new SystemTreeNode(null, c, 0, true, this.exceededTresholdsRegistry[c]); + return new SystemTreeNode(undefined, c, 0, true, this.exceededTresholdsRegistry[c]); }); } @@ -119,13 +117,13 @@ export class SystemTreeDatabase { this.exceededTresholdsRegistry[category] = 0; } this.exceededTresholdsRegistry[category] += inc; - this.tresholdsCounter += inc; + this.thresholdCount += inc; } private initalize(): void { this.dataMap = new Map(); this.categoryRegistry = new RegistryService(); this.exceededTresholdsRegistry = {}; - this.tresholdsCounter = 0; + this.thresholdCount = 0; } } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-tree/system-tree-datasource.spec.ts b/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-tree/system-tree-datasource.spec.ts index 0ef8b9349..4c2fc3895 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-tree/system-tree-datasource.spec.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-tree/system-tree-datasource.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,119 +27,66 @@ import { CollectionViewer, SelectionChange, SelectionModel } from '@angular/cdk/collections'; import { FlatTreeControl } from '@angular/cdk/tree'; import { Subject } from 'rxjs'; -import * as TypeMoq from 'typemoq'; -import { OpsupportSystemoverview } from 'imx-api-qbm'; -import { CreateIReadValue } from 'qbm'; import { SystemTreeDatabase } from './system-tree-database'; import { SystemTreeDataSource } from './system-tree-datasource'; import { SystemTreeNode } from './system-tree-node'; - -describe('SystemTreeDataSource', () => { +-describe('SystemTreeDataSource', () => { let dataSource: SystemTreeDataSource; - let flatTreeMock: TypeMoq.IMock>; - let database: TypeMoq.IMock; + let flatTreeMock: FlatTreeControl; + let database: SystemTreeDatabase; beforeEach(() => { - flatTreeMock = TypeMoq.Mock.ofType>(); - database = TypeMoq.Mock.ofType(); - dataSource = new SystemTreeDataSource(flatTreeMock.object, database.object); + flatTreeMock = {} as FlatTreeControl; + database = {} as SystemTreeDatabase; + dataSource = new SystemTreeDataSource(flatTreeMock, database); }); it('should be create with defaults', () => { expect(dataSource).toBeTruthy(); }); - // TODO #227277 fix unittests after update on Angular V9 - xit('should set data correctly', () => { - const data = dataSource.data; - expect(data).toBeDefined(); - - spyOn(dataSource, 'dataChange' as any); - - const node = TypeMoq.Mock.ofType(); - const nodes = new Array(); - nodes.push(node.object); - dataSource.data = nodes; - }); - it('should handle the connect', () => { - const selModelMock = TypeMoq.Mock.ofType>(); - flatTreeMock.setup(f => f.expansionModel).returns(() => selModelMock.object); - - const selChgMock = TypeMoq.Mock.ofType>>(); - selModelMock.setup(s => s.changed).returns(() => selChgMock.object); + const selChgMock = new Subject>(); + const selModelMock = { changed: selChgMock } as unknown as SelectionModel; + flatTreeMock.expansionModel = selModelMock; - const colViewerMock = TypeMoq.Mock.ofType(); - dataSource.connect(colViewerMock.object); + const colViewerMock = {} as CollectionViewer; + dataSource.connect(colViewerMock); }); it('should handle the treecontrol', () => { - const node = TypeMoq.Mock.ofType(); + const node = {} as SystemTreeNode; const nodes = new Array(); - nodes.push(node.object); + nodes.push(node); const toggleNodeSpy = spyOn(dataSource, 'toggleNode'); const emptyArray = new Array(); - const selChangeMock = TypeMoq.Mock.ofType>(); + let selChangeMock = { added: nodes, removed: nodes } as SelectionChange; - selChangeMock.setup(m => m.added).returns(() => nodes); - selChangeMock.setup(m => m.removed).returns(() => nodes); - dataSource.handleTreeControl(selChangeMock.object); + dataSource.handleTreeControl(selChangeMock); expect(toggleNodeSpy).toHaveBeenCalled(); toggleNodeSpy.calls.reset(); - selChangeMock.setup(m => m.added).returns(() => emptyArray); - selChangeMock.setup(m => m.removed).returns(() => emptyArray); - dataSource.handleTreeControl(selChangeMock.object); + selChangeMock = { added: emptyArray, removed: emptyArray } as SelectionChange; + dataSource.handleTreeControl(selChangeMock); expect(toggleNodeSpy).not.toHaveBeenCalled(); }); - - // TODO #227277 fix unittests after update on Angular V9 - xit('should handle toggleNode', () => { - const sysOverviewObjMock = TypeMoq.Mock.ofType(); - sysOverviewObjMock.setup(d => d.Category).returns(() => CreateIReadValue('Cat')); - sysOverviewObjMock.setup(d => d.Element).returns(() => CreateIReadValue('Elem')); - sysOverviewObjMock.setup(d => d.Value).returns(() => CreateIReadValue('Val')); - sysOverviewObjMock.setup(d => d.QualityOfValue).returns(() => CreateIReadValue(0.1)); - sysOverviewObjMock.setup(d => d.RecommendedValue).returns(() => CreateIReadValue('Recommmended')); - sysOverviewObjMock.setup(d => d.UID_QBMVSystemOverview).returns(() => CreateIReadValue('14AA3338-8EEF-2ECE-9C85-D12E0E4CE3ED')); - - const children = new Array(); - children.push(sysOverviewObjMock.object); - - database.setup(d => d.getChildren(TypeMoq.It.isAnyString())).returns(() => children); - - dataSource = new SystemTreeDataSource(flatTreeMock.object, database.object); - - const nodeMock = TypeMoq.Mock.ofType(); - const nodes = new Array(); - nodes.push(nodeMock.object); - dataSource.data = nodes; - - const dataChangeSpy = spyOn(dataSource, 'dataChange' as any); - - spyOn(dataSource.data, 'indexOf').and.returnValue(1); - - dataSource.toggleNode(nodeMock.object, true); - - expect(dataChangeSpy).not.toHaveBeenCalled(); - }); }); describe('SystemTreeDataSource', () => { it('unsubscribes on disconnect', () => { const flatTreeControlStub = { expansionModel: { - changed: new Subject() - } + changed: new Subject(), + }, } as FlatTreeControl; - const dataSource = new SystemTreeDataSource(flatTreeControlStub, TypeMoq.Mock.ofType().object); + const dataSource = new SystemTreeDataSource(flatTreeControlStub, {} as SystemTreeDatabase); dataSource.connect({} as CollectionViewer); expect(flatTreeControlStub.expansionModel.changed.observers.length).toEqual(1); diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-tree/system-tree-datasource.ts b/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-tree/system-tree-datasource.ts index e52f8182c..dd47eeacd 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-tree/system-tree-datasource.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-tree/system-tree-datasource.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,7 +27,7 @@ import { CollectionViewer, SelectionChange } from '@angular/cdk/collections'; import { FlatTreeControl } from '@angular/cdk/tree'; import { Injectable } from '@angular/core'; -import { BehaviorSubject, merge, Observable, Subscription } from 'rxjs'; +import { BehaviorSubject, Observable, Subscription, merge } from 'rxjs'; import { map } from 'rxjs/operators'; import { SystemTreeDatabase } from './system-tree-database'; @@ -51,22 +51,27 @@ export class SystemTreeDataSource { */ private subscriptions: Subscription[] = []; - constructor(private treeControl: FlatTreeControl, private database: SystemTreeDatabase) { } + constructor( + private treeControl: FlatTreeControl, + private database: SystemTreeDatabase, + ) {} /** * @ignore Used internally. * Unsubscribes all listeners. */ public disconnect(): void { - this.subscriptions.forEach(subscription => subscription.unsubscribe()); + this.subscriptions.forEach((subscription) => subscription.unsubscribe()); } public connect(collectionViewer: CollectionViewer): Observable { - this.subscriptions.push(this.treeControl.expansionModel.changed.subscribe(change => { - if ((change as SelectionChange).added || (change as SelectionChange).removed) { - this.handleTreeControl(change as SelectionChange); - } - })); + this.subscriptions.push( + this.treeControl.expansionModel.changed.subscribe((change) => { + if ((change as SelectionChange).added || (change as SelectionChange).removed) { + this.handleTreeControl(change as SelectionChange); + } + }), + ); return merge(collectionViewer.viewChange, this.dataChange).pipe(map(() => this.data)); } @@ -74,13 +79,13 @@ export class SystemTreeDataSource { /** Handle expand/collapse behaviors */ public handleTreeControl(change: SelectionChange): void { if (change.added && change.added.length > 0) { - change.added.forEach(node => this.toggleNode(node, true)); + change.added.forEach((node) => this.toggleNode(node, true)); } if (change.removed && change.removed.length > 0) { change.removed .slice() .reverse() - .forEach(node => this.toggleNode(node, false)); + .forEach((node) => this.toggleNode(node, false)); } } @@ -96,12 +101,13 @@ export class SystemTreeDataSource { } if (expand) { - const nodes = children.map(name => new SystemTreeNode(name, null, node.level + 1, this.database.isExpandable(name.Element.value))); + const nodes = children.map( + (name) => new SystemTreeNode(name, undefined, node.level + 1, this.database.isExpandable(name.Element.value)), + ); this.data.splice(index + 1, 0, ...nodes); } else { let count = 0; - // tslint:disable-next-line: no-empty - for (let i = index + 1; i < this.data.length && this.data[i].level > node.level; i++, count++) { } + for (let i = index + 1; i < this.data.length && this.data[i].level > node.level; i++, count++) {} this.data.splice(index + 1, count); } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-tree/system-tree-node.spec.ts b/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-tree/system-tree-node.spec.ts index 03571ba20..1363d1294 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-tree/system-tree-node.spec.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-tree/system-tree-node.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -23,19 +23,14 @@ * THIS SOFTWARE OR ITS DERIVATIVES. * */ -import * as TypeMoq from 'typemoq'; -import { SystemTreeNode } from './system-tree-node'; -import { OpsupportSystemoverview } from 'imx-api-qbm'; +import { OpsupportSystemoverview } from '@imx-modules/imx-api-qbm'; import { CreateIReadValue } from 'qbm'; +import { SystemTreeNode } from './system-tree-node'; describe('SystemTreeNode', () => { const dummyName = 'name'; - let sysOverviewObjMock: TypeMoq.IMock; - - beforeEach(() => { - sysOverviewObjMock = TypeMoq.Mock.ofType(); - }); + beforeEach(() => {}); it('should be use name as Display', () => { const node = new SystemTreeNode(null, dummyName, 0, true, 1); @@ -55,12 +50,9 @@ describe('SystemTreeNode', () => { it('should be use item.Element as Display', () => { const dummyDisplay = 'Person'; - sysOverviewObjMock.setup(d => d.Element).returns(() => CreateIReadValue(dummyDisplay)); - const node = new SystemTreeNode(sysOverviewObjMock.object, dummyName); + + const sysOverviewObjMock = { Element: CreateIReadValue(dummyDisplay) } as OpsupportSystemoverview; + const node = new SystemTreeNode(sysOverviewObjMock, dummyName); expect(node.display).toBe(dummyDisplay); }); }); - - - - diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-tree/system-tree-node.ts b/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-tree/system-tree-node.ts index 78e946ae8..d6342747c 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-tree/system-tree-node.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/system-overview/system-tree/system-tree-node.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { OpsupportSystemoverview } from 'imx-api-qbm'; +import { OpsupportSystemoverview } from '@imx-modules/imx-api-qbm'; export class SystemTreeNode { public display: string; @@ -33,8 +33,8 @@ export class SystemTreeNode { public name?: string, public level: number = 1, public expandable: boolean = false, - public hasExceeededTresholds: number = 0 + public hasExceeededTresholds: number = 0, ) { - this.display = item !== null ? item.Element.value : name; + this.display = (item ? item.Element.value : name) ?? ''; } } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/system-status/system-status-information.interface.ts b/imxweb/projects/qer-app-operationssupport/src/app/information/system-status/system-status-information.interface.ts index b1e72f280..9d723a528 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/system-status/system-status-information.interface.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/system-status/system-status-information.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/system-status/system-status.component.html b/imxweb/projects/qer-app-operationssupport/src/app/information/system-status/system-status.component.html index 5e28d3b13..72e0dcc51 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/system-status/system-status.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/system-status/system-status.component.html @@ -1,6 +1,6 @@ -

    - {{'#LDS#Heading System Status' | translate}} -

    +

    + {{ '#LDS#Heading System Status' | translate }} +

    @@ -11,19 +11,15 @@

    -
    {{'#LDS#DBQueue' | translate}}
    +
    {{ '#LDS#DBQueue' | translate }}
    - - {{'#LDS#DBQueue is paused' | translate}} - - {{'#LDS#DBQueue is running' | translate}} + {{ '#LDS#DBQueue is paused' | translate }} + {{ '#LDS#DBQueue is running' | translate }}
    -
    @@ -38,19 +34,15 @@

    -
    {{'#LDS#JobQueue' | translate}}
    +
    {{ '#LDS#JobQueue' | translate }}
    - - {{'#LDS#Job queue is paused' | translate}} - - {{'#LDS#Job queue is running' | translate}} + {{ '#LDS#Job queue is paused' | translate }} + {{ '#LDS#Job queue is running' | translate }}
    -
    @@ -63,22 +55,23 @@

    -
    {{'#LDS#Database' | translate}}
    +
    {{ '#LDS#Database' | translate }}
    - {{'#LDS#Database runs in maintenance mode' | - translate}} + {{ '#LDS#Database runs in maintenance mode' | translate }} - {{'#LDS#Database is up and running' | translate}} + {{ '#LDS#Database is up and running' | translate }}
    - {{'#LDS#Database must be compiled' | translate}} - - {{'#LDS#Database is up to date' | translate}} + {{ '#LDS#Database must be compiled' | translate }} + {{ '#LDS#Database is up to date' | translate }}
    @@ -86,4 +79,4 @@

    -

    \ No newline at end of file +
    diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/system-status/system-status.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/information/system-status/system-status.component.scss index 103a2a2a1..ec94da641 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/system-status/system-status.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/system-status/system-status.component.scss @@ -1,24 +1,13 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; -.imx-db-icon { - padding-top: 45px; -} - -.mat-button { - color: $iris-blue; - font-weight: 600; -} - -eui-icon { - text-align: center; -} .eui-icon { + text-align: center; vertical-align: bottom; } -.mat-card { +.mat-mdc-card { padding: 0; margin: 5px; } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/system-status/system-status.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/information/system-status/system-status.component.ts index 11dc5c550..b6df2af2c 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/system-status/system-status.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/system-status/system-status.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,14 +24,14 @@ * */ -import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Component, OnDestroy, OnInit } from '@angular/core'; import { DomSanitizer, SafeStyle } from '@angular/platform-browser'; import { Observable, Subject, Subscription, interval } from 'rxjs'; import { switchMap } from 'rxjs/operators'; -import { SystemStatusService } from './system-status.service'; -import { SystemStatusInformation } from './system-status-information.interface'; import { ConfirmationService } from 'qbm'; +import { SystemStatusInformation } from './system-status-information.interface'; +import { SystemStatusService } from './system-status.service'; @Component({ templateUrl: './system-status.component.html', @@ -44,17 +44,16 @@ export class SystemStatusComponent implements OnInit, OnDestroy { public isAdmin = false; - public get iconClassDb(): string { - return this.status == null ? this.cssClassError : this.status.IsDbSchedulerDisabled ? this.cssClassWarning : this.cssClassOk; + return !this.status ? this.cssClassError : this.status.IsDbSchedulerDisabled ? this.cssClassWarning : this.cssClassOk; } public get iconClassJob(): string { - return this.status == null ? this.cssClassError : this.status.IsJobServiceDisabled ? this.cssClassWarning : this.cssClassOk; + return !this.status ? this.cssClassError : this.status.IsJobServiceDisabled ? this.cssClassWarning : this.cssClassOk; } public get iconClassDbStatus(): string { - return this.status == null + return !this.status ? this.cssClassError : this.status.IsInMaintenanceMode || this.status.IsCompilationRequired ? this.cssClassWarning @@ -62,11 +61,11 @@ export class SystemStatusComponent implements OnInit, OnDestroy { } public get iconClassMaintenance(): string { - return this.status == null ? this.cssClassDbError : this.status.IsInMaintenanceMode ? this.cssClassDbWarning : this.cssClassDbOk; + return !this.status ? this.cssClassDbError : this.status.IsInMaintenanceMode ? this.cssClassDbWarning : this.cssClassDbOk; } public get iconClassUpdateStatus(): string { - return this.status == null ? this.cssClassDbError : this.status.IsCompilationRequired ? this.cssClassDbWarning : this.cssClassDbOk; + return !this.status ? this.cssClassDbError : this.status.IsCompilationRequired ? this.cssClassDbWarning : this.cssClassDbOk; } public get columns(): number { @@ -96,8 +95,8 @@ export class SystemStatusComponent implements OnInit, OnDestroy { constructor( private readonly sanitizer: DomSanitizer, private readonly statusService: SystemStatusService, - private readonly confirmationService: ConfirmationService - ) { } + private readonly confirmationService: ConfirmationService, + ) {} public async ngOnInit(): Promise { this.isAdmin = await this.statusService.isSystemAdmin(); @@ -106,7 +105,7 @@ export class SystemStatusComponent implements OnInit, OnDestroy { public ngOnDestroy(): void { this.refreshTimer.unsubscribe(); - this.subscriptions.forEach(subscription => subscription.unsubscribe()); + this.subscriptions.forEach((subscription) => subscription.unsubscribe()); } public async toggleDbQueue(): Promise { @@ -115,11 +114,13 @@ export class SystemStatusComponent implements OnInit, OnDestroy { return; } - if (await this.confirmationService.confirm({ - Title: '#LDS#Heading Start DBQueue', - Message: '#LDS#Are you sure you want to start the DBQueue?', - identifier: 'system-status-confirm-start-dbqueue' - })) { + if ( + await this.confirmationService.confirm({ + Title: '#LDS#Heading Start DBQueue', + Message: '#LDS#Are you sure you want to start the DBQueue?', + identifier: 'system-status-confirm-start-dbqueue', + }) + ) { this.changeIsDbServiceDisabled(); } } @@ -130,37 +131,45 @@ export class SystemStatusComponent implements OnInit, OnDestroy { return; } - if (await this.confirmationService.confirm({ - Title: '#LDS#Heading Start Job Queue', - Message: '#LDS#Are you sure you want to start the Job queue?', - identifier: 'system-status-confirm-start-jobqueue' - })) { + if ( + await this.confirmationService.confirm({ + Title: '#LDS#Heading Start Job Queue', + Message: '#LDS#Are you sure you want to start the Job queue?', + identifier: 'system-status-confirm-start-jobqueue', + }) + ) { this.changeIsJobServiceDisabled(); } } private changeIsJobServiceDisabled(): void { - const observer = this.statusService.setStatus(!this.status.IsJobServiceDisabled, this.status.IsDbSchedulerDisabled); - this.subscriptions.push(observer.subscribe(res => { - this.sub.next('foo'); - })); + const observer = this.statusService.setStatus(!!!this.status.IsJobServiceDisabled, !!this.status.IsDbSchedulerDisabled); + this.subscriptions.push( + observer.subscribe((res) => { + this.sub.next('foo'); + }), + ); } private initServerObserver(): void { let statusObserver: Observable; statusObserver = this.sub.pipe(switchMap((term: any) => this.statusService.getStatus())); - this.subscriptions.push(statusObserver.subscribe(res => { - this.status = res as SystemStatusInformation; - })); + this.subscriptions.push( + statusObserver.subscribe((res) => { + this.status = res as SystemStatusInformation; + }), + ); this.sub.next('foo'); - this.refreshTimer = interval(30000).subscribe(x => this.sub.next('foo')); + this.refreshTimer = interval(30000).subscribe((x) => this.sub.next('foo')); } private changeIsDbServiceDisabled(): void { - const observer = this.statusService.setStatus(this.status.IsJobServiceDisabled, !this.status.IsDbSchedulerDisabled); - this.subscriptions.push(observer.subscribe(res => { - this.sub.next('foo'); - })); + const observer = this.statusService.setStatus(!!this.status.IsJobServiceDisabled, !!!this.status.IsDbSchedulerDisabled); + this.subscriptions.push( + observer.subscribe((res) => { + this.sub.next('foo'); + }), + ); } } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/system-status/system-status.module.ts b/imxweb/projects/qer-app-operationssupport/src/app/information/system-status/system-status.module.ts index 195471750..c93dd340e 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/system-status/system-status.module.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/system-status/system-status.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -34,16 +34,9 @@ import { EuiCoreModule } from '@elemental-ui/core'; import { SystemStatusComponent } from './system-status.component'; import { SystemStatusService } from './system-status.service'; - @NgModule({ declarations: [SystemStatusComponent], - imports: [ - CommonModule, - TranslateModule, - MatButtonModule, - MatCardModule, - EuiCoreModule - ], - providers: [SystemStatusService] + imports: [CommonModule, TranslateModule, MatButtonModule, MatCardModule, EuiCoreModule], + providers: [SystemStatusService], }) -export class SystemStatusModule { } +export class SystemStatusModule {} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/information/system-status/system-status.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/information/system-status/system-status.service.ts index a4135d287..cc09aad4e 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/information/system-status/system-status.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/information/system-status/system-status.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,16 +24,19 @@ * */ -import { Observable, from } from 'rxjs'; import { Injectable } from '@angular/core'; +import { Observable, from } from 'rxjs'; import { imx_SessionService } from 'qbm'; -import { SystemStatusInformation } from './system-status-information.interface'; import { OpSupportUserService } from 'qer'; +import { SystemStatusInformation } from './system-status-information.interface'; @Injectable() export class SystemStatusService { - constructor(private session: imx_SessionService, private readonly userService: OpSupportUserService) { } + constructor( + private session: imx_SessionService, + private readonly userService: OpSupportUserService, + ) {} public getStatus(): Observable { return from(this.get()); @@ -50,11 +53,11 @@ export class SystemStatusService { public set(isJobServiceDisabled: boolean, isDbSchedulerDisabled: boolean): Promise { return this.session.Client.opsupport_systemstatus_post('', { IsDbSchedulerDisabled: isDbSchedulerDisabled, - IsJobServiceDisabled: isJobServiceDisabled + IsJobServiceDisabled: isJobServiceDisabled, }); } public async isSystemAdmin(): Promise { - return (await this.userService.getGroups()).some(role => role.Name === 'VID_BaseData_SystemStop_EditRights'); + return (await this.userService.getGroups()).some((role) => role.Name === 'VID_BaseData_SystemStop_EditRights'); } } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/journal/filter-tile-params.ts b/imxweb/projects/qer-app-operationssupport/src/app/journal/filter-tile-params.ts index 65f04c12a..86ea71487 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/journal/filter-tile-params.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/journal/filter-tile-params.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { FilterData } from 'imx-qbm-dbts'; +import { FilterData } from '@imx-modules/imx-qbm-dbts'; export class FilterTileParams { public caption: string; diff --git a/imxweb/projects/qer-app-operationssupport/src/app/journal/journal.component.html b/imxweb/projects/qer-app-operationssupport/src/app/journal/journal.component.html index fe9d9e919..3b9dc6198 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/journal/journal.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/journal/journal.component.html @@ -1,34 +1,42 @@
    -

    +

    {{ '#LDS#Heading Database Log' | translate }} -

    +
    - + - - - - - - + + + +
    - + - +
    - - - + + -
    {{data.HostName.Column.GetDisplayValue()}}
    -
    {{data.LogonUser.Column.GetDisplayValue()}}
    +
    {{ data.HostName.Column.GetDisplayValue() }}
    +
    {{ data.LogonUser.Column.GetDisplayValue() }}
    @@ -36,7 +44,7 @@

    - diff --git a/imxweb/projects/qer-app-operationssupport/src/app/journal/journal.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/journal/journal.component.scss index 64dd5ce64..fc29e01ee 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/journal/journal.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/journal/journal.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; :host { display: flex; @@ -7,18 +7,6 @@ flex-grow: 1; } -.imx-table-container { - flex: 1 1 auto; - display: flex; - flex-direction: column; - overflow: auto; - margin-bottom: -20px; - - > :nth-child(2) { - flex: 1 1 auto; - } -} - .imx-journal-textbox { max-height: 100px; overflow: auto; @@ -42,12 +30,6 @@ .imx-icon-column { margin-right: 20px; - .imx-warning-icon { - color: $corbin-orange; - } - .imx-error-icon { - color: $phoenix-red; - } } div[subtitle] { @@ -76,8 +58,3 @@ div[subtitle] { .mat-column-HostName { max-width: 300px; } - -.imx-refresh-button { - margin-right: 5px; - height: 50px; -} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/journal/journal.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/journal/journal.component.ts index 3eff98259..1f4e2c10b 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/journal/journal.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/journal/journal.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,17 +24,17 @@ * */ -import { Component, ViewChildren, QueryList, OnInit } from '@angular/core'; -import { EuiLoadingService } from '@elemental-ui/core'; import { OverlayRef } from '@angular/cdk/overlay'; +import { Component, OnInit, QueryList, ViewChildren } from '@angular/core'; +import { EuiLoadingService } from '@elemental-ui/core'; -import { CollectionLoadParameters, DataModel, DisplayColumns, EntitySchema, IClientProperty } from 'imx-qbm-dbts'; -import { FilterTileComponent, DataSourceToolbarSettings, DataSourceToolbarFilter, SettingsService } from 'qbm'; +import { CollectionLoadParameters, DataModel, DisplayColumns, EntitySchema, IClientProperty } from '@imx-modules/imx-qbm-dbts'; +import { DataSourceToolbarFilter, DataSourceToolbarSettings, FilterTileComponent, SettingsService } from 'qbm'; import { JournalService } from './journal.service'; @Component({ templateUrl: './journal.component.html', - styleUrls: ['./journal.component.scss'] + styleUrls: ['./journal.component.scss'], }) export class JournalComponent implements OnInit { @ViewChildren(FilterTileComponent) public filterTiles: QueryList; @@ -50,14 +50,15 @@ export class JournalComponent implements OnInit { constructor( public journalService: JournalService, private readonly settingsService: SettingsService, - private busyService: EuiLoadingService) { + private busyService: EuiLoadingService, + ) { this.entitySchemaJournal = journalService.OpsupportJournalEntitySchema; this.displayedColumns = [ this.entitySchemaJournal.Columns.MessageDate, this.entitySchemaJournal.Columns.ApplicationName, this.entitySchemaJournal.Columns.MessageString, this.entitySchemaJournal.Columns.MessageType, - this.entitySchemaJournal.Columns.HostName + this.entitySchemaJournal.Columns.HostName, ]; } @@ -78,7 +79,7 @@ export class JournalComponent implements OnInit { this.navigationState = navigationState; let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + setTimeout(() => (overlayRef = this.busyService.show())); try { const journalEntries = await this.journalService.getItems(navigationState); @@ -90,19 +91,17 @@ export class JournalComponent implements OnInit { navigationState: this.navigationState, dataModel: this.dataModel, }; - } finally { setTimeout(() => this.busyService.hide(overlayRef)); } } private async initFilters(): Promise { - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + setTimeout(() => (overlayRef = this.busyService.show())); try { this.dataModel = await this.journalService.getDataModel(); - this.filterOptions = this.dataModel.Filters; + this.filterOptions = this.dataModel.Filters ?? []; } finally { setTimeout(() => this.busyService.hide(overlayRef)); } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/journal/journal.module.ts b/imxweb/projects/qer-app-operationssupport/src/app/journal/journal.module.ts index beb76b666..a1a2bbc46 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/journal/journal.module.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/journal/journal.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -45,8 +45,8 @@ import { JournalService } from './journal.service'; EuiCoreModule, MatTooltipModule, MatButtonModule, - TranslateModule + TranslateModule, ], - providers: [JournalService] + providers: [JournalService], }) -export class JournalModule { } +export class JournalModule {} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/journal/journal.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/journal/journal.service.ts index d6a9c7e97..4e48ac553 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/journal/journal.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/journal/journal.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,23 +27,20 @@ import { Injectable } from '@angular/core'; import { imx_SessionService } from 'qbm'; -import { OpsupportJournal } from 'imx-api-qbm'; -import { CollectionLoadParameters, DataModel, EntitySchema, ExtendedTypedEntityCollection } from 'imx-qbm-dbts'; +import { OpsupportJournal } from '@imx-modules/imx-api-qbm'; +import { CollectionLoadParameters, DataModel, EntitySchema, ExtendedTypedEntityCollection } from '@imx-modules/imx-qbm-dbts'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class JournalService { - public get OpsupportJournalEntitySchema(): EntitySchema { return this.session.TypedClient.OpsupportJournal.GetSchema(); } - constructor(private session: imx_SessionService) { - } + constructor(private session: imx_SessionService) {} - public async getItems(parameter: CollectionLoadParameters): - Promise> { + public async getItems(parameter: CollectionLoadParameters): Promise> { return this.session.TypedClient.OpsupportJournal.Get(parameter); } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/object-overview/object-overview.component.html b/imxweb/projects/qer-app-operationssupport/src/app/object-overview/object-overview.component.html index 56f497d29..501263815 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/object-overview/object-overview.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/object-overview/object-overview.component.html @@ -1,87 +1,93 @@ - +
    -

    - {{display}} -

    +

    + {{ display }} +

    - {{tableDisplay}} + {{ tableDisplay }}
    - + -
    + + {{ '#LDS#There are currently no tasks for this object in any queues.' | translate }} + +
    + +
    +
    -

    {{ '#LDS#JobQueue' | translate }} -

    +

    {{ '#LDS#JobQueue' | translate }}

    +
    - - - + - - + + - {{dataItem.GetEntity().GetDisplay()}} + {{ dataItem.GetEntity().GetDisplay() }} - + {{ dataItem.Queue.Column.GetDisplayValue() }} - + {{ dataItem.TaskName.Column.GetDisplayValue() }} - - + +
    -
    - {{'#LDS#The queue cannot be displayed for this object type.' | translate}} +
    + {{ '#LDS#The queue cannot be displayed for this object type.' | translate }}
    -
    +
    -

    {{ '#LDS#DBQueue' | translate }}

    +

    {{ '#LDS#DBQueue' | translate }}

    - - - + - - + + - {{dataItem.GetEntity().GetDisplay()}} + {{ dataItem.GetEntity().GetDisplay() }} - + {{ dataItem.Object.Column.GetDisplayValue() }} - + {{ dataItem.SubObject.Column.GetDisplayValue() }} - + {{ dataItem.SortOrder.Column.GetDisplayValue() }} @@ -90,7 +96,7 @@

    {{ '#LDS#DBQueue' | translate }}

    - {{'#LDS#The queue cannot be displayed for this object type.' | translate}} + {{ '#LDS#The queue cannot be displayed for this object type.' | translate }}
    @@ -101,28 +107,14 @@

    {{ '#LDS#DBQueue' | translate }}

    - + + + - + - +
    - - - - - - - - - \ No newline at end of file diff --git a/imxweb/projects/qer-app-operationssupport/src/app/object-overview/object-overview.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/object-overview/object-overview.component.scss index fa45d5136..020f00c1c 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/object-overview/object-overview.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/object-overview/object-overview.component.scss @@ -1,18 +1,20 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; -@import "../../../shared/assets/variables.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; +@import 'base/variables'; +@import 'base/mixins'; :host { - display: flex; - flex-direction: column; - overflow: hidden; - height: 100%; + @include flex-column-container-fill(); +} +.imx-flex{ + .mat-headline-5{ + margin: 0; + } } .sub-headline { font-style: italic; font-weight: lighter; - margin-top: -2em; - margin-bottom: .5em; + margin-bottom: 0.5em; @media #{$IMX_Mediaquery_Smartphone} { margin-top: -0.5em; @@ -22,19 +24,8 @@ } } -:host ::ng-deep .mat-tab-body { - margin: 30px 0px; - - .mat-tab-body-content { - display: flex; - flex-direction: column; - } -} - .imx-job-queue-div { border-bottom: 1px solid $black-3; - padding-bottom: 30px; - margin-bottom: 30px; } .imx-object-overview-tab-text { @@ -49,31 +40,6 @@ .imx-object-overview-tab-text { font-size: medium; } - -:host ::ng-deep .mat-tab-body-wrapper { - position: relative; - overflow: hidden; - display: flex; - flex: 1; -} - -:host ::ng-deep .mat-tab-body { - margin: 30px 0px 0px 0px; -} - -:host ::ng-deep .mat-tab-body.mat-tab-body-active { - overflow-x: auto; +.imx-content-header, eui-alert{ + margin-top: 24px; } - -.imx-table-container { - margin-right: 30px; - max-height: 500px; - overflow: hidden; - display: flex; - flex-direction: column; -} - -.imx-refresh-button { - margin-right: 5px; - height: 50px; -} \ No newline at end of file diff --git a/imxweb/projects/qer-app-operationssupport/src/app/object-overview/object-overview.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/object-overview/object-overview.component.ts index 26162af6d..3efd4f7d8 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/object-overview/object-overview.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/object-overview/object-overview.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,35 +24,35 @@ * */ -import { Component, OnInit, ErrorHandler, AfterViewInit } from '@angular/core'; +import { AfterViewInit, Component, ErrorHandler, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; -import { DbObjectKey, DisplayColumns, EntitySchema, IClientProperty, ReadOnlyEntity, ValType } from 'imx-qbm-dbts'; -import { QueueEntriesData, ShapeData } from 'imx-api-qbm'; +import { EuiSidesheetService } from '@elemental-ui/core'; +import { QueueEntriesData, ShapeData } from '@imx-modules/imx-api-qbm'; +import { DbObjectKey, DisplayColumns, EntitySchema, IClientProperty, ReadOnlyEntity, ValType } from '@imx-modules/imx-qbm-dbts'; import { + AuthenticationService, + ClassloggerService, + ClientPropertyForTableColumns, + DataSourceToolbarSettings, MetadataService, OpsupportDbObjectService, ShapeClickArgs, - ClassloggerService, - DataSourceToolbarSettings, - AuthenticationService, - ClientPropertyForTableColumns + calculateSidesheetWidth, } from 'qbm'; +import { FeatureConfigService, ObjectOverviewContainer } from 'qer'; +import { ErrorMessageSidesheetComponent } from '../processes/error-message-sidesheet/error-message-sidesheet.component'; import { QueueJobsService } from '../processes/jobs/queue-jobs.service'; -import { ObjectOverviewContainer, FeatureConfigService } from 'qer'; import { ObjectOverviewService } from './object-overview.service'; import { PersonDbQueueInfo } from './person-db-queue-info'; import { PersonJobQueueInfo } from './person-job-queue-info'; -import { ErrorMessageSidesheetComponent } from '../processes/error-message-sidesheet/error-message-sidesheet.component'; -import { EuiSidesheetService } from '@elemental-ui/core'; @Component({ templateUrl: './object-overview.component.html', - styleUrls: ['./object-overview.component.scss'] + styleUrls: ['./object-overview.component.scss'], }) export class ObjectOverviewComponent implements OnInit, AfterViewInit, ObjectOverviewContainer { - public hyperviewShapes: ShapeData[] = []; public DisplayColumns = DisplayColumns; @@ -94,7 +94,6 @@ export class ObjectOverviewComponent implements OnInit, AfterViewInit, ObjectOve private featureService: FeatureConfigService, authentication: AuthenticationService, ) { - this.entitySchemaDbs = PersonDbQueueInfo.GetEntitySchema(); this.entitySchemaJobs = PersonJobQueueInfo.GetEntitySchema(); @@ -105,8 +104,8 @@ export class ObjectOverviewComponent implements OnInit, AfterViewInit, ObjectOve { ColumnName: 'actions', Type: ValType.String, - afterAdditionals: true - } + afterAdditionals: true, + }, ]; this.displayedColumnsDbs = [ @@ -116,7 +115,7 @@ export class ObjectOverviewComponent implements OnInit, AfterViewInit, ObjectOve this.entitySchemaDbs.Columns.SortOrder, ]; - authentication.onSessionResponse.subscribe(session => this.uidUser = session.UserUid); + authentication.onSessionResponse.subscribe((session) => (this.uidUser = session.UserUid ?? '')); } public busy = true; @@ -127,18 +126,17 @@ export class ObjectOverviewComponent implements OnInit, AfterViewInit, ObjectOve public tabIndex = 0; public async ngOnInit(): Promise { - - this.route.params.subscribe(res => { - // reinitialize if params changes - if (this.objectUID !== res.uid) { + this.route.params.subscribe((res) => { + // reinitialize if params changes + if (this.objectUID !== res.uid) { this.init(); if (this.hasHyperviewParam(res.tab)) { this.tabIndex = 3; } - } - }) + } + }); - this.init(); + this.init(); } public async ngAfterViewInit(): Promise { @@ -148,16 +146,16 @@ export class ObjectOverviewComponent implements OnInit, AfterViewInit, ObjectOve } } - /** + /** * Handles the event, when a shape was clicked. * @param args the {@link ShapeClickArgs|arguments of the clicked shape} */ - public async onShapeClick(args: ShapeClickArgs): Promise { - const objKey = DbObjectKey.FromXml(args.objectKey); - if (await this.objectIsSupported(objKey)) { - this.router.navigate(['object', objKey.TableName, objKey.Keys[0], 'hyperview']); - } + public async onShapeClick(args: ShapeClickArgs): Promise { + const objKey = DbObjectKey.FromXml(args.objectKey); + if (await this.objectIsSupported(objKey)) { + this.router.navigate(['object', objKey.TableName, objKey.Keys[0], 'hyperview']); } + } // Checks if the item has an ErrorMessage or not public hasContent(item: PersonJobQueueInfo): boolean { @@ -173,19 +171,18 @@ export class ObjectOverviewComponent implements OnInit, AfterViewInit, ObjectOve public reactivate(item: PersonJobQueueInfo): void { this.jobService .Post([item.UID_Job.value]) - .then((_: any) => this.loadQueue('jobQueue')) + .then((_: any) => this.loadQueue()) .catch((err: any) => this.errorHandler.handleError(err)); } // Shows the error message for a JobQueueInfo object public async showMessage(item: PersonJobQueueInfo): Promise { - - await this.sidesheet + await this.sidesheet .open(ErrorMessageSidesheetComponent, { title: await this.translationProvider.get('#LDS#Heading View Error Message').toPromise(), subTitle: item.GetEntity().GetDisplay(), padding: '0', - width: 'max(60%,600px)', + width: calculateSidesheetWidth(), testId: 'error-message-sidesheet', data: item.ErrorMessages.Column.GetDisplayValue(), }) @@ -202,50 +199,55 @@ export class ObjectOverviewComponent implements OnInit, AfterViewInit, ObjectOve this.logger.error(this, 'selection trigged for: ' + ev.objectKey); } - public async loadQueue(updateType: 'both' | 'jobQueue' | 'dbQueue' = 'both'): Promise { + public async loadQueue(): Promise { const cached = await this.objectIsSupported(this.objectKey); - if (this.queuesUnsupported) { + if (this.queuesUnsupported || !cached) { return; } - if (updateType === 'both' || updateType === 'jobQueue') { - const job = cached.JobQueue.map(entityData => - new PersonJobQueueInfo((new ReadOnlyEntity(PersonJobQueueInfo.GetEntitySchema(), entityData)))); + if (cached.JobQueue) { + const job = cached.JobQueue.map( + (entityData) => new PersonJobQueueInfo(new ReadOnlyEntity(PersonJobQueueInfo.GetEntitySchema(), entityData)), + ); this.jobTotal = job.length; this.dstSettingsJobs = { displayedColumns: this.displayedColumnsJobs, - dataSource: { Data: job, totalCount: job.length }, + dataSource: { Data: job, totalCount: this.jobTotal }, entitySchema: this.entitySchemaJobs, - navigationState: {} + navigationState: {}, }; } - if (updateType === 'both' || updateType === 'dbQueue') { - const db = cached.DbQueue.map(entityData => - new PersonDbQueueInfo((new ReadOnlyEntity(PersonDbQueueInfo.GetEntitySchema(), entityData)))); + if (cached.DbQueue) { + const db = cached.DbQueue.map( + (entityData) => new PersonDbQueueInfo(new ReadOnlyEntity(PersonDbQueueInfo.GetEntitySchema(), entityData)), + ); this.dbTotal = db.length; this.dstSettingsDb = { displayedColumns: this.displayedColumnsDbs, - dataSource: { Data: db, totalCount: db.length }, + dataSource: { Data: db, totalCount: this.dbTotal }, entitySchema: this.entitySchemaDbs, - navigationState: {} + navigationState: {}, }; } } + public get hideQueueTables(): boolean { + return this.jobTotal === 0 && this.dbTotal === 0; + } + private async init(): Promise { this.hyperviewShapes = []; this.busy = true; try { await this.initDataFromPath(this.route); const featureConfig = await this.featureService.getFeatureConfig(); - this.showPassCodeTab = featureConfig.EnableSetPasswords - && this.objectKey.TableName.toLowerCase() === this.tablePerson - && this.uidUser !== this.objectUID; + this.showPassCodeTab = + featureConfig.EnableSetPasswords && this.objectKey.TableName.toLowerCase() === this.tablePerson && this.uidUser !== this.objectUID; await this.loadQueue(); } finally { this.busy = false; @@ -255,9 +257,9 @@ export class ObjectOverviewComponent implements OnInit, AfterViewInit, ObjectOve /** * Check if the given object is supported. * @param objKey {@link DbObjectKey} that needs to check. - * @returns + * @returns */ - private async objectIsSupported(objKey: DbObjectKey): Promise { + private async objectIsSupported(objKey: DbObjectKey): Promise { const cached = await this.overviewService.get(objKey); this.queuesUnsupported = cached.Unsupported; @@ -273,24 +275,22 @@ export class ObjectOverviewComponent implements OnInit, AfterViewInit, ObjectOve * @param param route param to check * @returns true, if it's the hyperview-param */ - private hasHyperviewParam(param: string): boolean { + private hasHyperviewParam(param: string | null): boolean { return param === 'hyperview'; } - // initializes the variables provided by the route private async initDataFromPath(route: ActivatedRoute): Promise { const snapShotMap = route.snapshot.paramMap; - this.tablename = snapShotMap.get('table'); - this.objectUID = snapShotMap.get('uid'); + this.tablename = snapShotMap.get('table') ?? ''; + this.objectUID = snapShotMap.get('uid') ?? ''; this.objectKey = new DbObjectKey(this.tablename, this.objectUID); const metadata = await this.metadataService.GetTableMetadata(this.tablename); this.tableDisplay = metadata.DisplaySingular; const entity = await this.dbObjectService.Get({ tableName: this.tablename, uid: this.objectUID }); - this.display = entity.Display; + this.display = entity.Display ?? ''; } - } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/object-overview/object-overview.module.ts b/imxweb/projects/qer-app-operationssupport/src/app/object-overview/object-overview.module.ts index b3f36c2aa..fdb2763da 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/object-overview/object-overview.module.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/object-overview/object-overview.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -44,30 +44,27 @@ import { ObjectHistoryModule, ObjectHistoryApiService, QbmModule, + BusyIndicatorModule, } from 'qbm'; import { ObjectHyperviewModule, ObjectHyperviewService, OpsModule } from 'qer'; import { ObjectOverviewComponent } from './object-overview.component'; import { ObjectOverviewService } from './object-overview.service'; import { OpSupportHistoryApiService } from './opsupport-history-api.service'; -import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { OpsHyperviewService } from '../hyperview/ops-hyperview.service'; - @NgModule({ - declarations: [ - ObjectOverviewComponent, - ], + declarations: [ObjectOverviewComponent], imports: [ CommonModule, ObjectHistoryModule, DataTableModule, DataSourceToolbarModule, + BusyIndicatorModule, EuiCoreModule, FormsModule, MatTabsModule, MatButtonModule, MatCardModule, - MatProgressSpinnerModule, MatTooltipModule, MatFormFieldModule, MatSelectModule, @@ -77,18 +74,18 @@ import { OpsHyperviewService } from '../hyperview/ops-hyperview.service'; MatTableModule, MatPaginatorModule, OpsModule, - QbmModule + QbmModule, ], providers: [ { provide: ObjectHistoryApiService, - useClass: OpSupportHistoryApiService - }, + useClass: OpSupportHistoryApiService, + }, { provide: ObjectHyperviewService, - useClass: OpsHyperviewService - }, - ObjectOverviewService - ] + useClass: OpsHyperviewService, + }, + ObjectOverviewService, + ], }) -export class ObjectOverviewModule { } +export class ObjectOverviewModule {} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/object-overview/object-overview.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/object-overview/object-overview.service.ts index abf37dd04..023f1891e 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/object-overview/object-overview.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/object-overview/object-overview.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,8 +25,8 @@ */ import { Injectable } from '@angular/core'; -import { QueueEntriesData } from 'imx-api-qbm'; -import { DbObjectKey, EntitySchema } from 'imx-qbm-dbts'; +import { QueueEntriesData } from '@imx-modules/imx-api-qbm'; +import { DbObjectKey, EntitySchema } from '@imx-modules/imx-qbm-dbts'; import { imx_SessionService } from 'qbm'; @Injectable() @@ -35,10 +35,9 @@ export class ObjectOverviewService { return this.session.TypedClient.OpsupportQueueJobs.GetSchema(); } - constructor(private session: imx_SessionService) { } + constructor(private session: imx_SessionService) {} public get(objectKey: DbObjectKey): Promise { return this.session.Client.opsupport_queue_object_get(objectKey.TableName, objectKey.Keys[0]); } - } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/object-overview/opsupport-history-api.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/object-overview/opsupport-history-api.service.ts index 49727d884..8cd1129c5 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/object-overview/opsupport-history-api.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/object-overview/opsupport-history-api.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,20 +25,19 @@ */ import { Injectable } from '@angular/core'; -import { HistoryData } from 'imx-qbm-dbts'; -import { HistoryComparisonData } from 'imx-api-qbm'; +import { HistoryData } from '@imx-modules/imx-qbm-dbts'; +import { HistoryComparisonData } from '@imx-modules/imx-api-qbm'; import { ObjectHistoryApiService, imx_SessionService } from 'qbm'; @Injectable() export class OpSupportHistoryApiService implements ObjectHistoryApiService { - - constructor(private readonly session: imx_SessionService) { } + constructor(private readonly session: imx_SessionService) {} getHistoryData(table: string, uid: string): Promise { return this.session.Client.opsupport_history_get(table, uid); } - getHistoryComparisonData(table: string, uid: string,options?: {CompareDate?: Date;}): Promise { + getHistoryComparisonData(table: string, uid: string, options?: { CompareDate?: Date }): Promise { return this.session.Client.opsupport_history_comparison_get(table, uid, options); } -} \ No newline at end of file +} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/object-overview/person-db-queue-info.ts b/imxweb/projects/qer-app-operationssupport/src/app/object-overview/person-db-queue-info.ts index af606bb8b..9f6be2728 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/object-overview/person-db-queue-info.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/object-overview/person-db-queue-info.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { DisplayColumns, EntitySchema, IReadValue, TypedEntity, ValType } from 'imx-qbm-dbts'; +import { DisplayColumns, EntitySchema, IReadValue, TypedEntity, ValType } from '@imx-modules/imx-qbm-dbts'; export class PersonDbQueueInfo extends TypedEntity { public readonly Object: IReadValue = this.GetEntityValue('Object'); @@ -42,11 +42,11 @@ export class PersonDbQueueInfo extends TypedEntity { Type: ValType.String, IsReadOnly: true, }, - SortOrder: { + SortOrder: { ColumnName: 'SortOrder', Type: ValType.String, IsReadOnly: true, - } + }, }; columns[DisplayColumns.DISPLAY_PROPERTYNAME] = DisplayColumns.DISPLAY_PROPERTY; columns[DisplayColumns.DISPLAY_LONG_PROPERTYNAME] = DisplayColumns.DISPLAY_PROPERTY_LONG; diff --git a/imxweb/projects/qer-app-operationssupport/src/app/object-overview/person-job-queue-info.ts b/imxweb/projects/qer-app-operationssupport/src/app/object-overview/person-job-queue-info.ts index 509a8e590..40922a6d8 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/object-overview/person-job-queue-info.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/object-overview/person-job-queue-info.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { DisplayColumns, EntitySchema, IReadValue, TypedEntity, ValType } from 'imx-qbm-dbts'; +import { DisplayColumns, EntitySchema, IReadValue, TypedEntity, ValType } from '@imx-modules/imx-qbm-dbts'; export class PersonJobQueueInfo extends TypedEntity { public readonly Queue: IReadValue = this.GetEntityValue('Queue'); @@ -58,8 +58,7 @@ export class PersonJobQueueInfo extends TypedEntity { ColumnName: 'ErrorMessages', Type: ValType.String, IsReadOnly: true, - } - + }, }; columns[DisplayColumns.DISPLAY_PROPERTYNAME] = DisplayColumns.DISPLAY_PROPERTY; columns[DisplayColumns.DISPLAY_LONG_PROPERTYNAME] = DisplayColumns.DISPLAY_PROPERTY_LONG; diff --git a/imxweb/projects/qer-app-operationssupport/src/app/object-search/object-search.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/object-search/object-search.component.ts index d5de3c63c..c487c154d 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/object-search/object-search.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/object-search/object-search.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,12 +26,12 @@ import { Component } from '@angular/core'; import { Router } from '@angular/router'; -import { imx_QBM_SearchService, MetadataService, DbObjectInfo } from 'qbm'; +import { DbObjectInfo, imx_QBM_SearchService, MetadataService } from 'qbm'; @Component({ selector: 'imx-object-search', templateUrl: './object-search.component.html', - styleUrls: ['./object-search.component.scss'] + styleUrls: ['./object-search.component.scss'], }) export class ObjectSearchComponent { public get SearchService(): imx_QBM_SearchService { @@ -45,14 +45,21 @@ export class ObjectSearchComponent { constructor( private objectSearchService: imx_QBM_SearchService, private router: Router, - private metadataService: MetadataService) { } + private metadataService: MetadataService, + ) {} public itemSelected(event: any): void { const dataItem = event as DbObjectInfo; - this.router.navigate([`/object/${dataItem.Key.TableName}/${dataItem.Key.Keys[0]}`]); + if (dataItem.Key?.TableName && dataItem.Key?.Keys?.[0]) { + this.router.navigate([`/object/${dataItem.Key.TableName}/${dataItem.Key.Keys[0]}`]); + } else { + throw Error('DataItem has missing properties - cannot navigate.'); + } } public async setCurrentItem(dataItem: DbObjectInfo): Promise { - this.currentMetadataItem = await this.MetadataService.GetTableMetadata(dataItem.Key.TableName); + if (dataItem.Key?.TableName) { + this.currentMetadataItem = await this.MetadataService.GetTableMetadata(dataItem.Key.TableName); + } } } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/object-search/object-search.module.ts b/imxweb/projects/qer-app-operationssupport/src/app/object-search/object-search.module.ts index d0714ced7..8b6ae4356 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/object-search/object-search.module.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/object-search/object-search.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -32,15 +32,9 @@ import { TranslateModule } from '@ngx-translate/core'; import { QbmModule } from 'qbm'; import { ObjectSearchComponent } from './object-search.component'; - @NgModule({ declarations: [ObjectSearchComponent], - imports: [ - CommonModule, - QbmModule, - MatTooltipModule, - TranslateModule - ], - exports: [ObjectSearchComponent] + imports: [CommonModule, QbmModule, MatTooltipModule, TranslateModule], + exports: [ObjectSearchComponent], }) -export class ObjectSearchModule { } +export class ObjectSearchModule {} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/permissions/permissions-helper.ts b/imxweb/projects/qer-app-operationssupport/src/app/permissions/permissions-helper.ts index ffc69651f..ca2ee71fd 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/permissions/permissions-helper.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/permissions/permissions-helper.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,6 +24,6 @@ * */ -export function isOutstandingManager(groups: string[]): boolean { - return groups.find(item => item === 'QER_4_ManageOutstanding') != null; +export function isOutstandingManager(groups: (string | undefined)[]): boolean { + return groups.find((item) => item === 'QER_4_ManageOutstanding') != null; } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/permissions/permissions.service.spec.ts b/imxweb/projects/qer-app-operationssupport/src/app/permissions/permissions.service.spec.ts index 2a55dae6e..0a9fd2317 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/permissions/permissions.service.spec.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/permissions/permissions.service.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -43,9 +43,9 @@ describe('PermissionsService', () => { .mock(OpSupportUserService, { getGroups: jasmine.createSpy('userGroups').and.callFake(() => userGroups), }) - .beforeCompileComponents(testBed => { + .beforeCompileComponents((testBed) => { testBed.configureTestingModule({ - schemas: [CUSTOM_ELEMENTS_SCHEMA] + schemas: [CUSTOM_ELEMENTS_SCHEMA], }); }); }); @@ -66,5 +66,5 @@ describe('PermissionsService', () => { expect(await service.isOutstandingManager()).toEqual(testcase.canSee); }); - }; + } }); diff --git a/imxweb/projects/qer-app-operationssupport/src/app/permissions/permissions.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/permissions/permissions.service.ts index b373db69d..7ddf783b7 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/permissions/permissions.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/permissions/permissions.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,12 +30,12 @@ import { OpSupportUserService } from 'qer'; import { isOutstandingManager } from './permissions-helper'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class PermissionsService { - constructor(private readonly userService: OpSupportUserService) { } + constructor(private readonly userService: OpSupportUserService) {} public async isOutstandingManager(): Promise { - return isOutstandingManager((await this.userService.getGroups()).map(group => group.Name)); + return isOutstandingManager((await this.userService.getGroups()).map((group) => group.Name)); } } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/error-message-sidesheet/error-message-sidesheet.component.html b/imxweb/projects/qer-app-operationssupport/src/app/processes/error-message-sidesheet/error-message-sidesheet.component.html index 7da1f9b6e..f9261d618 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/error-message-sidesheet/error-message-sidesheet.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/error-message-sidesheet/error-message-sidesheet.component.html @@ -6,12 +6,7 @@
    -
    diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/error-message-sidesheet/error-message-sidesheet.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/processes/error-message-sidesheet/error-message-sidesheet.component.scss index 669fb287f..dbb33d096 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/error-message-sidesheet/error-message-sidesheet.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/error-message-sidesheet/error-message-sidesheet.component.scss @@ -1,7 +1,4 @@ -.eui-sidesheet-content { - flex-direction: column; - display: flex; -} +@import 'base/mixins'; .imx-text-container { overflow: auto; @@ -9,13 +6,11 @@ margin-bottom: 10px; } -.mat-card{ - display: flex; - flex-direction: column; +.mat-mdc-card { + @include flex-column-container($max-height: 100%); flex: 1 1 auto; - max-height: 100%; - .mat-card-content{ - overflow: auto; + .mat-mdc-card-content { + overflow: auto; } } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/error-message-sidesheet/error-message-sidesheet.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/processes/error-message-sidesheet/error-message-sidesheet.component.ts index d2559c173..fff5b215d 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/error-message-sidesheet/error-message-sidesheet.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/error-message-sidesheet/error-message-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -40,14 +40,14 @@ export class ErrorMessageSidesheetComponent implements OnInit { @Inject(EUI_SIDESHEET_DATA) public readonly data: string, private sidesheetRef: EuiSidesheetRef, private clipboard: Clipboard, - private readonly snackbar: SnackBarService + private readonly snackbar: SnackBarService, ) {} public ngOnInit(): void {} public copyToClipboard(): void { this.clipboard.copy(this.data); - this.snackbar.open({ key: '#LDS#The error message has been successfully copied to the clipboard.'}); + this.snackbar.open({ key: '#LDS#The error message has been successfully copied to the clipboard.' }); this.sidesheetRef.close(); } } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/frozen-jobs.component.html b/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/frozen-jobs.component.html index 21f0b1cc8..40de002e9 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/frozen-jobs.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/frozen-jobs.component.html @@ -1,45 +1,46 @@ -

    - {{'#LDS#Frozen process steps' | translate}} "{{queueName}}" ({{jobCount}}) -

    +

    {{ '#LDS#Frozen process steps' | translate }} "{{ queueName }}" ({{ jobCount }})

    - + - - + +
    {{ dataItem.JobChainName.Column.GetDisplayValue() }}
    - + {{ dataItem.TaskName.Column.GetDisplayValue() }} - - + +
    -
    +
    - + -
    - - diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/frozen-jobs.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/frozen-jobs.component.scss index 71c8c43e8..171a2ee12 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/frozen-jobs.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/frozen-jobs.component.scss @@ -1,45 +1,6 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; - :host { display: flex; flex-direction: column; overflow: hidden; flex-grow: 1; } - -.imx-table-container { - flex: 1 1 auto; - height: calc(100% - 50px); - display: flex; - flex-direction: column; - - > :nth-child(2) { - flex: 1 1 auto; - } -} - -.imx-button-bar { - display: flex; - justify-content: flex-end; - margin-top: -10px; - - > * { - margin-left: 5px; - } -} - -.imx-refresh-button { - margin-right: 5px; - height: 50px; -} - -.mat-raised-button.rightButtonFloat { - clear: both; - width: auto; - float: right; - position: absolute; - right: 0; - padding: 0 20px; - background-color: $iris-blue; - color: $white; -} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/frozen-jobs.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/frozen-jobs.component.ts index d9bbfac9f..13b6e2bf2 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/frozen-jobs.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/frozen-jobs.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,23 +24,23 @@ * */ +import { OverlayRef } from '@angular/cdk/overlay'; import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; import { MatDialog } from '@angular/material/dialog'; -import { OverlayRef } from '@angular/cdk/overlay'; +import { ActivatedRoute } from '@angular/router'; import { EuiLoadingService, EuiSidesheetConfig, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { OpsupportQueueFrozenjobs } from 'imx-api-qbm'; +import { OpsupportQueueFrozenjobs } from '@imx-modules/imx-api-qbm'; +import { CollectionLoadParameters, DisplayColumns, EntitySchema, IClientProperty, TypedEntity, ValType } from '@imx-modules/imx-qbm-dbts'; +import { calculateSidesheetWidth, DataSourceToolbarSettings, MessageDialogComponent, SettingsService, SnackBarService } from 'qbm'; import { QueueJobsService } from '../jobs/queue-jobs.service'; -import { FrozenJobsService, JobQueueParameters } from './frozen-jobs.service'; -import { SnackBarService, MessageDialogComponent, DataSourceToolbarSettings, SettingsService } from 'qbm'; -import { CollectionLoadParameters, DisplayColumns, EntitySchema, IClientProperty, ValType } from 'imx-qbm-dbts'; +import { FrozenJobsService } from './frozen-jobs.service'; import { SingleFrozenJobComponent } from './single-frozen-job.component'; @Component({ templateUrl: './frozen-jobs.component.html', - styleUrls: ['./frozen-jobs.component.scss'] + styleUrls: ['./frozen-jobs.component.scss'], }) export class FrozenJobsComponent implements OnInit { public queueName = ''; @@ -64,7 +64,7 @@ export class FrozenJobsComponent implements OnInit { private busyService: EuiLoadingService, private frozenJobs: FrozenJobsService, private settingsService: SettingsService, - private translator: TranslateService + private translator: TranslateService, ) { this.entitySchemaFrozenJobs = frozenJobs.EntitySchema; this.displayedColumns = [ @@ -72,13 +72,13 @@ export class FrozenJobsComponent implements OnInit { this.entitySchemaFrozenJobs.Columns.TaskName, { ColumnName: 'actions', - Type: ValType.String - } + Type: ValType.String, + }, ]; } public async ngOnInit(): Promise { - this.queueName = this.route.snapshot.paramMap.get('queueName'); + this.queueName = this.route.snapshot.paramMap.get('queueName') ?? ''; await this.getData({ StartIndex: 0, PageSize: this.settingsService.DefaultPageSize, queueName: this.queueName }); } @@ -86,10 +86,9 @@ export class FrozenJobsComponent implements OnInit { return this.getData({ StartIndex: 0, search: keywords, queueName: this.queueName }); } - public async reactivate(item: OpsupportQueueFrozenjobs): Promise { let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + setTimeout(() => (overlayRef = this.busyService.show())); try { await this.jobService.Post([item.UID_Job.value]); this.snackbarService.open({ key: '#LDS#Process "{0}" is retrying.', parameters: [item.JobChainName.value] }); @@ -99,8 +98,8 @@ export class FrozenJobsComponent implements OnInit { } } - public onSelectionChanged(jobs: OpsupportQueueFrozenjobs[]): void { - this.selectedJobs = jobs; + public onSelectionChanged(jobs: TypedEntity[]): void { + this.selectedJobs = jobs as OpsupportQueueFrozenjobs[]; } public async viewDetails(job: OpsupportQueueFrozenjobs): Promise { @@ -108,17 +107,21 @@ export class FrozenJobsComponent implements OnInit { const opts: EuiSidesheetConfig = { title: await this.translator.get('#LDS#Heading View Process Details').toPromise(), subTitle: job.JobChainName.Column.GetDisplayValue(), - width: 'max(1000px, 80%)', + width: calculateSidesheetWidth(1100, 0.7), icon: 'reboot', testId: 'frozen-jobs-process-details-sidesheet', data: { UID_Tree: job.UID_Tree.value, - load: (startId:string) => {return this.jobService.getTreeData(startId)} + load: (startId: string) => { + return this.jobService.getTreeData(startId); + }, }, }; - this.sideSheet.open(SingleFrozenJobComponent, opts) + this.sideSheet + .open(SingleFrozenJobComponent, opts) // After the sidesheet closes, reload the current data to refresh any changes that might have been made - .afterClosed().subscribe(() => this.refresh()); + .afterClosed() + .subscribe(() => this.refresh()); } public async showMessage(item: OpsupportQueueFrozenjobs): Promise { @@ -126,9 +129,9 @@ export class FrozenJobsComponent implements OnInit { data: { ShowOk: true, Title: await this.translator.get('#LDS#Error message').toPromise(), - Message: item.ErrorMessages.Column.GetDisplayValue() + Message: item.ErrorMessages.Column.GetDisplayValue(), }, - panelClass: 'imx-messageDialog' + panelClass: 'imx-messageDialog', }); } @@ -139,7 +142,7 @@ export class FrozenJobsComponent implements OnInit { public async reactivateSelected(): Promise { if (this.selectedJobs.length > 0) { let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + setTimeout(() => (overlayRef = this.busyService.show())); try { await this.jobService.Post(this.selectedJobs.map((job: OpsupportQueueFrozenjobs) => job.UID_Job.value)); this.snackbarService.open({ key: '#LDS#{0} processes are retrying.', parameters: [this.selectedJobs.length] }); @@ -155,23 +158,21 @@ export class FrozenJobsComponent implements OnInit { await this.getData({ StartIndex: 0, PageSize: this.settingsService.DefaultPageSize, queueName: this.queueName }); } - public async getData(navigationState: JobQueueParameters): Promise { + public async getData(navigationState: CollectionLoadParameters): Promise { this.navigationState = navigationState; let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + setTimeout(() => (overlayRef = this.busyService.show())); try { - - const journalEntries = await this.frozenJobs.Get(navigationState); + const journalEntries = await this.frozenJobs.Get({ ...navigationState, queueName: this.queueName }); this.jobCount = journalEntries.totalCount; this.dstSettings = { displayedColumns: this.displayedColumns, dataSource: journalEntries, entitySchema: this.entitySchemaFrozenJobs, - navigationState: this.navigationState + navigationState: this.navigationState, }; - } finally { setTimeout(() => this.busyService.hide(overlayRef)); } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/frozen-jobs.service.spec.ts b/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/frozen-jobs.service.spec.ts index 20bd94560..105d6e9d4 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/frozen-jobs.service.spec.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/frozen-jobs.service.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { FrozenJobsService } from './frozen-jobs.service'; -import { OpsupportQueueFrozenjobs } from 'imx-api-qbm'; +import { OpsupportQueueFrozenjobs } from '@imx-modules/imx-api-qbm'; import { testTypedEntityReadOnlyProvider } from '../../test-utilities/typed-entity-provider.spec'; import { ImxApiDtoMock } from '../../test-utilities/imx-api-mock.spec'; @@ -39,8 +39,8 @@ describe('FrozenJobsService', () => { typedClient: { OpsupportQueueFrozenjobs: jasmine.createSpyObj('OpsupportQueueFrozenjobs', { GetSchema: () => OpsupportQueueFrozenjobs.GetEntitySchema(), - Get: Promise.resolve({ Data: data, totalCount: data.length }) - }) - } + Get: Promise.resolve({ Data: data, totalCount: data.length }), + }), + }, }); }); diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/frozen-jobs.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/frozen-jobs.service.ts index 8932a2830..99321bc6b 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/frozen-jobs.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/frozen-jobs.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,16 +26,16 @@ import { Injectable } from '@angular/core'; -import { TypedEntityCollectionData, EntitySchema, CollectionLoadParameters, TypedEntity } from 'imx-qbm-dbts'; +import { TypedEntityCollectionData, EntitySchema, CollectionLoadParameters, TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { imx_SessionService } from 'qbm'; -import { OpsupportQueueFrozenjobs } from 'imx-api-qbm'; +import { OpsupportQueueFrozenjobs } from '@imx-modules/imx-api-qbm'; export interface JobQueueParameters extends CollectionLoadParameters { queueName: string; } @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class FrozenJobsService { public get EntitySchema(): EntitySchema { diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/queue-tree.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/queue-tree.service.ts index 884f686da..a0acdfdbb 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/queue-tree.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/queue-tree.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,18 +26,25 @@ import { Injectable } from '@angular/core'; +import { HistoryOperationsData, OpsupportQueueJobaffects, ReactivateJobMode } from '@imx-modules/imx-api-qbm'; +import { + EntityCollectionData, + EntityData, + EntitySchema, + ExtendedTypedEntityCollection, + IEntityColumn, + TypedEntity, + TypedEntityCollectionData, +} from '@imx-modules/imx-qbm-dbts'; import { CdrFactoryService, ImxDataSource, imx_SessionService } from 'qbm'; -import { EntityCollectionData, EntityData, EntitySchema, ExtendedTypedEntityCollection, IEntityColumn, TypedEntity, TypedEntityCollectionData } from 'imx-qbm-dbts'; -import { HistoryOperationsData, OpsupportQueueJobaffects, ReactivateJobMode } from 'imx-api-qbm'; import { QueueJobsService } from '../jobs/queue-jobs.service'; @Injectable() export class QueueTreeService extends ImxDataSource { - public startUid: string; public items: TypedEntity[]; - public load: (startId: string) => Promise> + public load: (startId: string) => Promise>; constructor( private session: imx_SessionService, @@ -53,26 +60,39 @@ export class QueueTreeService extends ImxDataSource { public itemsProvider = async () => { let result: ExtendedTypedEntityCollection; - result = await this.load(this.startUid);//await this.session.TypedClient.OpsupportQueueTree.Get({ uidtree: this.startUid }); - return this.data = this.items = result?.Data; - } - - public childItemsProvider = (item: TypedEntity) => { + result = await this.load(this.startUid); //await this.session.TypedClient.OpsupportQueueTree.Get({ uidtree: this.startUid }); - const child1 = this.items ? this.items.find(el => this.getColumn(el, 'UID_Job').GetValue() === this.getColumn(item, 'UID_JobError').GetValue()) : null; - const child2 = this.items ? this.items.find(el => this.getColumn(el, 'UID_Job').GetValue() === this.getColumn(item, 'UID_JobSuccess').GetValue()) : null; + this.items = result?.Data; + return this.items; + }; - const res = []; - if (child1) { res.push(child1); } - if (child2) { res.push(child2); } + public childItemsProvider = (item: TypedEntity) => { + const child1 = this.items + ? this.items.find((el) => this.getColumn(el, 'UID_Job')?.GetValue() === this.getColumn(item, 'UID_JobError')?.GetValue()) + : undefined; + const child2 = this.items + ? this.items.find((el) => this.getColumn(el, 'UID_Job')?.GetValue() === this.getColumn(item, 'UID_JobSuccess')?.GetValue()) + : undefined; + + const res: TypedEntity[] = []; + if (child1) { + res.push(child1); + } + if (child2) { + res.push(child2); + } return Promise.resolve(res); - } + }; public hasChildrenProvider = (data: TypedEntity) => { - return (this.getColumn(data, 'UID_JobError').GetValue() != null && this.getColumn(data, 'UID_JobError').GetValue() !== '') - || (this.getColumn(data, 'UID_JobSuccess').GetValue() != null && this.getColumn(data, 'UID_JobSuccess').GetValue() !== ''); - } + const error = this.getColumn(data, 'UID_JobError')?.GetValue(); + const success = this.getColumn(data, 'UID_JobSuccess')?.GetValue() + return ( + ( error && success) || + ( error !== '' && success !== '') + ); + }; public async GetAffectedObjects(uidJob: string): Promise { return (await this.session.TypedClient.OpsupportQueueJobaffects.Get(uidJob)).Data; @@ -84,11 +104,13 @@ export class QueueTreeService extends ImxDataSource { public GetTotalSteps(): number { let count = 1; - this.items.forEach(el => { - if (this.getColumn(el, 'UID_JobError').GetValue() !== '') { + this.items.forEach((el) => { + const error = this.getColumn(el, 'UID_JobError')?.GetValue(); + if (error && error !== '') { count++; } - if (this.getColumn(el, 'UID_JobSuccess').GetValue() !== '') { + const success = this.getColumn(el, 'UID_JobSuccess')?.GetValue(); + if (success && success !== '') { count++; } }); @@ -96,13 +118,13 @@ export class QueueTreeService extends ImxDataSource { } public GetCompleteSteps(): number { - const root = this.items.find(el => this.getColumn(el, 'IsRootJob').GetValue()); - return this.GetCompleteSubSteps(this.getColumn(root, 'UID_Job').GetValue()); + const root = this.items.find((el) => this.getColumn(el, 'IsRootJob')?.GetValue()); + return root ? this.GetCompleteSubSteps(this.getColumn(root, 'UID_Job')?.GetValue()) : 0; } public RemoveEmpty(ent: EntityData[]): EntityData[] { const ret: EntityData[] = []; - ent.forEach(el => { + ent.forEach((el) => { if (el !== null) { ret.push(el); } @@ -111,34 +133,43 @@ export class QueueTreeService extends ImxDataSource { } public CanBeReactivated(): boolean { - if (!this.items) { return false; } - return this.getFrozenItem() !== undefined; + if (!this.items) { + return false; + } + return !!this.getFrozenItem(); } - public async Reactivate(mode: ReactivateJobMode): Promise { + public async Reactivate(mode: ReactivateJobMode): Promise { const frozen = this.getFrozenItem(); - if (frozen) { - return this.jobService.Retry(mode, [this.getColumn(frozen, 'UID_Job').GetValue()]); + const uidJob = frozen ? this.getColumn(frozen, 'UID_Job')?.GetValue() : undefined; + if (frozen && uidJob) { + return this.jobService.Retry(mode, [uidJob]); } - return Promise.resolve(null); } - private GetCompleteSubSteps(uidJob: string): number { - - if (uidJob === '') { return 0; } - const current = this.items.find(el => this.getColumn(el, 'UID_Job').GetValue() === uidJob); + private GetCompleteSubSteps(uidJob?: string): number { + if (!uidJob || uidJob === '') { + return 0; + } + const current = this.items.find((el) => this.getColumn(el, 'UID_Job')?.GetValue() === uidJob); const count = current && (this.getColumn(current, 'Ready2EXE')?.GetValue() ?? 'FINISHED') === 'FINISHED' ? 1 : 0; - - return count + this.GetCompleteSubSteps(this.getColumn(current, 'UID_JobSuccess').GetValue()) + this.GetCompleteSubSteps(this.getColumn(current, 'UID_JobError').GetValue()); + return current + ? count + + this.GetCompleteSubSteps(this.getColumn(current, 'UID_JobSuccess')?.GetValue()) + + this.GetCompleteSubSteps(this.getColumn(current, 'UID_JobError')?.GetValue()) + : count; } - public getFrozenItem(): TypedEntity { - return this.items.find(el => this.getColumn(el, 'Ready2EXE')?.GetValue()?.toUpperCase() === 'FROZEN' || - this.getColumn(el, 'Ready2EXE')?.GetValue()?.toUpperCase() === 'OVERLIMIT'); + public getFrozenItem(): TypedEntity | undefined { + return this.items.find( + (el) => + this.getColumn(el, 'Ready2EXE')?.GetValue()?.toUpperCase() === 'FROZEN' || + this.getColumn(el, 'Ready2EXE')?.GetValue()?.toUpperCase() === 'OVERLIMIT', + ); } - private getColumn(entity: TypedEntity, name: string): IEntityColumn { + private getColumn(entity: TypedEntity, name: string): IEntityColumn | undefined { return CdrFactoryService.tryGetColumn(entity.GetEntity(), name); } } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/single-frozen-job.component.html b/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/single-frozen-job.component.html index 2e8b055c4..3a0326a7b 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/single-frozen-job.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/single-frozen-job.component.html @@ -1,53 +1,59 @@ -
    +
    - - +
    {{ '#LDS#The process does not exist or it has already been completed.' | translate }}
    -
    - - - -
    {{ displayAccessor(dataItem, rowIndex) }}
    -
    {{ timeAccessor(dataItem, rowIndex) }}
    -
    -
    - - -
    - -
    - {{ getColumn(dataItem, 'Ready2EXE')?.GetDisplayValue() }} - - - -
    -
    -
    +
    + + + + +
    {{ displayAccessor(data, rowIndex) }}
    +
    {{ timeAccessor(data, rowIndex) }}
    +
    +
    + + +
    + +
    + {{ getColumn(data, 'Ready2EXE')?.GetDisplayValue() }} + + + +
    +
    +
    +
    - +
    {{ '#LDS#Heading Affected Objects' | translate }}
    • -
      {{aff.ObjectKeyAffected.Column.GetDisplayValue()}}
      -
      {{aff.ObjectTypeDisplay.Column.GetDisplayValue()}}
      +
      {{ aff.ObjectKeyAffected.Column.GetDisplayValue() }}
      +
      {{ aff.ObjectTypeDisplay.Column.GetDisplayValue() }}
    @@ -66,20 +72,28 @@

    #LDS#Select how to proceed with the process.

    - + {{ '#LDS#Retry the frozen process step' | translate }} - - {{ '#LDS#End with success' | translate }} - {{ '#LDS#Continue with the success process step' | translate }} + + {{ + '#LDS#End with success' | translate + }} + {{ + '#LDS#Continue with the success process step' | translate + }} - - {{ '#LDS#End with error' | translate }} - {{ '#LDS#Continue with the error process step' | translate }} + + {{ '#LDS#End with error' | translate }} + + {{ + '#LDS#Continue with the error process step' | translate + }}
    - + diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/single-frozen-job.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/single-frozen-job.component.scss index d39b0bdbf..6f38955e7 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/single-frozen-job.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/single-frozen-job.component.scss @@ -3,40 +3,20 @@ flex-direction: column; flex-grow: 1; - .imx-flex-container{ - display: flex; - flex-direction: column; - flex: 1 1 auto; - padding-bottom: 20px; - } - - imx-busy-indicator{ + imx-busy-indicator { justify-self: center; align-self: center; } } -.imx-table-container { - flex: 1 1 auto; - - > :nth-child(2) { - flex: 1 1 auto; - } -} - .imx-refresh-line { margin-bottom: 10px; display: flex; flex-direction: row; } -.reactivate-radio { - display: block; -} - .inner-card { - margin-top: 1em; - + margin-bottom: 1em; mat-card-content { padding: 0 1em; } @@ -55,8 +35,10 @@ list-style-type: initial; } -.small-busy { - margin-left: 1em; - align-self: center; - flex: 1 1 auto; +.imx-single-frozen.imx-flex-container { + margin: 0; + + ::ng-deep .mat-mdc-table { + box-shadow: none; + } } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/single-frozen-job.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/single-frozen-job.component.ts index b9383e104..b3bec4fc7 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/single-frozen-job.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/frozen-jobs/single-frozen-job.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,16 +24,16 @@ * */ +import { OverlayRef } from '@angular/cdk/overlay'; import { Component, Inject, OnInit } from '@angular/core'; +import { EUI_SIDESHEET_DATA, EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { EuiLoadingService, EuiSidesheetService, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; -import { OverlayRef } from '@angular/cdk/overlay'; -import { IEntityColumn, TypedEntity, TypedEntityCollectionData } from 'imx-qbm-dbts'; -import { HistoryOperationsData, OpsupportQueueJobaffects, ReactivateJobMode } from 'imx-api-qbm'; -import { CdrFactoryService, ImxTranslationProviderService, SnackBarService } from 'qbm'; -import { QueueTreeService } from './queue-tree.service'; +import { HistoryOperationsData, OpsupportQueueJobaffects, ReactivateJobMode } from '@imx-modules/imx-api-qbm'; +import { IEntityColumn, TypedEntity, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; +import { calculateSidesheetWidth, CdrFactoryService, ImxTranslationProviderService, SnackBarService } from 'qbm'; import { ErrorMessageSidesheetComponent } from '../error-message-sidesheet/error-message-sidesheet.component'; +import { QueueTreeService } from './queue-tree.service'; @Component({ templateUrl: './single-frozen-job.component.html', @@ -51,23 +51,24 @@ export class SingleFrozenJobComponent implements OnInit { private backTitle = ''; public affectedObjects: OpsupportQueueJobaffects[] = []; - public operations: HistoryOperationsData; - public genprocid: string; + public operations: HistoryOperationsData | undefined; + public genprocid: string | undefined; public busy = false; public busyReload = false; constructor( - @Inject(EUI_SIDESHEET_DATA) public readonly data: { - UID_Tree: string, - disableRefresh: boolean, - load: (startId: string) => Promise> + @Inject(EUI_SIDESHEET_DATA) + public readonly data: { + UID_Tree: string; + disableRefresh: boolean; + load: (startId: string) => Promise>; }, public queueService: QueueTreeService, private sidesheet: EuiSidesheetService, private translate: TranslateService, private busyService: EuiLoadingService, private translationProvider: ImxTranslationProviderService, - private snackbarService: SnackBarService + private snackbarService: SnackBarService, ) { // TODO (TFS 805984): Use ngx-translate get and ldsReplace direct this.translationProvider @@ -75,21 +76,20 @@ export class SingleFrozenJobComponent implements OnInit { .subscribe((value: string) => this.translationProvider .Translate({ key: '#LDS#Go back to {0}', parameters: [`"${value}"`] }) - .subscribe((composedValue: string) => (this.backTitle = composedValue)) + .subscribe((composedValue: string) => (this.backTitle = composedValue)), ); this.queueService.load = data.load; } public ReactivateJobMode = ReactivateJobMode; - public mode: ReactivateJobMode = -1; + public mode: ReactivateJobMode; public async ngOnInit(): Promise { try { this.busy = true; await this.loadView(this.data.UID_Tree); - } - finally { + } finally { this.busy = false; } } @@ -124,21 +124,20 @@ export class SingleFrozenJobComponent implements OnInit { } const processTree = await this.queueService.LoadItems(); - const root = processTree.find((el: TypedEntity) => this.getColumn(el, 'IsRootJob').GetValue); + const root = processTree.find((el: TypedEntity) => this.getColumn(el, 'IsRootJob')?.GetValue()); if (root) { this.queueService.SetRoot(root); - this.jobDisplay = this.getColumn(root, 'JobChainName').GetDisplayValue(); + this.jobDisplay = this.getColumn(root, 'JobChainName')?.GetDisplayValue() ?? ''; this.queueService.ExpandAll(); // load affected objects - this.affectedObjects = await this.queueService.GetAffectedObjects(root.GetEntity().GetColumn("UID_Job").GetValue()); - this.genprocid = root.GetEntity().GetColumn("GenProcID").GetValue(); - this.operations = await this.queueService.GetChangeOperations(this.genprocid); - } - else { + this.affectedObjects = await this.queueService.GetAffectedObjects(root.GetEntity().GetColumn('UID_Job').GetValue()); + this.genprocid = root.GetEntity().GetColumn('GenProcID').GetValue(); + this.operations = this.genprocid ? await this.queueService.GetChangeOperations(this.genprocid) : undefined; + } else { this.affectedObjects = []; - this.operations = null; - this.genprocid = null; + this.operations = undefined; + this.genprocid = undefined; } } @@ -152,11 +151,11 @@ export class SingleFrozenJobComponent implements OnInit { public displayAccessor(data: TypedEntity, index: number): string { return index === 0 ? this.Display - : this.getColumn(data, 'JobName')?.GetDisplayValue() ?? this.getColumn(data, 'UID_JobOrigin')?.GetDisplayValue(); + : this.getColumn(data, 'JobName')?.GetDisplayValue() ?? this.getColumn(data, 'UID_JobOrigin')?.GetDisplayValue() ?? ''; } public hasProgress(item: TypedEntity): boolean { - return this.getColumn(item, 'IsRootJob').GetValue(); + return this.getColumn(item, 'IsRootJob')?.GetValue() ?? false; } public getTotalSteps(): number { @@ -168,7 +167,11 @@ export class SingleFrozenJobComponent implements OnInit { } public isFrozen(dataItem: TypedEntity): boolean { - return this.getColumn(dataItem, 'Ready2EXE')?.GetValue().toUpperCase() === 'FROZEN' || this.getColumn(dataItem, 'Ready2EXE')?.GetValue().toUpperCase() === 'OVERLIMIT' || this.getColumn(dataItem, 'WasError')?.GetValue(); + return ( + this.getColumn(dataItem, 'Ready2EXE')?.GetValue().toUpperCase() === 'FROZEN' || + this.getColumn(dataItem, 'Ready2EXE')?.GetValue().toUpperCase() === 'OVERLIMIT' || + this.getColumn(dataItem, 'WasError')?.GetValue() + ); } public async showMessage(dataItem: TypedEntity): Promise { @@ -176,23 +179,23 @@ export class SingleFrozenJobComponent implements OnInit { .open(ErrorMessageSidesheetComponent, { title: await this.translate.get('#LDS#Heading View Error Message').toPromise(), padding: '0', - width: 'max(50%,500px)', + width: calculateSidesheetWidth(800, 0.5), testId: 'error-message-sidesheet', - data: this.getColumn(dataItem, 'ErrorMessages').GetDisplayValue(), + data: this.getColumn(dataItem, 'ErrorMessages')?.GetDisplayValue(), }) .afterClosed() .toPromise(); } public getColumnDisplay(name: string): string { - return this.queueService.QueueTreeEntitySchema.Columns[name].Display; + return this.queueService.QueueTreeEntitySchema.Columns[name].Display ?? ''; } - public getValue(entity: TypedEntity, name: string): any { - return this.getColumn(entity, name).GetValue() ?? ''; + public getValue(entity: TypedEntity | undefined, name: string): any { + return this.getColumn(entity, name)?.GetValue() ?? ''; } - public getColumn(entity: TypedEntity, name: string): IEntityColumn { - return CdrFactoryService.tryGetColumn(entity.GetEntity(), name); + public getColumn(entity: TypedEntity | undefined, name: string): IEntityColumn | undefined { + return entity ? CdrFactoryService.tryGetColumn(entity?.GetEntity(), name) : undefined; } } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-chains/job-chains.component.html b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-chains/job-chains.component.html index 73ad9967b..8d6b02310 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-chains/job-chains.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-chains/job-chains.component.html @@ -1,26 +1,30 @@ -

    - {{'#LDS#Heading Process Steps per Process' | translate}} -

    +

    + {{ '#LDS#Heading Process Steps per Process' | translate }} +

    - - + + {{ data.JobChainName.Column.GetDisplayValue() }} - - - {{ data.Count.Column.GetDisplayValue() }} - - + + + {{ data.Count.Column.GetDisplayValue() }} + +
    - diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-chains/job-chains.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-chains/job-chains.component.scss index 3f894fce0..57a7affa1 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-chains/job-chains.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-chains/job-chains.component.scss @@ -5,18 +5,3 @@ flex-grow: 1; } -.imx-table-container { - flex: 1 1 auto; - display: flex; - flex-direction: column; - overflow: auto; - - > :nth-child(2) { - flex: 1 1 auto; - } -} - -.imx-refresh-button { - margin-right: 5px; - height: 50px; -} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-chains/job-chains.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-chains/job-chains.component.ts index 78ebf3a57..90416c738 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-chains/job-chains.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-chains/job-chains.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,16 +28,15 @@ import { OverlayRef } from '@angular/cdk/overlay'; import { Component, OnInit } from '@angular/core'; import { EuiLoadingService } from '@elemental-ui/core'; -import { DisplayColumns, EntitySchema, IClientProperty } from 'imx-qbm-dbts'; +import { DisplayColumns, EntitySchema, IClientProperty } from '@imx-modules/imx-qbm-dbts'; import { DataSourceToolbarSettings } from 'qbm'; import { JobChainsService } from './job-chains.service'; @Component({ templateUrl: './job-chains.component.html', - styleUrls: ['./job-chains.component.scss'] + styleUrls: ['./job-chains.component.scss'], }) export class JobChainsComponent implements OnInit { - public dstSettings: DataSourceToolbarSettings; public readonly entitySchemaJobChains: EntitySchema; public readonly DisplayColumns = DisplayColumns; @@ -45,12 +44,10 @@ export class JobChainsComponent implements OnInit { constructor( private jobChains: JobChainsService, - private busyService: EuiLoadingService) { + private busyService: EuiLoadingService, + ) { this.entitySchemaJobChains = jobChains.EntitySchema; - this.displayedColumns = [ - this.entitySchemaJobChains.Columns.JobChainName, - this.entitySchemaJobChains.Columns.Count - ]; + this.displayedColumns = [this.entitySchemaJobChains.Columns.JobChainName, this.entitySchemaJobChains.Columns.Count]; } public async ngOnInit(): Promise { @@ -62,19 +59,16 @@ export class JobChainsComponent implements OnInit { } public async getData(): Promise { - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + setTimeout(() => (overlayRef = this.busyService.show())); try { - const jobChainList = await this.jobChains.Get(); this.dstSettings = { displayedColumns: this.displayedColumns, dataSource: jobChainList, entitySchema: this.entitySchemaJobChains, - navigationState: {} + navigationState: {}, }; - } finally { setTimeout(() => this.busyService.hide(overlayRef)); } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-chains/job-chains.service.spec.ts b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-chains/job-chains.service.spec.ts index de87c66cf..b13b4708a 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-chains/job-chains.service.spec.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-chains/job-chains.service.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { JobChainsService } from './job-chains.service'; -import { OpsupportQueueJobchains } from 'imx-api-qbm'; +import { OpsupportQueueJobchains } from '@imx-modules/imx-api-qbm'; import { testTypedEntityReadOnlyProvider } from '../../test-utilities/typed-entity-provider.spec'; import { ImxApiDtoMock } from '../../test-utilities/imx-api-mock.spec'; @@ -36,11 +36,11 @@ describe('JobChainsService', () => { entityType: OpsupportQueueJobchains, data: data, parameters: {}, - typedClient: { + typedClient: { OpsupportQueueJobchains: jasmine.createSpyObj('OpsupportQueueJobchains', { GetSchema: () => OpsupportQueueJobchains.GetEntitySchema(), - Get: Promise.resolve({ Data: data, totalCount: data.length }) - }) - } + Get: Promise.resolve({ Data: data, totalCount: data.length }), + }), + }, }); }); diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-chains/job-chains.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-chains/job-chains.service.ts index 1ed91df82..9a290b8b5 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-chains/job-chains.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-chains/job-chains.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,19 +26,19 @@ import { Injectable } from '@angular/core'; -import { TypedEntityCollectionData, EntitySchema } from 'imx-qbm-dbts'; +import { TypedEntityCollectionData, EntitySchema } from '@imx-modules/imx-qbm-dbts'; import { imx_SessionService } from 'qbm'; -import { OpsupportQueueJobchains } from 'imx-api-qbm'; +import { OpsupportQueueJobchains } from '@imx-modules/imx-api-qbm'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class JobChainsService { public get EntitySchema(): EntitySchema { return this.session.TypedClient.OpsupportQueueJobchains.GetSchema(); } - constructor(private session: imx_SessionService) { } + constructor(private session: imx_SessionService) {} public Get(): Promise> { return this.session.TypedClient.OpsupportQueueJobchains.Get(); diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-history/job-history.component.html b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-history/job-history.component.html index 175110abe..cfe202dc9 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-history/job-history.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-history/job-history.component.html @@ -1,6 +1,6 @@ -

    +

    {{ '#LDS#Heading Process History' | translate }} -

    +
    (navigationStateChanged)="getData($event)" > - - + +
    - + #LDS#Error
    - + - {{getDateTimeString(dataItem.XDateInserted.value)}} + {{ getDateTimeString(dataItem.XDateInserted.value) }} - - + + - diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-history/job-history.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-history/job-history.component.scss index 238d26945..28b7a0104 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-history/job-history.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-history/job-history.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; :host { display: flex; @@ -6,23 +6,6 @@ overflow: hidden; flex-grow: 1; - .imx-table-container { - flex: 1 1 auto; - display: flex; - flex-direction: column; - overflow: auto; - margin-bottom: -20px; - - > :nth-child(2) { - flex: 1 1 auto; - } - } - - .imx-refresh-button { - margin-right: 5px; - height: 50px; - } - .imx-icon-container { display: flex; padding: 0 16px; @@ -32,11 +15,3 @@ } } } - -:host { - .imx-icon-container { - .imx-bug-icon { - color: $color-red-60; - } - } -} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-history/job-history.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-history/job-history.component.ts index f636c6b8c..f8fb0ef1e 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-history/job-history.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-history/job-history.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,13 +27,21 @@ import { OverlayRef } from '@angular/cdk/overlay'; import { Component, OnInit } from '@angular/core'; import { EuiLoadingService, EuiSidesheetConfig, EuiSidesheetService } from '@elemental-ui/core'; +import { OpsupportQueueJobhistory } from '@imx-modules/imx-api-qbm'; +import { + CollectionLoadParameters, + DataModel, + DisplayColumns, + EntitySchema, + IClientProperty, + TypedEntity, + ValType, +} from '@imx-modules/imx-qbm-dbts'; import { TranslateService } from '@ngx-translate/core'; -import { OpsupportQueueJobhistory } from 'imx-api-qbm'; -import { CollectionLoadParameters, DataModel, DisplayColumns, EntitySchema, IClientProperty, ValType } from 'imx-qbm-dbts'; -import { DataSourceToolbarSettings, SettingsService } from 'qbm'; -import { JobHistoryService } from './job-history.service'; +import { DataSourceToolbarSettings, SettingsService, calculateSidesheetWidth } from 'qbm'; import { ErrorMessageSidesheetComponent } from '../error-message-sidesheet/error-message-sidesheet.component'; import { SingleFrozenJobComponent } from '../frozen-jobs/single-frozen-job.component'; +import { JobHistoryService } from './job-history.service'; @Component({ selector: 'imx-job-history', @@ -54,7 +62,7 @@ export class JobHistoryComponent implements OnInit { private readonly settingsService: SettingsService, private busyService: EuiLoadingService, private readonly translate: TranslateService, - private readonly sidesheet: EuiSidesheetService + private readonly sidesheet: EuiSidesheetService, ) { this.entitySchema = jobHistory.EntitySchema; this.displayedColumns = [ @@ -111,7 +119,7 @@ export class JobHistoryComponent implements OnInit { title: await this.translate.get('#LDS#Heading View Message').toPromise(), subTitle: item.GetEntity().GetDisplay(), padding: '0', - width: 'max(60%,600px)', + width: calculateSidesheetWidth(), testId: 'error-message-sidesheet', data: item.ErrorMessages.value, }) @@ -119,14 +127,15 @@ export class JobHistoryComponent implements OnInit { .toPromise(); } - public async viewDetails(job: OpsupportQueueJobhistory): Promise { + public async viewDetails(job: TypedEntity): Promise { const opts: EuiSidesheetConfig = { title: await this.translate.get('#LDS#Heading View Process Details').toPromise(), + subTitle: job.GetEntity().GetDisplay(), padding: '1em', - width: 'max(800px,80%)', + width: calculateSidesheetWidth(1100, 0.7), icon: 'reboot', data: { - UID_Tree: job.UID_Tree.value, + UID_Tree: job.GetEntity().GetColumn('UID_Tree').GetValue(), disableRefresh: true, load: (startId: string) => { return this.jobHistory.get({ filter: [{ ColumnName: 'UID_Tree', Value1: startId }] }); diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-history/job-history.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-history/job-history.service.ts index 108ad1e65..80591aa55 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-history/job-history.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-history/job-history.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,8 +25,8 @@ */ import { Injectable } from '@angular/core'; -import { OpsupportQueueJobhistory } from 'imx-api-qbm'; -import { CollectionLoadParameters, DataModel, EntitySchema, ExtendedTypedEntityCollection } from 'imx-qbm-dbts'; +import { OpsupportQueueJobhistory } from '@imx-modules/imx-api-qbm'; +import { CollectionLoadParameters, DataModel, EntitySchema, ExtendedTypedEntityCollection } from '@imx-modules/imx-qbm-dbts'; import { imx_SessionService } from 'qbm'; @@ -40,8 +40,8 @@ export class JobHistoryService { return this.session.TypedClient.OpsupportQueueJobhistory.GetSchema(); } - public async get(parameters: CollectionLoadParameters):Promise> { - return this.session.TypedClient.OpsupportQueueJobhistory.Get(parameters); + public async get(parameters: CollectionLoadParameters): Promise> { + return this.session.TypedClient.OpsupportQueueJobhistory.Get(parameters); } public async getDataModel(): Promise { diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-performance/job-performance-queues.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-performance/job-performance-queues.service.ts index 7371acb71..91d9fdd3a 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-performance/job-performance-queues.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-performance/job-performance-queues.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-performance/job-performance.component.html b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-performance/job-performance.component.html index 89e80a736..e532460c2 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-performance/job-performance.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-performance/job-performance.component.html @@ -1,32 +1,38 @@ -

    - {{'#LDS#Heading Processing Performance' | translate}} -

    +

    + {{ '#LDS#Heading Processing Performance' | translate }} +

    - {{queue}} + {{ queue }}
    - + - - + + {{ data.TaskName.Column.GetDisplayValue() }} - + {{ data.ComponentClass.Column.GetDisplayValue() }} - + {{ data.CountPerMinute.Column.GetDisplayValue() }} @@ -36,7 +42,7 @@

    - diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-performance/job-performance.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-performance/job-performance.component.scss index 363260945..6fcf20988 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-performance/job-performance.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-performance/job-performance.component.scss @@ -3,21 +3,4 @@ flex-direction: column; overflow: hidden; flex-grow: 1; -} - -.imx-table-container { - flex: 1 1 auto; - display: flex; - flex-direction: column; - overflow: auto; - margin-bottom: -20px; - - > :nth-child(2) { - flex: 1 1 auto; - } -} - -.imx-refresh-button { - margin-right: 5px; - height: 50px; -} +} \ No newline at end of file diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-performance/job-performance.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-performance/job-performance.component.ts index 4cc27e75f..399fee1ba 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-performance/job-performance.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-performance/job-performance.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,20 +24,19 @@ * */ -import { Component, OnInit } from '@angular/core'; import { OverlayRef } from '@angular/cdk/overlay'; -import { EuiLoadingService } from '@elemental-ui/core'; +import { Component, OnInit } from '@angular/core'; import { MatSelectChange } from '@angular/material/select'; +import { EuiLoadingService } from '@elemental-ui/core'; -import { JobPerformanceQueuesService } from './job-performance-queues.service'; +import { CollectionLoadParameters, EntitySchema, IClientProperty } from '@imx-modules/imx-qbm-dbts'; import { ClassloggerService, DataSourceToolbarSettings, SettingsService } from 'qbm'; -import { JobPerformanceService, JobPerformanceParameters } from './job-performance.service'; -import { CollectionLoadParameters, EntitySchema, IClientProperty } from 'imx-qbm-dbts'; - +import { JobPerformanceQueuesService } from './job-performance-queues.service'; +import { JobPerformanceService } from './job-performance.service'; @Component({ templateUrl: './job-performance.component.html', - styleUrls: ['./job-performance.component.scss'] + styleUrls: ['./job-performance.component.scss'], }) export class JobPerformanceComponent implements OnInit { public get Queues(): string[] { @@ -58,7 +57,7 @@ export class JobPerformanceComponent implements OnInit { private jobPerformance: JobPerformanceService, private logger: ClassloggerService, private readonly settingsService: SettingsService, - private busyService: EuiLoadingService + private busyService: EuiLoadingService, ) { this.entitySchemaJobPerformance = jobPerformance.EntitySchema; this.displayedColumns = [ @@ -87,31 +86,27 @@ export class JobPerformanceComponent implements OnInit { await this.getData({ StartIndex: 0, PageSize: this.settingsService.DefaultPageSize, queue: this.queueName }); } - public async getData(navigationState: JobPerformanceParameters): Promise { - + public async getData(navigationState: CollectionLoadParameters): Promise { this.navigationState = navigationState; let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + setTimeout(() => (overlayRef = this.busyService.show())); try { - - const jobPeformances = await this.jobPerformance.Get(navigationState); + const jobPeformances = await this.jobPerformance.Get({ ...navigationState, queue: this.queueName }); this.dstSettings = { displayedColumns: this.displayedColumns, dataSource: jobPeformances, entitySchema: this.entitySchemaJobPerformance, - navigationState: this.navigationState + navigationState: this.navigationState, }; - } finally { setTimeout(() => this.busyService.hide(overlayRef)); } } - private async UpdateQueueNames(): Promise { let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + setTimeout(() => (overlayRef = this.busyService.show())); try { this.queues = await this.jobPerformanceQueues.GetItems(); if (!this.queueName && this.queues.length > 0) { diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-performance/job-performance.service.spec.ts b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-performance/job-performance.service.spec.ts index 4dfcac4c5..f03422a00 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-performance/job-performance.service.spec.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-performance/job-performance.service.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { OpsupportQueueJobperformance } from 'imx-api-qbm'; +import { OpsupportQueueJobperformance } from '@imx-modules/imx-api-qbm'; import { JobPerformanceService } from './job-performance.service'; import { testTypedEntityReadOnlyProvider } from '../../test-utilities/typed-entity-provider.spec'; import { ImxApiDtoMock } from '../../test-utilities/imx-api-mock.spec'; @@ -39,8 +39,8 @@ describe('QueueJobPerformanceService', () => { typedClient: { OpsupportQueueJobperformance: jasmine.createSpyObj('OpsupportQueueJobperformance', { GetSchema: () => OpsupportQueueJobperformance.GetEntitySchema(), - Get: Promise.resolve({ Data: data, totalCount: data.length }) - }) - } + Get: Promise.resolve({ Data: data, totalCount: data.length }), + }), + }, }); }); diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-performance/job-performance.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-performance/job-performance.service.ts index 93d909e8e..c0487c225 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/job-performance/job-performance.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/job-performance/job-performance.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,8 +26,8 @@ import { Injectable } from '@angular/core'; -import { TypedEntityCollectionData, EntitySchema, CollectionLoadParameters } from 'imx-qbm-dbts'; -import { OpsupportQueueJobperformance } from 'imx-api-qbm'; +import { TypedEntityCollectionData, EntitySchema, CollectionLoadParameters } from '@imx-modules/imx-qbm-dbts'; +import { OpsupportQueueJobperformance } from '@imx-modules/imx-api-qbm'; import { imx_SessionService } from 'qbm'; export interface JobPerformanceParameters extends CollectionLoadParameters { @@ -35,9 +35,9 @@ export interface JobPerformanceParameters extends CollectionLoadParameters { } @Injectable({ - providedIn: 'root' + providedIn: 'root', }) -export class JobPerformanceService { +export class JobPerformanceService { public get EntitySchema(): EntitySchema { return this.session.TypedClient.OpsupportQueueJobperformance.GetSchema(); } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/jobs-gridview/jobs-gridview.component.html b/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/jobs-gridview/jobs-gridview.component.html index ecbda74c7..c77c46f80 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/jobs-gridview/jobs-gridview.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/jobs-gridview/jobs-gridview.component.html @@ -10,25 +10,35 @@ > - - + +
    {{ dataItem.JobChainName.Column.GetDisplayValue() }}
    {{ dataItem.TaskName.Column.GetDisplayValue() }}
    - - + + - +
    -
    - @@ -45,7 +55,7 @@
    - diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/jobs-gridview/jobs-gridview.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/jobs-gridview/jobs-gridview.component.scss index fd9a85618..80b454b8e 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/jobs-gridview/jobs-gridview.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/jobs-gridview/jobs-gridview.component.scss @@ -1,50 +1,7 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; :host { display: flex; flex-direction: column; flex-grow: 1; overflow: hidden; } - -.imx-table-container { - flex: 1 1 auto; - display: flex; - flex-direction: column; - overflow: auto; - - > :nth-child(2) { - flex: 1 1 auto; - } - - ::ng-deep .mat-paginator { - margin-bottom: 0; - } -} - -div[subtitle] { - font-size: smaller; - color: $black-9; -} - - -.imx-button-bar { - display: flex; - justify-content: flex-end; - margin-top: 10px; - - > * { - margin-left: 5px; - } -} - -.imx-refresh-button { - margin-right: 5px; - height: 50px; -} - -.mat-raised-button.rightButtonFloat { - clear: both; - width: auto; - float: right; - padding: 0 20px; -} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/jobs-gridview/jobs-gridview.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/jobs-gridview/jobs-gridview.component.ts index 0a563e7f3..f9a8bb532 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/jobs-gridview/jobs-gridview.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/jobs-gridview/jobs-gridview.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,17 +24,32 @@ * */ -import { Component, Input, OnInit } from '@angular/core'; import { OverlayRef } from '@angular/cdk/overlay'; +import { Component, Input, OnInit } from '@angular/core'; import { EuiLoadingService, EuiSidesheetConfig, EuiSidesheetService } from '@elemental-ui/core'; -import { OpsupportQueueJobs, ReactivateJobMode } from 'imx-api-qbm'; -import { OpsupportQueueJobsParameters, QueueJobsService } from '../queue-jobs.service'; -import { SnackBarService, TextContainer, DataSourceToolbarSettings, DataSourceToolbarFilter, SettingsService, ClientPropertyForTableColumns } from 'qbm'; -import { CollectionLoadParameters, CompareOperator, DataModel, EntitySchema, FilterType, ValType } from 'imx-qbm-dbts'; -import { SingleFrozenJobComponent } from '../../frozen-jobs/single-frozen-job.component'; +import { OpsupportQueueJobs, ReactivateJobMode } from '@imx-modules/imx-api-qbm'; +import { + CollectionLoadParameters, + CompareOperator, + DataModel, + EntitySchema, + FilterType, + TypedEntity, + ValType, +} from '@imx-modules/imx-qbm-dbts'; import { TranslateService } from '@ngx-translate/core'; - +import { + calculateSidesheetWidth, + ClientPropertyForTableColumns, + DataSourceToolbarFilter, + DataSourceToolbarSettings, + SettingsService, + SnackBarService, + TextContainer, +} from 'qbm'; +import { SingleFrozenJobComponent } from '../../frozen-jobs/single-frozen-job.component'; +import { OpsupportQueueJobsParameters, QueueJobsService } from '../queue-jobs.service'; @Component({ selector: 'imx-jobs-gridview', @@ -68,7 +83,7 @@ export class JobsGridviewComponent implements OnInit { private busyService: EuiLoadingService, private readonly translator: TranslateService, private jobService: QueueJobsService, - settings: SettingsService + settings: SettingsService, ) { this.entitySchemaJobs = jobService.EntitySchema; this.displayedColumns = [ @@ -140,15 +155,16 @@ export class JobsGridviewComponent implements OnInit { return this.selectedJobs.length > 0; } - public async viewDetails(job: OpsupportQueueJobs): Promise { + public async viewDetails(job: TypedEntity): Promise { const opts: EuiSidesheetConfig = { title: await this.translator.get('#LDS#Heading Process Overview').toPromise(), - subTitle: job.JobChainName.Column.GetDisplayValue() + ' ' + job.XDateInserted.Column.GetDisplayValue(), - width: 'max(1000px, 80%)', + subTitle: + job.GetEntity().GetColumn('JobChainName').GetDisplayValue() + ' ' + job.GetEntity().GetColumn('XDateInserted').GetDisplayValue(), + width: calculateSidesheetWidth(1200, 0.7), icon: 'reboot', testId: 'job-details-sidesheet', data: { - UID_Tree: job.UID_Tree.value, + UID_Tree: job.GetEntity().GetColumn('UID_Tree').GetValue(), load: (startId: string) => { return this.jobService.getTreeData(startId); }, @@ -180,8 +196,8 @@ export class JobsGridviewComponent implements OnInit { this.retryJobs(this.selectedJobs, mode, { key: '#LDS#Your changes are being processed.', parameters: [this.selectedJobs.length] }); } - public onSelectionChanged(jobs: OpsupportQueueJobs[]): void { - this.selectedJobs = jobs; + public onSelectionChanged(jobs: TypedEntity[]): void { + this.selectedJobs = jobs as OpsupportQueueJobs[]; } public refresh(): void { @@ -232,7 +248,7 @@ export class JobsGridviewComponent implements OnInit { try { await this.jobService.Retry( mode, - jobs.map((job: OpsupportQueueJobs) => job.UID_Job.value) + jobs.map((job: OpsupportQueueJobs) => job.UID_Job.value), ); success = true; } finally { diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/jobs.component.html b/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/jobs.component.html index 9323645b7..a46774067 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/jobs.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/jobs.component.html @@ -1,15 +1,18 @@
    -

    #LDS#Processes

    - - {{infoText | translate}} +

    #LDS#Processes

    + + {{ infoText | translate }}
    - - {{(isShowGraph ? '#LDS#Chart view' : '#LDS#Table view') | translate}} + + {{ (isShowGraph ? '#LDS#Chart view' : '#LDS#Table view') | translate }} - - + diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/jobs.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/jobs.component.scss index ee92c4df9..3798d2400 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/jobs.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/jobs.component.scss @@ -2,7 +2,7 @@ display: contents; max-height: 100%; flex: 1 1 auto; - + ::ng-deep .imx-table-container .imx-data-table-no-results { height: 100%; display: flex; @@ -14,16 +14,6 @@ .imx-heading-wrapper { display: flex; - - .imx-helper-alert { - display: flex; - margin: 0 0 20px auto; - align-self: flex-end; - width: 70%; - } -} -.jobs-toggle-showGraph { - margin-bottom: 10px; } .imx-filter-title { @@ -37,7 +27,6 @@ flex-direction: column; } - imx-jobqueue-overview { display: flex; flex: 1 1 auto; @@ -48,4 +37,4 @@ imx-jobqueue-overview { .imx-job-line { display: flex; flex-direction: row; -} \ No newline at end of file +} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/jobs.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/jobs.component.ts index 68a53d3be..684117423 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/jobs.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/jobs.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,16 +28,16 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; @Component({ templateUrl: './jobs.component.html', - styleUrls: ['./jobs.component.scss'] + styleUrls: ['./jobs.component.scss'], }) export class JobsComponent implements OnInit { - public failed = false; public isShowGraph = false; - public infoText = '#LDS#Here you can get an overview of all processes of the JobQueue. You can view them individually in a table or grouped by status in a chart. Additionally, you can handle failed processes.'; + public infoText = + '#LDS#Here you can get an overview of all processes of the JobQueue. You can view them individually in a table or grouped by status in a chart. Additionally, you can handle failed processes.'; - constructor(private activeRoute: ActivatedRoute) { } + constructor(private activeRoute: ActivatedRoute) {} public ngOnInit(): void { const failed = this.activeRoute.snapshot.queryParamMap.get('failed'); @@ -46,5 +46,4 @@ export class JobsComponent implements OnInit { this.failed = failed === 'true'; } } - } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/queue-jobs.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/queue-jobs.service.ts index af426c7e2..1eea8fe7a 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/queue-jobs.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/jobs/queue-jobs.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,9 +26,17 @@ import { Injectable } from '@angular/core'; -import { EntityCollectionData, EntitySchema, TypedEntityCollectionData, FilterData, CollectionLoadParameters, DataModel, TypedEntity } from 'imx-qbm-dbts'; +import { OpsupportQueueJobs, ReactivateJobMode } from '@imx-modules/imx-api-qbm'; +import { + CollectionLoadParameters, + DataModel, + EntityCollectionData, + EntitySchema, + FilterData, + TypedEntity, + TypedEntityCollectionData, +} from '@imx-modules/imx-qbm-dbts'; import { DataSourceToolbarFilter, imx_SessionService } from 'qbm'; -import { OpsupportQueueJobs, ReactivateJobMode } from 'imx-api-qbm'; export interface OpsupportQueueJobsParameters extends CollectionLoadParameters { frozen?: boolean; @@ -42,18 +50,18 @@ export class QueueJobsService { return this.session.TypedClient.OpsupportQueueJobs.GetSchema(); } - constructor(private session: imx_SessionService) { } + constructor(private session: imx_SessionService) {} public Get(parameters: OpsupportQueueJobsParameters): Promise> { return this.session.TypedClient.OpsupportQueueJobs.Get(parameters); } public async getFilters(): Promise { - return (await this.getDataModel()).Filters; + return (await this.getDataModel()).Filters ?? []; } public async getDataModel(): Promise { - return this.session.Client.opsupport_queue_jobs_datamodel_get(undefined); + return this.session.Client.opsupport_queue_jobs_datamodel_get(); } public Post(jobs: string[]): Promise { @@ -64,8 +72,7 @@ export class QueueJobsService { return this.session.Client.opsupport_queue_reactivatejob_post({ Mode: mode, UidJobs: jobs }); } - - public getTreeData(startUid: string): Promise>{ + public getTreeData(startUid: string): Promise> { return this.session.TypedClient.OpsupportQueueTree.Get({ uidtree: startUid }); } } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/change-operation-sidesheet/change-operation-sidesheet.component.html b/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/change-operation-sidesheet/change-operation-sidesheet.component.html index cc18c5fbd..8facd62a4 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/change-operation-sidesheet/change-operation-sidesheet.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/change-operation-sidesheet/change-operation-sidesheet.component.html @@ -1,15 +1,15 @@
    -
    +
    - +
    -
    +
    @@ -17,33 +17,33 @@ - {{column.ColumnDisplay}} + {{ column.ColumnDisplay }} - + - +
    - {{'#LDS#Column name' | translate}} - + {{ '#LDS#Column name' | translate }} + - {{'#LDS#New value' | translate}} - + {{ '#LDS#New value' | translate }} + - {{'#LDS#Old value' | translate}} - + {{ '#LDS#Old value' | translate }} +
    diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/change-operation-sidesheet/change-operation-sidesheet.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/change-operation-sidesheet/change-operation-sidesheet.component.scss index 781140e5e..75ab9ad9c 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/change-operation-sidesheet/change-operation-sidesheet.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/change-operation-sidesheet/change-operation-sidesheet.component.scss @@ -1,4 +1,3 @@ - :host { .wrapper { height: 100%; @@ -17,5 +16,6 @@ height: 100%; align-items: center; justify-content: center; + } } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/change-operation-sidesheet/change-operation-sidesheet.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/change-operation-sidesheet/change-operation-sidesheet.component.ts index 9bb85ba30..2ceab1312 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/change-operation-sidesheet/change-operation-sidesheet.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/change-operation-sidesheet/change-operation-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,31 +26,31 @@ import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; -import { HistoryOperation } from 'imx-api-qbm'; +import { HistoryOperation } from '@imx-modules/imx-api-qbm'; import { Subscription } from 'rxjs'; @Component({ selector: 'imx-change-operation-sidesheet', templateUrl: './change-operation-sidesheet.component.html', - styleUrls: ['./change-operation-sidesheet.component.scss'] + styleUrls: ['./change-operation-sidesheet.component.scss'], }) export class ChangeOperationSidesheetComponent implements OnInit, OnDestroy { + public isMulti: boolean; public isOpening = true; private openSub$: Subscription; constructor( @Inject(EUI_SIDESHEET_DATA) public data: HistoryOperation, - private sidesheetRef: EuiSidesheetRef + private sidesheetRef: EuiSidesheetRef, ) {} - public get isMulti(): boolean { - return this.data?.Columns && this.data.Columns.length > 1; - } - public ngOnInit(): void { - this.openSub$ = this.sidesheetRef.componentInstance.onOpen().subscribe(_ => { - this.isOpening = false; - }); + this.isMulti = this.data.Columns ? this.data.Columns.length > 1 : false; + if (this.sidesheetRef.componentInstance) { + this.openSub$ = this.sidesheetRef.componentInstance.onOpen().subscribe((_) => { + this.isOpening = false; + }); + } } public ngOnDestroy(): void { diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/change-operation-table.component.html b/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/change-operation-table.component.html index 141159bbf..d66267cd5 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/change-operation-table.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/change-operation-table.component.html @@ -1,9 +1,8 @@ {{ columnDef.getTitle() }} - {{ columnDef.getValue(row) }} - + {{ columnDef.getValue(row) }} - - + + diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/change-operation-table.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/change-operation-table.component.scss index 4bcd5af05..7ed9716d9 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/change-operation-table.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/change-operation-table.component.scss @@ -1,5 +1,4 @@ - -.flex-property{ +.flex-property { flex: 1 1 auto; display: flex; flex-direction: column; diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/change-operation-table.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/change-operation-table.component.ts index 7ad686f4d..914707878 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/change-operation-table.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/change-operation-table.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,42 +24,42 @@ * */ -import { Component, OnInit, Input } from "@angular/core"; -import { EuiSidesheetService } from "@elemental-ui/core"; -import { TranslateService } from "@ngx-translate/core"; -import { HistoryOperation, ChangeType } from "imx-api-qbm"; -import { ChangeOperationSidesheetComponent } from "./change-operation-sidesheet/change-operation-sidesheet.component"; +import { Component, Input, OnInit } from '@angular/core'; +import { EuiSidesheetService } from '@elemental-ui/core'; +import { ChangeType, HistoryOperation } from '@imx-modules/imx-api-qbm'; +import { TranslateService } from '@ngx-translate/core'; +import { calculateSidesheetWidth } from 'qbm'; +import { ChangeOperationSidesheetComponent } from './change-operation-sidesheet/change-operation-sidesheet.component'; interface IColumn { id: string; title: string; - getValue: (row: HistoryOperation) => string; + getValue: (row: HistoryOperation) => string | undefined; } class Column implements IColumn { public id: string; public getTitle: () => string; - public getValue: (row: HistoryOperation) => string; + public getValue: (row: HistoryOperation) => string | undefined; public title: string; } @Component({ templateUrl: './change-operation-table.component.html', selector: 'imx-change-operation-table', - styleUrls: ['./change-operation-table.component.scss'] + styleUrls: ['./change-operation-table.component.scss'], }) export class ChangeOperationTableComponent implements OnInit { - - constructor(private translationProvider: TranslateService, + constructor( + private translationProvider: TranslateService, private sidesheet: EuiSidesheetService, - ) { - } + ) {} @Input() public data: HistoryOperation[] = []; public columnDefs: Column[] = []; public get columns(): string[] { - return this.columnDefs.map(c => c.id); + return this.columnDefs.map((c) => c.id); } ngOnInit(): void { @@ -75,51 +75,49 @@ export class ChangeOperationTableComponent implements OnInit { this.columnDefs.push(column); } - private async changeTypeTableColumn(){ + private async changeTypeTableColumn() { await this.addColumnDef({ id: 'ChangeTime', title: '#LDS#Operation performed on', - getValue: (row: HistoryOperation) => new Date(row.ChangeTime).toLocaleString(this.translationProvider.currentLang) + getValue: (row: HistoryOperation) => new Date(row.ChangeTime).toLocaleString(this.translationProvider.currentLang), }); await this.addColumnDef({ id: 'ChangeType', title: '#LDS#Type of operation', - getValue: (row: HistoryOperation) => Object.values(ChangeType)[row.ChangeType].toString() + getValue: (row: HistoryOperation) => Object.values(ChangeType)[row.ChangeType].toString(), }); await this.addColumnDef({ id: 'ObjectDisplay', title: '#LDS#Object name', - getValue: (row: HistoryOperation) => row.ObjectDisplay + getValue: (row: HistoryOperation) => row.ObjectDisplay, }); await this.addColumnDef({ id: 'DisplayType', title: '#LDS#Object type', - getValue: (row: HistoryOperation) => row.DisplayType + getValue: (row: HistoryOperation) => row.DisplayType, }); await this.addColumnDef({ id: 'User', title: '#LDS#Operation performed by', - getValue: (row: HistoryOperation) => row.User + getValue: (row: HistoryOperation) => row.User, }); } public async displayChangedPropertyListSidesheet(row: HistoryOperation) { if (row.Columns && row.Columns.length > 0) { let title = await this.translationProvider.get('#LDS#Heading View Operation Details').toPromise(); - let subtitle = await this.translationProvider.get('#LDS#Type of operation').toPromise() + Object.values(ChangeType)[row.ChangeType].toString(); + let subtitle = + (await this.translationProvider.get('#LDS#Type of operation').toPromise()) + Object.values(ChangeType)[row.ChangeType].toString(); - if( row.ObjectDisplay) - subtitle += await this.translationProvider.get('#LDS#Object').toPromise() + row.ObjectDisplay + if (row.ObjectDisplay) subtitle += (await this.translationProvider.get('#LDS#Object').toPromise()) + row.ObjectDisplay; - this.sidesheet.open(ChangeOperationSidesheetComponent, - { - title: title, - subTitle: subtitle , - width: 'max(400px, 40%)', - data: row, - testId: 'data-change-details-sidesheet' - } - ); + this.sidesheet.open(ChangeOperationSidesheetComponent, { + title: title, + subTitle: subtitle, + width: calculateSidesheetWidth(700, 0.4), + data: row, + testId: 'data-change-details-sidesheet', + }); } } } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/object-by-id.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/object-by-id.service.ts index e9ab9b228..65d5089b3 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/object-by-id.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/object-by-id.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,12 +26,12 @@ import { Injectable } from '@angular/core'; -import { EntitySchema, CollectionLoadParameters } from 'imx-qbm-dbts'; +import { EntitySchema, CollectionLoadParameters } from '@imx-modules/imx-qbm-dbts'; import { imx_SessionService } from 'qbm'; -import { HistoryOperationsData} from 'imx-api-qbm'; +import { HistoryOperationsData } from '@imx-modules/imx-api-qbm'; export interface OpsupportQueueJobsParameters extends CollectionLoadParameters { - processId:string; + processId: string; } @Injectable() @@ -40,7 +40,7 @@ export class ObjectByIdService { return this.session.TypedClient.OpsupportQueueJobs.GetSchema(); } - constructor(private session: imx_SessionService) { } + constructor(private session: imx_SessionService) {} public async getChangeOperation(parameters: string): Promise { return this.session.Client.opsupport_changeoperations_process_get(parameters); diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/objects-by-id.component.html b/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/objects-by-id.component.html index a3946b500..d15fb2559 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/objects-by-id.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/objects-by-id.component.html @@ -1,12 +1,12 @@ -

    +

    {{ '#LDS#Heading Processes and Operations by Process ID' | translate }} -

    + - +
    - + - + >
    -
    +

    {{ noDataText | translate }}

    - +
    -
    +

    diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/objects-by-id.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/objects-by-id.component.scss index 61be1948f..e743db4a8 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/objects-by-id.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/objects-by-id.component.scss @@ -1,77 +1,42 @@ @import '@elemental-ui/core/src/styles/_palette.scss'; +@import 'base/variables'; +@import 'base/mixins'; -$SearchBarWidth: 535px; -$SearchBarHeight: 45px; :host { - display: flex; - flex-direction: column; - flex-grow: 1; - height: 100%; - mat-card { - display: flex; - flex-direction: column; - flex: 1 1 auto; - overflow:hidden; - } + @include flex-column-container-fill(); } .imx-searchbar-card { - max-width: $SearchBarWidth; - width: $SearchBarWidth; + max-width: $IMX_SearchBar_Width; + width: $IMX_SearchBar_Width; padding: 0px 10px; display: inline-block; } -.mat-paginator { - box-shadow: none; - margin-bottom: 6px; -} - .imx-procces-action{ display: flex; align-items: center; margin-bottom: 20px; } -.mat-card .imx-searchbar-input { - border: 0 solid transparent; - font-size: 1.2em; - margin-bottom: 0; - width: 450px; - height: $SearchBarHeight; - box-sizing: border-box; - flex-grow: 0; - flex-shrink: 0; - /* use flex-basis, because of flex shorthand with calc()s doesn't work in IE */ - flex-basis: calc(100% - 52px); -} -.mat-card .imx-searchbar-input::placeholder { - font-style: italic; -} -.imx-searchbar-input::-ms-clear { - /* hide input-clear button (the 'X') in > IE 10 */ - display: none; - width: 0; - height: 0; -} -.mat-radio-button{ +.mat-mdc-radio-button { margin-left: 15px; } -.flex-property{ +.flex-property { flex: 1 1 auto; display: flex; flex-direction: column; overflow: auto; } -.imx_MatTableRow{ +.imx_MatTableRow { cursor: pointer; } -.imx_MatTableHeaderRow{ +.imx_MatTableHeaderRow { padding-top: 20px; padding-bottom: 20px; } -.imx-change-table-no-results { +.imx-no-results { text-align: center; margin: 20px 0; flex: 1 1 auto; @@ -79,10 +44,7 @@ $SearchBarHeight: 45px; flex-direction: column; justify-content: center; background-color: $color-gray-0; - .eui-icon { - font-size: 100px; - color: $color-gray-10; - } + p { margin: 0; font-size: 18px; @@ -91,12 +53,10 @@ $SearchBarHeight: 45px; } .eui-dark-theme { - :host{ - .imx-change-table-no-results { + :host { + .imx-no-results { background-color: $color-gray-70; - eui-icon { - color: $color-gray-20; - } + p { color: $color-gray-10; } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/objects-by-id.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/objects-by-id.component.ts index 639696309..250dea5df 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/objects-by-id.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/objects-by-id/objects-by-id.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,15 +25,15 @@ */ import { OverlayRef } from '@angular/cdk/overlay'; -import { Component, ViewChild } from '@angular/core'; -import { MatPaginator, PageEvent } from '@angular/material/paginator'; +import { Component } from '@angular/core'; +import { FormControl } from '@angular/forms'; +import { PageEvent } from '@angular/material/paginator'; import { MatRadioChange } from '@angular/material/radio'; import { EuiLoadingService } from '@elemental-ui/core'; import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; -import { FormControl } from '@angular/forms'; -import { HistoryOperation } from 'imx-api-qbm'; -import { TypedEntityCollectionData } from 'imx-qbm-dbts'; +import { HistoryOperation } from '@imx-modules/imx-api-qbm'; +import { TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { SettingsService } from 'qbm'; import { ObjectByIdService } from './object-by-id.service'; @@ -58,7 +58,7 @@ export class ObjectsByIdComponent { ]; public selectedType = this.filterOptions[0].value; public processId: string; - public searchControl = new FormControl(''); + public searchControl = new FormControl('', { nonNullable: true }); public isShowGraph = false; public historyData: HistoryOperation[] = []; @@ -66,7 +66,6 @@ export class ObjectsByIdComponent { public dataCollection: TypedEntityCollectionData; private stateCached: { page: number; pageSize: number; skip: number }; - @ViewChild(MatPaginator) private paginator: MatPaginator; public paginatorConfig = { index: 0, size: 5, @@ -75,11 +74,15 @@ export class ObjectsByIdComponent { hidden: false, }; - constructor(private busyService: EuiLoadingService, private objectByIdService: ObjectByIdService, private settings: SettingsService) { + constructor( + private busyService: EuiLoadingService, + private objectByIdService: ObjectByIdService, + private settings: SettingsService, + ) { this.paginatorConfig.size = this.settings.DefaultPageSize; this.searchControl.valueChanges .pipe(distinctUntilChanged(), debounceTime(300)) - .subscribe(async () => await this.search(this.searchControl.value)); + .subscribe(async () => await this.onSearch(this.searchControl.value)); } public async typeChanged(evt: MatRadioChange) { @@ -93,7 +96,7 @@ export class ObjectsByIdComponent { let overlayRef: OverlayRef; setTimeout(() => (overlayRef = this.busyService.show())); try { - this.historyData = (await this.objectByIdService.getChangeOperation(this.processId)).Events; + this.historyData = (await this.objectByIdService.getChangeOperation(this.processId)).Events ?? []; } catch { this.historyData = []; } finally { @@ -103,7 +106,7 @@ export class ObjectsByIdComponent { } } - private async search(term: string): Promise { + private async onSearch(term: string): Promise { this.processId = term; await this.getChangeOperation(); } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/processes/processes.module.ts b/imxweb/projects/qer-app-operationssupport/src/app/processes/processes.module.ts index 5b2d23d12..99fddbb9b 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/processes/processes.module.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/processes/processes.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -39,8 +39,15 @@ import { MatMenuModule } from '@angular/material/menu'; import { FormsModule } from '@angular/forms'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; - -import { BusyIndicatorModule, DataSourceToolbarModule, DataTableModule, JobQueueOverviewModule, LdsReplaceModule, ObjectHistoryModule, QbmModule } from 'qbm'; +import { + BusyIndicatorModule, + DataSourceToolbarModule, + DataTableModule, + JobQueueOverviewModule, + LdsReplaceModule, + ObjectHistoryModule, + QbmModule, +} from 'qbm'; import { FrozenJobsComponent } from './frozen-jobs/frozen-jobs.component'; import { SingleFrozenJobComponent } from './frozen-jobs/single-frozen-job.component'; import { JobChainsComponent } from './job-chains/job-chains.component'; @@ -63,7 +70,6 @@ import { ChangeOperationTableComponent } from './objects-by-id/change-operation- import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatProgressBarModule } from '@angular/material/progress-bar'; - @NgModule({ declarations: [ FrozenJobsComponent, @@ -103,7 +109,7 @@ import { MatProgressBarModule } from '@angular/material/progress-bar'; MatSlideToggleModule, JobQueueOverviewModule, ObjectHistoryModule, - BusyIndicatorModule + BusyIndicatorModule, ], providers: [ QueueTreeService, @@ -112,7 +118,7 @@ import { MatProgressBarModule } from '@angular/material/progress-bar'; JobPerformanceQueuesService, JobPerformanceService, FrozenJobsService, - ObjectByIdService - ] + ObjectByIdService, + ], }) -export class ProcessesModule { } +export class ProcessesModule {} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-details/job-servers-details.component.html b/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-details/job-servers-details.component.html index ccdca5b53..79c85380f 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-details/job-servers-details.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-details/job-servers-details.component.html @@ -1,17 +1,17 @@ - + -
    +
    - {{roles.Display}} + {{ roles.Display }}
    -
    +
    -
    {{tag.Display}}
    - {{tag.Description}} +
    {{ tag.Display }}
    + {{ tag.Description }}
    - \ No newline at end of file + diff --git a/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-details/job-servers-details.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-details/job-servers-details.component.scss index f01ec035c..a1cf04d75 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-details/job-servers-details.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-details/job-servers-details.component.scss @@ -1,11 +1,4 @@ -::ng-deep .mat-tab-body { - margin: 20px; -} - -.imx-server-details-machine-roles, .imx-server-details-tags { +.imx-server-details-machine-roles, +.imx-server-details-tags { margin-bottom: 20px; } - -.mat-tab-group { - overflow-y: auto; -} \ No newline at end of file diff --git a/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-details/job-servers-details.component.spec.ts b/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-details/job-servers-details.component.spec.ts index 80d576c65..3f643800f 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-details/job-servers-details.component.spec.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-details/job-servers-details.component.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,24 +28,25 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { JobServersDetailsComponent } from './job-servers-details.component'; import { EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { TranslateModule } from '@ngx-translate/core'; describe('JobServersDetailsComponent', () => { let component: JobServersDetailsComponent; let fixture: ComponentFixture; const sidesheetData = { - Tags:[], - MachineRoles:[] + Tags: [], + MachineRoles: [], }; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ JobServersDetailsComponent ], - providers:[ + declarations: [JobServersDetailsComponent], + imports: [TranslateModule.forRoot()], + providers: [ { provide: EUI_SIDESHEET_DATA, - useValue: sidesheetData - } - ] - }) - .compileComponents(); + useValue: sidesheetData, + }, + ], + }).compileComponents(); }); beforeEach(() => { diff --git a/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-details/job-servers-details.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-details/job-servers-details.component.ts index a3733b8bc..0517035d4 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-details/job-servers-details.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-details/job-servers-details.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,20 +27,21 @@ import { Component, Inject, OnInit } from '@angular/core'; import { EUI_SIDESHEET_DATA } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { OpsupportJobservers, ServerExtendedDataItem } from 'imx-api-qbm'; +import { OpsupportJobservers, ServerExtendedDataItem } from '@imx-modules/imx-api-qbm'; @Component({ selector: 'imx-job-servers-details', templateUrl: './job-servers-details.component.html', - styleUrls: ['./job-servers-details.component.scss'] + styleUrls: ['./job-servers-details.component.scss'], }) export class JobServersDetailsComponent { - - public tags:ServerExtendedDataItem[]; - public machineRoles:ServerExtendedDataItem[]; - constructor(@Inject(EUI_SIDESHEET_DATA) public serverDetails: OpsupportJobservers, - private translate : TranslateService) { - this.tags = serverDetails['Tags']; - this.machineRoles = serverDetails['MachineRoles']; - } + public tags: ServerExtendedDataItem[]; + public machineRoles: ServerExtendedDataItem[]; + constructor( + @Inject(EUI_SIDESHEET_DATA) public serverDetails: OpsupportJobservers, + private translate: TranslateService, + ) { + this.tags = serverDetails['Tags']; + this.machineRoles = serverDetails['MachineRoles']; + } } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-edit/job-servers-edit.component.html b/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-edit/job-servers-edit.component.html index 80a98a2e2..4700fd198 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-edit/job-servers-edit.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-edit/job-servers-edit.component.html @@ -2,13 +2,23 @@
    - +
    -
    - +
    +
    diff --git a/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-edit/job-servers-edit.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-edit/job-servers-edit.component.scss index 265f55c89..4d903483d 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-edit/job-servers-edit.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-edit/job-servers-edit.component.scss @@ -1 +1 @@ -//Job Servers Edit Component Styles \ No newline at end of file +//Job Servers Edit Component Styles diff --git a/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-edit/job-servers-edit.component.spec.ts b/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-edit/job-servers-edit.component.spec.ts index 5f81ba00b..954888a15 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-edit/job-servers-edit.component.spec.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-edit/job-servers-edit.component.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,11 +29,12 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { JobServersEditComponent } from './job-servers-edit.component'; import { EuiLoadingService, EuiSidesheetService, EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; import { of } from 'rxjs'; -import { OpsupportJobservers } from 'imx-api-qbm'; +import { OpsupportJobservers } from '@imx-modules/imx-api-qbm'; import { JobServersService } from '../job-servers.service'; import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar'; import { SnackBarService } from '../../../../../qbm/src/lib/snackbar/snack-bar.service'; import { ConfirmationService } from 'qbm'; +import { TranslateModule } from '@ngx-translate/core'; describe('JobServersEditComponent', () => { let component: JobServersEditComponent; let fixture: ComponentFixture; @@ -48,14 +49,14 @@ describe('JobServersEditComponent', () => { open: jasmine.createSpy('open'), dismiss: jasmine.createSpy('dismiss'), }; - let confirm = true; - const mockConfirmationService = { - confirm: jasmine.createSpy('confirm').and.callFake(() => Promise.resolve(confirm)), - }; + let confirm = true; + const mockConfirmationService = { + confirm: jasmine.createSpy('confirm').and.callFake(() => Promise.resolve(confirm)), + }; beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [JobServersEditComponent], - imports: [MatSnackBarModule], + imports: [MatSnackBarModule, TranslateModule.forRoot()], providers: [ { provide: MatSnackBar, diff --git a/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-edit/job-servers-edit.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-edit/job-servers-edit.component.ts index 1161dc531..f4f2bae7a 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-edit/job-servers-edit.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-edit/job-servers-edit.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,7 +28,7 @@ import { Component, Inject, OnInit } from '@angular/core'; import { EuiLoadingService, EuiSidesheetService, EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; import { FormGroup, FormArray } from '@angular/forms'; import { BaseCdr, ColumnDependentReference, ConfirmationService, SnackBarService } from 'qbm'; -import { OpsupportJobservers } from 'imx-api-qbm'; +import { OpsupportJobservers } from '@imx-modules/imx-api-qbm'; @Component({ selector: 'imx-job-servers-edit', @@ -39,12 +39,12 @@ export class JobServersEditComponent implements OnInit { public readonly serverDetailsFormGroup: FormGroup; public cdrList: ColumnDependentReference[] = []; constructor( - @Inject(EUI_SIDESHEET_DATA) public serverDetails:{data: OpsupportJobservers, properties: string[]}, + @Inject(EUI_SIDESHEET_DATA) public serverDetails: { data: OpsupportJobservers; properties: string[] }, private readonly sidesheet: EuiSidesheetService, private readonly sidesheetRef: EuiSidesheetRef, private readonly snackBarService: SnackBarService, private readonly euiLoadingService: EuiLoadingService, - private readonly confirmation: ConfirmationService + private readonly confirmation: ConfirmationService, ) { this.serverDetailsFormGroup = new FormGroup({ formArray: new FormArray([]), diff --git a/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-gridview.component.html b/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-gridview.component.html index 9e9c057ed..3719f3e96 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-gridview.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-gridview.component.html @@ -9,7 +9,7 @@ > - + @@ -55,9 +55,14 @@
    -
    @@ -68,7 +73,7 @@
    - diff --git a/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-gridview.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-gridview.component.scss index dfa0b6ba9..171a2ee12 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-gridview.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-gridview.component.scss @@ -4,26 +4,3 @@ overflow: hidden; flex-grow: 1; } - -.imx-table-container { - flex: 1 1 auto; - height: calc(100% - 50px); - display: flex; - flex-direction: column; - margin-bottom: -20px; - - > :nth-child(2) { - flex: 1 1 auto; - } -} - -.imx-refresh-button { - margin-right: 5px; - height: 50px; -} - -.imx-button-column{ - display: flex; - flex-direction: row; -} - diff --git a/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-gridview.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-gridview.component.ts index 7c18304e4..e373407b8 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-gridview.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers-gridview.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,16 +24,17 @@ * */ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { OverlayRef } from '@angular/cdk/overlay'; +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { OpsupportJobservers, ServerExtendedData } from 'imx-api-qbm'; -import { JobServersParameters, JobServersService } from './job-servers.service'; -import { DataSourceToolbarSettings, SettingsService, SnackBarService } from 'qbm'; -import { JobServersEditComponent } from './job-servers-edit/job-servers-edit.component'; +import { OpsupportJobservers } from '@imx-modules/imx-api-qbm'; +import { IClientProperty, TypedEntity } from '@imx-modules/imx-qbm-dbts'; +import { calculateSidesheetWidth, DataSourceToolbarSettings, SettingsService, SnackBarService } from 'qbm'; import { JobServersDetailsComponent } from './job-servers-details/job-servers-details.component'; +import { JobServersEditComponent } from './job-servers-edit/job-servers-edit.component'; +import { JobServersParameters, JobServersService } from './job-servers.service'; @Component({ selector: 'imx-job-servers-gridview', templateUrl: './job-servers-gridview.component.html', @@ -55,14 +56,14 @@ export class JobServersGridviewComponent implements OnInit { private readonly settingsService: SettingsService, private readonly sidesheet: EuiSidesheetService, private readonly snackBarService: SnackBarService, - private readonly translateService: TranslateService + private readonly translateService: TranslateService, ) {} public async ngOnInit(): Promise { let overlayRef = this.busyService.show(); try { await this.refresh(); - this.editableFields = (await this.gridDataService.getProjectConfig()).EditableFields.QBMServer; + this.editableFields = (await this.gridDataService.getProjectConfig()).EditableFields?.QBMServer; } finally { this.busyService.hide(overlayRef); } @@ -100,7 +101,7 @@ export class JobServersGridviewComponent implements OnInit { }, }; Object.assign(entitySchema.Columns, extraColumns); - const displayedColumns = []; + const displayedColumns: IClientProperty[] = []; displayedColumns.push(entitySchema.Columns.Ident_Server); if (this.navigationState.withconnection) { displayedColumns.push(entitySchema.Columns.Connection); @@ -120,7 +121,7 @@ export class JobServersGridviewComponent implements OnInit { dataSource: data, entitySchema, navigationState: this.navigationState, - extendedData: data.extendedData[0], + extendedData: data.extendedData?.[0] as any[], }; this.jobServersChecked.emit(this.navigationState.withconnection); @@ -128,7 +129,7 @@ export class JobServersGridviewComponent implements OnInit { setTimeout(() => this.busyService.hide(overlayRef)); } } - public async checkServer(data: any, event: Event) { + public async checkServer(data: TypedEntity, event: Event) { event.stopPropagation(); let overlayRef = this.busyService.show(); let uid = data.GetEntity().GetKeys()[0], @@ -136,7 +137,7 @@ export class JobServersGridviewComponent implements OnInit { connectionTime = await this.gridDataService.checkServerConnection(uid); this.busyService.hide(overlayRef); if (connectionTime.Value === -1) { - let textContainer = { key: '#LDS#The server does not respond.' }; + let textContainer = { key: '#LDS#The job server does not respond.' }; this.snackBarService.open(textContainer, '#LDS#Close', { duration: 6000 }); } else { data.GetEntity().Commit(true); @@ -151,27 +152,29 @@ export class JobServersGridviewComponent implements OnInit { return diff > 10; } - public edit(data: OpsupportJobservers): void { + public edit(data: TypedEntity): void { this.sidesheet.open(JobServersEditComponent, { - title: this.translateService.instant('#LDS#Heading Edit Server Settings'), + title: this.translateService.instant('#LDS#Heading Edit Job Server Settings'), + subTitle: data.GetEntity().GetDisplay(), disableClose: true, padding: '0', - width: 'max(50%,500px)', + width: calculateSidesheetWidth(800, 0.5), testId: 'job-servers-edit-sidesheet', data: { data, properties: this.editableFields }, }); } - public getServerDetails(data: ServerExtendedData[], event: Event): void { + public getServerDetails(data: OpsupportJobservers, event: Event): void { event.stopPropagation(); let overlayRef = this.busyService.show(); try { this.sidesheet.open(JobServersDetailsComponent, { - title: this.translateService.instant('#LDS#Heading View Server Details'), - width: 'max(50%,500px)', + title: this.translateService.instant('#LDS#Heading View Job Server Details'), + subTitle: data.GetEntity().GetDisplay(), + width: calculateSidesheetWidth(800, 0.5), padding: '0', - testId: 'job-servers-deatils-sidesheet', - data: data, + testId: 'job-servers-details-sidesheet', + data: this.dstSettings.extendedData, }); } finally { this.busyService.hide(overlayRef); diff --git a/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers.service.ts index ac22d295b..a581045a0 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/service-report/job-servers.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,9 +26,9 @@ import { Injectable } from '@angular/core'; -import { CollectionLoadParameters, EntitySchema, ExtendedTypedEntityCollection } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, EntitySchema, ExtendedTypedEntityCollection } from '@imx-modules/imx-qbm-dbts'; import { imx_SessionService } from 'qbm'; -import { OpsupportJobservers } from 'imx-api-qbm'; +import { OpsupportJobservers } from '@imx-modules/imx-api-qbm'; export interface JobServersParameters extends CollectionLoadParameters { withconnection?: boolean; @@ -37,24 +37,21 @@ export interface JobServersParameters extends CollectionLoadParameters { @Injectable() export class JobServersService { - public get OpsupportJobserversSchema(): EntitySchema { return this.session.TypedClient.OpsupportJobservers.GetSchema(); } - constructor(private readonly session: imx_SessionService) { - } + constructor(private readonly session: imx_SessionService) {} public async get(parameters?: JobServersParameters): Promise> { return this.session.TypedClient.OpsupportJobservers.Get(parameters); } - public async checkServerConnection(uid){ + public async checkServerConnection(uid) { return this.session.Client.opsupport_jobservers_check_post(uid); } - public async getProjectConfig(){ + public async getProjectConfig() { return this.session.Client.opsupport_projectconfig_get(); } - } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-availability.component.html b/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-availability.component.html index 06d1abc9c..ad2f2a212 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-availability.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-availability.component.html @@ -1,11 +1,11 @@ -

    - {{'#LDS#Heading Service Availability Check' | translate}} -

    - +

    + {{ '#LDS#Heading Job Server Overview' | translate }} +

    + - -
    - {{'#LDS#Last check' | translate}} + +
    + {{ '#LDS#Last check' | translate }} {{ lastUpdate }}
    diff --git a/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-availability.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-availability.component.scss index d4eeb9fd0..760447f0a 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-availability.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-availability.component.scss @@ -6,6 +6,6 @@ } .imx-job-grid { - display:flex; + display: flex; flex-direction: column; -} \ No newline at end of file +} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-availability.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-availability.component.ts index cbb033f64..6afb2229a 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-availability.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-availability.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -39,7 +39,7 @@ export class ServiceAvailabilityComponent { @ViewChild(JobServersGridviewComponent) private readonly jobGrid: JobServersGridviewComponent; - constructor(private translate: TranslateService){ } + constructor(private translate: TranslateService) {} public async Update(): Promise { await this.jobGrid.refresh(true); diff --git a/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-report.component.html b/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-report.component.html index 8073b03c8..29f99455b 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-report.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-report.component.html @@ -1,16 +1,17 @@
    -

    {{'#LDS#Status reports' | translate}}

    +

    {{ '#LDS#Status reports' | translate }}

    - + + + - {{'#LDS#Service availability check' | translate}} - {{'#LDS#Check the availability of services.' | translate}} + {{ '#LDS#Heading Job Server Overview' | translate }} + {{ '#LDS#Check and manage your job servers.' | translate }} - +
    - diff --git a/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-report.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-report.component.scss index 2d0011e34..c0037c602 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-report.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-report.component.scss @@ -4,33 +4,25 @@ margin-bottom: 10px; } - -.mat-card { +.mat-mdc-card { width: 350px; - .mat-card-content { + .mat-mdc-card-content { padding: 19px; } - .mat-card-header { + .mat-mdc-card-header { height: 124px; display: flex; justify-content: center; - - .mat-card-header-text { - margin: 0; - height: 0; - } - - eui-icon { - line-height: 124px; - } + align-items: center; } - .mat-card-actions { + + .mat-mdc-card-actions { padding-bottom: 20px; padding-right: 20px; display: flex; flex-direction: column; align-items: flex-end; } -} \ No newline at end of file +} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-report.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-report.component.ts index 7ebe02838..9898f3e6e 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-report.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-report.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -32,9 +32,8 @@ import { Router } from '@angular/router'; templateUrl: './service-report.component.html', styleUrls: ['./service-report.component.scss'], }) -export class ServiceReportComponent { - - constructor(private router: Router) { } +export class ServiceReportComponent { + constructor(private router: Router) {} public CheckServiceAvailability(): void { this.router.navigate(['ServiceAvailability']); diff --git a/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-report.module.ts b/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-report.module.ts index a73e6c5bf..b411f5126 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-report.module.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/service-report/service-report.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -61,11 +61,11 @@ import { JobServersDetailsComponent } from './job-servers-details/job-servers-de MatCardModule, MatButtonModule, MatTooltipModule, - FormsModule, + FormsModule, ReactiveFormsModule, - MatTabsModule + MatTabsModule, ], providers: [JobServersService], - exports: [ServiceReportComponent] + exports: [ServiceReportComponent], }) -export class ServiceReportModule { } +export class ServiceReportModule {} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/service-report/services-inactive.component.html b/imxweb/projects/qer-app-operationssupport/src/app/service-report/services-inactive.component.html index db19efdec..c548543aa 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/service-report/services-inactive.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/service-report/services-inactive.component.html @@ -1,4 +1,4 @@ -

    - {{'#LDS#Inactive services' | translate}} -

    +

    + {{ '#LDS#Inactive services' | translate }} +

    diff --git a/imxweb/projects/qer-app-operationssupport/src/app/service-report/services-inactive.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/service-report/services-inactive.component.ts index 6ec09ba5a..f5d7d5bf3 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/service-report/services-inactive.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/service-report/services-inactive.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,8 +29,6 @@ import { Component } from '@angular/core'; @Component({ selector: 'imx-services-inactive', templateUrl: './services-inactive.component.html', - styles: [] + styles: [], }) -export class ServicesInactiveComponent { - -} +export class ServicesInactiveComponent {} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-information/sync-information.component.html b/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-information/sync-information.component.html index 6a391f968..caf38b965 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-information/sync-information.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-information/sync-information.component.html @@ -1,42 +1,48 @@
    -

    - {{'#LDS#Synchronization' | translate}} -

    +

    + {{ '#LDS#Synchronization' | translate }} +

    - + - - + + {{ data.DisplayName.Column.GetDisplayValue() }} - + {{ data.Description.Column.GetDisplayValue() }} - + {{ data.NextSyncDate.Column.GetDisplayValue() }} - + {{ data.CountJournalFailure.Column.GetDisplayValue() }} - + {{ data.LastSyncCountObjects.Column.GetDisplayValue() }} - + @@ -44,7 +50,7 @@

    - diff --git a/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-information/sync-information.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-information/sync-information.component.scss index 14ab8ce52..024835feb 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-information/sync-information.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-information/sync-information.component.scss @@ -4,20 +4,3 @@ overflow: hidden; height: 100%; } - - -.imx-table-container { - display: flex; - flex-direction: column; - overflow: hidden; - height: inherit; - - > :nth-child(2) { - flex: 1 1 auto; - } -} - -.imx-refresh-button { - margin-right: 5px; - height: 50px; -} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-information/sync-information.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-information/sync-information.component.ts index 00d19443f..58125e077 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-information/sync-information.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-information/sync-information.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,18 +29,17 @@ import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { EuiLoadingService } from '@elemental-ui/core'; -import { OpsupportSyncShell } from 'imx-api-dpr'; -import { EntitySchema, IClientProperty, ValType } from 'imx-qbm-dbts'; +import { OpsupportSyncShell } from '@imx-modules/imx-api-dpr'; +import { EntitySchema, IClientProperty, ValType } from '@imx-modules/imx-qbm-dbts'; import { DataSourceToolbarSettings, ClientPropertyForTableColumns, SettingsService } from 'qbm'; import { OpsupportSyncShellParameters, SyncService } from '../sync.service'; @Component({ selector: 'imx-sync-information', templateUrl: './sync-information.component.html', - styleUrls: ['./sync-information.component.scss'] + styleUrls: ['./sync-information.component.scss'], }) export class SyncInformationComponent implements OnInit { - public dstSettings: DataSourceToolbarSettings; public readonly entitySchemaSyncInfo: EntitySchema; private navigationState: OpsupportSyncShellParameters; @@ -50,7 +49,7 @@ export class SyncInformationComponent implements OnInit { private dataSource: SyncService, private router: Router, private busyService: EuiLoadingService, - private readonly settings: SettingsService + private readonly settings: SettingsService, ) { this.navigationState = { StartIndex: 0, PageSize: settings.DefaultPageSize }; this.entitySchemaSyncInfo = dataSource.syncShellSchema; @@ -64,8 +63,8 @@ export class SyncInformationComponent implements OnInit { ColumnName: 'actions', Type: ValType.String, afterAdditionals: true, - untranslatedDisplay: '#LDS#Actions' - } + untranslatedDisplay: '#LDS#Actions', + }, ]; } @@ -85,17 +84,15 @@ export class SyncInformationComponent implements OnInit { this.navigationState = { ...this.navigationState, ...navigationState }; let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + setTimeout(() => (overlayRef = this.busyService.show())); try { - const applications = await this.dataSource.getSyncShell(navigationState); this.dstSettings = { displayedColumns: this.displayedColumns, dataSource: applications, entitySchema: this.entitySchemaSyncInfo, - navigationState: this.navigationState + navigationState: this.navigationState, }; - } finally { setTimeout(() => this.busyService.hide(overlayRef)); } @@ -104,5 +101,4 @@ export class SyncInformationComponent implements OnInit { public viewDetails(syncShell: OpsupportSyncShell): void { this.router.navigate(['/SyncJournal/' + syncShell.UID_DPRShell.value]); } - } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-journal/sync-journal.component.html b/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-journal/sync-journal.component.html index 81a22b2fe..7eb439fdc 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-journal/sync-journal.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-journal/sync-journal.component.html @@ -1,38 +1,49 @@
    -

    - {{'#LDS#Synchronization log' | translate}} {{ caption }} -

    +

    {{ '#LDS#Synchronization log' | translate }} {{ caption }}

    - -
    - + + +
    + - - + + {{ data.CreationTime.Column.GetDisplayValue() }} - + {{ data.ProjectionConfigDisplay.Column.GetDisplayValue() }} - + {{ data.ProjectionState.Column.GetDisplayValue() }} - + {{ data.ProjectionStartInfoDisplay.Column.GetDisplayValue() }} - + @@ -40,7 +51,7 @@

    - diff --git a/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-journal/sync-journal.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-journal/sync-journal.component.scss index e772dfef1..171a2ee12 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-journal/sync-journal.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-journal/sync-journal.component.scss @@ -4,19 +4,3 @@ overflow: hidden; flex-grow: 1; } - -.imx-table-container { - flex: 1 1 auto; - display: flex; - flex-direction: column; - overflow: auto; - - > :nth-child(2) { - flex: 1 1 auto; - } -} - -.imx-refresh-button { - margin-right: 5px; - height: 50px; -} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-journal/sync-journal.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-journal/sync-journal.component.ts index 690ec80f3..3943a96e8 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-journal/sync-journal.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-journal/sync-journal.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,8 +29,8 @@ import { ActivatedRoute } from '@angular/router'; import { OverlayRef } from '@angular/cdk/overlay'; import { EuiDownloadDirective, EuiLoadingService } from '@elemental-ui/core'; -import { OpsupportSyncJournal } from 'imx-api-dpr'; -import { EntitySchema, IClientProperty, ValType } from 'imx-qbm-dbts'; +import { OpsupportSyncJournal } from '@imx-modules/imx-api-dpr'; +import { EntitySchema, IClientProperty, ValType } from '@imx-modules/imx-qbm-dbts'; import { DataSourceToolbarSettings, ElementalUiConfigService, ClientPropertyForTableColumns, SettingsService } from 'qbm'; import { OpsSyncJournalParameters, SyncService } from '../sync.service'; import { SyncSummaryService } from './sync-summary.service'; @@ -38,10 +38,9 @@ import { SyncSummaryService } from './sync-summary.service'; @Component({ selector: 'imx-sync-journal', templateUrl: './sync-journal.component.html', - styleUrls: ['./sync-journal.component.scss'] + styleUrls: ['./sync-journal.component.scss'], }) export class SyncJournalComponent implements OnInit { - public caption = ''; public dstSettings: DataSourceToolbarSettings; @@ -57,8 +56,8 @@ export class SyncJournalComponent implements OnInit { private syncSummaryService: SyncSummaryService, private readonly elementalUiConfigService: ElementalUiConfigService, private readonly settings: SettingsService, - private busyService: EuiLoadingService) { - + private busyService: EuiLoadingService, + ) { this.entitySchemaSyncInfo = syncShellService.syncJournalSchema; this.displayedColumns = [ this.entitySchemaSyncInfo.Columns.CreationTime, @@ -69,8 +68,8 @@ export class SyncJournalComponent implements OnInit { ColumnName: 'actions', Type: ValType.String, afterAdditionals: true, - untranslatedDisplay: '#LDS#Actions' - } + untranslatedDisplay: '#LDS#Actions', + }, ]; } @@ -78,11 +77,11 @@ export class SyncJournalComponent implements OnInit { const filter = this.activeRoute.snapshot.queryParamMap.get('filter'); const parameters = { shell: '', - filter: filter ? JSON.parse(filter) : null + filter: filter ? JSON.parse(filter) : null, }; let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + setTimeout(() => (overlayRef = this.busyService.show())); try { const uidSyncShell = this.activeRoute.snapshot.paramMap.get('uidSyncShell'); if (uidSyncShell) { @@ -95,8 +94,10 @@ export class SyncJournalComponent implements OnInit { setTimeout(() => this.busyService.hide(overlayRef)); } await this.getData({ - StartIndex: 0, PageSize: this.settings.DefaultPageSize, - shell: parameters.shell, filter: parameters.filter + StartIndex: 0, + PageSize: this.settings.DefaultPageSize, + shell: parameters.shell, + filter: parameters.filter, }); } @@ -104,17 +105,15 @@ export class SyncJournalComponent implements OnInit { this.navigationState = navigationState; let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + setTimeout(() => (overlayRef = this.busyService.show())); try { - const applications = await this.syncShellService.getSyncJournal(navigationState); this.dstSettings = { displayedColumns: this.displayedColumns, dataSource: applications, entitySchema: this.entitySchemaSyncInfo, - navigationState: this.navigationState + navigationState: this.navigationState, }; - } finally { setTimeout(() => this.busyService.hide(overlayRef)); } @@ -122,10 +121,9 @@ export class SyncJournalComponent implements OnInit { public async showReport(journalItem: OpsupportSyncJournal): Promise { let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + setTimeout(() => (overlayRef = this.busyService.show())); try { - const blob = await this.syncSummaryService - .Get({ journal: journalItem.UID_DPRJournal.value }); + const blob = await this.syncSummaryService.Get({ journal: journalItem.UID_DPRJournal.value }); let fileName = journalItem.UID_DPRJournal.Column.GetDisplayValue(); if (blob.type.startsWith('application/pdf')) { @@ -137,13 +135,12 @@ export class SyncJournalComponent implements OnInit { if (this.directive) { this.directive.downloadOptions = { - ... this.elementalUiConfigService.Config.downloadOptions, + ...this.elementalUiConfigService.Config.downloadOptions, url: csvUrl, - fileName + fileName, }; this.directive.onClick(); } - } finally { setTimeout(() => this.busyService.hide(overlayRef)); } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-journal/sync-summary.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-journal/sync-summary.service.ts index 9d93548c2..fd1e98137 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-journal/sync-summary.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/sync/sync-journal/sync-summary.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qer-app-operationssupport/src/app/sync/sync.module.ts b/imxweb/projects/qer-app-operationssupport/src/app/sync/sync.module.ts index e000ec327..f1dd7bc33 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/sync/sync.module.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/sync/sync.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -37,24 +37,9 @@ import { SyncJournalComponent } from './sync-journal/sync-journal.component'; import { SyncService } from './sync.service'; import { SyncSummaryService } from './sync-journal/sync-summary.service'; - @NgModule({ - declarations: [ - SyncInformationComponent, - SyncJournalComponent, - ], - imports: [ - CommonModule, - EuiCoreModule, - DataTableModule, - DataSourceToolbarModule, - MatButtonModule, - MatTooltipModule, - TranslateModule - ], - providers: [ - SyncService, - SyncSummaryService, - ] + declarations: [SyncInformationComponent, SyncJournalComponent], + imports: [CommonModule, EuiCoreModule, DataTableModule, DataSourceToolbarModule, MatButtonModule, MatTooltipModule, TranslateModule], + providers: [SyncService, SyncSummaryService], }) -export class SyncModule { } +export class SyncModule {} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/sync/sync.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/sync/sync.service.ts index ae3eae844..3e3cd529e 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/sync/sync.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/sync/sync.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,8 +26,8 @@ import { Injectable } from '@angular/core'; -import { CollectionLoadParameters, EntitySchema, FilterData, TypedEntityCollectionData } from 'imx-qbm-dbts'; -import { OpsupportSyncJournal, OpsupportSyncShell } from 'imx-api-dpr'; +import { OpsupportSyncJournal, OpsupportSyncShell } from '@imx-modules/imx-api-dpr'; +import { CollectionLoadParameters, EntitySchema, FilterData, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { DprApiService } from '../../../dpr-api-client.service'; export interface OpsupportSyncShellParameters extends CollectionLoadParameters { @@ -49,7 +49,7 @@ export class SyncService { return this.dprClient.typedClient.OpsupportSyncJournal.GetSchema(); } - constructor(private readonly dprClient: DprApiService) { } + constructor(private readonly dprClient: DprApiService) {} public getSyncShell(parameters: OpsupportSyncShellParameters): Promise> { return this.dprClient.typedClient.OpsupportSyncShell.Get(parameters); @@ -59,9 +59,8 @@ export class SyncService { return this.dprClient.typedClient.OpsupportSyncJournal.Get(parameters); } - public async GetDisplayName(uidShell: string, withfrozenjobs: boolean = false): Promise { - const syncShellItem = (await this.getSyncShell({ withfrozenjobs })).Data.find(a => a.UID_DPRShell.value === uidShell); + public async GetDisplayName(uidShell: string, withfrozenjobs: boolean = false): Promise { + const syncShellItem = (await this.getSyncShell({ withfrozenjobs })).Data.find((a) => a.UID_DPRShell.value === uidShell); return syncShellItem ? syncShellItem.DisplayName.value : null; } - } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/test-utilities/imx-api-mock.spec.ts b/imxweb/projects/qer-app-operationssupport/src/app/test-utilities/imx-api-mock.spec.ts index 2619ab6fe..b8b80d0c9 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/test-utilities/imx-api-mock.spec.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/test-utilities/imx-api-mock.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,18 +24,15 @@ * */ -import * as TypeMoq from 'typemoq'; - -import { EntityData, IEntity, IEntityColumn, IReadValue, TypedEntity } from 'imx-qbm-dbts'; -import { OpsupportSyncJournal, OpsupportSyncShell } from 'imx-api-dpr'; +import { OpsupportSyncJournal, OpsupportSyncShell } from '@imx-modules/imx-api-dpr'; import { - OpsupportQueueJobchains, - OpsupportQueueTree, OpsupportQueueFrozenjobs, - OpsupportQueueJobs, + OpsupportQueueJobchains, OpsupportQueueJobperformance, -} from 'imx-api-qbm'; -import { BaseImxApiDtoMock, BaseImxApiDataMock, CreateIEntityColumn, CreateIReadValue, CreateIEntity } from 'qbm'; + OpsupportQueueJobs, +} from '@imx-modules/imx-api-qbm'; +import { EntityData, IEntity, IEntityColumn } from '@imx-modules/imx-qbm-dbts'; +import { BaseImxApiDataMock, BaseImxApiDtoMock, CreateIEntity, CreateIEntityColumn, CreateIReadValue } from 'qbm'; export interface JobChainsMock { JobChainName: string; @@ -117,38 +114,32 @@ export class ImxApiDtoMock extends BaseImxApiDtoMock { const jobChainName = CreateIEntityColumn(properties.JobChainName); const count = CreateIEntityColumn(properties.Count.toString()); - const mock = TypeMoq.Mock.ofType(); - mock.setup((property) => property.JobChainName).returns(() => CreateIReadValue(properties.JobChainName, jobChainName)); - mock.setup((property) => property.Count).returns(() => CreateIReadValue(properties.Count, count)); - - mock - .setup((property) => property.GetEntity()) - .returns(() => + return { + JobChainName: CreateIReadValue(properties.JobChainName, jobChainName), + Count: CreateIReadValue(properties.Count, count), + GetEntity: () => CreateIEntity((name: string) => { if (name === 'JobChainName') { return jobChainName; } return count; - }) - ); - - return mock.object; + }), + } as OpsupportQueueJobchains; } public static CreateOpsupportSyncJournalCollection(propertiesCollection: OpsupportSyncJournalMock[]): OpsupportSyncJournal[] { - return propertiesCollection.map((properties) => { - const mock = TypeMoq.Mock.ofType(); - mock - .setup((property) => property.CreationTime) - .returns(() => CreateIReadValue(properties.CreationTime, CreateIEntityColumn(properties.CreationTime.toLocaleString()))); - mock.setup((property) => property.ProjectionConfigDisplay).returns(() => CreateIReadValue(properties.ProjectionConfigDisplay)); - mock.setup((property) => property.ProjectionStartInfoDisplay).returns(() => CreateIReadValue(properties.ProjectionStartInfoDisplay)); - mock.setup((property) => property.ProjectionState).returns(() => CreateIReadValue(properties.ProjectionState)); - mock.setup((property) => property.UID_DPRJournal).returns(() => CreateIReadValue(properties.UID_DPRJournal)); - mock.setup((property) => property.UID_DPRProjectionConfig).returns(() => CreateIReadValue(properties.UID_DPRProjectionConfig)); - return mock.object; - }); + return propertiesCollection.map( + (properties) => + ({ + CreationTime: CreateIReadValue(properties.CreationTime, CreateIEntityColumn(properties.CreationTime.toLocaleString())), + ProjectionConfigDisplay: CreateIReadValue(properties.ProjectionConfigDisplay), + ProjectionStartInfoDisplay: CreateIReadValue(properties.ProjectionStartInfoDisplay), + ProjectionState: CreateIReadValue(properties.ProjectionState), + UID_DPRJournal: CreateIReadValue(properties.UID_DPRJournal), + UID_DPRProjectionConfig: CreateIReadValue(properties.UID_DPRProjectionConfig), + }) as OpsupportSyncJournal, + ); } public static CreateSingleOpsupportQueueTreeBranch(properties: OpsupportQueueTreeParams): IEntity { @@ -159,31 +150,6 @@ export class ImxApiDtoMock extends BaseImxApiDtoMock { } } return this.createEntity(columns); - // const mock = TypeMoq.Mock.ofType(); - // mock.setup(obj => obj.UID_Job).returns(() => CreateIReadValue(properties.UID_Job ? properties.UID_Job : '')); - // mock.setup(obj => obj.BasisObjectKey).returns(() => CreateIReadValue(properties.BasisObjectKey ? properties.BasisObjectKey : '')); - // mock.setup(obj => obj.DeferOnError).returns(() => CreateIReadValue(properties.DeferOnError ? properties.DeferOnError : false)); - // mock.setup(obj => obj.ErrorMessages).returns(() => CreateIReadValue(properties.ErrorMessages ? properties.ErrorMessages : '')); - // mock.setup(obj => obj.GenProcID).returns(() => CreateIReadValue(properties.GenProcID ? properties.GenProcID : '')); - // mock.setup(obj => obj.IsRootJob).returns(() => CreateIReadValue(properties.IsRootJob ? properties.IsRootJob : false)); - // mock.setup(obj => obj.JobChainName).returns(() => CreateIReadValue(properties.JobChainName ? properties.JobChainName : '')); - // mock.setup(obj => obj.LimitationCount).returns(() => CreateIReadValue(properties.LimitationCount ? properties.LimitationCount : 0)); - // mock.setup(obj => obj.Queue).returns(() => CreateIReadValue(properties.Queue ? properties.Queue : '')); - // mock.setup(obj => obj.Ready2EXE).returns(() => CreateIReadValue(properties.Ready2EXE ? properties.Ready2EXE : '')); - // mock.setup(obj => obj.TaskName).returns(() => CreateIReadValue(properties.TaskName ? properties.TaskName : '')); - // mock.setup(obj => obj.UID_JobError).returns(() => CreateIReadValue(properties.UID_JobError ? properties.UID_JobError : '')); - // mock.setup(obj => obj.UID_JobOrigin).returns(() => CreateIReadValue(properties.UID_JobOrigin ? properties.UID_JobOrigin : '')); - // mock.setup(obj => obj.UID_JobSameServer) - // .returns(() => CreateIReadValue(properties.UID_JobSameServer ? properties.UID_JobSameServer : '')); - // mock.setup(obj => obj.UID_JobSuccess).returns(() => CreateIReadValue(properties.UID_JobSuccess ? properties.UID_JobSuccess : '')); - // mock.setup(obj => obj.UID_Tree).returns(() => CreateIReadValue(properties.UID_Tree ? properties.UID_Tree : '')); - // mock.setup(obj => obj.XDateInserted) - // .returns(() => CreateIReadValue(properties.XDateInserted ? properties.XDateInserted : new Date(2001, 1, 1))); - // mock.setup(obj => obj.XDateUpdated) - // .returns(() => CreateIReadValue(properties.XDateUpdated ? properties.XDateUpdated : new Date(2001, 1, 1))); - // mock.setup(obj => obj.XUserInserted).returns(() => CreateIReadValue(properties.XUserInserted ? properties.XUserInserted : '')); - // mock.setup(obj => obj.XUserUpdated).returns(() => CreateIReadValue(properties.XUserUpdated ? properties.XUserUpdated : '')); - // return mock.object; } public static createColumn(value?) { @@ -213,81 +179,72 @@ export class ImxApiDtoMock extends BaseImxApiDtoMock { } public static CreateOpsupportQueueFrozenjobs(properties: IJobQueueParams): OpsupportQueueFrozenjobs { - const mock = TypeMoq.Mock.ofType(); - mock - .setup((property) => property.TaskName) - .returns(() => CreateIReadValue(properties.TaskName, CreateIEntityColumn(properties.TaskName))); - mock - .setup((property) => property.JobChainName) - .returns(() => CreateIReadValue(properties.JobChainName, CreateIEntityColumn(properties.JobChainName))); - mock.setup((property) => property.UID_Tree).returns(() => CreateIReadValue(properties.Uid_Tree)); - mock.setup((property) => property.UID_Job).returns(() => CreateIReadValue(properties.Uid_Job)); - mock.setup((property) => property.UID_JobSuccess).returns(() => CreateIReadValue(properties.Uid_JobSuccess)); - mock.setup((property) => property.UID_JobError).returns(() => CreateIReadValue(properties.Uid_JobError)); - mock - .setup((property) => property.ErrorMessages) - .returns(() => CreateIReadValue(properties.ErrorMessages ? properties.ErrorMessages : '')); - mock.setup((property) => property.Queue).returns(() => CreateIReadValue(properties.Queue)); - mock.setup((property) => property.Retries).returns(() => CreateIReadValue(properties.Retries)); - mock.setup((property) => property.Ready2EXE).returns(() => CreateIReadValue(properties.ReadyToExe)); - mock.setup((property) => property.IsRootJob).returns(() => CreateIReadValue(properties.IsRootJob)); - mock.setup((property) => property.XDateInserted).returns(() => CreateIReadValue(properties.XDateInserted)); - return mock.object; + return { + TaskName: CreateIReadValue(properties.TaskName, CreateIEntityColumn(properties.TaskName)), + JobChainName: CreateIReadValue(properties.JobChainName, CreateIEntityColumn(properties.JobChainName)), + UID_Tree: CreateIReadValue(properties.Uid_Tree), + UID_Job: CreateIReadValue(properties.Uid_Job), + UID_JobSuccess: CreateIReadValue(properties.Uid_JobSuccess), + UID_JobError: CreateIReadValue(properties.Uid_JobError), + ErrorMessages: CreateIReadValue(properties.ErrorMessages ? properties.ErrorMessages : ''), + Retries: CreateIReadValue(properties.Retries), + Queue: CreateIReadValue(properties.Queue), + Ready2EXE: CreateIReadValue(properties.ReadyToExe), + IsRootJob: CreateIReadValue(properties.IsRootJob), + XDateInserted: CreateIReadValue(properties.XDateInserted), + } as OpsupportQueueFrozenjobs; } public static CreateOpsupportSyncShellCollection(propertiesCollection: IOpsupportSyncShell[]): OpsupportSyncShell[] { - return propertiesCollection.map((properties) => { - const mock = TypeMoq.Mock.ofType(); - mock - .setup((property) => property.CountJournalFailure) - .returns(() => CreateIReadValue(properties.CountJournalFailure, CreateIEntityColumn(properties.CountJournalFailure.toString()))); - mock - .setup((property) => property.Description) - .returns(() => CreateIReadValue(properties.Description, CreateIEntityColumn(properties.Description))); - mock - .setup((property) => property.DisplayName) - .returns(() => CreateIReadValue(properties.DisplayName, CreateIEntityColumn(properties.DisplayName))); - mock - .setup((property) => property.LastSyncCountObjects) - .returns(() => CreateIReadValue(properties.LastSyncCountObjects, CreateIEntityColumn(properties.LastSyncCountObjects.toString()))); - mock - .setup((property) => property.NextSyncDate) - .returns(() => CreateIReadValue(properties.NextSyncDate, CreateIEntityColumn(properties.NextSyncDate.toLocaleString()))); - mock - .setup((property) => property.UID_DPRShell) - .returns(() => CreateIReadValue(properties.UID_DPRShell, CreateIEntityColumn(properties.UID_DPRShell))); - return mock.object; - }); + return propertiesCollection.map( + (properties) => + ({ + CountJournalFailure: CreateIReadValue( + properties.CountJournalFailure, + CreateIEntityColumn(properties.CountJournalFailure.toString()), + ), + Description: CreateIReadValue(properties.Description, CreateIEntityColumn(properties.Description)), + DisplayName: CreateIReadValue(properties.DisplayName, CreateIEntityColumn(properties.DisplayName)), + LastSyncCountObjects: CreateIReadValue( + properties.LastSyncCountObjects, + CreateIEntityColumn(properties.LastSyncCountObjects.toString()), + ), + NextSyncDate: CreateIReadValue(properties.NextSyncDate, CreateIEntityColumn(properties.NextSyncDate.toLocaleString())), + UID_DPRShell: CreateIReadValue(properties.UID_DPRShell, CreateIEntityColumn(properties.UID_DPRShell)), + }) as OpsupportSyncShell, + ); } public static CreateOpsupportQueueJobsCollection(propertiesCollection: IJobQueueParams[]): OpsupportQueueJobs[] { - return propertiesCollection.map((properties) => { - const mock = TypeMoq.Mock.ofType(); - mock.setup((property) => property.TaskName).returns(() => CreateIReadValue(properties.TaskName)); - mock.setup((property) => property.JobChainName).returns(() => CreateIReadValue(properties.JobChainName)); - mock.setup((property) => property.UID_Tree).returns(() => CreateIReadValue(properties.Uid_Tree)); - mock.setup((property) => property.UID_Job).returns(() => CreateIReadValue(properties.Uid_Job)); - mock.setup((property) => property.UID_JobSuccess).returns(() => CreateIReadValue(properties.Uid_JobSuccess)); - mock.setup((property) => property.UID_JobError).returns(() => CreateIReadValue(properties.Uid_JobError)); - mock.setup((property) => property.Queue).returns(() => CreateIReadValue(properties.Queue)); - mock.setup((property) => property.Retries).returns(() => CreateIReadValue(properties.Retries)); - mock.setup((property) => property.Ready2EXE).returns(() => CreateIReadValue(properties.ReadyToExe)); - mock.setup((property) => property.IsRootJob).returns(() => CreateIReadValue(properties.IsRootJob)); - mock.setup((property) => property.XDateInserted).returns(() => CreateIReadValue(properties.XDateInserted)); - mock.setup((property) => property.CombinedStatus).returns(() => CreateIReadValue(properties.CombinedStatus)); - return mock.object; - }); + return propertiesCollection.map( + (properties) => + ({ + TaskName: CreateIReadValue(properties.TaskName), + JobChainName: CreateIReadValue(properties.JobChainName), + UID_Tree: CreateIReadValue(properties.Uid_Tree), + UID_Job: CreateIReadValue(properties.Uid_Job), + UID_JobSuccess: CreateIReadValue(properties.Uid_JobSuccess), + UID_JobError: CreateIReadValue(properties.Uid_JobError), + Queue: CreateIReadValue(properties.Queue), + Retries: CreateIReadValue(properties.Retries), + Ready2EXE: CreateIReadValue(properties.ReadyToExe), + IsRootJob: CreateIReadValue(properties.IsRootJob), + XDateInserted: CreateIReadValue(properties.XDateInserted), + CombinedStatus: CreateIReadValue(properties.CombinedStatus), + }) as OpsupportQueueJobs, + ); } public static CreateOpsupportQueueJobperformanceCollection(propertiesCollection: IJobQueueParams[]): OpsupportQueueJobperformance[] { - return propertiesCollection.map((properties) => { - const mock = TypeMoq.Mock.ofType(); - mock.setup((property) => property.TaskName).returns(() => CreateIReadValue(properties.TaskName)); - mock.setup((property) => property.Queue).returns(() => CreateIReadValue(properties.Queue)); - mock.setup((property) => property.ComponentClass).returns(() => CreateIReadValue('componentClassDummy')); - mock.setup((property) => property.CountPerMinute).returns(() => CreateIReadValue(23)); - return mock.object; - }); + return propertiesCollection.map( + (properties) => + ({ + TaskName: CreateIReadValue(properties.TaskName), + Queue: CreateIReadValue(properties.Queue), + ComponentClass: CreateIReadValue('componentClassDummy'), + CountPerMinute: CreateIReadValue(23), + }) as OpsupportQueueJobperformance, + ); } } @@ -300,8 +257,8 @@ export class ImxApiDataMock extends BaseImxApiDataMock { TaskName: `TaskName${i}`, JobChainName: `JobChainName${i}`, }), - numOfEntries - ) + numOfEntries, + ), ); } @@ -322,7 +279,7 @@ export class ImxApiDataMock extends BaseImxApiDataMock { }, }, }), - numOfEntries + numOfEntries, ); } @@ -346,51 +303,38 @@ export class ImxApiDataMock extends BaseImxApiDataMock { }, }, }), - numOfEntries + numOfEntries, ); } } export class DummyJobData { static getItem(params: IJobQueueParams): OpsupportQueueJobs { - const dummyTaskName = TypeMoq.Mock.ofType>(); - const dummyJobChainName = TypeMoq.Mock.ofType>(); - const dummyUidTree = TypeMoq.Mock.ofType>(); - const dummyUidJob = TypeMoq.Mock.ofType>(); - const dummyUidJobSuccess = TypeMoq.Mock.ofType>(); - const dummyUidJobError = TypeMoq.Mock.ofType>(); - const dummyQueue = TypeMoq.Mock.ofType>(); - const dummyRetries = TypeMoq.Mock.ofType>(); - const dummyIsRootJob = TypeMoq.Mock.ofType>(); - const dummyXDataInserted = TypeMoq.Mock.ofType>(); - const dummyReadyToExe = TypeMoq.Mock.ofType>(); + const dummyTaskName = { value: params.TaskName ? params.TaskName : '' }; + const dummyJobChainName = { value: params.JobChainName ? params.JobChainName : '' }; + const dummyUidTree = { value: params.Uid_Tree }; + const dummyUidJob = { value: params.Uid_Job }; + const dummyUidJobSuccess = { value: params.Uid_JobSuccess ? params.Uid_JobSuccess : '' }; + const dummyUidJobError = { value: params.Uid_JobError ? params.Uid_JobError : '' }; + const dummyQueue = { value: params.Queue ? params.Queue : '' }; + const dummyRetries = { value: params.Retries ? params.Retries : 0 }; + const dummyIsRootJob = { value: params.IsRootJob ? params.IsRootJob : false }; + const dummyXDataInserted = { value: params.XDateInserted ? params.XDateInserted : new Date(2001, 1, 1) }; + const dummyReadyToExe = { value: params.ReadyToExe ? params.ReadyToExe : 'TRUE' }; - dummyTaskName.setup((el) => el.value).returns(() => (params.TaskName ? params.TaskName : '')); - dummyJobChainName.setup((el) => el.value).returns(() => (params.JobChainName ? params.JobChainName : '')); - dummyUidTree.setup((el) => el.value).returns(() => params.Uid_Tree); - dummyUidJob.setup((el) => el.value).returns(() => params.Uid_Job); - dummyUidJobSuccess.setup((el) => el.value).returns(() => (params.Uid_JobSuccess ? params.Uid_JobSuccess : '')); - dummyUidJobError.setup((el) => el.value).returns(() => (params.Uid_JobError ? params.Uid_JobError : '')); - dummyQueue.setup((el) => el.value).returns(() => (params.Queue ? params.Queue : '')); - dummyRetries.setup((el) => el.value).returns(() => (params.Retries ? params.Retries : 0)); - dummyIsRootJob.setup((el) => el.value).returns(() => (params.IsRootJob ? params.IsRootJob : false)); - dummyXDataInserted.setup((el) => el.value).returns(() => (params.XDateInserted ? params.XDateInserted : new Date(2001, 1, 1))); - dummyReadyToExe.setup((el) => el.value).returns(() => (params.ReadyToExe ? params.ReadyToExe : 'TRUE')); - - const dummy = TypeMoq.Mock.ofType(); - dummy.setup((obj) => obj.TaskName).returns(() => dummyTaskName.object); - dummy.setup((obj) => obj.JobChainName).returns(() => dummyJobChainName.object); - dummy.setup((obj) => obj.UID_Tree).returns(() => dummyUidTree.object); - dummy.setup((obj) => obj.UID_Job).returns(() => dummyUidJob.object); - dummy.setup((obj) => obj.UID_JobSuccess).returns(() => dummyUidJobSuccess.object); - dummy.setup((obj) => obj.UID_JobError).returns(() => dummyUidJobError.object); - dummy.setup((obj) => obj.Queue).returns(() => dummyQueue.object); - dummy.setup((obj) => obj.Retries).returns(() => dummyRetries.object); - dummy.setup((obj) => obj.IsRootJob).returns(() => dummyIsRootJob.object); - dummy.setup((obj) => obj.XDateInserted).returns(() => dummyXDataInserted.object); - dummy.setup((obj) => obj.Ready2EXE).returns(() => dummyReadyToExe.object); - dummy.setup((obj) => obj.CombinedStatus).returns(() => CreateIReadValue(params.CombinedStatus)); - - return dummy.object; + return { + TaskName: dummyTaskName, + JobChainName: dummyJobChainName, + UID_Tree: dummyUidTree, + UID_Job: dummyUidJob, + UID_JobSuccess: dummyUidJobSuccess, + UID_JobError: dummyUidJobError, + Queue: dummyQueue, + Retries: dummyRetries, + IsRootJob: dummyIsRootJob, + XDateInserted: dummyXDataInserted, + Ready2EXE: dummyReadyToExe, + CombinedStatus: CreateIReadValue(params.CombinedStatus), + } as OpsupportQueueJobs; } } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/test-utilities/imx-session.service.spy.spec.ts b/imxweb/projects/qer-app-operationssupport/src/app/test-utilities/imx-session.service.spy.spec.ts index 3e71c9e1e..e1b2da7d2 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/test-utilities/imx-session.service.spy.spec.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/test-utilities/imx-session.service.spy.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,8 +24,8 @@ * */ -import { Client } from 'imx-api-qbm'; -import { DbObjectKey } from 'imx-qbm-dbts'; +import { Client } from '@imx-modules/imx-api-qbm'; +import { DbObjectKey } from '@imx-modules/imx-qbm-dbts'; import { ISessionState, DbObjectInfo } from 'qbm'; import { ImxApiDataMock, ImxApiDtoMock } from '../test-utilities/imx-api-mock.spec'; @@ -49,19 +49,19 @@ export class SessionServiceSpy { private dummySearchGetResponse: Promise = Promise.resolve([ { Key: new DbObjectKey('testtable', 'aKey'), Display: 'theDisplay' }, { Key: new DbObjectKey('testtable', 'anotherKey'), Display: 'anotherDisplay' }, - { Key: new DbObjectKey('testtable', 'aThirdKey'), Display: 'aThirdDisplay' } + { Key: new DbObjectKey('testtable', 'aThirdKey'), Display: 'aThirdDisplay' }, ]); private dummyEntityCollectionData = Promise.resolve({ Display: 'dummyDisplay', LongDisplay: 'dummyLongDisplay', Keys: ['', '', '', '', '', '', '', '', '', ''], - Columns: {} + Columns: {}, }); private historyEvents = [ { ChangeTime: new Date(2019, 1, 1), IsRemoveEvent: true }, - { ChangeTime: new Date(2019, 1, 2), IsRemoveEvent: true } + { ChangeTime: new Date(2019, 1, 2), IsRemoveEvent: true }, ]; public dummyTranslationDict: { @@ -79,11 +79,11 @@ export class SessionServiceSpy { public dummyProviderUrl = 'dummyProviderUrl'; public dummyCaptions = { text1_key: 'text1_value', - text2_key: 'text2_value' + text2_key: 'text2_value', } as { [key: string]: string }; public dummyTypedEntityCollection = { - totalCount: 10 + totalCount: 10, } as TypedEntityCollectionMock; public dbQueue = ImxApiDataMock.CreateDbQueue(); @@ -117,14 +117,14 @@ export class SessionServiceSpy { call: false, push: {}, sms: false, - verify: {} + verify: {}, }, systemStatusInformation: { IsDbSchedulerDisabled: true, IsJobServiceDisabled: false, IsCompilationRequired: true, - IsInMaintenanceMode: true - } + IsInMaintenanceMode: true, + }, }; return jasmine.createSpyObj('Client', { imx_metadata_table_get: Promise.resolve({ Display: this.dummyMetaTableDisplay }), @@ -138,7 +138,7 @@ export class SessionServiceSpy { opsupport_queue_object_get: Promise.resolve({ DbQueue: ImxApiDtoMock.CreateEntityDataCollection(this.dbQueue), JobQueue: ImxApiDtoMock.CreateEntityDataCollection(this.jobQueue), - Unsupported: false + Unsupported: false, }), opsupport_queue_reactivatejob_post: this.dummyEntityCollectionData, opsupport_search_get: this.dummySearchGetResponse, @@ -148,30 +148,32 @@ export class SessionServiceSpy { opsupport_starling_verify_get: Promise.resolve(config.starlingApi.verify), opsupport_sync_summary_get: Promise.resolve(), opsupport_systemstatus_get: Promise.resolve(config.systemStatusInformation), - opsupport_systemstatus_post: Promise.resolve(config.systemStatusInformation) + opsupport_systemstatus_post: Promise.resolve(config.systemStatusInformation), }); } private getSessionStateSpy(): jasmine.Spy { - return jasmine.createSpy('getSessionState').and.returnValue(Promise.resolve({ - IsLoggedOut: true, - IsLoggedIn: this.currentState.isLoggedIn, - Config: this.sessionResponseConfig, - SecondaryAuthName: this.dummySecondaryAuthName, - UserUid: this.dummyUserUid, - Status: { - PrimaryAuth: { - IsAuthenticated: this.currentState.isLoggedIn - } - } - })); + return jasmine.createSpy('getSessionState').and.returnValue( + Promise.resolve({ + IsLoggedOut: true, + IsLoggedIn: this.currentState.isLoggedIn, + Config: this.sessionResponseConfig, + SecondaryAuthName: this.dummySecondaryAuthName, + UserUid: this.dummyUserUid, + Status: { + PrimaryAuth: { + IsAuthenticated: this.currentState.isLoggedIn, + }, + }, + }), + ); } private getLogoutSpy(): jasmine.Spy { return jasmine.createSpy('logout').and.returnValue( Promise.resolve({ - IsLoggedOut: true - }) + IsLoggedOut: true, + }), ); } @@ -180,8 +182,8 @@ export class SessionServiceSpy { Promise.resolve({ IsLoggedIn: true, Config: this.sessionResponseConfig, - Username: this.dummyUserName - } as ISessionState) + Username: this.dummyUserName, + } as ISessionState), ); } } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/test-utilities/imx-translation-provider.service.spy.spec.ts b/imxweb/projects/qer-app-operationssupport/src/app/test-utilities/imx-translation-provider.service.spy.spec.ts index 680945f38..81b270eee 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/test-utilities/imx-translation-provider.service.spy.spec.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/test-utilities/imx-translation-provider.service.spy.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,8 +29,8 @@ import { of } from 'rxjs'; import { TextContainer } from 'qbm'; export class TranslationProviderServiceSpy { - public multilanguageTranslationDict = {}; - public Translate = jasmine.createSpy('Translate').and.callFake((text: TextContainer) => of(text.key + '_übersetzt')); - public GetColumnDisplay = jasmine.createSpy('GetColumnDisplay').and.callThrough(); - public currentLang = 'en-us'; + public multilanguageTranslationDict = {}; + public Translate = jasmine.createSpy('Translate').and.callFake((text: TextContainer) => of(text.key + '_übersetzt')); + public GetColumnDisplay = jasmine.createSpy('GetColumnDisplay').and.callThrough(); + public currentLang = 'en-us'; } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/test-utilities/router-mock.spec.ts b/imxweb/projects/qer-app-operationssupport/src/app/test-utilities/router-mock.spec.ts index 104d17ca8..c7addec45 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/test-utilities/router-mock.spec.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/test-utilities/router-mock.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -35,14 +35,16 @@ export class RoutingMock { return { snapshot: { paramMap: { - get: spyResult != null ? jasmine.createSpy('get').and - .returnValue(spyResult) : jasmine.createSpy('get').and.callFake((key: string) => key) + get: + spyResult != null + ? jasmine.createSpy('get').and.returnValue(spyResult) + : jasmine.createSpy('get').and.callFake((key: string) => key), }, queryParamMap: { - get: jasmine.createSpy('get').and.returnValue(queryParamsMap) - } + get: jasmine.createSpy('get').and.returnValue(queryParamsMap), + }, }, - queryParams + queryParams, }; } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/test-utilities/test-helper.module.spec.ts b/imxweb/projects/qer-app-operationssupport/src/app/test-utilities/test-helper.module.spec.ts index e2c2f4a9f..8cdf80e00 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/test-utilities/test-helper.module.spec.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/test-utilities/test-helper.module.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,9 +28,9 @@ import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core'; import { TranslateModule, TranslateLoader, TranslateParser, TranslateFakeLoader, TranslateDefaultParser } from '@ngx-translate/core'; @NgModule({ - exports: [TranslateModule], - imports: [TranslateModule.forRoot({ loader: { provide: TranslateLoader, useClass: TranslateFakeLoader } })], - providers: [{ provide: TranslateParser, useClass: TranslateDefaultParser }], - schemas: [NO_ERRORS_SCHEMA] + exports: [TranslateModule], + imports: [TranslateModule.forRoot({ loader: { provide: TranslateLoader, useClass: TranslateFakeLoader } })], + providers: [{ provide: TranslateParser, useClass: TranslateDefaultParser }], + schemas: [NO_ERRORS_SCHEMA], }) -export class TestHelperModule { } +export class TestHelperModule {} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/test-utilities/typed-entity-provider.spec.ts b/imxweb/projects/qer-app-operationssupport/src/app/test-utilities/typed-entity-provider.spec.ts index 0443620a0..22953e5de 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/test-utilities/typed-entity-provider.spec.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/test-utilities/typed-entity-provider.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,7 +26,7 @@ import { TestBed } from '@angular/core/testing'; -import { ValType, EntitySchema, IClientProperty } from 'imx-qbm-dbts'; +import { ValType, EntitySchema, IClientProperty } from '@imx-modules/imx-qbm-dbts'; import { imx_SessionService } from 'qbm'; export interface ClientPropertyMock { @@ -39,11 +39,11 @@ export interface ClientPropertyMock { export function CreateEntitySchema(properties: ClientPropertyMock[]): EntitySchema { const columns: { [id: string]: IClientProperty } = {}; properties.forEach( - property => + (property) => (columns[property.name] = { Type: property.type ? property.type : ValType.String, Display: property.Display, - }) + }), ); return { Columns: columns }; } @@ -56,16 +56,20 @@ export interface TypedEntityReadOnlyProviderTestConfig { typedClient?: any; } -export function testTypedEntityReadOnlyProvider(testconfig: TypedEntityReadOnlyProviderTestConfig): void { - beforeEach(() => TestBed.configureTestingModule({ - providers: [ - testconfig.type, - { - provide: imx_SessionService, - useValue: { TypedClient: testconfig.typedClient } - } - ] - })); +export function testTypedEntityReadOnlyProvider( + testconfig: TypedEntityReadOnlyProviderTestConfig, +): void { + beforeEach(() => + TestBed.configureTestingModule({ + providers: [ + testconfig.type, + { + provide: imx_SessionService, + useValue: { TypedClient: testconfig.typedClient }, + }, + ], + }), + ); it('should be created', () => { expect(TestBed.get(testconfig.type)).toBeTruthy(); diff --git a/imxweb/projects/qer-app-operationssupport/src/app/unresolved-refs/unresolved-refs.component.html b/imxweb/projects/qer-app-operationssupport/src/app/unresolved-refs/unresolved-refs.component.html index c4c942e6a..ea61acf16 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/unresolved-refs/unresolved-refs.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/unresolved-refs/unresolved-refs.component.html @@ -1,34 +1,34 @@
    -

    +

    {{ '#LDS#Heading Unresolved References' | translate }} -

    +
    - - + +
    {{ data?.DisplayName?.Column.GetDisplayValue() }}
    - +
    {{ data?.OwnerObject?.Column.GetDisplayValue() }}
    - +
    {{ data?.ShellDisplay?.Column.GetDisplayValue() }}
    - +
    {{ data?.SystemDisplay?.Column.GetDisplayValue() }}
    - +
    {{ data?.Data?.Column.GetDisplayValue() }}
    @@ -37,7 +37,7 @@

    - diff --git a/imxweb/projects/qer-app-operationssupport/src/app/unresolved-refs/unresolved-refs.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/unresolved-refs/unresolved-refs.component.scss index 044b6f74b..a84ea5995 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/unresolved-refs/unresolved-refs.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/unresolved-refs/unresolved-refs.component.scss @@ -5,23 +5,7 @@ flex-grow: 1; } -.imx-table-container { - flex: 1 1 auto; - display: flex; - flex-direction: column; - overflow: auto; - - > :nth-child(2) { - flex: 1 1 auto; - } -} - .imx-unresolved-refs-data { max-height: 100px; overflow: auto; } - -.imx-refresh-button { - margin-right: 5px; - height: 50px; -} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/unresolved-refs/unresolved-refs.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/unresolved-refs/unresolved-refs.component.ts index 192aa9ac1..f3a034771 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/unresolved-refs/unresolved-refs.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/unresolved-refs/unresolved-refs.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,16 +29,15 @@ import { OverlayRef } from '@angular/cdk/overlay'; import { EuiLoadingService } from '@elemental-ui/core'; import { DataSourceToolbarSettings } from 'qbm'; -import { DisplayColumns, EntitySchema, IClientProperty } from 'imx-qbm-dbts'; +import { DisplayColumns, EntitySchema, IClientProperty } from '@imx-modules/imx-qbm-dbts'; import { UnresolvedRefsService } from './unresolved-refs.service'; @Component({ selector: 'imx-unresolved-refs-gridview', templateUrl: './unresolved-refs.component.html', - styleUrls: ['./unresolved-refs.component.scss'] + styleUrls: ['./unresolved-refs.component.scss'], }) export class UnresolvedRefsComponent implements OnInit { - public dstSettings: DataSourceToolbarSettings; public readonly entitySchemaRefs: EntitySchema; public readonly DisplayColumns = DisplayColumns; @@ -54,7 +53,7 @@ export class UnresolvedRefsComponent implements OnInit { this.entitySchemaRefs.Columns.OwnerObject, this.entitySchemaRefs.Columns.ShellDisplay, this.entitySchemaRefs.Columns.SystemDisplay, - this.entitySchemaRefs.Columns.Data + this.entitySchemaRefs.Columns.Data, ]; } @@ -67,19 +66,16 @@ export class UnresolvedRefsComponent implements OnInit { } public async getData(): Promise { - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + setTimeout(() => (overlayRef = this.busyService.show())); try { - const journalEntries = await this.unresolvedRefsService.get(); this.dstSettings = { displayedColumns: this.displayedColumns, dataSource: journalEntries, entitySchema: this.entitySchemaRefs, - navigationState: {} + navigationState: {}, }; - } finally { setTimeout(() => this.busyService.hide(overlayRef)); } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/unresolved-refs/unresolved-refs.module.ts b/imxweb/projects/qer-app-operationssupport/src/app/unresolved-refs/unresolved-refs.module.ts index da39edec0..7836073b0 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/unresolved-refs/unresolved-refs.module.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/unresolved-refs/unresolved-refs.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -35,20 +35,9 @@ import { DataSourceToolbarModule, DataTableModule } from 'qbm'; import { UnresolvedRefsComponent } from './unresolved-refs.component'; import { UnresolvedRefsService } from './unresolved-refs.service'; - - - @NgModule({ declarations: [UnresolvedRefsComponent], - imports: [ - CommonModule, - TranslateModule, - DataSourceToolbarModule, - DataTableModule, - EuiCoreModule, - MatTooltipModule, - MatButtonModule - ], - providers: [UnresolvedRefsService] + imports: [CommonModule, TranslateModule, DataSourceToolbarModule, DataTableModule, EuiCoreModule, MatTooltipModule, MatButtonModule], + providers: [UnresolvedRefsService], }) -export class UnresolvedRefsModule { } +export class UnresolvedRefsModule {} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/unresolved-refs/unresolved-refs.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/unresolved-refs/unresolved-refs.service.ts index 77e77b4e8..9e0a931cd 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/unresolved-refs/unresolved-refs.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/unresolved-refs/unresolved-refs.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,12 +26,12 @@ import { Injectable } from '@angular/core'; -import { OpsupportSyncDatastore } from 'imx-api-dpr'; -import { EntitySchema, TypedEntityCollectionData } from 'imx-qbm-dbts'; +import { OpsupportSyncDatastore } from '@imx-modules/imx-api-dpr'; +import { EntitySchema, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { DprApiService } from '../../../dpr-api-client.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class UnresolvedRefsService { public schema: EntitySchema; diff --git a/imxweb/projects/qer-app-operationssupport/src/app/web-applications/web-applications.component.html b/imxweb/projects/qer-app-operationssupport/src/app/web-applications/web-applications.component.html index e15b2398b..d1a7848df 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/web-applications/web-applications.component.html +++ b/imxweb/projects/qer-app-operationssupport/src/app/web-applications/web-applications.component.html @@ -1,35 +1,41 @@
    -

    - {{'#LDS#Heading Web Applications' | translate}} -

    +

    + {{ '#LDS#Heading Web Applications' | translate }} +

    - + - - + + {{ data.BaseURL.Column.GetDisplayValue() }} - + {{ data.UID_DialogProduct.Column.GetDisplayValue() }} - + {{ data.IsDebug.Column.GetDisplayValue() }} - + {{ data.IsPrivate.Column.GetDisplayValue() }} - + {{ data.AutoUpdateLevel.Column.GetDisplayValue() }} @@ -39,7 +45,7 @@

    - diff --git a/imxweb/projects/qer-app-operationssupport/src/app/web-applications/web-applications.component.scss b/imxweb/projects/qer-app-operationssupport/src/app/web-applications/web-applications.component.scss index 09dc25eb1..f1c455826 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/web-applications/web-applications.component.scss +++ b/imxweb/projects/qer-app-operationssupport/src/app/web-applications/web-applications.component.scss @@ -5,27 +5,11 @@ flex-grow: 1; } - .mat-column-IsDebug, - .mat-column-IsPrivate { - max-width: 240px; - } - - .mat-column-BaseURL { - min-width: 440px; - } - -.imx-table-container { - flex: 1 1 auto; - display: flex; - flex-direction: column; - max-height: calc(100% - 45px); - - > :nth-child(2) { - flex: 1 1 auto; - } +.mat-column-IsDebug, +.mat-column-IsPrivate { + max-width: 240px; } -.imx-refresh-button { - margin-right: 5px; - height: 50px; +.mat-column-BaseURL { + min-width: 440px; } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/web-applications/web-applications.component.ts b/imxweb/projects/qer-app-operationssupport/src/app/web-applications/web-applications.component.ts index d6016d6a6..bbea2f66e 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/web-applications/web-applications.component.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/web-applications/web-applications.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,7 +27,7 @@ import { OverlayRef } from '@angular/cdk/overlay'; import { Component, OnInit } from '@angular/core'; import { EuiLoadingService } from '@elemental-ui/core'; -import { CollectionLoadParameters, EntitySchema, IClientProperty } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, EntitySchema, IClientProperty } from '@imx-modules/imx-qbm-dbts'; import { DataSourceToolbarSettings, SettingsService } from 'qbm'; import { WebApplicationsService } from './web-applications.service'; @@ -37,17 +37,15 @@ import { WebApplicationsService } from './web-applications.service'; styleUrls: ['./web-applications.component.scss'], }) export class WebApplicationsComponent implements OnInit { - public dstSettings: DataSourceToolbarSettings; public readonly entitySchemaWebApplications: EntitySchema; private navigationState: CollectionLoadParameters; private readonly displayedColumns: IClientProperty[]; - constructor( private webApplicationsService: WebApplicationsService, private busyService: EuiLoadingService, - private readonly settings: SettingsService + private readonly settings: SettingsService, ) { this.entitySchemaWebApplications = webApplicationsService.schema; this.displayedColumns = [ @@ -75,17 +73,15 @@ export class WebApplicationsComponent implements OnInit { this.navigationState = navigationState; let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + setTimeout(() => (overlayRef = this.busyService.show())); try { - const applications = await this.webApplicationsService.get(navigationState); this.dstSettings = { displayedColumns: this.displayedColumns, dataSource: applications, entitySchema: this.entitySchemaWebApplications, - navigationState: this.navigationState + navigationState: this.navigationState, }; - } finally { setTimeout(() => this.busyService.hide(overlayRef)); } diff --git a/imxweb/projects/qer-app-operationssupport/src/app/web-applications/web-applications.module.ts b/imxweb/projects/qer-app-operationssupport/src/app/web-applications/web-applications.module.ts index 1def5cc6c..c69a02dbe 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/web-applications/web-applications.module.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/web-applications/web-applications.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -35,21 +35,9 @@ import { DataSourceToolbarModule, DataTableModule } from 'qbm'; import { WebApplicationsComponent } from './web-applications.component'; import { WebApplicationsService } from './web-applications.service'; - - @NgModule({ - declarations: [ - WebApplicationsComponent - ], - imports: [ - CommonModule, - DataTableModule, - DataSourceToolbarModule, - EuiCoreModule, - MatTooltipModule, - MatButtonModule, - TranslateModule - ], - providers: [WebApplicationsService] + declarations: [WebApplicationsComponent], + imports: [CommonModule, DataTableModule, DataSourceToolbarModule, EuiCoreModule, MatTooltipModule, MatButtonModule, TranslateModule], + providers: [WebApplicationsService], }) -export class WebApplicationsModule { } +export class WebApplicationsModule {} diff --git a/imxweb/projects/qer-app-operationssupport/src/app/web-applications/web-applications.service.ts b/imxweb/projects/qer-app-operationssupport/src/app/web-applications/web-applications.service.ts index 2771526c8..f913c48ea 100644 --- a/imxweb/projects/qer-app-operationssupport/src/app/web-applications/web-applications.service.ts +++ b/imxweb/projects/qer-app-operationssupport/src/app/web-applications/web-applications.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,8 +27,8 @@ import { Injectable } from '@angular/core'; import { imx_SessionService } from 'qbm'; -import { OpsupportWebapplications } from 'imx-api-qbm'; -import { CollectionLoadParameters, EntitySchema, ExtendedTypedEntityCollection } from 'imx-qbm-dbts'; +import { OpsupportWebapplications } from '@imx-modules/imx-api-qbm'; +import { CollectionLoadParameters, EntitySchema, ExtendedTypedEntityCollection } from '@imx-modules/imx-qbm-dbts'; @Injectable() export class WebApplicationsService { @@ -36,7 +36,7 @@ export class WebApplicationsService { return this.session.TypedClient.OpsupportWebapplications.GetSchema(); } - constructor(private session: imx_SessionService) { } + constructor(private session: imx_SessionService) {} public async get(parameter: CollectionLoadParameters): Promise> { return this.session.TypedClient.OpsupportWebapplications.Get(parameter); diff --git a/imxweb/projects/qer-app-operationssupport/src/environments/environment.prod.ts b/imxweb/projects/qer-app-operationssupport/src/environments/environment.prod.ts index 7fe5e91b2..2ac427ed2 100644 --- a/imxweb/projects/qer-app-operationssupport/src/environments/environment.prod.ts +++ b/imxweb/projects/qer-app-operationssupport/src/environments/environment.prod.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,5 +30,5 @@ export const environment = { production: true, clientUrl: '', appName: packageJson.name, - appVersion: packageJson.version + appVersion: packageJson.version, }; diff --git a/imxweb/projects/qer-app-operationssupport/src/environments/environment.ts b/imxweb/projects/qer-app-operationssupport/src/environments/environment.ts index de9c0390d..1321fa2e3 100644 --- a/imxweb/projects/qer-app-operationssupport/src/environments/environment.ts +++ b/imxweb/projects/qer-app-operationssupport/src/environments/environment.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -35,5 +35,5 @@ export const environment = { production: false, clientUrl: 'http://localhost:8182', appName: packageJson.name, - appVersion: packageJson.version + appVersion: packageJson.version, }; diff --git a/imxweb/projects/qer-app-operationssupport/src/index.html b/imxweb/projects/qer-app-operationssupport/src/index.html index 4b785ba4e..f08db67fd 100644 --- a/imxweb/projects/qer-app-operationssupport/src/index.html +++ b/imxweb/projects/qer-app-operationssupport/src/index.html @@ -1,15 +1,13 @@ + + + + + + - - - - - - - - - - - + + + diff --git a/imxweb/projects/qer-app-operationssupport/src/main.ts b/imxweb/projects/qer-app-operationssupport/src/main.ts index 68f7fed01..aa265c318 100644 --- a/imxweb/projects/qer-app-operationssupport/src/main.ts +++ b/imxweb/projects/qer-app-operationssupport/src/main.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -36,4 +36,4 @@ if (environment.production) { platformBrowserDynamic() .bootstrapModule(AppModule) - .catch(err => console.log(err)); + .catch((err) => console.log(err)); diff --git a/imxweb/projects/qer-app-operationssupport/src/polyfills.ts b/imxweb/projects/qer-app-operationssupport/src/polyfills.ts index 48573d378..027c82242 100644 --- a/imxweb/projects/qer-app-operationssupport/src/polyfills.ts +++ b/imxweb/projects/qer-app-operationssupport/src/polyfills.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -41,9 +41,8 @@ */ /*************************************************************************************************** -* BROWSER POLYFILLS -*/ - + * BROWSER POLYFILLS + */ /** IE10 and IE11 requires the following for NgClass support on SVG elements */ // import 'classlist.js'; // Run `npm install --save classlist.js`. @@ -51,31 +50,25 @@ /** IE10 and IE11 requires the following for the Reflect API. */ // import 'core-js/es6/reflect'; - /** Evergreen browsers require these. */ // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. -import 'core-js/es7/reflect'; - +import 'core-js/es/reflect'; // Used for support array.includes -import 'core-js/es7/array'; +import 'core-js/es/array'; /** * Required to support Web Animations `@angular/platform-browser/animations`. * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation */ import 'web-animations-js'; - - /*************************************************************************************************** * Zone JS is required by default for Angular itself. */ -import 'zone.js/dist/zone'; // Included with Angular CLI. - - +import 'zone.js'; // Included with Angular CLI. /*************************************************************************************************** * APPLICATION IMPORTS */ -import 'whatwg-fetch'; import 'url-polyfill'; +import 'whatwg-fetch'; diff --git a/imxweb/projects/qer-app-operationssupport/src/styles.scss b/imxweb/projects/qer-app-operationssupport/src/styles.scss index ba6c0d14a..4df681e05 100644 --- a/imxweb/projects/qer-app-operationssupport/src/styles.scss +++ b/imxweb/projects/qer-app-operationssupport/src/styles.scss @@ -1,13 +1,22 @@ /* You can add global styles to this file, and also import other style files */ -@use '@angular/material' as mat; - -$material_icons_font_path: "~node_modules/@elemental-ui/core/assets/MaterialIcons"; -$cadence_font_path: "~node_modules/@elemental-ui/core/assets/Cadence"; -$source_sans_pro_font_path: "~node_modules/@elemental-ui/core/assets/Source_Sans_Pro"; - @import '@elemental-ui/core/src/styles/core.scss'; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/variables'; +@import 'base/margins-paddings'; + +@import 'components/paginator'; +@import 'components/progress-bar'; +@import 'components/spinner'; +@import 'components/ripple'; +@import 'components/table'; +@import 'components/tabs'; +@import 'components/toolbar'; +@import 'components/tree'; -@import "variables"; +/* overall used styles */ +.imx-hidden { + display: none; +} /* #region Grid styles*/ @@ -16,67 +25,13 @@ $source_sans_pro_font_path: "~node_modules/@elemental-ui/core/assets/Source_Sans font-weight: bold; } -.mat-row.imx_MatTableRow { - min-height: 60px; -} - -.mat-header-row.imx_MatTableHeaderRow { - margin-top: 3px; - margin-bottom: 0; - padding-bottom: 12px; - padding-top: 12px; - min-height: 0; -} - -.imx-ListViewItem, -.mat-cell.imx_ColumnCell { +.imx-ListViewItem { font-size: 15px; text-overflow: ellipsis; - color: $VI_Common_Color_Gray; + color: $color-gray-60; margin: 0 10px; } -.imx_ColumnHeader { - font-size: 13px; - color: $VI_Common_Color_Gray; - margin: -8px 10px; - padding: 10px 0; - line-height: 24px; - display: block; - overflow: hidden; - text-overflow: ellipsis; -} - -.mat-header-cell.imx_CheckableCell { - flex: 0 0 45px; -} - -.mat-cell.imx_CheckableCell { - flex: 0 0 45px; -} - -.mat-sort-header-button:disabled { - background-color: Transparent !important; - color: $VI_Common_Color_Gray !important; - cursor: default; -} - -.mat-table .mat-checkbox-checked.mat-accent .mat-checkbox-background, -.mat-table .mat-checkbox-indeterminate.mat-accent .mat-checkbox-background { - background-color: $color-orange-60; -} - -.mat-ripple-element { - background: transparent !important; -} - -.mat-paginator-navigation-next.mat-icon-button, -.mat-paginator-navigation-previous.mat-icon-button { - background: transparent !important; - width: 16px !important; - margin-right: 10px !important; -} - .imx-ListViewItem { margin-top: 10px; margin-bottom: 10px; @@ -86,41 +41,20 @@ $source_sans_pro_font_path: "~node_modules/@elemental-ui/core/assets/Source_Sans /* #region MatCard styles */ -.mat-card { - - .mat-card-title { +.mat-mdc-card { + .mat-mdc-card-title { font-weight: 600; - font-size: 24px; - color: $gunmetal; - text-overflow: ellipsis; white-space: nowrap; - overflow: hidden; } - - mat-card-subtitle { - margin-top: 10px; - height: 50px; - font-weight: 400 !important; - font-size: 14px; - color: $black-6; - } - mat-card-actions { - text-transform: uppercase; - font-size: 14px; - letter-spacing: .5px; - color: $iris-blue; margin: 0 !important; - .mat-button { - text-align: center; - font-weight: 600 !important; + .mat-mdc-button { text-transform: uppercase; } } } - /* #endregion */ /* #region OpsWeb styles */ @@ -150,7 +84,7 @@ $source_sans_pro_font_path: "~node_modules/@elemental-ui/core/assets/Source_Sans margin-right: 2em; width: 500px; height: 400px; - border: solid 1px $VI_Common_Color_LightGray; + border: solid 1px $color-gray-30; margin-bottom: 1em; vertical-align: top; display: inline-flex; @@ -165,7 +99,7 @@ $source_sans_pro_font_path: "~node_modules/@elemental-ui/core/assets/Source_Sans width: auto; z-index: 50; height: auto; - border: solid 1px $VI_Common_Color_LightGray; + border: solid 1px $color-gray-30; display: block; position: fixed; flex-direction: none; @@ -177,7 +111,7 @@ $source_sans_pro_font_path: "~node_modules/@elemental-ui/core/assets/Source_Sans margin-right: 2em; width: 700px; height: 400px; - border: solid 1px $VI_Common_Color_LightGray; + border: solid 1px $color-gray-30; margin-bottom: 1em; vertical-align: top; display: inline-flex; @@ -188,7 +122,7 @@ $source_sans_pro_font_path: "~node_modules/@elemental-ui/core/assets/Source_Sans margin-right: 2em; width: 700px; height: 600px; - border: solid 1px $VI_Common_Color_LightGray; + border: solid 1px $color-gray-30; margin-bottom: 1em; vertical-align: top; } @@ -197,7 +131,7 @@ $source_sans_pro_font_path: "~node_modules/@elemental-ui/core/assets/Source_Sans font-size: 2em; background-color: #efefef; margin: 0; - padding: .5rem; + padding: 0.5rem; color: $black; } @@ -205,7 +139,7 @@ $source_sans_pro_font_path: "~node_modules/@elemental-ui/core/assets/Source_Sans font-size: 2em; background-color: $white; margin: 0; - padding: .5rem; + padding: 0.5rem; color: $iris-blue; } @@ -213,16 +147,16 @@ $source_sans_pro_font_path: "~node_modules/@elemental-ui/core/assets/Source_Sans font-size: 2em; background-color: $white; margin: 0; - padding: .5rem; + padding: 0.5rem; color: $iris-blue; } .imx-ops-tile:hover { - border-color: $VI_Common_Color_Gray; + border-color: $color-gray-60; } .imx-ops-tile-inner { - padding: 0 .5rem; + padding: 0 0.5rem; flex: 1 1 auto; overflow: auto; } @@ -232,7 +166,7 @@ $source_sans_pro_font_path: "~node_modules/@elemental-ui/core/assets/Source_Sans } .imx-ops-tile-innerBottom { - padding: 0 .5rem; + padding: 0 0.5rem; flex-direction: row; } @@ -255,7 +189,7 @@ $source_sans_pro_font_path: "~node_modules/@elemental-ui/core/assets/Source_Sans max-height: 400px; } -@media screen and (max-height:650px) { +@media screen and (max-height: 650px) { .contentBounds { max-height: 200px; } @@ -284,7 +218,7 @@ $source_sans_pro_font_path: "~node_modules/@elemental-ui/core/assets/Source_Sans font-weight: normal; font-style: normal; font-stretch: normal; - line-height: .83; + line-height: 0.83; letter-spacing: normal; } @@ -298,33 +232,28 @@ $source_sans_pro_font_path: "~node_modules/@elemental-ui/core/assets/Source_Sans padding-right: 5px; } - - -imx-service-availability .mat-card { +imx-service-availability .mat-mdc-card { width: 411px; margin-bottom: 56px; - .mat-card-content { + .mat-mdc-card-content { display: flex; padding: 24px; button { flex: 0 0 auto; align-self: center; - font-size: 15px; margin-right: 26px; } span:first-child { text-transform: uppercase; color: $black-7; - font-size: 14px; margin-bottom: 5px; } span { display: block; - font-size: 20px; } } } @@ -332,7 +261,7 @@ imx-service-availability .mat-card { /*#endregion*/ /* #region SystemOverview styles */ -$tresholdExceededColor: $corbin-orange; +$thresholdExceededColor: $color-orange-60; imx-system-overview { display: block; @@ -350,15 +279,6 @@ imx-system-overview { left: -2px; } - .mat-tree { - margin-left: -15px; - } - - .mat-tree-node { - min-height: 23px; - margin-left: 5px; - } - .imx-system-overview-name { min-width: 360px; padding-left: 10px; @@ -403,24 +323,23 @@ imx-system-overview { } } - .imx-treshold-exceeded { - color: $phoenix-red; + .imx-threshold-exceeded { + color: $color-red-60; font-weight: 600; font-size: 14px; } - .imx-treshold-exceeded-label { + .imx-threshold-exceeded-label { font-weight: 600; font-size: 15px; .eui-icon { - color: $corbin-orange; padding-right: 10px; } } .imx-recommendedValue-exceeded { - color: $tresholdExceededColor; + color: $thresholdExceededColor; } .example-tooltip-red { @@ -450,32 +369,10 @@ imx-object-history { font-size: 14px; } - .imx-viewmode-dropdown { - margin: auto 6px; - - .mat-form-field-infix { - width: 98px; - color: $VI_Common_Color_Font; - border: 0; - } - - .mat-form-field-flex { - background-color: $white; - padding-left: 6px; - } - } - .imx-loader-hidden { visibility: hidden; } } - -.mat-option.mat-option-object-history { - height: unset !important; - padding: 4px 6px !important; - margin: 0 !important; -} - .grid-title-inline, .grid-title-inline > h1 { display: inline; @@ -486,9 +383,6 @@ imx-object-history { margin-top: 1.22em; } -.mat-paginator { - margin-bottom: 20px; -} /*#endregion ObjectHistory styles*/ /* #region ObjectOverview styles*/ @@ -499,116 +393,38 @@ imx-object-history { /*#endregion ObjectOverview styles*/ -/* #region SystemStatus*/ - -.greenCheck { - margin-right: .5em; - line-height: 20px; - color: $aspen-green; -} - -.warningSign { - line-height: 20px; - margin-right: .5em; - color: $QBM_Primary_Orange; -} - -.errorSign { - line-height: 20px; - margin-right: .5em; - color: $phoenix-red; -} - -/*#endregion*/ .eui-dark-theme { .page { - color: $color-gray-5; - background-color: $color-gray-80; - } - .pageContent { - color: $color-gray-5; - background-color: $color-gray-80; - } - .mat-card .mat-card-title{ - color: $color-gray-5; - } - .imx-service-availability .mat-card{ color: $color-gray-5; + background-color: $color-gray-80; } - .mat-cell, .mat-footer-cell{ + .pageContent { color: $color-gray-5; + background-color: $color-gray-80; } - .imx-normalCell ,.imx-title{ + .imx-service-availability .mat-mdc-card { color: $color-gray-5; } - .eui-sidesheet { - .mat-toolbar{ - background-color: $color-blue-80; - } - } - - imx-object-history { - .imx-viewmode-dropdown { - .mat-form-field-flex { - background-color: $color-gray-80; - } - } - } imx-object-history-timeline { background-color: $color-gray-80; } - - .mat-table .mat-checkbox-checked.mat-accent .mat-checkbox-background, - .mat-table .mat-checkbox-indeterminate.mat-accent .mat-checkbox-background { - background-color: $color-orange-40; - } } .eui-contrast-theme { .page { - color: $color-gray-0; - background-color: $color-gray-100; - } - .pageContent { - color: $color-gray-0; - background-color: $color-gray-100; - } - .mat-card .mat-card-title{ color: $color-gray-0; + background-color: $color-gray-100; } - .imx-service-availability .mat-card{ - color: $color-gray-0; - } - .mat-tab-label{ - background-color: $color-gray-90; - } - .mat-cell, .mat-footer-cell{ + .pageContent { color: $color-gray-0; + background-color: $color-gray-100; } - .imx-normalCell ,.imx-title{ + .imx-service-availability .mat-mdc-card { color: $color-gray-0; } - .eui-sidesheet { - .mat-toolbar{ - background-color: $color-blue-80; - } - } - - imx-object-history { - .imx-viewmode-dropdown { - .mat-form-field-flex { - background-color: $color-gray-100; - } - } - } imx-object-history-timeline { background-color: $color-gray-80; } - - .mat-table .mat-checkbox-checked.mat-accent .mat-checkbox-background, - .mat-table .mat-checkbox-indeterminate.mat-accent .mat-checkbox-background { - background-color: $color-orange-40; - } } diff --git a/imxweb/projects/qer-app-operationssupport/src/test.ts b/imxweb/projects/qer-app-operationssupport/src/test.ts index b80edded4..0eacdebc4 100644 --- a/imxweb/projects/qer-app-operationssupport/src/test.ts +++ b/imxweb/projects/qer-app-operationssupport/src/test.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,30 +26,12 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/long-stack-trace-zone'; -import 'zone.js/dist/proxy.js'; -import 'zone.js/dist/sync-test'; -import 'zone.js/dist/jasmine-patch'; -import 'zone.js/dist/async-test'; -import 'zone.js/dist/fake-async-test'; +// Zone must be imported first. Be careful with prettier import organizing +import 'zone.js'; +import 'zone.js/testing'; + import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; -import { TestHelperModule } from './app/test-utilities/test-helper.module.spec'; - -// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any. -declare const __karma__: any; -declare const require: any; - -// Prevent Karma from running prematurely. -__karma__.loaded = function() {}; - // First, initialize the Angular testing environment. -getTestBed().initTestEnvironment([BrowserDynamicTestingModule, TestHelperModule], platformBrowserDynamicTesting()); -// Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); -// And load the modules. -context.keys().map(context); - -// Finally, start Karma to run the tests. -__karma__.start(); +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); diff --git a/imxweb/projects/qer-app-operationssupport/tsconfig-es5.app.json b/imxweb/projects/qer-app-operationssupport/tsconfig-es5.app.json index e1d0d6abd..036b8797e 100644 --- a/imxweb/projects/qer-app-operationssupport/tsconfig-es5.app.json +++ b/imxweb/projects/qer-app-operationssupport/tsconfig-es5.app.json @@ -1,6 +1,6 @@ { - "extends": "./tsconfig.app.json", - "compilerOptions": { - "target": "es2020" + "extends": "./tsconfig.app.json", + "compilerOptions": { + "target": "es2020" } -} \ No newline at end of file +} diff --git a/imxweb/projects/qer-app-operationssupport/tsconfig.app.json b/imxweb/projects/qer-app-operationssupport/tsconfig.app.json index 6f4758564..df14e7d10 100644 --- a/imxweb/projects/qer-app-operationssupport/tsconfig.app.json +++ b/imxweb/projects/qer-app-operationssupport/tsconfig.app.json @@ -1,18 +1,14 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "strictNullChecks": true, "outDir": "../../out-tsc/app", "types": [] }, "angularCompilerOptions": { - "fullTemplateTypeCheck": true, - "strictInjectionParameters": true, + "strictTemplates": true, + "strictInjectionParameters": true }, - "files": [ - "src/main.ts", - "src/polyfills.ts" - ], - "include": [ - "projects/qer-app-operationssupport/src/**/*.d.ts" - ] + "files": ["src/main.ts", "src/polyfills.ts"], + "include": ["projects/qer-app-operationssupport/src/**/*.d.ts"] } diff --git a/imxweb/projects/qer-app-operationssupport/tsconfig.spec.json b/imxweb/projects/qer-app-operationssupport/tsconfig.spec.json index a809b0a66..6f92d1611 100644 --- a/imxweb/projects/qer-app-operationssupport/tsconfig.spec.json +++ b/imxweb/projects/qer-app-operationssupport/tsconfig.spec.json @@ -1,18 +1,10 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "strictNullChecks": false, "outDir": "../../out-tsc/spec", - "types": [ - "jasmine", - "node" - ] + "types": ["jasmine", "node"] }, - "files": [ - "src/test.ts", - "src/polyfills.ts" - ], - "include": [ - "**/*.spec.ts", - "**/*.d.ts" - ] + "files": ["src/test.ts", "src/polyfills.ts"], + "include": ["**/*.spec.ts", "**/*.d.ts"] } diff --git a/imxweb/projects/qer-app-operationssupport/tslint.json b/imxweb/projects/qer-app-operationssupport/tslint.json deleted file mode 100644 index ef4889cbe..000000000 --- a/imxweb/projects/qer-app-operationssupport/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "imx", - "camelCase" - ], - "component-selector": [ - true, - "element", - "imx", - "kebab-case" - ] - } -} diff --git a/imxweb/projects/qer-app-portal/.compodocrc.json b/imxweb/projects/qer-app-portal/.compodocrc.json index f06b44253..c9604d495 100644 --- a/imxweb/projects/qer-app-portal/.compodocrc.json +++ b/imxweb/projects/qer-app-portal/.compodocrc.json @@ -1,6 +1,6 @@ { "name": "IMX Web - QER Portal", - "output": "../../documentation/v92/qer-app-portal", + "output": "../../documentation/v93/qer-app-portal", "theme": "material", "assetsFolder": "../../compodoc/assets", "customLogo": "../../compodoc/assets/images/oneidentity-logo.png", diff --git a/imxweb/projects/qer-app-portal/.eslintrc.json b/imxweb/projects/qer-app-portal/.eslintrc.json new file mode 100644 index 000000000..e5e911b09 --- /dev/null +++ b/imxweb/projects/qer-app-portal/.eslintrc.json @@ -0,0 +1,35 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*", "**/*.spec.ts"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": ["imxweb/projects/qer-app-portal//tsconfig.app.json", "imxweb/projects/qer-app-portal//tsconfig.spec.json"], + "createDefaultProgram": true + }, + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "imx", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "imx", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "rules": {} + } + ] +} diff --git a/imxweb/projects/qer-app-portal/karma.conf.js b/imxweb/projects/qer-app-portal/karma.conf.js index 5b7e2c040..19124f6ed 100644 --- a/imxweb/projects/qer-app-portal/karma.conf.js +++ b/imxweb/projects/qer-app-portal/karma.conf.js @@ -12,10 +12,10 @@ module.exports = function (config) { require('karma-jasmine-html-reporter'), require('karma-coverage-istanbul-reporter'), require('@angular-devkit/build-angular/plugins/karma'), - require('karma-junit-reporter') + require('karma-junit-reporter'), ], client: { - clearContext: false // leave Jasmine Spec Runner output visible in browser + clearContext: false, // leave Jasmine Spec Runner output visible in browser }, coverageIstanbulReporter: { dir: require('path').join(__dirname, 'results'), @@ -26,7 +26,7 @@ module.exports = function (config) { branches: 0, functions: 0, lines: 0, - } + }, }, junitReporter: { outputDir: require('path').join(__dirname, 'results'), @@ -34,14 +34,15 @@ module.exports = function (config) { reporters: ['progress', 'coverage-istanbul', 'junit'], captureTimeout: 210000, browserDisconnectTolerance: 3, - browserDisconnectTimeout : 210000, - browserNoActivityTimeout : 210000, + browserDisconnectTimeout: 210000, + browserNoActivityTimeout: 210000, port: 9876, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], singleRun: false, - restartOnFileChange: true + failOnEmptyTestSuite: false, + restartOnFileChange: true, }); }; diff --git a/imxweb/projects/qer-app-portal/package.json b/imxweb/projects/qer-app-portal/package.json index cd2818945..d2f6d2c86 100644 --- a/imxweb/projects/qer-app-portal/package.json +++ b/imxweb/projects/qer-app-portal/package.json @@ -1,7 +1,6 @@ { "name": "qer-app-portal", - "version": "9.2.1", + "version": "9.3.0", "private": true, - "peerDependencies": { - } + "peerDependencies": {} } diff --git a/imxweb/projects/qer-app-portal/project.json b/imxweb/projects/qer-app-portal/project.json new file mode 100644 index 000000000..09da3754a --- /dev/null +++ b/imxweb/projects/qer-app-portal/project.json @@ -0,0 +1,214 @@ +{ + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "name": "qer-app-portal", + "sourceRoot": "projects/qer-app-portal/src", + "projectType": "application", + "prefix": "imx", + "generators": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "options": { + "aot": true, + "allowedCommonJsDependencies": [ + "lodash", + "highlight.js", + "file-saver", + "billboard.js", + "moment-timezone", + "core-js/fn/map", + "core-js/fn/set", + "core-js/fn/weak-map", + "core-js/fn/array/from", + "core-js/fn/object/assign", + "core-js/es/array/from", + "core-js/es/object/assign", + "core-js/es/map", + "core-js/es/set", + "core-js/es/weak-map", + "lodash.debounce", + "lodash.clamp", + "moment", + "@elemental-ui/cadence-icon/codepoints" + ], + "outputPath": "dist/qer-app-portal", + "index": "projects/qer-app-portal/src/index.html", + "main": "projects/qer-app-portal/src/main.ts", + "polyfills": ["projects/qer-app-portal/src/polyfills.ts"], + "tsConfig": "projects/qer-app-portal/tsconfig.app.json", + "assets": [ + "projects/qer-app-portal/src/assets", + "projects/qer-app-portal/src/appconfig.json", + { + "glob": "**/*", + "input": "./shared/assets/", + "output": "./assets" + }, + { + "glob": "**/*", + "input": "./node_modules/@elemental-ui/core/assets", + "output": "./assets" + } + ], + "styles": [ + "shared/scss/styles.scss", + "projects/qer-app-portal/src/styles.scss", + "projects/qbm/src/lib/styles/imx-page-title.scss", + "projects/qbm/src/lib/styles/data-explorer-common.scss", + "projects/qbm/src/lib/styles/data-explorer-details-common.scss" + ], + "stylePreprocessorOptions": { + "includePaths": [ + "./shared/scss", + "./node_modules", + "./node_modules/@elemental-ui/cadence-icon", + "./node_modules/@elemental-ui/core" + ] + } + }, + "configurations": { + "production": { + "fileReplacements": [ + { + "replace": "projects/qer-app-portal/src/environments/environment.ts", + "with": "projects/qer-app-portal/src/environments/environment.prod.ts" + } + ], + "optimization": true, + "outputHashing": "all", + "sourceMap": false, + "namedChunks": false, + "aot": true, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": true, + "budgets": [ + { + "type": "initial", + "maximumWarning": "20mb", + "maximumError": "40mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "6kb" + } + ] + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + }, + "remote-dev": { + "fileReplacements": [ + { + "replace": "projects/qer-app-portal/src/environments/environment.ts", + "with": "../imxweb_envs/qer-app-portal/environments/environment.remote-dev.ts" + } + ], + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + }, + "remote-qs": { + "fileReplacements": [ + { + "replace": "projects/qer-app-portal/src/environments/environment.ts", + "with": "../imxweb_envs/qer-app-portal/environments/environment.remote-qs.ts" + } + ], + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + }, + "es5": { + "tsConfig": "./projects/qer-app-portal/tsconfig-es5.app.json" + } + }, + "outputs": ["{options.outputPath}"], + "dependsOn": ["^build", "prebuild"] + }, + "prebuild": { + "executor": "nx:run-commands", + "options": { + "commands": ["node prebuild.js qer-app-portal"] + }, + "dependsOn": [ + { + "projects": ["qer", "aad", "aob", "apc", "att", "cpl", "hds", "olg", "pol", "qam", "rmb", "rms", "rps", "sac", "tsb"], + "target": "build" + } + ], + "cache": false + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "options": { + "browserTarget": "qer-app-portal:build" + }, + "configurations": { + "production": { + "browserTarget": "qer-app-portal:build:production" + }, + "development": { + "browserTarget": "qer-app-portal:build:development" + }, + "remote-dev": { + "browserTarget": "qer-app-portal:build:remote-dev" + }, + "remote-qs": { + "browserTarget": "qer-app-portal:build:remote-qs" + }, + "es5": { + "browserTarget": "qer-app-portal:build:es5" + } + } + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "qer-app-portal:build" + } + }, + "test": { + "executor": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/qer-app-portal/src/test.ts", + "polyfills": "projects/qer-app-portal/src/polyfills.ts", + "tsConfig": "projects/qer-app-portal/tsconfig.spec.json", + "karmaConfig": "projects/qer-app-portal/karma.conf.js", + "styles": ["projects/qer-app-portal/src/styles.scss"], + "stylePreprocessorOptions": { + "includePaths": [ + "./shared/assets", + "./shared/scss", + "./node_modules", + "./node_modules/@elemental-ui/cadence-icon", + "./node_modules/@elemental-ui/core" + ] + }, + "assets": ["projects/qer-app-portal/src/appconfig.json", "projects/qer-app-portal/src/assets"] + } + }, + "lint": { + "executor": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": ["projects/qer-app-portal/tsconfig.app.json", "projects/qer-app-portal/tsconfig.spec.json"], + "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + } + } + } +} diff --git a/imxweb/projects/qer-app-portal/src/app/app-routing.module.ts b/imxweb/projects/qer-app-portal/src/app/app-routing.module.ts index 6ab1af599..9532ecc4f 100644 --- a/imxweb/projects/qer-app-portal/src/app/app-routing.module.ts +++ b/imxweb/projects/qer-app-portal/src/app/app-routing.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,14 +24,11 @@ * */ -import { NgModule, InjectionToken } from '@angular/core'; -import { Routes, RouterModule, ActivatedRouteSnapshot, Router } from '@angular/router'; +import { InjectionToken, NgModule } from '@angular/core'; +import { ActivatedRouteSnapshot, Router, RouterModule, Routes } from '@angular/router'; import { AuthenticationGuardService, LoginComponent, RouteGuardService } from 'qbm'; -import { - PasswordQueryComponent, - StartComponent -} from 'qer'; +import { PasswordQueryComponent, StartComponent } from 'qer'; const externalUrlProvider = new InjectionToken('externalUrlRedirectResolver'); @@ -55,19 +52,19 @@ const routes: Routes = [ canActivate: [RouteGuardService], resolve: { url: externalUrlProvider, - } + }, }, { path: 'passwordquestions', component: PasswordQueryComponent, canActivate: [RouteGuardService], - resolve: [RouteGuardService] + resolve: [RouteGuardService], }, - { path: '**', redirectTo: 'dashboard' } + { path: '**', redirectTo: 'dashboard' }, ]; @NgModule({ - imports: [RouterModule.forRoot([], { useHash: true, relativeLinkResolution: 'legacy' })], + imports: [RouterModule.forRoot([], { useHash: true })], exports: [RouterModule], providers: [ { @@ -77,17 +74,14 @@ const routes: Routes = [ if (externalUrl && externalUrl.toLocaleLowerCase() !== 'undefined') { window.open(externalUrl, '_self'); } - } + }, }, ], - }) export class AppRoutingModule { - constructor( - private readonly router: Router) { - + constructor(private readonly router: Router) { const config = this.router.config; - routes.forEach(route => { + routes.forEach((route) => { config.push(route); }); this.router.resetConfig(config); diff --git a/imxweb/projects/qer-app-portal/src/app/app.component.html b/imxweb/projects/qer-app-portal/src/app/app.component.html index b202569c6..9eb3ed928 100644 --- a/imxweb/projects/qer-app-portal/src/app/app.component.html +++ b/imxweb/projects/qer-app-portal/src/app/app.component.html @@ -18,7 +18,6 @@
    -
    diff --git a/imxweb/projects/qer-app-portal/src/app/app.component.scss b/imxweb/projects/qer-app-portal/src/app/app.component.scss index 353f07c22..25822db1c 100644 --- a/imxweb/projects/qer-app-portal/src/app/app.component.scss +++ b/imxweb/projects/qer-app-portal/src/app/app.component.scss @@ -1,5 +1,5 @@ -@import "variables.scss"; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/variables'; .page { display: flex; @@ -13,7 +13,7 @@ flex-direction: column; overflow: hidden; height: inherit; - margin: 40px 60px; + margin: 24px; &--full-page { margin: 0; @@ -36,22 +36,6 @@ } } -@media only screen and (max-width: 768px) { - - .eui-top-navigation-mobile { - .eui-top-navigation-mobile-footer { - .mat-button { - &.imx-masthead--icon-button { - width: 100%; - - .mat-button-wrapper span { - display: inline-block; - } - } - } - } - } -} .eui-dark-theme { .page { diff --git a/imxweb/projects/qer-app-portal/src/app/app.component.ts b/imxweb/projects/qer-app-portal/src/app/app.component.ts index 6e5a18f4f..2cb7fd4c0 100644 --- a/imxweb/projects/qer-app-portal/src/app/app.component.ts +++ b/imxweb/projects/qer-app-portal/src/app/app.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -23,28 +23,30 @@ * THIS SOFTWARE OR ITS DERIVATIVES. * */ -import { Component, Inject, ErrorHandler, OnDestroy, OnInit } from '@angular/core'; -import { EventType, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, Router, RouterEvent } from '@angular/router'; +import { Component, ErrorHandler, Inject, OnDestroy, OnInit } from '@angular/core'; +import { Event, EventType, NavigationEnd, NavigationStart, Router } from '@angular/router'; import { Subscription } from 'rxjs'; import { AuthenticationService, + ClassloggerService, + ConfirmationService, IeWarningService, ImxTranslationProviderService, ISessionState, MenuService, + Message, SplashService, SystemInfoService, + UserMessageService, } from 'qbm'; -import { ProjectConfigurationService, UserModelService, SettingsComponent, QerApiService } from 'qer'; +import { ProjectConfigurationService, QerApiService, SettingsComponent, UserModelService } from 'qer'; -import { ProfileSettings, QerProjectConfig } from 'imx-api-qer'; -import { ProjectConfig } from 'imx-api-qbm'; import { MatDialog } from '@angular/material/dialog'; -import { EuiLoadingService, EuiTheme, EuiThemeService, EuiTopNavigationItem } from '@elemental-ui/core'; -import { TranslateService } from '@ngx-translate/core'; -import { APP_BASE_HREF } from '@angular/common'; +import { EuiTheme, EuiThemeService, EuiTopNavigationItem } from '@elemental-ui/core'; +import { ProjectConfig } from '@imx-modules/imx-api-qbm'; +import { ProfileSettings, QerProjectConfig } from '@imx-modules/imx-api-qer'; import { getBaseHref, HEADLESS_BASEHREF } from './app.module'; @Component({ @@ -56,12 +58,13 @@ export class AppComponent implements OnInit, OnDestroy { public menuItems: EuiTopNavigationItem[]; public isLoggedIn = false; public hideMenu = false; - public hideUserMessage = false; public showPageContent = true; + public message: Message | undefined; private routerStatus: EventType; private readonly subscriptions: Subscription[] = []; constructor( + private readonly logger: ClassloggerService, private readonly authentication: AuthenticationService, private readonly router: Router, private readonly splash: SplashService, @@ -75,8 +78,8 @@ export class AppComponent implements OnInit, OnDestroy { private readonly themeService: EuiThemeService, private readonly errorHandler: ErrorHandler, private readonly translationProvider: ImxTranslationProviderService, - private readonly translateService: TranslateService, - @Inject(APP_BASE_HREF) private baseHref: string + private readonly confirmationService: ConfirmationService, + private readonly userMessageService: UserMessageService, ) { this.subscriptions.push( this.authentication.onSessionResponse.subscribe(async (sessionState: ISessionState) => { @@ -89,33 +92,49 @@ export class AppComponent implements OnInit, OnDestroy { } } - this.isLoggedIn = sessionState.IsLoggedIn; + this.isLoggedIn = sessionState.IsLoggedIn ?? false; if (this.isLoggedIn) { - // Close the splash screen that opened in app service initialisation - // Needs to close here when running in containers (auth skipped) - splash.close(); + const isUseProfileLangChecked = (await this.qerClient.client.portal_profile_get()).UseProfileLanguage ?? false; + // Set session culture if isUseProfileLangChecked is true + if (isUseProfileLangChecked) { + // Use culture if available, if not fetch + const culture = sessionState.culture + ? sessionState.culture + : (await this.qerClient.client.portal_profile_person_get())?.ProfileLanguage; + // If culture is found, use it, otherwise fallback to the app default + if (culture) { + this.logger.debug(this, `ProfileLangChecked is true, culture available: Setting ${culture} as profile language`); + await this.translationProvider.reinit(culture, sessionState.cultureFormat ?? culture, this.router); + } + } const config: QerProjectConfig & ProjectConfig = await projectConfig.getConfig(); - const features = (await userModelService.getFeatures()).Features; + const features = (await userModelService.getFeatures()).Features ?? []; const systemInfo = await systemInfoService.get(); const groups = (await userModelService.getGroups()).map((group) => group.Name || ''); - const isUseProfileLangChecked = (await this.qerClient.v2Client.portal_profile_get()).UseProfileLanguage ?? false; - // Set session culture if isUseProfileLangChecked is true, set browser culture otherwise - if (isUseProfileLangChecked) { - await this.translationProvider.init(sessionState.culture, sessionState.cultureFormat); - } else { - const browserCulture = this.translateService.getBrowserCultureLang(); - await this.translationProvider.init(browserCulture); - } - - this.menuItems = await menuService.getMenuItems(systemInfo.PreProps, features, true, config, groups); ieWarningService.showIe11Banner(); - this.applyProfileSettings(); + await this.applyProfileSettings(); + this.menuItems = await menuService.getMenuItems(systemInfo.PreProps ?? [], features, true, config, groups); + // Close the splash screen that opened in app service initialisation + // Needs to close here when running in containers (auth skipped) + splash.close(); } - }) + }), ); + + this.subscriptions.push( + this.userMessageService.subject.subscribe((message) => { + this.message = message; + if (!!this.message && this.message.type === 'error' && !this.message.target) { + this.confirmationService.showErrorMessage({ + Message: this.message?.text, + }); + } + }), + ); + this.setupRouter(); } @@ -139,7 +158,7 @@ export class AppComponent implements OnInit, OnDestroy { } public async ngOnInit(): Promise { - this.authentication.update(); + await this.authentication.update(); } public ngOnDestroy(): void { @@ -167,31 +186,18 @@ export class AppComponent implements OnInit, OnDestroy { } private setupRouter(): void { - this.router.events.subscribe((event: RouterEvent) => { + this.router.events.subscribe((event: Event) => { if (event instanceof NavigationStart) { - this.hideUserMessage = true; this.routerStatus = event.type; if (this.isLoggedIn && event.url === '/') { // show the splash screen, when the user logs out! this.splash.init({ applicationName: 'One Identity Manager Portal' }); } } - - if (event instanceof NavigationCancel) { - this.hideUserMessage = false; - this.routerStatus = event.type; - } - if (event instanceof NavigationEnd) { - this.hideUserMessage = false; + this.routerStatus = event.type; this.hideMenu = event.url === '/'; this.showPageContent = true; - this.routerStatus = event.type; - } - - if (event instanceof NavigationError) { - this.hideUserMessage = false; - this.routerStatus = event.type; } }); } diff --git a/imxweb/projects/qer-app-portal/src/app/app.module.ts b/imxweb/projects/qer-app-portal/src/app/app.module.ts index fb692c57f..5afacef7e 100644 --- a/imxweb/projects/qer-app-portal/src/app/app.module.ts +++ b/imxweb/projects/qer-app-portal/src/app/app.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { HttpClientModule } from '@angular/common/http'; +import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; import { APP_INITIALIZER, ErrorHandler, NgModule } from '@angular/core'; import { MatDialogModule } from '@angular/material/dialog'; import { MatPaginatorIntl } from '@angular/material/paginator'; @@ -44,7 +44,6 @@ import { ImxTranslateLoader, LdsReplacePipe, MastHeadModule, - MenuModule, ObjectHistoryApiService, ObjectHistoryModule, Paginator, @@ -52,38 +51,40 @@ import { } from 'qbm'; import { AddressbookModule, + ApprovalWorkFlowModule, ApprovalsModule, ArchivedRequestsModule, + DataExplorerViewModule, DelegationModule, IdentitiesModule, ItshopPatternModule, + MyResponsibilitiesViewModule, NewRequestModule, ObjectHyperviewService, ProductSelectionModule, ProfileModule, QerModule, QpmIntegrationModule, + QueueStatusComponent, RelatedApplicationsModule, RequestConfigModule, RequestHistoryModule, ResourcesModule, RiskConfigModule, RoleManangementModule, + RoleMembershipsModule, ServiceCategoriesModule, ServiceItemsEditModule, ShoppingCartModule, + SourceDetectiveModule, StatisticsModule, - ViewDevicesModule, - MyResponsibilitiesViewModule, - ApprovalWorkFlowModule, - DataExplorerViewModule, + TeamResponsibilitiesModule, UserProcessModule, - SourceDetectiveModule, - RoleMembershipsModule, - TeamResponsibilitiesModule + ViewDevicesModule, } from 'qer'; import { APP_BASE_HREF } from '@angular/common'; +import { RECAPTCHA_V3_SITE_KEY } from 'ng-recaptcha-2'; import appConfigJson from '../appconfig.json'; import { environment } from '../environments/environment'; import { AppRoutingModule } from './app-routing.module'; @@ -98,6 +99,7 @@ export function getBaseHref(): string { } @NgModule({ declarations: [AppComponent], + bootstrap: [AppComponent], imports: [ AppRoutingModule, AuthenticationModule, @@ -105,14 +107,12 @@ export function getBaseHref(): string { BrowserModule, EuiCoreModule, EuiMaterialModule, - HttpClientModule, IdentitiesModule, ResourcesModule, LoggerModule.forRoot({ level: NgxLoggerLevel.DEBUG, serverLogLevel: NgxLoggerLevel.OFF }), MatDialogModule, MatTabsModule, MastHeadModule, - MenuModule, AddressbookModule, QerModule, ProfileModule, @@ -148,11 +148,13 @@ export function getBaseHref(): string { ViewDevicesModule, MyResponsibilitiesViewModule, ApprovalWorkFlowModule, + UserProcessModule, + TeamResponsibilitiesModule, DataExplorerViewModule, - UserProcessModule, + UserProcessModule, SourceDetectiveModule, RoleMembershipsModule, - TeamResponsibilitiesModule + QueueStatusComponent, ], providers: [ { provide: 'environment', useValue: environment }, @@ -173,7 +175,7 @@ export function getBaseHref(): string { }, { provide: ObjectHyperviewService, - useClass: PortalHyperviewService + useClass: PortalHyperviewService, }, { provide: MatPaginatorIntl, @@ -185,7 +187,14 @@ export function getBaseHref(): string { useValue: getBaseHref(), }, CdrRegistryService, + { + provide: RECAPTCHA_V3_SITE_KEY, + useFactory: (config: AppService) => { + return config.recaptchaSiteKeyV3; + }, + deps: [AppService], + }, + provideHttpClient(withInterceptorsFromDi()), ], - bootstrap: [AppComponent], }) export class AppModule {} diff --git a/imxweb/projects/qer-app-portal/src/app/app.service.ts b/imxweb/projects/qer-app-portal/src/app/app.service.ts index a8846abeb..2c75619c1 100644 --- a/imxweb/projects/qer-app-portal/src/app/app.service.ts +++ b/imxweb/projects/qer-app-portal/src/app/app.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,38 +24,32 @@ * */ -import { Injectable } from '@angular/core'; import { Title } from '@angular/platform-browser'; import { TranslateService } from '@ngx-translate/core'; -import { Globals } from 'imx-qbm-dbts'; +import { Injectable, Injector, createNgModule } from '@angular/core'; +import { ImxConfig, TypedClient } from '@imx-modules/imx-api-qbm'; +import { Globals } from '@imx-modules/imx-qbm-dbts'; import { AppConfigService, - imx_SessionService, + AuthenticationService, + CaptchaService, CdrRegistryService, - ImxTranslationProviderService, ClassloggerService, - AuthenticationService, - PluginLoaderService, + ImxTranslationProviderService, SplashService, - SystemInfoService + SystemInfoService, + imx_SessionService, } from 'qbm'; -import { - NotificationStreamService -} from 'qer'; +import { NotificationStreamService } from 'qer'; import { environment } from '../environments/environment'; -import { TypedClient } from 'imx-api-qbm'; -import { PortalDocConfigurationService } from './portal-doc-configuration.service'; - -import * as QBM from 'qbm'; -import * as QER from 'qer'; - -declare var SystemJS: any; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class AppService { + public recaptchaSiteKeyV3: string | null = null; + private imxConfig: ImxConfig; constructor( public readonly registry: CdrRegistryService, private readonly logger: ClassloggerService, @@ -65,22 +59,24 @@ export class AppService { private readonly session: imx_SessionService, private readonly translationProvider: ImxTranslationProviderService, private readonly title: Title, - private readonly pluginLoader: PluginLoaderService, private readonly authentication: AuthenticationService, private readonly notificationService: NotificationStreamService, private readonly splash: SplashService, - private readonly docSvc: PortalDocConfigurationService, - ) { } + private readonly injector: Injector, + private readonly captchaService: CaptchaService, + ) {} public async init(): Promise { this.showSplash(); await this.config.init(environment.clientUrl); - this.translateService.addLangs(this.config.Config.Translation.Langs); - const browserCulture = this.translateService.getBrowserCultureLang(); - this.logger.debug(this, `Set ${browserCulture} as default language`); - this.translateService.setDefaultLang(browserCulture); - await this.translateService.use(browserCulture).toPromise(); + if (this.config.Config.Translation?.Langs) { + this.translateService.addLangs(this.config.Config.Translation.Langs); + } + + await this.translationProvider.init(); + + this.imxConfig = await this.systemInfoService.getImxConfig(); this.translateService.onLangChange.subscribe(() => { this.setTitle(); @@ -88,8 +84,8 @@ export class AppService { this.setTitle(); - this.authentication.onSessionResponse.subscribe(sessionState => { - if(sessionState && sessionState.IsLoggedIn) { + this.authentication.onSessionResponse.subscribe((sessionState) => { + if (sessionState && sessionState.IsLoggedIn) { // when the user logs in, start listening to notifications this.notificationService.openStream(); } @@ -97,17 +93,17 @@ export class AppService { this.session.TypedClient = new TypedClient(this.config.v2client, this.translationProvider); - this.docSvc.setupPaths(); - - SystemJS.set('qbm', SystemJS.newModule(QBM)); - SystemJS.set('qer', SystemJS.newModule(QER)); + await this.loadModules(environment.appName); - await this.pluginLoader.loadModules(environment.appName); + if (this.imxConfig.RecaptchaPublicKey) { + this.captchaService.enableReCaptcha(this.imxConfig.RecaptchaPublicKey); + this.recaptchaSiteKeyV3 = this.imxConfig.RecaptchaPublicKey; + } + this.captchaService.captchaImageUrl = 'portal/captchaimage'; } private async setTitle(): Promise { - const imxConfig = await this.systemInfoService.getImxConfig(); - const name = imxConfig.ProductName || Globals.QIM_ProductNameFull; + const name = this.imxConfig.ProductName || Globals.QIM_ProductNameFull; this.config.Config.Title = await this.translateService.get('#LDS#Heading Web Portal').toPromise(); const title = `${name} ${this.config.Config.Title}`; this.title.setTitle(title); @@ -134,4 +130,37 @@ export class AppService { this.splash.update({ applicationName: title, message: loadingMsg }); } + public async loadModules(appName: string): Promise { + const apps = await this.session.Client.imx_applications_get(); + + const appInfo = apps.filter((app) => app.Name === appName)[0]; + + this.logger.debug(this, `▶️ Found config section for ${appInfo.DisplayName}`); + + if (appInfo.PlugIns == null || appInfo.PlugIns.length === 0) { + this.logger.debug(this, `❌ No plugins found`); + return; + } + + this.logger.debug(this, `▶️ Found ${appInfo.PlugIns.length} plugin(s)`); + + for (const plugin of appInfo.PlugIns) { + this.logger.debug(this, `⚙️ Plugin: ${plugin.Container}`); + + try { + this.logger.debug(this, '▶️ Importing module. DEV mode.'); + await import(`html/qer-app-portal/${plugin.Container}/fesm2022/${plugin.Container}.mjs`) + .then((m) => { + if (plugin.Name) { + createNgModule(m[plugin.Name], this.injector); + } + }) + .catch((error) => + this.logger.error(this, `💥 Loading of ${plugin.Name} (${plugin.Container}) failed with the following error: ${error.message}`), + ); + } catch (e) { + this.logger.error(this, `💥 Loading of ${plugin.Name} (${plugin.Container}) failed with the following error: ${e.message}`); + } + } + } } diff --git a/imxweb/projects/qer-app-portal/src/app/hyperview/portal-hyperview.service.ts b/imxweb/projects/qer-app-portal/src/app/hyperview/portal-hyperview.service.ts index e1fdb716a..5960dc8c7 100644 --- a/imxweb/projects/qer-app-portal/src/app/hyperview/portal-hyperview.service.ts +++ b/imxweb/projects/qer-app-portal/src/app/hyperview/portal-hyperview.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,10 +26,10 @@ import { Injectable } from '@angular/core'; -import { ShapeData } from 'imx-api-qer'; +import { ShapeData } from '@imx-modules/imx-api-qer'; import { ApiClientService } from 'qbm'; -import { ObjectHyperviewService, ProjectConfigurationService, QerApiService } from 'qer'; +import { ObjectHyperviewService, ProjectConfigurationService, QerApiService, QerPermissionsService } from 'qer'; @Injectable({ providedIn: 'root', @@ -38,15 +38,15 @@ export class PortalHyperviewService implements ObjectHyperviewService { constructor( private readonly apiClient: QerApiService, private readonly apiProvider: ApiClientService, - private readonly configService: ProjectConfigurationService + private readonly configService: ProjectConfigurationService, + private readonly qerPermissionsService: QerPermissionsService, ) {} - public async get(tableName: string, uid: string, nodeName?: string): Promise { - return this.apiProvider.request(() => this.apiClient.client.portal_hyperview_get(tableName, uid, { node: nodeName })); + public async get(tableName: string, uid: string, node?: string): Promise { + return this.apiProvider.request(() => this.apiClient.client.portal_hyperview_get(tableName, uid, { node })) as Promise; } - public async getNavigationPermission(): Promise { const projectConfig = await this.configService.getConfig(); - return !projectConfig?.DisableHyperViewNavigation; + return projectConfig.EnableHyperViewNavigation && this.qerPermissionsService.isHyperviewNavigation(); } } diff --git a/imxweb/projects/qer-app-portal/src/app/portal-doc-configuration.service.ts b/imxweb/projects/qer-app-portal/src/app/portal-doc-configuration.service.ts deleted file mode 100644 index 6d7a7974d..000000000 --- a/imxweb/projects/qer-app-portal/src/app/portal-doc-configuration.service.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* - * ONE IDENTITY LLC. PROPRIETARY INFORMATION - * - * This software is confidential. One Identity, LLC. or one of its affiliates or - * subsidiaries, has supplied this software to you under terms of a - * license agreement, nondisclosure agreement or both. - * - * You may not copy, disclose, or use this software except in accordance with - * those terms. - * - * - * Copyright 2023 One Identity LLC. - * ALL RIGHTS RESERVED. - * - * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR - * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, OR - * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE - * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE - * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING - * THIS SOFTWARE OR ITS DERIVATIVES. - * - */ - -import { Injectable } from "@angular/core"; -import { DocDocument, DocChapterService } from "qbm"; - -@Injectable({ - providedIn: 'root' -}) -export class PortalDocConfigurationService { - - constructor(private readonly docSvc: DocChapterService) { } - - setupPaths() { - // Web Portal User Guide - var portalDoc: DocDocument = { - paths: { - "en-US": "imx/doc/OneIM_QER_WebPortal_en-us.html5/OneIM_QER_WebPortal.html", - "de-DE": "imx/doc/OneIM_QER_WebPortal_de-de.html5/OneIM_QER_WebPortal.html", - "de-CH": "imx/doc/OneIM_QER_WebPortal_de-de.html5/OneIM_QER_WebPortal.html", - "de-AT": "imx/doc/OneIM_QER_WebPortal_de-de.html5/OneIM_QER_WebPortal.html" - } - }; - - // Declare the route/chapter mapping. Extend this section as new routes get added. - this.docSvc.chapters["addressbook"] = { - chapterUid: "443F4A50-2D9E-4886-9805-E780645F2B3B", - document: portalDoc - }; - } -} \ No newline at end of file diff --git a/imxweb/projects/qer-app-portal/src/app/portal-history.service.ts b/imxweb/projects/qer-app-portal/src/app/portal-history.service.ts index 33e8b9bb3..814cfa429 100644 --- a/imxweb/projects/qer-app-portal/src/app/portal-history.service.ts +++ b/imxweb/projects/qer-app-portal/src/app/portal-history.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,21 +25,20 @@ */ import { Injectable } from '@angular/core'; -import { HistoryData } from 'imx-qbm-dbts'; +import { HistoryData } from '@imx-modules/imx-qbm-dbts'; import { QerApiService } from 'qer'; import { ObjectHistoryApiService } from 'qbm'; -import { HistoryComparisonData } from 'imx-api-qer'; +import { HistoryComparisonData } from '@imx-modules/imx-api-qer'; @Injectable() export class PortalHistoryService implements ObjectHistoryApiService { - - constructor(private readonly apiService: QerApiService) { } + constructor(private readonly apiService: QerApiService) {} getHistoryData(table: string, uid: string): Promise { return this.apiService.client.portal_history_get(table, uid); } - getHistoryComparisonData(table: string, uid: string,options?: {CompareDate?: Date;}): Promise { + getHistoryComparisonData(table: string, uid: string, options?: { CompareDate?: Date }): Promise { return this.apiService.client.portal_history_comparison_get(table, uid, options); } -} \ No newline at end of file +} diff --git a/imxweb/projects/qer-app-portal/src/environments/environment.prod.ts b/imxweb/projects/qer-app-portal/src/environments/environment.prod.ts index a3125ce37..bb4762cd2 100644 --- a/imxweb/projects/qer-app-portal/src/environments/environment.prod.ts +++ b/imxweb/projects/qer-app-portal/src/environments/environment.prod.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,5 +28,5 @@ export const environment = { production: true, clientUrl: '', appName: 'qer-app-portal', - appVersion: '1.0.0' + appVersion: '1.0.0', }; diff --git a/imxweb/projects/qer-app-portal/src/environments/environment.ts b/imxweb/projects/qer-app-portal/src/environments/environment.ts index 5ebebe3ce..3bc9bea0c 100644 --- a/imxweb/projects/qer-app-portal/src/environments/environment.ts +++ b/imxweb/projects/qer-app-portal/src/environments/environment.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -32,10 +32,9 @@ export const environment = { production: false, clientUrl: 'http://localhost:8182', appName: 'qer-app-portal', - appVersion: '1.0.0' + appVersion: '1.0.0', }; - /* * For easier debugging in development mode, you can import the following file * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. @@ -43,4 +42,4 @@ export const environment = { * This import should be commented out in production mode because it will have a negative impact * on performance if an error is thrown. */ -// import 'zone.js/dist/zone-error'; // Included with Angular CLI. +// import 'zone.js'; // Included with Angular CLI. diff --git a/imxweb/projects/qer-app-portal/src/index.html b/imxweb/projects/qer-app-portal/src/index.html index 4b785ba4e..f08db67fd 100644 --- a/imxweb/projects/qer-app-portal/src/index.html +++ b/imxweb/projects/qer-app-portal/src/index.html @@ -1,15 +1,13 @@ + + + + + + - - - - - - - - - - - + + + diff --git a/imxweb/projects/qer-app-portal/src/main.ts b/imxweb/projects/qer-app-portal/src/main.ts index 5b7a3816d..8cb3e4f09 100644 --- a/imxweb/projects/qer-app-portal/src/main.ts +++ b/imxweb/projects/qer-app-portal/src/main.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -34,5 +34,6 @@ if (environment.production) { enableProdMode(); } -platformBrowserDynamic().bootstrapModule(AppModule) - .catch(err => console.error(err)); +platformBrowserDynamic() + .bootstrapModule(AppModule) + .catch((err) => console.error(err)); diff --git a/imxweb/projects/qer-app-portal/src/polyfills.ts b/imxweb/projects/qer-app-portal/src/polyfills.ts index f87c86a13..e38109681 100644 --- a/imxweb/projects/qer-app-portal/src/polyfills.ts +++ b/imxweb/projects/qer-app-portal/src/polyfills.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -41,9 +41,8 @@ */ /*************************************************************************************************** -* BROWSER POLYFILLS -*/ - + * BROWSER POLYFILLS + */ /** IE10 and IE11 requires the following for NgClass support on SVG elements */ // import 'classlist.js'; // Run `npm install --save classlist.js`. @@ -51,16 +50,15 @@ /** IE10 and IE11 requires the following for the Reflect API. */ // import 'core-js/es6/reflect'; - /** Evergreen browsers require these. */ // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. -import 'core-js/es7/reflect'; +import 'core-js/es/reflect'; // Used for support array.includes -import 'core-js/es7/array'; +import 'core-js/es/array'; // Used for support object.values -import 'core-js/es7/object'; +import 'core-js/es/object'; /** * Required to support Web Animations `@angular/platform-browser/animations`. @@ -68,17 +66,13 @@ import 'core-js/es7/object'; */ import 'web-animations-js'; - - /*************************************************************************************************** * Zone JS is required by default for Angular itself. */ -import 'zone.js/dist/zone'; // Included with Angular CLI. - - +import 'zone.js'; // Included with Angular CLI. /*************************************************************************************************** * APPLICATION IMPORTS */ +import 'url-polyfill'; import 'whatwg-fetch'; -import 'url-polyfill'; \ No newline at end of file diff --git a/imxweb/projects/qer-app-portal/src/styles.scss b/imxweb/projects/qer-app-portal/src/styles.scss index 52a903849..436a3fb21 100644 --- a/imxweb/projects/qer-app-portal/src/styles.scss +++ b/imxweb/projects/qer-app-portal/src/styles.scss @@ -1,20 +1,14 @@ /* You can add global styles to this file, and also import other style files */ @use '@angular/material' as mat; - -$material_icons_font_path: "~node_modules/@elemental-ui/core/assets/MaterialIcons"; -$cadence_font_path: "~node_modules/@elemental-ui/core/assets/Cadence"; -$source_sans_pro_font_path: "~node_modules/@elemental-ui/core/assets/Source_Sans_Pro"; - @import '@elemental-ui/core/src/styles/core.scss'; - -.imx-dialog-panel-class > .mat-dialog-container { - background-color: mat.get-color-from-palette($asher-gray-palette, 200); +.imx-dialog-panel-class > .mat-mdc-dialog-container { + background-color: mat.m2-get-color-from-palette($asher-gray-palette, 200); } -.mat-snack-bar-container.mat-snack-bar-center.eui-alert-banner-panel { - position: fixed; - left: 0; +.mat-mdc-snack-bar-container.mat-snack-bar-center.eui-alert-banner-panel { + position: fixed; + left: 0; } // ToDo (als Teil von 254128): später wieder einbinden, wenn wir den Styleguide soweit haben // .pageContent > :last-child > h1 { @@ -26,93 +20,76 @@ $source_sans_pro_font_path: "~node_modules/@elemental-ui/core/assets/Source_Sans imx-data-explorer-groups, imx-data-explorer-identities, imx-data-explorer-accounts { + flex: 1 1 auto; + display: flex; + flex-direction: column; + overflow: hidden; + > .data-explorer--groups, + .data-explorer--accounts { flex: 1 1 auto; + overflow: hidden; display: flex; flex-direction: column; - overflow: hidden; - > .data-explorer--groups, - .data-explorer--accounts { - flex: 1 1 auto; - overflow: hidden; - display: flex; - flex-direction: column; - > :nth-child(2) { - flex: 1 1 auto; - display: flex; - flex-direction: column; - } + > :nth-child(2) { + flex: 1 1 auto; + display: flex; + flex-direction: column; } + } - .data-explorer--accounts { - > :nth-child(2) { - overflow: hidden; - } + .data-explorer--accounts { + > :nth-child(2) { + overflow: hidden; } + } - .data-explorer-table { - flex: 1 1 auto; - overflow: hidden; - display: flex; - flex-direction: column; - } + .data-explorer-table { + flex: 1 1 auto; + overflow: hidden; + display: flex; + flex-direction: column; + } - .data-explorer-table .mat-sidenav-content { - overflow-y: auto !important; - } } -.data-explorer.data-explorer--identities.identities--fullscreen -{ - display: flex !important; // TODO: Task 268556 : Styling direkt in der Komponente umsetzen - flex-direction: column; +.data-explorer.data-explorer--identities.identities--fullscreen { + display: flex !important; // TODO: Task 268556 : Styling direkt in der Komponente umsetzen + flex-direction: column; } .governance-sidesheet__tab-content { - height: calc(100% - 120px); + height: calc(100% - 120px); } .eui-dark-theme { - .loading-progress{ - background-color: $color-gray-70; - } - .mat-tab-group .mat-tab-header{ - background-color:$color-gray-80; - } - .eui-sidesheet - { - background-color:$color-gray-80; - .mat-toolbar{ - background-color: $color-blue-80; - } - .background--asher-gray{ - background-color: $color-gray-80; - } - } - .eui-sidesheet-actions.mat-card.eui-sidesheet-actions--white{ - background-color: $color-gray-70; + .loading-progress { + background-color: $color-gray-70; + } + + .eui-sidesheet { + background-color: $color-gray-80; + .background--asher-gray { + background-color: $color-gray-80; } + } + .eui-sidesheet-actions.mat-mdc-card.eui-sidesheet-actions--white { + background-color: $color-gray-70; + } } .eui-contrast-theme { - .loading-progress{ - background-color: $color-gray-90; - } - .mat-tab-group .mat-tab-header{ - background-color:$color-gray-100; + .loading-progress { + background-color: $color-gray-90; } - .eui-sidesheet - { - background-color:$color-gray-100; - .mat-toolbar{ - background-color: $color-blue-90; - } - .background--asher-gray{ - background-color: $color-gray-90; - } - } - .eui-sidesheet-actions.mat-card.eui-sidesheet-actions--white{ + + .eui-sidesheet { + background-color: $color-gray-100; + .background--asher-gray { background-color: $color-gray-90; + } + } + .eui-sidesheet-actions.mat-mdc-card.eui-sidesheet-actions--white { + background-color: $color-gray-90; } } - diff --git a/imxweb/projects/qer-app-portal/src/test.ts b/imxweb/projects/qer-app-portal/src/test.ts index 9f9f4fc1f..0eacdebc4 100644 --- a/imxweb/projects/qer-app-portal/src/test.ts +++ b/imxweb/projects/qer-app-portal/src/test.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,21 +26,12 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone-testing'; -import { getTestBed } from '@angular/core/testing'; -import { - BrowserDynamicTestingModule, - platformBrowserDynamicTesting -} from '@angular/platform-browser-dynamic/testing'; +// Zone must be imported first. Be careful with prettier import organizing +import 'zone.js'; +import 'zone.js/testing'; -declare const require: any; +import { getTestBed } from '@angular/core/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; // First, initialize the Angular testing environment. -getTestBed().initTestEnvironment( - BrowserDynamicTestingModule, - platformBrowserDynamicTesting() -); -// Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); -// And load the modules. -context.keys().map(context); +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); diff --git a/imxweb/projects/qer-app-portal/tsconfig-es5.app.json b/imxweb/projects/qer-app-portal/tsconfig-es5.app.json index e1d0d6abd..036b8797e 100644 --- a/imxweb/projects/qer-app-portal/tsconfig-es5.app.json +++ b/imxweb/projects/qer-app-portal/tsconfig-es5.app.json @@ -1,6 +1,6 @@ { - "extends": "./tsconfig.app.json", - "compilerOptions": { - "target": "es2020" + "extends": "./tsconfig.app.json", + "compilerOptions": { + "target": "es2020" } -} \ No newline at end of file +} diff --git a/imxweb/projects/qer-app-portal/tsconfig.app.json b/imxweb/projects/qer-app-portal/tsconfig.app.json index ba711311b..d5376ba56 100644 --- a/imxweb/projects/qer-app-portal/tsconfig.app.json +++ b/imxweb/projects/qer-app-portal/tsconfig.app.json @@ -1,14 +1,13 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "strictNullChecks": true, "outDir": "../../out-tsc/app", "types": [] }, - "files": [ - "src/main.ts", - "src/polyfills.ts" - ], - "include": [ - "projects/qer-app-portal/src/**/*.d.ts" - ] + "angularCompilerOptions": { + "strictTemplates": true + }, + "files": ["src/main.ts", "src/polyfills.ts"], + "include": ["projects/qer-app-portal/src/**/*.d.ts"] } diff --git a/imxweb/projects/qer-app-portal/tsconfig.spec.json b/imxweb/projects/qer-app-portal/tsconfig.spec.json index a809b0a66..6f92d1611 100644 --- a/imxweb/projects/qer-app-portal/tsconfig.spec.json +++ b/imxweb/projects/qer-app-portal/tsconfig.spec.json @@ -1,18 +1,10 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "strictNullChecks": false, "outDir": "../../out-tsc/spec", - "types": [ - "jasmine", - "node" - ] + "types": ["jasmine", "node"] }, - "files": [ - "src/test.ts", - "src/polyfills.ts" - ], - "include": [ - "**/*.spec.ts", - "**/*.d.ts" - ] + "files": ["src/test.ts", "src/polyfills.ts"], + "include": ["**/*.spec.ts", "**/*.d.ts"] } diff --git a/imxweb/projects/qer-app-portal/tslint.json b/imxweb/projects/qer-app-portal/tslint.json deleted file mode 100644 index 8bab15f53..000000000 --- a/imxweb/projects/qer-app-portal/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "imx", - "camelCase" - ], - "component-selector": [ - true, - "element", - "imx", - "kebab-case" - ] - } -} diff --git a/imxweb/projects/qer-app-pwdportal/.compodocrc.json b/imxweb/projects/qer-app-pwdportal/.compodocrc.json index b3353f21d..1f0abb84b 100644 --- a/imxweb/projects/qer-app-pwdportal/.compodocrc.json +++ b/imxweb/projects/qer-app-pwdportal/.compodocrc.json @@ -1,6 +1,6 @@ { "name": "IMX Web - Password Reset Portal", - "output": "../../documentation/v92/qer-app-pwdportal", + "output": "../../documentation/v93/qer-app-pwdportal", "theme": "material", "assetsFolder": "../../compodoc/assets", "customLogo": "../../compodoc/assets/images/oneidentity-logo.png", diff --git a/imxweb/projects/qer-app-pwdportal/.eslintrc.json b/imxweb/projects/qer-app-pwdportal/.eslintrc.json new file mode 100644 index 000000000..9b3d9538c --- /dev/null +++ b/imxweb/projects/qer-app-pwdportal/.eslintrc.json @@ -0,0 +1,35 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*", "**/*.spec.ts"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": ["imxweb/projects/qer-app-pwdportal/tsconfig.app.json", "imxweb/projects/qer-app-pwdportal/tsconfig.spec.json"], + "createDefaultProgram": true + }, + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "imx", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "imx", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "rules": {} + } + ] +} diff --git a/imxweb/projects/qer-app-pwdportal/karma.conf.js b/imxweb/projects/qer-app-pwdportal/karma.conf.js index b454fa9d7..80eeaba08 100644 --- a/imxweb/projects/qer-app-pwdportal/karma.conf.js +++ b/imxweb/projects/qer-app-pwdportal/karma.conf.js @@ -12,10 +12,10 @@ module.exports = function (config) { require('karma-jasmine-html-reporter'), require('karma-coverage-istanbul-reporter'), require('@angular-devkit/build-angular/plugins/karma'), - require('karma-junit-reporter') + require('karma-junit-reporter'), ], client: { - clearContext: false // leave Jasmine Spec Runner output visible in browser + clearContext: false, // leave Jasmine Spec Runner output visible in browser }, coverageIstanbulReporter: { dir: require('path').join(__dirname, 'results'), @@ -26,7 +26,7 @@ module.exports = function (config) { functions: 0, lines: 0, }, - fixWebpackSourcePaths: true + fixWebpackSourcePaths: true, }, junitReporter: { outputDir: require('path').join(__dirname, 'results'), @@ -35,13 +35,14 @@ module.exports = function (config) { port: 9876, captureTimeout: 210000, browserDisconnectTolerance: 3, - browserDisconnectTimeout : 210000, - browserNoActivityTimeout : 210000, + browserDisconnectTimeout: 210000, + browserNoActivityTimeout: 210000, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], singleRun: false, - restartOnFileChange: true + failOnEmptyTestSuite: false, + restartOnFileChange: true, }); }; diff --git a/imxweb/projects/qer-app-pwdportal/package.json b/imxweb/projects/qer-app-pwdportal/package.json index e98f32e1e..6cbd74a95 100644 --- a/imxweb/projects/qer-app-pwdportal/package.json +++ b/imxweb/projects/qer-app-pwdportal/package.json @@ -1,5 +1,5 @@ { "name": "qer-app-pwdportal", - "version": "9.2.1", + "version": "9.3.0", "private": true -} \ No newline at end of file +} diff --git a/imxweb/projects/qer-app-pwdportal/project.json b/imxweb/projects/qer-app-pwdportal/project.json new file mode 100644 index 000000000..11e8fa5f8 --- /dev/null +++ b/imxweb/projects/qer-app-pwdportal/project.json @@ -0,0 +1,211 @@ +{ + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "name": "qer-app-pwdportal", + "projectType": "application", + "sourceRoot": "projects/qer-app-pwdportal/src", + "prefix": "imx", + "generators": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "options": { + "allowedCommonJsDependencies": [ + "lodash", + "highlight.js", + "file-saver", + "billboard.js", + "moment-timezone", + "core-js/fn/map", + "core-js/fn/set", + "core-js/fn/weak-map", + "core-js/fn/array/from", + "core-js/fn/object/assign", + "core-js/es/array/from", + "core-js/es/object/assign", + "core-js/es/map", + "core-js/es/set", + "core-js/es/weak-map", + "lodash.debounce", + "lodash.clamp", + "moment", + "@elemental-ui/cadence-icon/codepoints" + ], + "outputPath": "dist/qer-app-pwdportal", + "index": "projects/qer-app-pwdportal/src/index.html", + "main": "projects/qer-app-pwdportal/src/main.ts", + "polyfills": ["projects/qer-app-pwdportal/src/polyfills.ts"], + "tsConfig": "projects/qer-app-pwdportal/tsconfig.app.json", + "aot": true, + "assets": [ + "projects/qer-app-pwdportal/src/appconfig.json", + "projects/qer-app-pwdportal/src/assets", + { + "glob": "**/*", + "input": "./shared/assets/", + "output": "./assets" + }, + { + "glob": "**/*", + "input": "./node_modules/@elemental-ui/core/assets", + "output": "./assets" + } + ], + "styles": [ + "shared/scss/styles.scss", + "projects/qer-app-pwdportal/src/styles.scss", + "projects/qbm/src/lib/styles/imx-page-title.scss" + ], + "stylePreprocessorOptions": { + "includePaths": [ + "./shared/scss", + "./node_modules", + "./node_modules/@elemental-ui/cadence-icon", + "./node_modules/@elemental-ui/core" + ] + } + }, + "configurations": { + "production": { + "fileReplacements": [ + { + "replace": "projects/qer-app-pwdportal/src/environments/environment.ts", + "with": "projects/qer-app-pwdportal/src/environments/environment.prod.ts" + } + ], + "optimization": true, + "outputHashing": "all", + "sourceMap": false, + "namedChunks": false, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": true, + "budgets": [ + { + "type": "initial", + "maximumWarning": "20mb", + "maximumError": "40mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "6kb" + } + ] + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + }, + "remote-dev": { + "fileReplacements": [ + { + "replace": "projects/qer-app-pwdportal/src/environments/environment.ts", + "with": "../imxweb_envs/qer-app-pwdportal/environments/environment.remote-dev.ts" + } + ], + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + }, + "remote-qs": { + "fileReplacements": [ + { + "replace": "projects/qer-app-pwdportal/src/environments/environment.ts", + "with": "../imxweb_envs/qer-app-pwdportal/environments/environment.remote-qs.ts" + } + ], + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + }, + "es5": { + "tsConfig": "./projects/qer-app-pwdportal/tsconfig-es5.app.json" + } + }, + "outputs": ["{options.outputPath}"], + "dependsOn": ["^build", "prebuild"] + }, + "prebuild": { + "executor": "nx:run-commands", + "options": { + "commands": ["node prebuild.js qer-app-pwdportal"] + }, + "dependsOn": [ + { + "projects": ["att"], + "target": "build" + } + ], + "cache": false + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "options": { + "browserTarget": "qer-app-pwdportal:build" + }, + "configurations": { + "production": { + "browserTarget": "qer-app-pwdportal:build:production" + }, + "development": { + "browserTarget": "qer-app-pwdportal:build:development" + }, + "remote-dev": { + "browserTarget": "qer-app-pwdportal:build:remote-dev" + }, + "remote-qs": { + "browserTarget": "qer-app-pwdportal:build:remote-qs" + }, + "es5": { + "browserTarget": "qer-app-pwdportal:build:es5" + } + } + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "qer-app-pwdportal:build" + } + }, + "test": { + "executor": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/qer-app-pwdportal/src/test.ts", + "polyfills": "projects/qer-app-pwdportal/src/polyfills.ts", + "tsConfig": "projects/qer-app-pwdportal/tsconfig.spec.json", + "karmaConfig": "projects/qer-app-pwdportal/karma.conf.js", + "assets": ["projects/qer-app-pwdportal/src/appconfig.json", "projects/qer-app-pwdportal/src/assets"], + "styles": ["projects/qer-app-pwdportal/src/styles.scss"], + "stylePreprocessorOptions": { + "includePaths": [ + "./shared/assets", + "./shared/scss", + "./node_modules", + "./node_modules/@elemental-ui/cadence-icon", + "./node_modules/@elemental-ui/core" + ] + } + } + }, + "lint": { + "executor": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": ["projects/qer-app-pwdportal/tsconfig.app.json", "projects/qer-app-pwdportal/tsconfig.spec.json"], + "exclude": ["**/node_modules/**"] + } + } + } +} diff --git a/imxweb/projects/qer-app-pwdportal/src/app/app-routing.module.ts b/imxweb/projects/qer-app-pwdportal/src/app/app-routing.module.ts index cc07e1b98..4d94ea670 100644 --- a/imxweb/projects/qer-app-pwdportal/src/app/app-routing.module.ts +++ b/imxweb/projects/qer-app-pwdportal/src/app/app-routing.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,9 +25,9 @@ */ import { InjectionToken, NgModule } from '@angular/core'; -import { Routes, RouterModule, ActivatedRouteSnapshot } from '@angular/router'; +import { ActivatedRouteSnapshot, RouterModule, Routes } from '@angular/router'; import { AuthenticationGuardService, LoginComponent, RouteGuardService } from 'qbm'; -import { PasswordDashboardComponent, PasswordResetComponent, PasswordQuestionsComponent } from 'qer'; +import { PasswordDashboardComponent, PasswordQuestionsModule, PasswordResetComponent } from 'qer'; const externalUrlProvider = new InjectionToken('externalUrlRedirectResolver'); @@ -36,31 +36,25 @@ const routes: Routes = [ path: '', component: LoginComponent, canActivate: [AuthenticationGuardService], - resolve: [RouteGuardService] + resolve: [RouteGuardService], }, { path: 'dashboard', component: PasswordDashboardComponent, canActivate: [RouteGuardService], - resolve: [RouteGuardService] + resolve: [RouteGuardService], }, { path: 'resetpassword', component: PasswordResetComponent, canActivate: [RouteGuardService], - resolve: [RouteGuardService] + resolve: [RouteGuardService], }, - { - path: 'password-questions', - component: PasswordQuestionsComponent, - canActivate: [RouteGuardService], - resolve: [RouteGuardService] - }, - { path: '**', redirectTo: 'dashboard' } + { path: '**', redirectTo: 'dashboard' }, ]; @NgModule({ - imports: [RouterModule.forRoot(routes, { useHash: true, relativeLinkResolution: 'legacy' })], + imports: [RouterModule.forRoot(routes, { useHash: true }), PasswordQuestionsModule], exports: [RouterModule], providers: [ { @@ -70,9 +64,8 @@ const routes: Routes = [ if (externalUrl && externalUrl.toLocaleLowerCase() !== 'undefined') { window.open(externalUrl, '_self'); } - } + }, }, ], }) - -export class AppRoutingModule { } +export class AppRoutingModule {} diff --git a/imxweb/projects/qer-app-pwdportal/src/app/app.component.html b/imxweb/projects/qer-app-pwdportal/src/app/app.component.html index 73aaaf49e..a2e4c8d4a 100644 --- a/imxweb/projects/qer-app-pwdportal/src/app/app.component.html +++ b/imxweb/projects/qer-app-pwdportal/src/app/app.component.html @@ -1,15 +1,14 @@
    - - - - - - - -
    - - -
    + + + + + + + +
    + +
    diff --git a/imxweb/projects/qer-app-pwdportal/src/app/app.component.scss b/imxweb/projects/qer-app-pwdportal/src/app/app.component.scss index c2f8a644d..86c7a5516 100644 --- a/imxweb/projects/qer-app-pwdportal/src/app/app.component.scss +++ b/imxweb/projects/qer-app-pwdportal/src/app/app.component.scss @@ -6,9 +6,21 @@ } .pageContent { - margin: 20px 30px; + margin: 24px; display: flex; flex-direction: column; overflow: hidden; height: inherit; + ::ng-deep { + imx-icon-tile { + imx-tile { + .mat-mdc-card { + height: 150px; + .mat-mdc-card-subtitle { + overflow: hidden; + } + } + } + } + } } diff --git a/imxweb/projects/qer-app-pwdportal/src/app/app.component.ts b/imxweb/projects/qer-app-pwdportal/src/app/app.component.ts index 1b1a537b4..83b0b1c72 100644 --- a/imxweb/projects/qer-app-pwdportal/src/app/app.component.ts +++ b/imxweb/projects/qer-app-pwdportal/src/app/app.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,16 +25,25 @@ */ import { Component, ErrorHandler, OnDestroy, OnInit } from '@angular/core'; -import { EventType, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, Router, RouterEvent } from '@angular/router'; +import { Event, EventType, NavigationEnd, NavigationStart, Router, RouterEvent } from '@angular/router'; import { Subscription } from 'rxjs'; -import { AppConfigService, AuthenticationService, ISessionState, ImxTranslationProviderService, SplashService } from 'qbm'; import { MatDialog } from '@angular/material/dialog'; +import { EuiTheme, EuiThemeService } from '@elemental-ui/core'; +import { ProfileSettings } from '@imx-modules/imx-api-qer'; +import { + AppConfigService, + AuthenticationService, + ClassloggerService, + ConfirmationService, + ImxTranslationProviderService, + ISessionState, + Message, + SplashService, + UserMessageService, +} from 'qbm'; import { QerApiService, SettingsComponent } from 'qer'; -import { EuiLoadingService, EuiTheme, EuiThemeService } from '@elemental-ui/core'; -import { ProfileSettings } from 'imx-api-qer'; import { getBaseHref, HEADLESS_BASEHREF } from './app.module'; -import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'imx-root', @@ -43,12 +52,13 @@ import { TranslateService } from '@ngx-translate/core'; }) export class AppComponent implements OnInit, OnDestroy { public isLoggedIn = false; - public hideUserMessage = false; public showPageContent = true; + public message: Message | undefined; private routerStatus: EventType; private readonly subscriptions: Subscription[] = []; constructor( + private readonly logger: ClassloggerService, private readonly authentication: AuthenticationService, private readonly router: Router, private readonly splash: SplashService, @@ -58,7 +68,8 @@ export class AppComponent implements OnInit, OnDestroy { private readonly themeService: EuiThemeService, private readonly errorHandler: ErrorHandler, private readonly translationProvider: ImxTranslationProviderService, - private readonly translateService: TranslateService + private readonly confirmationService: ConfirmationService, + private readonly userMessageService: UserMessageService, ) { this.subscriptions.push( this.authentication.onSessionResponse.subscribe(async (sessionState: ISessionState) => { @@ -71,15 +82,20 @@ export class AppComponent implements OnInit, OnDestroy { } } - this.isLoggedIn = sessionState.IsLoggedIn; + this.isLoggedIn = sessionState.IsLoggedIn ?? false; if (this.isLoggedIn) { - const isUseProfileLangChecked = (await this.qerClient.v2Client.passwordreset_profile_get()).UseProfileLanguage ?? false; - // Set session culture if isUseProfileLangChecked is true, set browser culture otherwise + const isUseProfileLangChecked = (await this.qerClient.client.passwordreset_profile_get()).UseProfileLanguage ?? false; + // Set session culture if isUseProfileLangChecked is true if (isUseProfileLangChecked) { - await this.translationProvider.init(sessionState.culture, sessionState.cultureFormat); - } else { - const browserCulture = this.translateService.getBrowserCultureLang(); - await this.translationProvider.init(browserCulture); + // Use culture if available, if not fetch + const culture = sessionState.culture + ? sessionState.culture + : (await this.qerClient.client.passwordreset_profile_person_get())?.ProfileLanguage; + // If culture is found, use it, otherwise fallback to the app default + if (culture) { + this.logger.debug(this, `ProfileLangChecked is true, culture available: Setting ${culture} as profile language`); + await this.translationProvider.reinit(culture, sessionState.cultureFormat ?? culture, this.router); + } } // Close the splash screen that opened in app service initialisation @@ -87,14 +103,25 @@ export class AppComponent implements OnInit, OnDestroy { this.splash.close(); this.applyProfileSettings(); } - }) + }), + ); + + this.subscriptions.push( + this.userMessageService.subject.subscribe((message) => { + this.message = message; + if (!!this.message && this.message.type === 'error' && !this.message.target) { + this.confirmationService.showErrorMessage({ + Message: this.message?.text, + }); + } + }), ); this.setupRouter(); } public async ngOnInit(): Promise { - this.authentication.update(); + await this.authentication.update(); } public ngOnDestroy(): void { @@ -114,35 +141,23 @@ export class AppComponent implements OnInit, OnDestroy { } private setupRouter(): void { - this.router.events.subscribe((event: RouterEvent) => { + this.router.events.subscribe((event: Event & RouterEvent) => { if (event instanceof NavigationStart) { - this.hideUserMessage = true; this.routerStatus = event.type; if (this.isLoggedIn) { if (event.url === '/') { // show the splash screen, when the user logs out! this.splash.init({ applicationName: 'Password Reset Portal' }); - } else if (event.url === `/${this.config.Config.routeConfig.start}`) { + } else if (event.url === `/${this.config.Config.routeConfig?.start}`) { // closes the splash-screen, if its displayed between Login and Dashboard this.splash.close(); } } } - if (event instanceof NavigationCancel) { - this.hideUserMessage = false; - this.routerStatus = event.type; - } - if (event instanceof NavigationEnd) { - this.hideUserMessage = false; - this.showPageContent = true; - this.routerStatus = event.type; - } - - if (event instanceof NavigationError) { - this.hideUserMessage = false; this.routerStatus = event.type; + this.showPageContent = true; } }); } diff --git a/imxweb/projects/qer-app-pwdportal/src/app/app.module.ts b/imxweb/projects/qer-app-pwdportal/src/app/app.module.ts index 75a8b0d47..f45cff2d6 100644 --- a/imxweb/projects/qer-app-pwdportal/src/app/app.module.ts +++ b/imxweb/projects/qer-app-pwdportal/src/app/app.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { HttpClientModule } from '@angular/common/http'; +import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; import { APP_INITIALIZER, ErrorHandler, NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; @@ -33,6 +33,8 @@ import { MissingTranslationHandler, TranslateLoader, TranslateModule, TranslateS import { LoggerModule, NgxLoggerLevel } from 'ngx-logger'; import { APP_BASE_HREF } from '@angular/common'; +import { MatPaginatorIntl } from '@angular/material/paginator'; +import { RECAPTCHA_V3_SITE_KEY } from 'ng-recaptcha-2'; import { AuthenticationModule, CustomThemeModule, @@ -41,18 +43,16 @@ import { ImxTranslateLoader, LdsReplacePipe, MastHeadModule, - MenuModule, Paginator, SqlWizardApiService, UserMessageModule, } from 'qbm'; +import { PasscodeLoginModule, PasswordModule, QaLoginModule, QerModule } from 'qer'; +import appConfigJson from '../appconfig.json'; import { environment } from '../environments/environment'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { AppService } from './app.service'; -import { MatPaginatorIntl } from '@angular/material/paginator'; -import { PasswordModule, QaLoginModule, PasscodeLoginModule, ProfileModule, QerModule } from 'qer'; -import appConfigJson from '../appconfig.json'; import { PwdSqlWizardApiService } from './pwd-sql-wizard-api.service'; export const HEADLESS_BASEHREF = '/headless'; @@ -61,6 +61,7 @@ export function getBaseHref(): string { } @NgModule({ declarations: [AppComponent], + bootstrap: [AppComponent], imports: [ AppRoutingModule, AuthenticationModule, @@ -68,10 +69,8 @@ export function getBaseHref(): string { BrowserModule, EuiCoreModule, EuiMaterialModule, - HttpClientModule, LoggerModule.forRoot({ level: NgxLoggerLevel.DEBUG, serverLogLevel: NgxLoggerLevel.OFF }), MastHeadModule, - MenuModule, QaLoginModule, QerModule, PasscodeLoginModule, @@ -88,7 +87,6 @@ export function getBaseHref(): string { }, }), UserMessageModule, - ProfileModule, ], providers: [ { provide: 'environment', useValue: environment }, @@ -116,7 +114,14 @@ export function getBaseHref(): string { provide: SqlWizardApiService, useClass: PwdSqlWizardApiService, }, + { + provide: RECAPTCHA_V3_SITE_KEY, + useFactory: (config: AppService) => { + return config.recaptchaSiteKeyV3; + }, + deps: [AppService], + }, + provideHttpClient(withInterceptorsFromDi()), ], - bootstrap: [AppComponent], }) export class AppModule {} diff --git a/imxweb/projects/qer-app-pwdportal/src/app/app.service.ts b/imxweb/projects/qer-app-pwdportal/src/app/app.service.ts index 744248e97..6a354846f 100644 --- a/imxweb/projects/qer-app-pwdportal/src/app/app.service.ts +++ b/imxweb/projects/qer-app-pwdportal/src/app/app.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,35 +24,30 @@ * */ -import { Injectable } from '@angular/core'; +import { Injectable, Injector, createNgModule } from '@angular/core'; import { Title } from '@angular/platform-browser'; import { TranslateService } from '@ngx-translate/core'; -import { Globals } from 'imx-qbm-dbts'; +import { TypedClient } from '@imx-modules/imx-api-qbm'; +import { Globals } from '@imx-modules/imx-qbm-dbts'; import { AppConfigService, - AuthenticationService, - imx_SessionService, + CaptchaService, CdrRegistryService, - ImxTranslationProviderService, ClassloggerService, - PluginLoaderService, + ImxTranslationProviderService, SplashService, - SystemInfoService + SystemInfoService, + imx_SessionService, } from 'qbm'; +import { PasswordService, QerApiService } from 'qer'; import { environment } from '../environments/environment'; -import { TypedClient } from 'imx-api-qbm'; - -import * as QBM from 'qbm'; -import * as QER from 'qer'; - -declare var SystemJS: any; - @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class AppService { + public recaptchaSiteKeyV3: string | null = null; constructor( public readonly registry: CdrRegistryService, private readonly logger: ClassloggerService, @@ -62,20 +57,21 @@ export class AppService { private readonly session: imx_SessionService, private readonly translationProvider: ImxTranslationProviderService, private readonly title: Title, - private readonly pluginLoader: PluginLoaderService, - private readonly authentication: AuthenticationService, + private readonly qerApi: QerApiService, + private readonly passwordService: PasswordService, private readonly splash: SplashService, - ) { } + private readonly captchaService: CaptchaService, + private readonly injector: Injector, + ) {} public async init(): Promise { this.showSplash(); await this.config.init(environment.clientUrl); - this.translateService.addLangs(this.config.Config.Translation.Langs); - const browserCulture = this.translateService.getBrowserCultureLang(); - this.logger.debug(this, `Set ${browserCulture} as default language`); - this.translateService.setDefaultLang(browserCulture); - await this.translateService.use(browserCulture).toPromise(); + if (this.config.Config.Translation?.Langs) { + this.translateService.addLangs(this.config.Config.Translation.Langs); + } + await this.translationProvider.init(); this.translateService.onLangChange.subscribe(() => { this.setTitle(); @@ -84,10 +80,18 @@ export class AppService { this.setTitle(); this.session.TypedClient = new TypedClient(this.config.v2client, this.translationProvider); - SystemJS.set('qbm', SystemJS.newModule(QBM)); - SystemJS.set('qer', SystemJS.newModule(QER)); - await this.pluginLoader.loadModules(environment.appName); + await this.loadModules(environment.appName); + + const featureConfig = await this.qerApi.v2Client.passwordreset_authconfig_get(); + + this.captchaService.captchaImageUrl = 'passwordreset/captchaimage'; + if (featureConfig.RecaptchaPublicKey) { + this.captchaService.enableReCaptcha(featureConfig.RecaptchaPublicKey); + this.recaptchaSiteKeyV3 = featureConfig.RecaptchaPublicKey; + } + + await this.passwordService.registerCustomAuthFlows(featureConfig); } private async setTitle(): Promise { @@ -118,4 +122,38 @@ export class AppService { const loadingMsg = await this.translateService.get('#LDS#Loading...').toPromise(); this.splash.update({ applicationName: title, message: loadingMsg }); } + + private async loadModules(appName: string): Promise { + const apps = await this.session.Client.imx_applications_get(); + + const appInfo = apps.filter((app) => app.Name === appName)[0]; + + this.logger.debug(this, `▶️ Found config section for ${appInfo.DisplayName}`); + + if (appInfo.PlugIns == null || appInfo.PlugIns.length === 0) { + this.logger.debug(this, `❌ No plugins found`); + return; + } + + this.logger.debug(this, `▶️ Found ${appInfo.PlugIns.length} plugin(s)`); + + for (const plugin of appInfo.PlugIns) { + this.logger.debug(this, `⚙️ Plugin: ${plugin.Container}`); + + try { + this.logger.debug(this, '▶️ Importing module. DEV mode.'); + await import(`html/qer-app-pwdportal/${plugin.Container}/fesm2022/${plugin.Container}.mjs`) + .then((m) => { + if (plugin.Name) { + createNgModule(m[plugin.Name], this.injector); + } + }) + .catch((error) => + this.logger.error(this, `💥 Loading of ${plugin.Name} (${plugin.Container}) failed with the following error: ${error.message}`), + ); + } catch (e) { + this.logger.error(this, `💥 Loading of ${plugin.Name} (${plugin.Container}) failed with the following error: ${e.message}`); + } + } + } } diff --git a/imxweb/projects/qer-app-pwdportal/src/app/pwd-sql-wizard-api.service.ts b/imxweb/projects/qer-app-pwdportal/src/app/pwd-sql-wizard-api.service.ts index 7cf64b37e..e86c914a6 100644 --- a/imxweb/projects/qer-app-pwdportal/src/app/pwd-sql-wizard-api.service.ts +++ b/imxweb/projects/qer-app-pwdportal/src/app/pwd-sql-wizard-api.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { Injectable } from '@angular/core'; -import { FilterProperty, CollectionLoadParameters, EntityCollectionData } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, EntityCollectionData, FilterProperty } from '@imx-modules/imx-qbm-dbts'; import { SqlWizardApiService } from 'qbm'; @Injectable({ @@ -37,7 +37,7 @@ export class PwdSqlWizardApiService extends SqlWizardApiService { getFilterProperties(table: string): Promise { return new Promise((resolve) => resolve([])); } - + getCandidates(parentTable: string, options?: CollectionLoadParameters): Promise { return new Promise((resolve) => resolve({ TotalCount: 0 })); } diff --git a/imxweb/projects/qer-app-pwdportal/src/environments/environment.prod.ts b/imxweb/projects/qer-app-pwdportal/src/environments/environment.prod.ts index ec3b6404f..eb3f27de9 100644 --- a/imxweb/projects/qer-app-pwdportal/src/environments/environment.prod.ts +++ b/imxweb/projects/qer-app-pwdportal/src/environments/environment.prod.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,5 +28,5 @@ export const environment = { production: true, clientUrl: '', appName: 'qer-app-pwdportal', - appVersion: '1.0.0' + appVersion: '1.0.0', }; diff --git a/imxweb/projects/qer-app-pwdportal/src/environments/environment.ts b/imxweb/projects/qer-app-pwdportal/src/environments/environment.ts index d49e085b6..7478e82ff 100644 --- a/imxweb/projects/qer-app-pwdportal/src/environments/environment.ts +++ b/imxweb/projects/qer-app-pwdportal/src/environments/environment.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -32,7 +32,7 @@ export const environment = { production: false, clientUrl: 'http://localhost:8182', appName: 'qer-app-pwdportal', - appVersion: '1.0.0' + appVersion: '1.0.0', }; /* @@ -42,4 +42,4 @@ export const environment = { * This import should be commented out in production mode because it will have a negative impact * on performance if an error is thrown. */ -// import 'zone.js/dist/zone-error'; // Included with Angular CLI. +// import 'zone.js'; // Included with Angular CLI. diff --git a/imxweb/projects/qer-app-pwdportal/src/index.html b/imxweb/projects/qer-app-pwdportal/src/index.html index 702cf2308..53a0e20ab 100644 --- a/imxweb/projects/qer-app-pwdportal/src/index.html +++ b/imxweb/projects/qer-app-pwdportal/src/index.html @@ -1,19 +1,17 @@ + + + + + + - - - - - - + + + +
    - - - -
    - - - - - \ No newline at end of file + + + diff --git a/imxweb/projects/qer-app-pwdportal/src/main.ts b/imxweb/projects/qer-app-pwdportal/src/main.ts index 5b7a3816d..8cb3e4f09 100644 --- a/imxweb/projects/qer-app-pwdportal/src/main.ts +++ b/imxweb/projects/qer-app-pwdportal/src/main.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -34,5 +34,6 @@ if (environment.production) { enableProdMode(); } -platformBrowserDynamic().bootstrapModule(AppModule) - .catch(err => console.error(err)); +platformBrowserDynamic() + .bootstrapModule(AppModule) + .catch((err) => console.error(err)); diff --git a/imxweb/projects/qer-app-pwdportal/src/polyfills.ts b/imxweb/projects/qer-app-pwdportal/src/polyfills.ts index 31ad3d496..9c1e09bff 100644 --- a/imxweb/projects/qer-app-pwdportal/src/polyfills.ts +++ b/imxweb/projects/qer-app-pwdportal/src/polyfills.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -41,9 +41,8 @@ */ /*************************************************************************************************** -* BROWSER POLYFILLS -*/ - + * BROWSER POLYFILLS + */ /** IE10 and IE11 requires the following for NgClass support on SVG elements */ // import 'classlist.js'; // Run `npm install --save classlist.js`. @@ -51,13 +50,12 @@ /** IE10 and IE11 requires the following for the Reflect API. */ // import 'core-js/es6/reflect'; - /** Evergreen browsers require these. */ // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. -import 'core-js/es7/reflect'; +import 'core-js/es/reflect'; // Used for support array.includes -import 'core-js/es7/array'; +import 'core-js/es/array'; /** * Required to support Web Animations `@angular/platform-browser/animations`. @@ -65,17 +63,13 @@ import 'core-js/es7/array'; */ import 'web-animations-js'; - - /*************************************************************************************************** * Zone JS is required by default for Angular itself. */ -import 'zone.js/dist/zone'; // Included with Angular CLI. - - +import 'zone.js'; // Included with Angular CLI. /*************************************************************************************************** * APPLICATION IMPORTS */ -import 'whatwg-fetch'; import 'url-polyfill'; +import 'whatwg-fetch'; diff --git a/imxweb/projects/qer-app-pwdportal/src/styles.scss b/imxweb/projects/qer-app-pwdportal/src/styles.scss index c4916f42e..c04561172 100644 --- a/imxweb/projects/qer-app-pwdportal/src/styles.scss +++ b/imxweb/projects/qer-app-pwdportal/src/styles.scss @@ -1,12 +1,7 @@ /* You can add global styles to this file, and also import other style files */ -@use '@angular/material' as mat; - -$material_icons_font_path: "~node_modules/@elemental-ui/core/assets/MaterialIcons"; -$cadence_font_path: "~node_modules/@elemental-ui/core/assets/Cadence"; -$source_sans_pro_font_path: "~node_modules/@elemental-ui/core/assets/Source_Sans_Pro"; - @import '@elemental-ui/core/src/styles/core.scss'; +@import 'components/toolbar'; .eui-dark-theme { .page { @@ -17,11 +12,6 @@ $source_sans_pro_font_path: "~node_modules/@elemental-ui/core/assets/Source_Sans color: $color-gray-5; background-color: $color-gray-80; } - .eui-sidesheet { - .mat-toolbar{ - background-color: $color-blue-80; - } - } } .eui-contrast-theme { @@ -33,9 +23,4 @@ $source_sans_pro_font_path: "~node_modules/@elemental-ui/core/assets/Source_Sans color: $color-gray-0; background-color: $color-gray-100; } - .eui-sidesheet { - .mat-toolbar{ - background-color: $color-blue-80; - } - } } diff --git a/imxweb/projects/qer-app-pwdportal/src/test.ts b/imxweb/projects/qer-app-pwdportal/src/test.ts index 46112a57e..f5b670cb3 100644 --- a/imxweb/projects/qer-app-pwdportal/src/test.ts +++ b/imxweb/projects/qer-app-pwdportal/src/test.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,26 +26,23 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone-testing'; +// Zone must be imported first. Be careful with prettier import organizing +import 'zone.js'; +import 'zone.js/testing'; + import { getTestBed } from '@angular/core/testing'; -import { - BrowserDynamicTestingModule, - platformBrowserDynamicTesting -} from '@angular/platform-browser-dynamic/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; declare const require: { - context(path: string, deep?: boolean, filter?: RegExp): { + context( + path: string, + deep?: boolean, + filter?: RegExp, + ): { keys(): string[]; (id: string): T; }; }; // First, initialize the Angular testing environment. -getTestBed().initTestEnvironment( - BrowserDynamicTestingModule, - platformBrowserDynamicTesting() -); -// Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); -// And load the modules. -context.keys().map(context); +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); diff --git a/imxweb/projects/qer-app-pwdportal/tsconfig-es5.app.json b/imxweb/projects/qer-app-pwdportal/tsconfig-es5.app.json index e1d0d6abd..036b8797e 100644 --- a/imxweb/projects/qer-app-pwdportal/tsconfig-es5.app.json +++ b/imxweb/projects/qer-app-pwdportal/tsconfig-es5.app.json @@ -1,6 +1,6 @@ { - "extends": "./tsconfig.app.json", - "compilerOptions": { - "target": "es2020" + "extends": "./tsconfig.app.json", + "compilerOptions": { + "target": "es2020" } -} \ No newline at end of file +} diff --git a/imxweb/projects/qer-app-pwdportal/tsconfig.app.json b/imxweb/projects/qer-app-pwdportal/tsconfig.app.json index d99eeed8a..640419855 100644 --- a/imxweb/projects/qer-app-pwdportal/tsconfig.app.json +++ b/imxweb/projects/qer-app-pwdportal/tsconfig.app.json @@ -1,14 +1,13 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "strictNullChecks": true, "outDir": "../../out-tsc/app", "types": [] }, - "files": [ - "src/main.ts", - "src/polyfills.ts" - ], - "include": [ - "projects/qer-app-pwdportal/src/**/*.d.ts" - ] + "angularCompilerOptions": { + "strictTemplates": true + }, + "files": ["src/main.ts", "src/polyfills.ts"], + "include": ["projects/qer-app-pwdportal/src/**/*.d.ts"] } diff --git a/imxweb/projects/qer-app-pwdportal/tsconfig.spec.json b/imxweb/projects/qer-app-pwdportal/tsconfig.spec.json index a809b0a66..143838d98 100644 --- a/imxweb/projects/qer-app-pwdportal/tsconfig.spec.json +++ b/imxweb/projects/qer-app-pwdportal/tsconfig.spec.json @@ -2,17 +2,8 @@ "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "../../out-tsc/spec", - "types": [ - "jasmine", - "node" - ] + "types": ["jasmine", "node"] }, - "files": [ - "src/test.ts", - "src/polyfills.ts" - ], - "include": [ - "**/*.spec.ts", - "**/*.d.ts" - ] + "files": ["src/test.ts", "src/polyfills.ts"], + "include": ["**/*.spec.ts", "**/*.d.ts"] } diff --git a/imxweb/projects/qer-app-pwdportal/tslint.json b/imxweb/projects/qer-app-pwdportal/tslint.json deleted file mode 100644 index 8bab15f53..000000000 --- a/imxweb/projects/qer-app-pwdportal/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "imx", - "camelCase" - ], - "component-selector": [ - true, - "element", - "imx", - "kebab-case" - ] - } -} diff --git a/imxweb/projects/qer/.compodocrc.json b/imxweb/projects/qer/.compodocrc.json index bc4741cb2..25da3e915 100644 --- a/imxweb/projects/qer/.compodocrc.json +++ b/imxweb/projects/qer/.compodocrc.json @@ -1,6 +1,6 @@ { "name": "IMX Web - QER Library", - "output": "../../documentation/v92/qer", + "output": "../../documentation/v93/qer", "theme": "material", "assetsFolder": "../../compodoc/assets", "customLogo": "../../compodoc/assets/images/oneidentity-logo.png", diff --git a/imxweb/projects/qer/.eslintrc.json b/imxweb/projects/qer/.eslintrc.json new file mode 100644 index 000000000..07dc1c3d2 --- /dev/null +++ b/imxweb/projects/qer/.eslintrc.json @@ -0,0 +1,35 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*", "**/*.spec.ts"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": ["imxweb/projects/qer/tsconfig.lib.json", "imxweb/projects/qer/tsconfig.spec.json"], + "createDefaultProgram": true + }, + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "imx", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "imx", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "rules": {} + } + ] +} diff --git a/imxweb/projects/qer/karma.conf.js b/imxweb/projects/qer/karma.conf.js index 211193c9d..3e53a3ac8 100644 --- a/imxweb/projects/qer/karma.conf.js +++ b/imxweb/projects/qer/karma.conf.js @@ -15,7 +15,7 @@ module.exports = function (config) { require('karma-junit-reporter'), ], client: { - clearContext: false // leave Jasmine Spec Runner output visible in browser + clearContext: false, // leave Jasmine Spec Runner output visible in browser }, coverageIstanbulReporter: { dir: require('path').join(__dirname, 'results'), @@ -23,7 +23,7 @@ module.exports = function (config) { fixWebpackSourcePaths: true, 'report-config': { html: { - subdir: 'coverage-html' + subdir: 'coverage-html', }, }, thresholds: { @@ -40,13 +40,14 @@ module.exports = function (config) { port: 9876, captureTimeout: 210000, browserDisconnectTolerance: 3, - browserDisconnectTimeout : 210000, - browserNoActivityTimeout : 210000, + browserDisconnectTimeout: 210000, + browserNoActivityTimeout: 210000, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], singleRun: false, - restartOnFileChange: true + failOnEmptyTestSuite: false, + restartOnFileChange: true, }); }; diff --git a/imxweb/projects/qer/ng-package.json b/imxweb/projects/qer/ng-package.json index 63a6e5dbc..06284498a 100644 --- a/imxweb/projects/qer/ng-package.json +++ b/imxweb/projects/qer/ng-package.json @@ -3,7 +3,7 @@ "dest": "../../dist/qer", "lib": { "entryFile": "src/public_api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] }, "deleteDestPath": false } diff --git a/imxweb/projects/qer/package.json b/imxweb/projects/qer/package.json index 831c0fbdb..86129c1c9 100644 --- a/imxweb/projects/qer/package.json +++ b/imxweb/projects/qer/package.json @@ -1,12 +1,9 @@ { "name": "qer", - "version": "9.2.1", + "version": "9.3.0", "private": true, - "dependencies": { - - }, - "peerDependencies": { - }, + "dependencies": {}, + "peerDependencies": {}, "bundledDependencies": [ "imx-api-qer" ] diff --git a/imxweb/projects/qer/project.json b/imxweb/projects/qer/project.json new file mode 100644 index 000000000..cbc81a10c --- /dev/null +++ b/imxweb/projects/qer/project.json @@ -0,0 +1,61 @@ +{ + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "name": "qer", + "sourceRoot": "projects/qer/src", + "implicitDependencies": ["qbm"], + "projectType": "library", + "prefix": "imx", + "generators": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:ng-packagr", + "options": { + "tsConfig": "projects/qer/tsconfig.lib.json", + "project": "projects/qer/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "projects/qer/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "projects/qer/tsconfig.lib.json" + }, + "remote-dev": { + "tsConfig": "projects/qer/tsconfig.lib.json" + }, + "remote-qs": { + "tsConfig": "projects/qer/tsconfig.lib.json" + } + }, + "outputs": ["{workspaceRoot}/dist/qer"] + }, + "test": { + "executor": "@angular-devkit/build-angular:karma", + "options": { + "stylePreprocessorOptions": { + "includePaths": [ + "./shared/assets", + "./shared/scss", + "./node_modules", + "./node_modules/@elemental-ui/cadence-icon", + "./node_modules/@elemental-ui/core" + ] + }, + "main": "projects/qer/src/test.ts", + "tsConfig": "projects/qer/tsconfig.spec.json", + "karmaConfig": "projects/qer/karma.conf.js" + } + }, + "lint": { + "executor": "@angular-devkit/build-angular:eslint", + "options": { + "tsConfig": ["projects/qer/tsconfig.lib.json", "projects/qer/tsconfig.spec.json"], + "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + } + } + } +} diff --git a/imxweb/projects/qer/src/default-mocks.spec.ts b/imxweb/projects/qer/src/default-mocks.spec.ts index 6d2d0e5f1..fbcbe85bb 100644 --- a/imxweb/projects/qer/src/default-mocks.spec.ts +++ b/imxweb/projects/qer/src/default-mocks.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,7 +28,7 @@ import { of, Subject } from 'rxjs'; import { ngMocks } from 'ng-mocks'; import { EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { ISessionState,AuthenticationService,RouteGuardService } from 'qbm'; +import { ISessionState, AuthenticationService, RouteGuardService } from 'qbm'; export class QerDefaultMocks { public static readonly afterClosedSubject = new Subject(); diff --git a/imxweb/projects/qer/src/lib/about/portal-about.service.ts b/imxweb/projects/qer/src/lib/about/portal-about.service.ts new file mode 100644 index 000000000..d8de2d2e6 --- /dev/null +++ b/imxweb/projects/qer/src/lib/about/portal-about.service.ts @@ -0,0 +1,55 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Injectable } from '@angular/core'; +import { PortalSysteminfoThirdparty } from '@imx-modules/imx-api-qer'; +import { CollectionLoadParameters, EntitySchema, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; +import { AboutService, ApiClientService } from 'qbm'; +import { QerApiService } from '../qer-api-client.service'; + +@Injectable({ + providedIn: 'root', +}) +export class PortalAboutService extends AboutService { + constructor( + private readonly apiClient: QerApiService, + private apiProvider: ApiClientService, + ) { + super(); + } + + get EntitySchema(): EntitySchema { + return this.apiClient.typedClient.PortalSysteminfoThirdparty.GetSchema(); + } + + async get(parameters?: CollectionLoadParameters): Promise | undefined> { + return this.apiProvider.request(() => + this.apiClient.typedClient.PortalSysteminfoThirdparty.Get(parameters, { + signal: this.abortController.signal, + }), + ); + } +} diff --git a/imxweb/projects/qer/src/lib/addressbook/addressbook-detail/addressbook-detail.component.html b/imxweb/projects/qer/src/lib/addressbook/addressbook-detail/addressbook-detail.component.html index dded405e8..068ad0955 100644 --- a/imxweb/projects/qer/src/lib/addressbook/addressbook-detail/addressbook-detail.component.html +++ b/imxweb/projects/qer/src/lib/addressbook/addressbook-detail/addressbook-detail.component.html @@ -14,4 +14,3 @@
    - diff --git a/imxweb/projects/qer/src/lib/addressbook/addressbook-detail/addressbook-detail.component.scss b/imxweb/projects/qer/src/lib/addressbook/addressbook-detail/addressbook-detail.component.scss deleted file mode 100644 index 19f31db09..000000000 --- a/imxweb/projects/qer/src/lib/addressbook/addressbook-detail/addressbook-detail.component.scss +++ /dev/null @@ -1,8 +0,0 @@ -@import '@elemental-ui/core/src/styles/_palette.scss'; - -:host { - .eui-sidesheet-content { - display: flex; - flex-direction: column; - } -} diff --git a/imxweb/projects/qer/src/lib/addressbook/addressbook-detail/addressbook-detail.component.ts b/imxweb/projects/qer/src/lib/addressbook/addressbook-detail/addressbook-detail.component.ts index 53f11d5f4..e8a3a50db 100644 --- a/imxweb/projects/qer/src/lib/addressbook/addressbook-detail/addressbook-detail.component.ts +++ b/imxweb/projects/qer/src/lib/addressbook/addressbook-detail/addressbook-detail.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,7 +26,7 @@ import { Component, Inject, OnInit } from '@angular/core'; import { Router } from '@angular/router'; -import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; import { ProjectConfigurationService } from '../../project-configuration/project-configuration.service'; import { AddressbookDetail } from './addressbook-detail.interface'; @@ -34,19 +34,18 @@ import { AddressbookDetail } from './addressbook-detail.interface'; @Component({ selector: 'imx-addressbook-detail', templateUrl: './addressbook-detail.component.html', - styleUrls: ['./addressbook-detail.component.scss'] }) export class AddressbookDetailComponent implements OnInit { constructor( @Inject(EUI_SIDESHEET_DATA) public readonly data: AddressbookDetail, private readonly qerConfig: ProjectConfigurationService, public readonly sidesheetRef: EuiSidesheetRef, - public readonly router: Router - ) { } + public readonly router: Router, + ) {} public isShowOrgChart: boolean; ngOnInit(): void { - this.qerConfig.getConfig().then(config => this.isShowOrgChart = config.PersonConfig.ShowOrgChart); + this.qerConfig.getConfig().then((config) => (this.isShowOrgChart = config.PersonConfig?.ShowOrgChart ?? false)); } } diff --git a/imxweb/projects/qer/src/lib/addressbook/addressbook-detail/addressbook-detail.interface.ts b/imxweb/projects/qer/src/lib/addressbook/addressbook-detail/addressbook-detail.interface.ts index 969f46c63..15f2a70f7 100644 --- a/imxweb/projects/qer/src/lib/addressbook/addressbook-detail/addressbook-detail.interface.ts +++ b/imxweb/projects/qer/src/lib/addressbook/addressbook-detail/addressbook-detail.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { IEntityColumn } from 'imx-qbm-dbts'; +import { IEntityColumn } from '@imx-modules/imx-qbm-dbts'; export interface AddressbookDetail { columns: IEntityColumn[]; diff --git a/imxweb/projects/qer/src/lib/addressbook/addressbook.component.html b/imxweb/projects/qer/src/lib/addressbook/addressbook.component.html index 4d3819ac9..3db28186c 100644 --- a/imxweb/projects/qer/src/lib/addressbook/addressbook.component.html +++ b/imxweb/projects/qer/src/lib/addressbook/addressbook.component.html @@ -1,31 +1,15 @@ -

    - {{ '#LDS#Heading Address Book' | translate }} - -

    - - - - -
    - - -
    - +
    +

    + {{ '#LDS#Heading Address Book' | translate }} + +

    + +
    + + + diff --git a/imxweb/projects/qer/src/lib/addressbook/addressbook.component.scss b/imxweb/projects/qer/src/lib/addressbook/addressbook.component.scss index 9f9a7d75f..5e346edf2 100644 --- a/imxweb/projects/qer/src/lib/addressbook/addressbook.component.scss +++ b/imxweb/projects/qer/src/lib/addressbook/addressbook.component.scss @@ -1,13 +1,8 @@ -@import '../../../../../shared/scss/common-table.scss'; +@import 'base/mixins'; + :host { - @include imx-flex-fill-control-hidden-overflow(); + @include flex-column-container-fill(); height: 100%; - - .mat-card { - @include imx-flex-fill-control-hidden-overflow(); - margin-right: 3px; - margin-bottom: 3px; - } } .imx-addressbook-table { diff --git a/imxweb/projects/qer/src/lib/addressbook/addressbook.component.ts b/imxweb/projects/qer/src/lib/addressbook/addressbook.component.ts index c94b32d0f..56f0447b4 100644 --- a/imxweb/projects/qer/src/lib/addressbook/addressbook.component.ts +++ b/imxweb/projects/qer/src/lib/addressbook/addressbook.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,15 +25,31 @@ */ import { Component, OnInit } from '@angular/core'; -import { OverlayRef } from '@angular/cdk/overlay'; import { EuiLoadingService, EuiSidesheetConfig, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { DataSourceToolbarSettings, ClassloggerService, SettingsService, DataSourceWrapper, DataTableGroupedData, BusyService } from 'qbm'; -import { CollectionLoadParameters } from 'imx-qbm-dbts'; -import { PersonConfig, PortalPersonAll } from 'imx-api-qer'; - +import { PersonConfig, PortalPersonAll, ViewConfigData } from '@imx-modules/imx-api-qer'; +import { + CollectionLoadParameters, + DataModel, + DisplayColumns, + EntitySchema, + IClientProperty, + TypedEntityCollectionData, +} from '@imx-modules/imx-qbm-dbts'; +import { + BusyService, + ClassloggerService, + DataSourceToolbarViewConfig, + DataViewInitParameters, + DataViewSource, + SettingsService, + calculateSidesheetWidth, +} from 'qbm'; + +import { PersonService } from '../person/person.service'; import { ProjectConfigurationService } from '../project-configuration/project-configuration.service'; +import { ViewConfigService } from '../view-config/view-config.service'; import { AddressbookDetailComponent } from './addressbook-detail/addressbook-detail.component'; import { AddressbookService } from './addressbook.service'; @@ -44,18 +60,17 @@ import { AddressbookService } from './addressbook.service'; selector: 'imx-addressbook', templateUrl: './addressbook.component.html', styleUrls: ['./addressbook.component.scss'], + providers: [DataViewSource], }) export class AddressbookComponent implements OnInit { - /** - * Settings needed by the DataSourceToolbarComponent - */ - public dstSettings: DataSourceToolbarSettings; - - public groupData: { [key: string]: DataTableGroupedData } = {}; + public displayedColumns: IClientProperty[]; + public dataModel: DataModel; + public entitySchema: EntitySchema; public busyService = new BusyService(); - private personConfig: PersonConfig; - private dstWrapper: DataSourceWrapper; + private viewConfig: DataSourceToolbarViewConfig; + private personConfig: PersonConfig | undefined; + private viewConfigPath = 'person/all'; constructor( private readonly euiBusyService: EuiLoadingService, @@ -64,7 +79,10 @@ export class AddressbookComponent implements OnInit { private readonly settingsService: SettingsService, private readonly addressbookService: AddressbookService, private readonly sidesheet: EuiSidesheetService, - private readonly translateService: TranslateService + private readonly translateService: TranslateService, + private readonly personService: PersonService, + private readonly viewConfigService: ViewConfigService, + public dataSource: DataViewSource, ) {} public async ngOnInit(): Promise { @@ -72,41 +90,29 @@ export class AddressbookComponent implements OnInit { try { this.personConfig = (await this.configService.getConfig()).PersonConfig; - - this.dstWrapper = await this.addressbookService.createDataSourceWrapper( - this.personConfig.VI_MyData_WhitePages_ResultAttributes, - 'address-book' - ); - - this.dstSettings = await this.dstWrapper.getDstSettings({ PageSize: this.settingsService.DefaultPageSize, StartIndex: 0 }); - } finally { - isBusy.endBusy(); - } - } - - /** - * Occurs when the navigation state has changed - e.g. users clicks on the next page button. - * - */ - public async onNavigationStateChanged(newState: CollectionLoadParameters): Promise { - const isBusy = this.busyService.beginBusy(); - - try { - this.dstSettings = await this.dstWrapper.getDstSettings(newState); - } finally { - isBusy.endBusy(); - } - } - - public async onGroupingChange(groupKey: string): Promise { - const isBusy = this.busyService.beginBusy(); - - try { - const groupData = this.groupData[groupKey]; - groupData.settings = await this.dstWrapper.getGroupDstSettings(groupData.navigationState); - groupData.settings.dataModel = this.dstSettings.dataModel; - groupData.settings.entitySchema = this.dstSettings.entitySchema; - groupData.data = groupData.settings.dataSource; + this.dataModel = await this.personService.getDataModel(); + this.entitySchema = this.personService.schemaPersonAll; + this.displayedColumns = [ + this.entitySchema.Columns[DisplayColumns.DISPLAY_PROPERTYNAME], + ...(this.personConfig?.VI_MyData_WhitePages_ResultAttributes ?? []) + .filter((columnName) => this.entitySchema.Columns[columnName]) + .map((columnName) => this.entitySchema.Columns[columnName]), + ]; + this.viewConfig = await this.viewConfigService.getInitialDSTExtension(this.dataModel, this.viewConfigPath); + const dataViewInitParameters: DataViewInitParameters = { + execute: (params: CollectionLoadParameters, signal: AbortSignal): Promise> => + this.personService.getAll(params, signal), + schema: this.entitySchema, + columnsToDisplay: this.displayedColumns, + dataModel: this.dataModel, + highlightEntity: (entity: PortalPersonAll) => { + this.onHighlightedEntityChanged(entity); + }, + groupExecute: (columnName: string, parameters: CollectionLoadParameters, signal: AbortSignal) => + this.personService.getGroupInfo({ ...parameters, by: columnName }), + viewConfig: this.viewConfig, + }; + this.dataSource.init(dataViewInitParameters); } finally { isBusy.endBusy(); } @@ -121,34 +127,37 @@ export class AddressbookComponent implements OnInit { this.logger.debug(this, `Selected person changed`); this.logger.trace(this, 'New selected person', personAll); - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.euiBusyService.show())); + if (this.euiBusyService.overlayRefs.length === 0) { + this.euiBusyService.show(); + } let config: EuiSidesheetConfig; try { config = { - title: await this.translateService.get('#LDS#Heading View Identity Details').toPromise(), + title: this.translateService.instant('#LDS#Heading View Identity Details'), subTitle: personAll.GetEntity().GetDisplay(), padding: '0', - width: 'max(600px, 60%)', + width: calculateSidesheetWidth(), testId: 'addressbook-view-identity-details', - data: await this.addressbookService.getDetail(personAll, this.personConfig.VI_MyData_WhitePages_DetailAttributes), + data: await this.addressbookService.getDetail(personAll, this.personConfig?.VI_MyData_WhitePages_DetailAttributes ?? []), }; } finally { - setTimeout(() => this.euiBusyService.hide(overlayRef)); + this.euiBusyService.hide(); } this.sidesheet.open(AddressbookDetailComponent, config); } - public async onSearch(search: string): Promise { - const isBusy = this.busyService.beginBusy(); + public async updateConfig(config: ViewConfigData): Promise { + await this.viewConfigService.putViewConfig(config); + this.viewConfig = await this.viewConfigService.getDSTExtensionChanges(this.viewConfigPath); + this.dataSource.viewConfig.set(this.viewConfig); + } - try { - this.dstSettings = await this.dstWrapper.getDstSettings({ StartIndex: 0, search }); - } finally { - isBusy.endBusy(); - } + public async deleteConfigById(id: string): Promise { + await this.viewConfigService.deleteViewConfig(id); + this.viewConfig = await this.viewConfigService.getDSTExtensionChanges(this.viewConfigPath); + this.dataSource.viewConfig.set(this.viewConfig); } } diff --git a/imxweb/projects/qer/src/lib/addressbook/addressbook.module.ts b/imxweb/projects/qer/src/lib/addressbook/addressbook.module.ts index 2d146203a..9637c7bba 100644 --- a/imxweb/projects/qer/src/lib/addressbook/addressbook.module.ts +++ b/imxweb/projects/qer/src/lib/addressbook/addressbook.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,21 +24,30 @@ * */ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; +import { MatExpansionModule } from '@angular/material/expansion'; import { RouterModule, Routes } from '@angular/router'; import { EuiCoreModule } from '@elemental-ui/core'; -import { MatExpansionModule } from '@angular/material/expansion'; import { TranslateModule } from '@ngx-translate/core'; -import { DataSourceToolbarModule, DataTableModule, CdrModule, RouteGuardService, HELP_CONTEXTUAL, HelpContextualModule } from 'qbm'; +import { + CdrModule, + DataSourceToolbarModule, + DataTableModule, + DataViewModule, + HELP_CONTEXTUAL, + HelpContextualModule, + RouteGuardService, +} from 'qbm'; -import { AddressbookComponent } from './addressbook.component'; -import { AddressbookDetailComponent } from './addressbook-detail/addressbook-detail.component'; +import { MatTableModule } from '@angular/material/table'; import { OrgChartModule } from '../org-chart/org-chart.module'; +import { AddressbookDetailComponent } from './addressbook-detail/addressbook-detail.component'; +import { AddressbookComponent } from './addressbook.component'; const routes: Routes = [ { @@ -46,9 +55,9 @@ const routes: Routes = [ component: AddressbookComponent, canActivate: [RouteGuardService], resolve: [RouteGuardService], - data:{ - contextId: HELP_CONTEXTUAL.Addressbook - } + data: { + contextId: HELP_CONTEXTUAL.Addressbook, + }, }, ]; @@ -69,6 +78,8 @@ const routes: Routes = [ OrgChartModule, ReactiveFormsModule, HelpContextualModule, + DataViewModule, + MatTableModule, ], exports: [AddressbookComponent], }) diff --git a/imxweb/projects/qer/src/lib/addressbook/addressbook.service.ts b/imxweb/projects/qer/src/lib/addressbook/addressbook.service.ts index f506a97ed..c9cd9eb64 100644 --- a/imxweb/projects/qer/src/lib/addressbook/addressbook.service.ts +++ b/imxweb/projects/qer/src/lib/addressbook/addressbook.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,41 +26,17 @@ import { Injectable } from '@angular/core'; -import { PortalPersonAll } from 'imx-api-qer'; -import { DisplayColumns } from 'imx-qbm-dbts'; -import { DataSourceWrapper } from 'qbm'; +import { TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { PersonService } from '../person/person.service'; import { AddressbookDetail } from './addressbook-detail/addressbook-detail.interface'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class AddressbookService { - constructor(private readonly personService: PersonService) { } + constructor(private readonly personService: PersonService) {} - public async createDataSourceWrapper(columnNames: string[], identifier?: string): Promise { - - const entitySchema = this.personService.schemaPersonAll; - - const displayedColumns = columnNames - .filter(columnName => entitySchema.Columns[columnName]) - .map(columnName => entitySchema.Columns[columnName]); - displayedColumns.unshift(entitySchema.Columns[DisplayColumns.DISPLAY_PROPERTYNAME]); - - return new DataSourceWrapper( - state => this.personService.getAll(state), - displayedColumns, - entitySchema, - { - dataModel: await this.personService.getDataModel(), - getGroupInfo: parameters => this.personService.getGroupInfo(parameters), - groupingFilterOptions: ['withmanager', 'orphaned'] - }, - identifier - ); - } - - public async getDetail(personAll: PortalPersonAll, columnNames: string[]): Promise { + public async getDetail(personAll: TypedEntity, columnNames: string[]): Promise { const personUid = personAll.GetEntity().GetKeys()[0]; const personDetailEntity = (await this.personService.get(personUid)).Data[0].GetEntity(); @@ -69,9 +45,9 @@ export class AddressbookService { return { columns: columnNames - .filter(columnName => entitySchema.Columns[columnName]) - .map(columnName => personDetailEntity.GetColumn(columnName)), - personUid + .filter((columnName) => entitySchema.Columns[columnName]) + .map((columnName) => personDetailEntity.GetColumn(columnName)), + personUid, }; } } diff --git a/imxweb/projects/qer/src/lib/admin/authentication-factors.interface.ts b/imxweb/projects/qer/src/lib/admin/authentication-factors.interface.ts index 08cd58bee..8884e7005 100644 --- a/imxweb/projects/qer/src/lib/admin/authentication-factors.interface.ts +++ b/imxweb/projects/qer/src/lib/admin/authentication-factors.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qer/src/lib/admin/feature-config.service.ts b/imxweb/projects/qer/src/lib/admin/feature-config.service.ts index 83ac81276..14d4eca40 100644 --- a/imxweb/projects/qer/src/lib/admin/feature-config.service.ts +++ b/imxweb/projects/qer/src/lib/admin/feature-config.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,17 +25,15 @@ */ import { Injectable } from '@angular/core'; -import { FeatureConfig } from 'imx-api-qer'; -import { MethodDescriptor, TimeZoneInfo } from 'imx-qbm-dbts'; +import { FeatureConfig } from '@imx-modules/imx-api-qer'; +import { MethodDescriptor, TimeZoneInfo } from '@imx-modules/imx-qbm-dbts'; import { AppConfigService } from 'qbm'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class FeatureConfigService { - - constructor(private readonly config: AppConfigService) { } - + constructor(private readonly config: AppConfigService) {} public async getFeatureConfig(): Promise { const data = await this.config.apiClient.processRequest(this.getFeatureConfigDescriptor()); @@ -49,7 +47,7 @@ export class FeatureConfigService { parameters, method: 'GET', headers: { - 'imx-timezone': TimeZoneInfo.get() + 'imx-timezone': TimeZoneInfo.get(), }, credentials: 'include', observe: 'response', diff --git a/imxweb/projects/qer/src/lib/admin/qer-permissions-helper.ts b/imxweb/projects/qer/src/lib/admin/qer-permissions-helper.ts index 3950d2a97..58b024ef9 100644 --- a/imxweb/projects/qer/src/lib/admin/qer-permissions-helper.ts +++ b/imxweb/projects/qer/src/lib/admin/qer-permissions-helper.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -64,15 +64,12 @@ export function isStructStatistics(features: string[]): boolean { export function isCancelPwO(features: string[]): boolean { return features.find((item) => item === 'QER_CancelPwO') != null; } -export function isPasswordHelpdesk(features: string[]): boolean { - return features.find((item) => item === 'Portal_UI_PasswordHelpdesk') != null; -} export function isStatistics(features: string[]): boolean { return features.find((item) => item === 'Portal_UI_TSBStatistics') != null; } +export function isHyperviewNavigation(features: string[]): boolean { + return features.find((item) => item === 'Portal_HyperView_Navigation') != null; +} export function isAuditor(groups: string[]): boolean { return groups.find((item) => item.toUpperCase() === 'VI_4_AUDITING_AUDITOR') != null; } -export function isTsbNameSpaceAdminBase(groups: string[]): boolean { - return groups.find((item) => item.toUpperCase() === 'TSB_4_NAMESPACEADMIN_BASE') != null; -} diff --git a/imxweb/projects/qer/src/lib/admin/qer-permissions.service.ts b/imxweb/projects/qer/src/lib/admin/qer-permissions.service.ts index 5df25ee12..6243d881e 100644 --- a/imxweb/projects/qer/src/lib/admin/qer-permissions.service.ts +++ b/imxweb/projects/qer/src/lib/admin/qer-permissions.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,8 +28,10 @@ import { Injectable } from '@angular/core'; import { UserModelService } from '../user/user-model.service'; import { + hasFeatures, + isAuditor, isCancelPwO, - isPasswordHelpdesk, + isHyperviewNavigation, isPersonAdmin, isPersonManager, isResourceAdmin, @@ -38,12 +40,9 @@ import { isRuleAdmin, isShopAdmin, isShopStatistics, + isStatistics, isStructAdmin, isStructStatistics, - hasFeatures, - isAuditor, - isStatistics, - isTsbNameSpaceAdminBase, } from './qer-permissions-helper'; @Injectable({ @@ -53,51 +52,48 @@ export class QerPermissionsService { constructor(private readonly userService: UserModelService) {} public async isPersonAdmin(): Promise { - return isPersonAdmin((await this.userService.getFeatures()).Features); + return isPersonAdmin((await this.userService.getFeatures()).Features || []); } public async isPersonManager(): Promise { - return isPersonManager((await this.userService.getFeatures()).Features); + return isPersonManager((await this.userService.getFeatures()).Features || []); } public async isStructAdmin(): Promise { - return isStructAdmin((await this.userService.getFeatures()).Features); + return isStructAdmin((await this.userService.getFeatures()).Features || []); } public async isShopAdmin(): Promise { - return isShopAdmin((await this.userService.getFeatures()).Features); + return isShopAdmin((await this.userService.getFeatures()).Features || []); } public async isRuleAdmin(): Promise { - return isRuleAdmin((await this.userService.getFeatures()).Features); + return isRuleAdmin((await this.userService.getFeatures()).Features || []); } public async isCancelPwO(): Promise { - return isCancelPwO((await this.userService.getFeatures()).Features); + return isCancelPwO((await this.userService.getFeatures()).Features || []); } public async isResourceAdmin(): Promise { - return isResourceAdmin((await this.userService.getFeatures()).Features); + return isResourceAdmin((await this.userService.getFeatures()).Features || []); } public async isRoleAdmin(): Promise { - return isRoleAdmin((await this.userService.getFeatures()).Features); + return isRoleAdmin((await this.userService.getFeatures()).Features || []); } public async hasFeatures(features: string[]): Promise { - return hasFeatures((await this.userService.getFeatures()).Features, features); + return hasFeatures((await this.userService.getFeatures()).Features || [], features); } public async isRoleStatistics(): Promise { - return isRoleStatistics((await this.userService.getFeatures()).Features); + return isRoleStatistics((await this.userService.getFeatures()).Features || []); } public async isShopStatistics(): Promise { - return isShopStatistics((await this.userService.getFeatures()).Features); + return isShopStatistics((await this.userService.getFeatures()).Features || []); } public async isStructStatistics(): Promise { - return isStructStatistics((await this.userService.getFeatures()).Features); - } - public async isPasswordHelpdesk(): Promise { - return isPasswordHelpdesk((await this.userService.getFeatures()).Features); + return isStructStatistics((await this.userService.getFeatures()).Features || []); } public async isStatistics(): Promise { - return isStatistics((await this.userService.getFeatures()).Features); + return isStatistics((await this.userService.getFeatures()).Features || []); } - public async isAuditor(): Promise { - return isAuditor((await this.userService.getGroups()).map((group) => group.Name)); + public async isHyperviewNavigation(): Promise { + return isHyperviewNavigation((await this.userService.getFeatures()).Features || []); } - public async isTsbNameSpaceAdminBase(): Promise { - return isTsbNameSpaceAdminBase((await this.userService.getGroups()).map((group) => group.Name)); + public async isAuditor(): Promise { + return isAuditor((await this.userService.getGroups()).map((group) => group?.Name || '')); } } diff --git a/imxweb/projects/qer/src/lib/approval-workflows/approval-level-form/approval-level-form.component.html b/imxweb/projects/qer/src/lib/approval-workflows/approval-level-form/approval-level-form.component.html index b34cc463e..995cc6a95 100644 --- a/imxweb/projects/qer/src/lib/approval-workflows/approval-level-form/approval-level-form.component.html +++ b/imxweb/projects/qer/src/lib/approval-workflows/approval-level-form/approval-level-form.component.html @@ -1,24 +1,36 @@
    - + {{ requestData.HelpText }}
    - - + +
    - -
    diff --git a/imxweb/projects/qer/src/lib/approval-workflows/approval-level-form/approval-level-form.component.scss b/imxweb/projects/qer/src/lib/approval-workflows/approval-level-form/approval-level-form.component.scss index 987ded229..32ef97297 100644 --- a/imxweb/projects/qer/src/lib/approval-workflows/approval-level-form/approval-level-form.component.scss +++ b/imxweb/projects/qer/src/lib/approval-workflows/approval-level-form/approval-level-form.component.scss @@ -1,3 +1,3 @@ -@import '../approval-workflow-styles.scss'; +@import 'base/mixins'; @include Awm-Form-Sidesheet; diff --git a/imxweb/projects/qer/src/lib/approval-workflows/approval-level-form/approval-level-form.component.ts b/imxweb/projects/qer/src/lib/approval-workflows/approval-level-form/approval-level-form.component.ts index 803b48a4a..8151b7904 100644 --- a/imxweb/projects/qer/src/lib/approval-workflows/approval-level-form/approval-level-form.component.ts +++ b/imxweb/projects/qer/src/lib/approval-workflows/approval-level-form/approval-level-form.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -35,14 +35,14 @@ import { FormDataService } from '../form-data.service'; @Component({ selector: 'imx-approval-level-form', templateUrl: './approval-level-form.component.html', - styleUrls: ['./approval-level-form.component.scss'] + styleUrls: ['./approval-level-form.component.scss'], }) export class ApprovalLevelFormComponent implements OnInit, OnDestroy { public readonly formGroup: FormGroup; public cdrList: ColumnDependentReference[] = []; public isInActiveFormControl = new FormControl(); public initialState: { - [key: string]: any + [key: string]: any; }; private readonly subscriptions: Subscription[] = []; @@ -58,7 +58,7 @@ export class ApprovalLevelFormComponent implements OnInit, OnDestroy { this.subscriptions.push( this.sidesheetRef.closeClicked().subscribe(async () => { await this.formService.cancelChanges(this.formGroup, this.sidesheetRef, this.requestData); - }) + }), ); } get formArray(): FormArray { @@ -68,8 +68,8 @@ export class ApprovalLevelFormComponent implements OnInit, OnDestroy { public async ngOnInit(): Promise { const columnConstraints: ColumnConstraints = { LevelDisplay: { - minLength: 1 - } + minLength: 1, + }, }; this.cdrList = await this.formService.setup(this.requestData, columnConstraints); } diff --git a/imxweb/projects/qer/src/lib/approval-workflows/approval-step-form/approval-step-form.component.html b/imxweb/projects/qer/src/lib/approval-workflows/approval-step-form/approval-step-form.component.html index d847cb9ca..8a5763adf 100644 --- a/imxweb/projects/qer/src/lib/approval-workflows/approval-step-form/approval-step-form.component.html +++ b/imxweb/projects/qer/src/lib/approval-workflows/approval-step-form/approval-step-form.component.html @@ -1,8 +1,8 @@ - +
    - + {{ requestData.HelpText.General }}
    @@ -11,7 +11,7 @@ @@ -23,7 +23,7 @@
    - + {{ requestData.HelpText.Mail }}
    @@ -31,8 +31,8 @@ @@ -43,12 +43,16 @@
    - - diff --git a/imxweb/projects/qer/src/lib/approval-workflows/approval-workflow-edit/approval-workflow-edit-info/approval-workflow-edit-info.component.scss b/imxweb/projects/qer/src/lib/approval-workflows/approval-workflow-edit/approval-workflow-edit-info/approval-workflow-edit-info.component.scss deleted file mode 100644 index 3b6c8d112..000000000 --- a/imxweb/projects/qer/src/lib/approval-workflows/approval-workflow-edit/approval-workflow-edit-info/approval-workflow-edit-info.component.scss +++ /dev/null @@ -1,61 +0,0 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; -@import "../../approval-workflow-styles.scss"; - -:host { - h2 { - font-weight: bold; - } - - .no-top { - margin-top: 0; - } - - .no-bottom { - margin-bottom: 0; - } - - mat-dialog-actions { - justify-content: flex-end; - } - ul { - margin: 5px 0; - } - li { - list-style-type: unset; - } - - kbd { - @include EUI-Elevation-1; - border-radius: 4px; - display: inline-block; - font-size: .85em; - font-weight: 700; - line-height: 1; - padding: 2px 4px; - margin: 5px; - white-space: nowrap; - } -} - -// Theming -:host { - kbd { - background-color: $color-gray-5; - } -} - -.eui-dark-theme { - :host { - kbd { - background-color: $color-gray-60; - } - } -} - -.eui-contrast-theme { - :host { - kbd { - background-color: $color-gray-80; - } - } -} diff --git a/imxweb/projects/qer/src/lib/approval-workflows/approval-workflow-edit/approval-workflow-edit.component.html b/imxweb/projects/qer/src/lib/approval-workflows/approval-workflow-edit/approval-workflow-edit.component.html index 345af7ffd..96197819d 100644 --- a/imxweb/projects/qer/src/lib/approval-workflows/approval-workflow-edit/approval-workflow-edit.component.html +++ b/imxweb/projects/qer/src/lib/approval-workflows/approval-workflow-edit/approval-workflow-edit.component.html @@ -1,41 +1,56 @@
    -
    - - - - -
    - - +
    + + + + +
    +
    -
    -
    -
    +
    +
    +
    @@ -54,7 +69,7 @@
    - - - - - - - - - - - -
    diff --git a/imxweb/projects/qer/src/lib/approval-workflows/approval-workflow-form/approval-workflow-form.component.scss b/imxweb/projects/qer/src/lib/approval-workflows/approval-workflow-form/approval-workflow-form.component.scss index 987ded229..32ef97297 100644 --- a/imxweb/projects/qer/src/lib/approval-workflows/approval-workflow-form/approval-workflow-form.component.scss +++ b/imxweb/projects/qer/src/lib/approval-workflows/approval-workflow-form/approval-workflow-form.component.scss @@ -1,3 +1,3 @@ -@import '../approval-workflow-styles.scss'; +@import 'base/mixins'; @include Awm-Form-Sidesheet; diff --git a/imxweb/projects/qer/src/lib/approval-workflows/approval-workflow-form/approval-workflow-form.component.ts b/imxweb/projects/qer/src/lib/approval-workflows/approval-workflow-form/approval-workflow-form.component.ts index 0c30f0c25..e2150f4d7 100644 --- a/imxweb/projects/qer/src/lib/approval-workflows/approval-workflow-form/approval-workflow-form.component.ts +++ b/imxweb/projects/qer/src/lib/approval-workflows/approval-workflow-form/approval-workflow-form.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,13 +26,13 @@ import { Component, ErrorHandler, Inject, OnDestroy, OnInit } from '@angular/core'; import { FormArray, FormBuilder, FormControl, FormGroup } from '@angular/forms'; -import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; import { ColumnDependentReference } from 'qbm'; import { Subscription } from 'rxjs'; +import { ApprovalWorkflowDataService } from '../approval-workflow-data.service'; import { ColumnConstraints, RequestWorkflowData } from '../approval-workflow.interface'; import { FormDataService } from '../form-data.service'; -import { ApprovalWorkflowDataService } from '../approval-workflow-data.service'; @Component({ selector: 'imx-approval-workflow-form', @@ -44,7 +44,7 @@ export class ApprovalWorkflowFormComponent implements OnInit, OnDestroy { public cdrList: ColumnDependentReference[] = []; public isInActiveFormControl = new FormControl(); public initialState: { - [key: string]: any + [key: string]: any; }; private readonly subscriptions: Subscription[] = []; @@ -56,14 +56,14 @@ export class ApprovalWorkflowFormComponent implements OnInit, OnDestroy { public readonly translate: TranslateService, public readonly sidesheetRef: EuiSidesheetRef, private approvalWorkFlowDataService: ApprovalWorkflowDataService, - private errorHandler: ErrorHandler - ) { + private errorHandler: ErrorHandler, + ) { this.formGroup = new FormGroup({ formArray: formBuilder.array([]) }); this.subscriptions.push( this.sidesheetRef.closeClicked().subscribe(async () => { await this.formService.cancelChanges(this.formGroup, this.sidesheetRef, this.requestData); - }) + }), ); } get formArray(): FormArray { @@ -74,9 +74,9 @@ export class ApprovalWorkflowFormComponent implements OnInit, OnDestroy { const columnConstraints: ColumnConstraints = { DaysToAbort: { valueConstraint: { - MinValue: 0 - } - } + MinValue: 0, + }, + }, }; this.cdrList = this.formService.setup(this.requestData, columnConstraints); } @@ -86,7 +86,7 @@ export class ApprovalWorkflowFormComponent implements OnInit, OnDestroy { if (this.requestData.SaveBeforeClosing) { this.approvalWorkFlowDataService.handleOpenLoader(); try { - await this.requestData.Object.GetEntity().Commit(true); + await this.requestData.Object?.GetEntity().Commit(true); } catch (error) { this.errorHandler.handleError(error); closeSheet = false; diff --git a/imxweb/projects/qer/src/lib/approval-workflows/approval-workflow-home/approval-workflow-home.component.html b/imxweb/projects/qer/src/lib/approval-workflows/approval-workflow-home/approval-workflow-home.component.html index 7d65eb620..d7bd92251 100644 --- a/imxweb/projects/qer/src/lib/approval-workflows/approval-workflow-home/approval-workflow-home.component.html +++ b/imxweb/projects/qer/src/lib/approval-workflows/approval-workflow-home/approval-workflow-home.component.html @@ -1,7 +1,7 @@ -

    +

    {{ '#LDS#Heading Approval Workflows' | translate }} -

    + [options]="['search']" [busyService]="busyService" [settings]="dstSettings" - useThemedStyle="true" + [useThemedStyle]="true" (search)="getData({ search: $event })" (navigationStateChanged)="getData($event)" data-imx-identifier="approval-workflow-dst" @@ -20,15 +20,15 @@

    #dataTable [dst]="dst" class="imx-pickcategory-table" - detailViewVisible="false" - showSelectedItemsMenu="false" - selectable="true" + [detailViewVisible]="false" + [showSelectedItemsMenu]="false" + [selectable]="true" (selectionChanged)="onSelectionChanged($event)" mode="manual" (highlightedEntityChanged)="viewDetails({ workFlow: $event })" data-imx-identifier="approval-workflow-datatable" > - +
    {{ item.GetEntity().GetDisplay() }}
    {{ item.Description.Column.GetDisplayValue() }}
    @@ -48,16 +48,27 @@

    -
    - -
    - -

    - + > - + + -
    +
    -

    #LDS#Select select a recipient or requester whose requests you want to display.

    +

    #LDS#Select a recipient or requester whose requests you want to display.

    - {{ infoText | translate}} + {{ infoText | translate }}

    diff --git a/imxweb/projects/qer/src/lib/archived-requests/archived-requests.component.scss b/imxweb/projects/qer/src/lib/archived-requests/archived-requests.component.scss index c6d99aeef..fe3e5d20c 100644 --- a/imxweb/projects/qer/src/lib/archived-requests/archived-requests.component.scss +++ b/imxweb/projects/qer/src/lib/archived-requests/archived-requests.component.scss @@ -6,7 +6,7 @@ overflow: hidden; flex-grow: 1; - .imx-data-table-no-results { + .imx-no-results { text-align: center; margin: 20px 0; flex: 1 1 auto; @@ -14,10 +14,6 @@ flex-direction: column; justify-content: center; - .eui-icon { - font-size: 100px; - } - p { margin: 0; font-size: 18px; @@ -29,26 +25,14 @@ align-items: center; margin: 0 0 40px; - h1 { + h2 { font-weight: inherit; margin: 0; } } -.imx-content-card { - flex: 1 1 auto; - display: flex; - flex-direction: column; - overflow: hidden; - margin: 2px; -} - :host { - .imx-data-table-no-results { - .eui-icon { - color: $color-gray-10; - } - + .imx-no-results { p { color: $color-gray-80; } @@ -57,11 +41,7 @@ .eui-dark-theme { :host { - .imx-data-table-no-results { - eui-icon { - color: $color-gray-20; - } - + .imx-no-results { p { color: $color-gray-10; } @@ -71,11 +51,7 @@ .eui-contrast-theme { :host { - .imx-data-table-no-results { - eui-icon { - color: $color-gray-10; - } - + .imx-no-results { p { color: $color-gray-0; } diff --git a/imxweb/projects/qer/src/lib/archived-requests/archived-requests.component.ts b/imxweb/projects/qer/src/lib/archived-requests/archived-requests.component.ts index 643653a22..e38a10a09 100644 --- a/imxweb/projects/qer/src/lib/archived-requests/archived-requests.component.ts +++ b/imxweb/projects/qer/src/lib/archived-requests/archived-requests.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,10 +25,10 @@ */ import { Component, OnDestroy } from '@angular/core'; -import { AuthenticationService, ColumnDependentReference } from 'qbm'; import { AbstractControl, FormGroup } from '@angular/forms'; -import { ArchivedRequestsService } from './archived-requests.service'; +import { AuthenticationService, ColumnDependentReference, ISessionState } from 'qbm'; import { Subscription } from 'rxjs'; +import { ArchivedRequestsService } from './archived-requests.service'; @Component({ templateUrl: './archived-requests.component.html', @@ -43,9 +43,12 @@ export class ArchivedRequestsComponent implements OnDestroy { private sessionSubscription: Subscription; - constructor(private archived: ArchivedRequestsService, authService: AuthenticationService) { - this.sessionSubscription = authService.onSessionResponse.subscribe(async (session) => { - await this.initRecipientForm(session.UserUid, session.Username); + constructor( + private archived: ArchivedRequestsService, + authService: AuthenticationService, + ) { + this.sessionSubscription = authService.onSessionResponse.subscribe(async (session: ISessionState) => { + await this.initRecipientForm(session.UserUid || '', session.Username || ''); }); } diff --git a/imxweb/projects/qer/src/lib/archived-requests/archived-requests.module.ts b/imxweb/projects/qer/src/lib/archived-requests/archived-requests.module.ts index 3766f0864..f42195fba 100644 --- a/imxweb/projects/qer/src/lib/archived-requests/archived-requests.module.ts +++ b/imxweb/projects/qer/src/lib/archived-requests/archived-requests.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,6 @@ * */ - import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; @@ -41,22 +40,21 @@ import { DateModule, HELP_CONTEXTUAL, HelpContextualModule, - InfoButtonComponent, InfoModalDialogModule, MenuItem, MenuService, ParameterizedTextModule, QbmModule, - RouteGuardService + RouteGuardService, } from 'qbm'; +import { isShopAdmin } from '../admin/qer-permissions-helper'; import { ItshopModule } from '../itshop/itshop.module'; -import { RequestsFeatureGuardService } from '../requests-feature-guard.service'; import { JustificationModule } from '../justification/justification.module'; -import { ArchivedRequestsComponent } from './archived-requests.component'; -import {ArchivedRequestsService} from './archived-requests.service'; import { RequestHistoryModule } from '../request-history/request-history.module'; -import { isShopAdmin } from '../admin/qer-permissions-helper'; +import { RequestsFeatureGuardService } from '../requests-feature-guard.service'; +import { ArchivedRequestsComponent } from './archived-requests.component'; +import { ArchivedRequestsService } from './archived-requests.service'; const routes: Routes = [ { @@ -64,10 +62,10 @@ const routes: Routes = [ component: ArchivedRequestsComponent, canActivate: [RouteGuardService, RequestsFeatureGuardService], resolve: [RouteGuardService], - data:{ - contextId: HELP_CONTEXTUAL.ArchivedRequest - } - } + data: { + contextId: HELP_CONTEXTUAL.ArchivedRequest, + }, + }, ]; @NgModule({ @@ -89,54 +87,42 @@ const routes: Routes = [ TranslateModule, RequestHistoryModule, HelpContextualModule, - InfoModalDialogModule - ], - declarations: [ - ArchivedRequestsComponent + InfoModalDialogModule, ], - exports: [ - ArchivedRequestsComponent - ], - providers: [ - ArchivedRequestsService - ] + declarations: [ArchivedRequestsComponent], + exports: [ArchivedRequestsComponent], + providers: [ArchivedRequestsService], }) export class ArchivedRequestsModule { constructor( private readonly menuService: MenuService, - logger: ClassloggerService + logger: ClassloggerService, ) { logger.info(this, '▶️ ArchivedRequestsModule loaded'); this.setupMenu(); } private async setupMenu(): Promise { - this.menuService.addMenuFactories( - (preProps: string[], features: string[]) => { - - const items: MenuItem[] = []; - if (isShopAdmin(features)) { - items.push( - { - id: 'QER_Request_ArchivedRequest', - route: 'archivedrequest', - title: '#LDS#Menu Entry Archived requests', - sorting: '10-40', - } - ); - } + this.menuService.addMenuFactories((preProps: string[], features: string[]) => { + const items: MenuItem[] = []; + if (isShopAdmin(features)) { + items.push({ + id: 'QER_Request_ArchivedRequest', + route: 'archivedrequest', + title: '#LDS#Menu Entry Archived requests', + sorting: '10-40', + }); + } - if (items.length === 0) { - return null; - } - return { - id: 'ROOT_Request', - title: '#LDS#Requests', - sorting: '10', - items - }; - }, - ); + if (items.length === 0) { + return; + } + return { + id: 'ROOT_Request', + title: '#LDS#Requests', + sorting: '10', + items, + }; + }); } } - diff --git a/imxweb/projects/qer/src/lib/archived-requests/archived-requests.service.ts b/imxweb/projects/qer/src/lib/archived-requests/archived-requests.service.ts index 30c5f55b1..92f4e5b0d 100644 --- a/imxweb/projects/qer/src/lib/archived-requests/archived-requests.service.ts +++ b/imxweb/projects/qer/src/lib/archived-requests/archived-requests.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,10 +25,17 @@ */ import { Injectable } from '@angular/core'; -import { DisplayBuilder, FkCandidateProvider, IClientProperty, MetaTableRelationData, ReadWriteEntity, ValType } from 'imx-qbm-dbts'; +import { + DisplayBuilder, + FkCandidateProvider, + IClientProperty, + MetaTableRelationData, + ReadWriteEntity, + ValType, +} from '@imx-modules/imx-qbm-dbts'; +import { TranslateService } from '@ngx-translate/core'; import { BaseCdr, ImxTranslationProviderService } from 'qbm'; import { QerApiService } from '../qer-api-client.service'; -import { TranslateService } from '@ngx-translate/core'; @Injectable({ providedIn: 'root', @@ -37,28 +44,29 @@ export class ArchivedRequestsService { constructor( private readonly qerClient: QerApiService, private translate: TranslateService, - private translateService: ImxTranslationProviderService + private translateService: ImxTranslationProviderService, ) {} public createRecipientCdr(): BaseCdr { const columnProperties = {}; - const property = this.createRequesterProperty(); - columnProperties[property.ColumnName] = property; + const columnName = 'PersonColumnName'; + const property = this.createRequesterProperty(columnName); + columnProperties[columnName] = property; const entityColumn = new ReadWriteEntity( { Columns: columnProperties }, {}, this.createRequesterFkProvider(property.FkRelation), undefined, - new DisplayBuilder(this.translateService) - ).GetColumn(property.ColumnName); + new DisplayBuilder(this.translateService), + ).GetColumn(columnName); return new BaseCdr(entityColumn, '#LDS#Recipient or requester'); } - private createRequesterProperty(): IClientProperty { + private createRequesterProperty(columnName: string): IClientProperty { const fkRelation = { - ChildColumnName: 'PersonColumnName', + ChildColumnName: columnName, ParentTableName: 'Person', ParentColumnName: 'UID_Person', IsMemberRelation: false, @@ -71,11 +79,11 @@ export class ArchivedRequestsService { }; } - private createRequesterFkProvider(fkRelation: MetaTableRelationData): FkCandidateProvider { + private createRequesterFkProvider(fkRelation: MetaTableRelationData | undefined): FkCandidateProvider { return new FkCandidateProvider([ { - columnName: fkRelation.ChildColumnName, - fkTableName: fkRelation.ParentTableName, + columnName: fkRelation?.ChildColumnName || '', + fkTableName: fkRelation?.ParentTableName || '', parameterNames: ['OrderBy', 'StartIndex', 'PageSize', 'filter', 'search'], load: async (_, parameters = {}) => this.qerClient.client.portal_candidates_Person_get(parameters), getDataModel: async () => ({}), diff --git a/imxweb/projects/qer/src/lib/businessowner-addon-tile/businessowner-addon-tile.component.html b/imxweb/projects/qer/src/lib/businessowner-addon-tile/businessowner-addon-tile.component.html index 31385e239..f54b455db 100644 --- a/imxweb/projects/qer/src/lib/businessowner-addon-tile/businessowner-addon-tile.component.html +++ b/imxweb/projects/qer/src/lib/businessowner-addon-tile/businessowner-addon-tile.component.html @@ -1,3 +1,11 @@ - + diff --git a/imxweb/projects/qer/src/lib/businessowner-addon-tile/businessowner-addon-tile.component.ts b/imxweb/projects/qer/src/lib/businessowner-addon-tile/businessowner-addon-tile.component.ts index 3ad160621..3e82d5e65 100644 --- a/imxweb/projects/qer/src/lib/businessowner-addon-tile/businessowner-addon-tile.component.ts +++ b/imxweb/projects/qer/src/lib/businessowner-addon-tile/businessowner-addon-tile.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,10 +28,9 @@ import { Component, Input, Output, EventEmitter } from '@angular/core'; @Component({ templateUrl: './businessowner-addon-tile.component.html', - selector: 'imx-businessowner-addon-tile' + selector: 'imx-businessowner-addon-tile', }) export class BusinessOwnerAddOnTileComponent { - @Input() public squareText: string; @Input() public description: string; @Input() public actionText = '#LDS#View'; diff --git a/imxweb/projects/qer/src/lib/businessowner-addon-tile/businessowner-addon-tile.module.ts b/imxweb/projects/qer/src/lib/businessowner-addon-tile/businessowner-addon-tile.module.ts index 455e79651..33f86bdff 100644 --- a/imxweb/projects/qer/src/lib/businessowner-addon-tile/businessowner-addon-tile.module.ts +++ b/imxweb/projects/qer/src/lib/businessowner-addon-tile/businessowner-addon-tile.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -36,15 +36,7 @@ import { BusinessOwnerAddOnTileComponent } from './businessowner-addon-tile.comp @NgModule({ declarations: [BusinessOwnerAddOnTileComponent], - imports: [ - CommonModule, - TileModule, - TranslateModule, - FormsModule, - MatDialogModule, - MatTabsModule - ], + imports: [CommonModule, TileModule, TranslateModule, FormsModule, MatDialogModule, MatTabsModule], exports: [BusinessOwnerAddOnTileComponent], - }) -export class BusinessownerAddonTileModule { } +export class BusinessownerAddonTileModule {} diff --git a/imxweb/projects/qer/src/lib/businessowner-overview-tile/businessowner-overview-tile.component.html b/imxweb/projects/qer/src/lib/businessowner-overview-tile/businessowner-overview-tile.component.html index 482eda7e8..ce311eed6 100644 --- a/imxweb/projects/qer/src/lib/businessowner-overview-tile/businessowner-overview-tile.component.html +++ b/imxweb/projects/qer/src/lib/businessowner-overview-tile/businessowner-overview-tile.component.html @@ -1,3 +1,3 @@ - \ No newline at end of file + diff --git a/imxweb/projects/qer/src/lib/businessowner-overview-tile/businessowner-overview-tile.component.ts b/imxweb/projects/qer/src/lib/businessowner-overview-tile/businessowner-overview-tile.component.ts index da3cba16a..05948c2da 100644 --- a/imxweb/projects/qer/src/lib/businessowner-overview-tile/businessowner-overview-tile.component.ts +++ b/imxweb/projects/qer/src/lib/businessowner-overview-tile/businessowner-overview-tile.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,10 +28,9 @@ import { Component, Input, Output, EventEmitter } from '@angular/core'; @Component({ templateUrl: './businessowner-overview-tile.component.html', - selector: 'imx-businessowner-overview-tile' + selector: 'imx-businessowner-overview-tile', }) export class BusinessOwnerOverviewTileComponent { - /* TODO (TFS 806002) ExpressionColumnList({"ScriptItemUID":"ExpressionColumnList2","ColumnList":"GetConfig(PropertyList())"}); }*/ diff --git a/imxweb/projects/qer/src/lib/businessowner-overview-tile/businessowner-overview-tile.module.ts b/imxweb/projects/qer/src/lib/businessowner-overview-tile/businessowner-overview-tile.module.ts index ba30e971a..0de355962 100644 --- a/imxweb/projects/qer/src/lib/businessowner-overview-tile/businessowner-overview-tile.module.ts +++ b/imxweb/projects/qer/src/lib/businessowner-overview-tile/businessowner-overview-tile.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -37,14 +37,7 @@ import { BusinessOwnerOverviewTileComponent } from './businessowner-overview-til @NgModule({ declarations: [BusinessOwnerOverviewTileComponent], - imports: [ - CommonModule, - TileModule, - TranslateModule, - FormsModule, - MatDialogModule, - MatTabsModule - ], + imports: [CommonModule, TileModule, TranslateModule, FormsModule, MatDialogModule, MatTabsModule], exports: [BusinessOwnerOverviewTileComponent], }) -export class BusinessownerOverviewTileModule { } +export class BusinessownerOverviewTileModule {} diff --git a/imxweb/projects/qer/src/lib/data-explorer-view/data-explorer-registry.service.ts b/imxweb/projects/qer/src/lib/data-explorer-view/data-explorer-registry.service.ts index 47ccfc592..fc2a5c942 100644 --- a/imxweb/projects/qer/src/lib/data-explorer-view/data-explorer-registry.service.ts +++ b/imxweb/projects/qer/src/lib/data-explorer-view/data-explorer-registry.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { Injectable } from '@angular/core'; -import { ProjectConfig } from 'imx-api-qbm'; +import { ProjectConfig } from '@imx-modules/imx-api-qbm'; import { SideNavigationExtension, SideNavigationFactory } from 'qbm'; @Injectable({ @@ -34,10 +34,15 @@ import { SideNavigationExtension, SideNavigationFactory } from 'qbm'; export class DataExplorerRegistryService { private items: SideNavigationFactory[] = []; - public getNavItems(preProps: string[], features: string[], projectConfig?: ProjectConfig, groups?: string[]): SideNavigationExtension[] { + public getNavItems( + preProps: string[], + features: string[], + projectConfig?: ProjectConfig, + groups?: string[], + ): (SideNavigationExtension | undefined)[] { return this.items .map((factory) => factory(preProps, features, projectConfig, groups)) - .sort((a, b) => a.sortOrder - b.sortOrder) + .sort((a, b) => (a?.sortOrder ?? 0) - (b?.sortOrder ?? 0)) .filter((item) => item !== undefined); } diff --git a/imxweb/projects/qer/src/lib/data-explorer-view/data-explorer-view.component.html b/imxweb/projects/qer/src/lib/data-explorer-view/data-explorer-view.component.html index 4fc67b390..a0e4a7220 100644 --- a/imxweb/projects/qer/src/lib/data-explorer-view/data-explorer-view.component.html +++ b/imxweb/projects/qer/src/lib/data-explorer-view/data-explorer-view.component.html @@ -3,6 +3,6 @@ [isAdmin]="isAdmin" [componentName]="componentName" [componentTitle]="componentTitle" - [navItems]="navItems" + [navItems]="navItems ?? []" [contextId]="contextId" > diff --git a/imxweb/projects/qer/src/lib/data-explorer-view/data-explorer-view.component.scss b/imxweb/projects/qer/src/lib/data-explorer-view/data-explorer-view.component.scss index 4bc9c8950..7ec7b6c09 100644 --- a/imxweb/projects/qer/src/lib/data-explorer-view/data-explorer-view.component.scss +++ b/imxweb/projects/qer/src/lib/data-explorer-view/data-explorer-view.component.scss @@ -1,158 +1,4 @@ -@import '@elemental-ui/core/src/styles/_eui_palette.scss'; -@import '@elemental-ui/core/src/styles/_palette.scss'; :host { height: 100%; } - -.snavigation { - height: 100%; - - .mat-sidenav-container { - height: 100%; - background-color: $asher-gray; - - .mat-sidenav { - width: 230px; - - .snavigation-side { - display: flex; - flex-direction: column; - height: 100%; - - .snavigation-side-toggle { - display: none; - padding: 16px 12px 0; - margin-bottom: 10px; - - .mat-button { - min-width: 0; - padding: 0 4px; - height: 28px; - - .mat-icon { - margin-top: -12px; - } - } - } - - .snavigation-side-content { - flex: 1; - padding: 20px; - padding-bottom: 16px; - - .snavigation-side-heading { - font-size: 14px; - font-weight: bold; - margin-bottom: 10px; - white-space: nowrap; - } - - .snavigation-item { - margin: 0 -20px; - padding: 10px 20px; - display: flex; - align-items: center; - justify-content: flex-start; - cursor: pointer; - - & > .eui-icon { - margin-right: 8px; - color: rgba($black, 0.4); - } - - & > span { - flex: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - &:hover:not(.snavigation-item--selected) { - background-color: rgba($iris-tint, 0.5); - } - - &.snavigation-item--selected { - background-color: $iris-blue; - color: $white; - } - } - } - } - } - - .mat-sidenav-content { - padding: 20px; - position: relative; - display: flex; - flex-direction: column; - - &.snavigation--backdrop-showing { - overflow: hidden; - } - - .snavigation-backdrop { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba($black, 0.32); - z-index: 200; - } - - .mat-table tr:hover td { - background-color: rgba($black, 0.04); - cursor: pointer; - } - } - } -} - -@media only screen and (max-width: 768px) { - .snavigation .mat-sidenav-container { - .mat-sidenav { - transition: width 0.5s; - - .snavigation-side { - .snavigation-side-toggle { - display: block; - } - .snavigation-side-content { - padding: 16px; - } - } - - &:not(.snavigation-side--expanded) { - width: 58px; - - .snavigation-side-content { - display: none; - } - } - } - - .mat-sidenav-content { - padding: 16px; - } - } -} -.eui-dark-theme { - :host { - .snavigation .mat-sidenav-container{ - background-color: $color-gray-80; - } - } -} -.eui-contrast-theme { - :host { - .snavigation .mat-sidenav-container{ - background-color: $color-gray-100; - } - - .snavigation-item--selected { - border-top: 1px solid $color-gray-0; - border-bottom: 1px solid $color-gray-0; - } - } -} diff --git a/imxweb/projects/qer/src/lib/data-explorer-view/data-explorer-view.component.ts b/imxweb/projects/qer/src/lib/data-explorer-view/data-explorer-view.component.ts index fd48a2682..c70f8c8d6 100644 --- a/imxweb/projects/qer/src/lib/data-explorer-view/data-explorer-view.component.ts +++ b/imxweb/projects/qer/src/lib/data-explorer-view/data-explorer-view.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -40,13 +40,13 @@ export class DataExplorerViewComponent implements OnInit { public componentName = 'data-explorer-view'; public componentTitle = '#LDS#Heading Data Explorer'; public contextId = HELP_CONTEXTUAL.DataExplorer; - public navItems: SideNavigationExtension[] = []; + public navItems: (SideNavigationExtension | undefined)[] = []; constructor( private readonly systemInfoService: SystemInfoService, private readonly userModelService: UserModelService, private readonly dataExplorerRegistryService: DataExplorerRegistryService, - private cdref: ChangeDetectorRef + private cdref: ChangeDetectorRef, ) {} public async ngOnInit(): Promise { @@ -55,9 +55,9 @@ export class DataExplorerViewComponent implements OnInit { private async loadNavItems(): Promise { const systemInfo = await this.systemInfoService.get(); - const features = (await this.userModelService.getFeatures()).Features; - const groups = (await this.userModelService.getGroups()).map((group) => group.Name || ''); - this.navItems = this.dataExplorerRegistryService.getNavItems(systemInfo.PreProps, features, undefined, groups); + const features = (await this.userModelService.getFeatures()).Features || []; + const groups = (await this.userModelService.getGroups()).map((group) => group?.Name || ''); + this.navItems = this.dataExplorerRegistryService.getNavItems(systemInfo.PreProps || [], features, undefined, groups); this.cdref.detectChanges(); } } diff --git a/imxweb/projects/qer/src/lib/data-explorer-view/data-explorer-view.module.ts b/imxweb/projects/qer/src/lib/data-explorer-view/data-explorer-view.module.ts index bd06fc77e..d86fd0105 100644 --- a/imxweb/projects/qer/src/lib/data-explorer-view/data-explorer-view.module.ts +++ b/imxweb/projects/qer/src/lib/data-explorer-view/data-explorer-view.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -32,8 +32,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { MatTooltipModule } from '@angular/material/tooltip'; import { RouteGuardService, SideNavigationViewModule } from 'qbm'; -import { ApplicationGuardService} from '../guards/application-guard.service'; -import { DataExplorerGuardService } from '../guards/data-explorer-guard.service'; +import { ApplicationGuardService } from '../guards/application-guard.service'; import { DataExplorerRegistryService } from './data-explorer-registry.service'; import { DataExplorerViewComponent } from './data-explorer-view.component'; @@ -41,21 +40,13 @@ const routes: Routes = [ { path: 'admin/dataexplorer', component: DataExplorerViewComponent, - canActivate: [ - RouteGuardService, - ApplicationGuardService, - DataExplorerGuardService - ], + canActivate: [RouteGuardService, ApplicationGuardService], resolve: [RouteGuardService], }, { path: 'admin/dataexplorer/:tab', component: DataExplorerViewComponent, - canActivate: [ - RouteGuardService, - ApplicationGuardService, - DataExplorerGuardService - ], + canActivate: [RouteGuardService, ApplicationGuardService], resolve: [RouteGuardService], }, ]; diff --git a/imxweb/projects/qer/src/lib/delegation/delegation-guard.service.ts b/imxweb/projects/qer/src/lib/delegation/delegation-guard.service.ts new file mode 100644 index 000000000..d1294fd62 --- /dev/null +++ b/imxweb/projects/qer/src/lib/delegation/delegation-guard.service.ts @@ -0,0 +1,56 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Injectable } from '@angular/core'; +import { Router } from '@angular/router'; +import { SystemInfoService } from 'qbm'; +import { ProjectConfigurationService } from '../project-configuration/project-configuration.service'; + +@Injectable({ + providedIn: 'root', +}) +export class DelegationGuardService { + constructor( + private projectConfig: ProjectConfigurationService, + private systemInfoService: SystemInfoService, + private readonly router: Router, + ) {} + + public async canActivate(): Promise { + const config = await this.projectConfig.getConfig(); + const preProps = (await this.systemInfoService.get()).PreProps ?? []; + + if ( + preProps.includes('ITSHOP') && + preProps.includes('DELEGATION') && + (config.EnableNewDelegationIndividual || config.EnableNewDelegationSubstitute) + ) { + return true; + } + this.router.navigate(['']); + return false; + } +} diff --git a/imxweb/projects/qer/src/lib/delegation/delegation.component.html b/imxweb/projects/qer/src/lib/delegation/delegation.component.html index e9a725a26..73542c035 100644 --- a/imxweb/projects/qer/src/lib/delegation/delegation.component.html +++ b/imxweb/projects/qer/src/lib/delegation/delegation.component.html @@ -1,13 +1,13 @@ -

    +

    {{ '#LDS#Heading Create Delegation' | translate }} -

    +

    - + {{ '#LDS#Create delegation for me' | translate }} @@ -16,7 +16,13 @@

    - + {{ '#LDS#Select the identity whose memberships and responsibilities you want to delegate' | translate }} @@ -28,8 +34,14 @@

    (controlCreated)="addControl(senderFormGroup, cdrPersonSender.column.ColumnName, $event)" >

    -
    -
    @@ -46,19 +58,29 @@

    >

    -
    -
    - + {{ '#LDS#Select the type of delegation' | translate }}
    - #LDS#Delegate all memberships and responsibilities + #LDS#Delegate memberships and responsibilities grouped by topic #LDS#Select individual memberships and responsibilities to delegate @@ -66,7 +88,7 @@

    -
    +
    @@ -90,7 +112,10 @@

    - + {{ '#LDS#Delegate compliance violation approval decisions' | translate }}

    @@ -103,7 +128,7 @@

    {{ '#LDS#Delegate manager responsibilities for one identity' | translate }} - {{ '#LDS#Delegate manager responsibilities for {0} identities' | translate | ldsReplace : countReports }} + {{ '#LDS#Delegate manager responsibilities for {0} identities' | translate | ldsReplace: countReports }}

    @@ -112,7 +137,7 @@

    {{ '#LDS#Delegate attestation approval decisions' | translate }}

    - +

    {{ '#LDS#Delegate responsibilities for roles of the following role classes' | translate }}:

    @@ -127,7 +152,7 @@

    {{ (roleClass.CountRolesOwned.value > 1 ? '#LDS#Responsible for {0} roles' : '#LDS#Responsible for one role') | translate - | ldsReplace : roleClass.CountRolesOwned.value + | ldsReplace: roleClass.CountRolesOwned.value }} @@ -139,10 +164,10 @@

    -
    +
    >
    {{ delegateable.TargetObjectKey.Column.GetDisplayValue() }} - ({{ delegateable.TargetType.Column.GetDisplayValue() }}) + + ({{ delegateable.TargetType.Column.GetDisplayValue() }})
    -
    ({{ delegateable.Criteria.Column.GetDisplayValue() }})
    +
    ({{ delegateable.Criteria.Column.GetDisplayValue() }})
    @@ -186,11 +213,15 @@

    -
    - +
    +
    - + {{ '#LDS#Specify additional options' | translate }}
    @@ -230,29 +267,32 @@

    [attr.data-imx-identifier]="'imx-delegation-step-4' + cdr.column.ColumnName" *ngFor="let cdr of cdrTimeSpan" [cdr]="cdr" - (controlCreated)="addControl(delegationForm, cdr.column.ColumnName, $event)" + (controlCreated)="addControl(delegationForm, cdr?.column?.ColumnName ?? '', $event)" > - {{ '#LDS#The specified validity period is invalid. The validity end date is before the validity start date. Please change the validity period.' | translate }} + {{ + '#LDS#The specified validity period is invalid. The validity end date is before the validity start date. Please change the validity period.' + | translate + }}

    -
    +
    @@ -289,9 +334,22 @@

    +
    + +
    + -
    +
    -

    {{ '#LDS#There are no role memberships or responsibilities you can delegate.' | translate }}

    +

    + {{ + (navigationState.search + ? '#LDS#There are no role memberships or responsibilities matching your search.' + : '#LDS#There are no role memberships or responsibilities you can delegate.' + ) | translate + }} +

    diff --git a/imxweb/projects/qer/src/lib/delegation/delegation.component.scss b/imxweb/projects/qer/src/lib/delegation/delegation.component.scss index 091437dce..5a767d1b6 100644 --- a/imxweb/projects/qer/src/lib/delegation/delegation.component.scss +++ b/imxweb/projects/qer/src/lib/delegation/delegation.component.scss @@ -1,4 +1,5 @@ @import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/mixins'; @mixin stepper-margin { margin-top: 16px; @@ -6,23 +7,12 @@ } :host { - display: flex; - flex-direction: column; - height: 100%; - overflow: hidden; + @include flex-column-container($height: 100%, $overflow: hidden); .imx-stepper-content { @include stepper-margin(); } - .imx-step-button, - .imx-step-button-save { - display: flex; - > *:not(:last-child) { - margin-right: 10px; - } - } - //For hiding components during loading .hidden { display: none; @@ -34,61 +24,35 @@ } .imx-roles-form { - display: flex; - flex-direction: column; - height: 390px; + @include flex-column-container($height: 390px); border-radius: 5px; padding: 5px; @include stepper-margin(); } - .imx-step-button-save { - align-self: flex-end; - margin-top: 16px; - } - .imx-type-selector { @include stepper-margin(); - > .mat-radio-button { - margin-left: 25px; - } } .imx-delegatable-elements { - overflow: hidden; margin-bottom: 10px; max-height: 600px; - display: flex; - flex: 1 1 auto; - } - - .imx-delegatable-elements mat-list-option .subtext { - font-size: 11px; - font-style: italic; - margin-bottom: 10px; - } - - .imx-delegatable-elements mat-list-option { - font-size: smaller; + @include flex-row-container-fill(); + overflow: hidden; } .imx-delegation-stepper { - flex: 1 1 auto; + @include flex-column-container-fill(); max-width: 100%; - display: flex; - flex-direction: column; margin: 3px; - overflow: hidden; > :first-child { - display: flex; - flex-direction: column; - flex: 1 1 auto; + @include flex-column-container-fill(); overflow: auto; } } - .imx-delegation-list { + .imx-list-container { display: flex; flex-direction: column; flex: 1 1 auto; @@ -105,41 +69,6 @@ > :last-child { flex: 1 1 auto; } - - ::ng-deep .mat-paginator { - box-shadow: none; - } - } - - .multi-select-formcontrol-eui-search { - width: 100%; - display: flex; - - .eui-search { - width: 100%; - } - - ::ng-deep .eui-search.mat-form-field.mat-form-field-appearance-outline { - min-width: 280px; - flex: 1; - margin-bottom: 10px; - } - } - - .imx-selected-entitlements { - margin: 0; - line-height: 36px; - font-size: 13.3px; - flex: auto; - - ::ng-deep .imx-badge.eui-badge .eui-badge-content { - font-size: 13.3px; - line-height: 14px; - } - } - - .imx-badge { - background-color: inherit; } } @@ -148,28 +77,19 @@ } .imx-small-error { - font-size: small; - margin: -25px 0 20px 10px; + font-size: 12px; + margin-bottom: 12px; } .imx-delegation-success { margin: 20px; - flex: 1 1 auto; width: max-content; } - .imx-alert-content { - flex: 1 1 auto; - display: flex; - flex-direction: column; - } - .imx-global-elements { + @include flex-column-container-fill(); padding-left: 1em; overflow-y: auto; - flex: 1 1 auto; - display: flex; - flex-direction: column; imx-busy-indicator { flex: 1 1 auto; @@ -194,7 +114,7 @@ margin: 30px 0 0 0; } - .imx-no-delegatable-items { + .imx-no-results { display: flex; margin: 20px 0; flex: 1 1 auto; @@ -202,10 +122,6 @@ flex-direction: column; align-items: center; - .eui-icon { - font-size: 100px; - } - p { margin: 0; font-size: 18px; @@ -214,18 +130,13 @@ .imx-additional-informations { @include stepper-margin(); - overflow: auto; } } // Theming .eui-light-theme { :host { - .imx-no-delegatable-items { - .eui-icon { - color: $color-gray-10; - } - + .imx-no-results { p { color: $color-gray-50; } @@ -234,24 +145,12 @@ .imx-roles-form { border: $color-gray-10 1px solid; } - - .imx-delegatable-elements mat-list-option .subtext { - color: $color-gray-40; - } } } .eui-dark-theme { :host { - .imx-delegatable-elements mat-list-option .subtext { - color: $color-gray-20; - } - - .imx-no-delegatable-items { - .eui-icon { - color: $color-gray-20; - } - + .imx-no-results { p { color: $color-gray-10; } @@ -261,15 +160,7 @@ .eui-contrast-theme { :host { - .imx-delegatable-elements mat-list-option .subtext { - color: $color-gray-0; - } - - .imx-no-delegatable-items { - .eui-icon { - color: $color-gray-10; - } - + .imx-no-results { p { color: $color-gray-0; } diff --git a/imxweb/projects/qer/src/lib/delegation/delegation.component.ts b/imxweb/projects/qer/src/lib/delegation/delegation.component.ts index 6da6a3cd5..1455064ee 100644 --- a/imxweb/projects/qer/src/lib/delegation/delegation.component.ts +++ b/imxweb/projects/qer/src/lib/delegation/delegation.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,29 +24,38 @@ * */ +import { STEPPER_GLOBAL_OPTIONS, StepperSelectionEvent } from '@angular/cdk/stepper'; import { ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { AbstractControl, UntypedFormControl, UntypedFormGroup, ValidatorFn } from '@angular/forms'; -import { StepperSelectionEvent, STEPPER_GLOBAL_OPTIONS } from '@angular/cdk/stepper'; -import { EuiLoadingService } from '@elemental-ui/core'; import { MatSelectionList, MatSelectionListChange } from '@angular/material/list'; import { PageEvent } from '@angular/material/paginator'; -import { Subscription } from 'rxjs'; -import { distinctUntilChanged, debounceTime } from 'rxjs/operators'; import { MatStep, MatStepper } from '@angular/material/stepper'; +import { EuiLoadingService } from '@elemental-ui/core'; +import { Subscription } from 'rxjs'; +import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; -import { ColumnDependentReference, BaseCdr, EntityService, AuthenticationService, ClassloggerService, BusyService } from 'qbm'; import { - PortalDelegations, - PortalDelegable, - QerProjectConfig, GlobalDelegationInput, + PortalDelegable, + PortalDelegations, PortalDelegationsGlobalRoleclasses, -} from 'imx-api-qer'; + QerProjectConfig, +} from '@imx-modules/imx-api-qer'; +import { + AuthenticationService, + BaseCdr, + BusyService, + ClassloggerService, + ColumnDependentReference, + EntityService, + ISessionState, +} from 'qbm'; import { ProjectConfigurationService } from '../project-configuration/project-configuration.service'; -import { DelegationService } from './delegation.service'; import { UserModelService } from '../user/user-model.service'; +import { DelegationService } from './delegation.service'; -import { CollectionLoadParameters } from 'imx-qbm-dbts'; +import { CollectionLoadParameters } from '@imx-modules/imx-qbm-dbts'; +import moment from 'moment'; import { QerPermissionsService } from '../admin/qer-permissions.service'; /** @@ -87,19 +96,20 @@ export class DelegationComponent implements OnInit, OnDestroy { public cdrList: ColumnDependentReference[]; public completed = false; - public state: string; + public state: string | undefined; public withSubordinates = false; public isManager = false; public cdrPersonSender: ColumnDependentReference; - public readonly recipientFormGroup = new UntypedFormGroup({}); + public recipientFormGroup: UntypedFormGroup; public readonly delegationForm = new UntypedFormGroup({}, DelegationComponent.dateIsAscending()); - public readonly senderFormGroup = new UntypedFormGroup({}); + public senderFormGroup: UntypedFormGroup; public busyService = new BusyService(); public isLoading = false; public initialized = false; + public isSaving = false; @ViewChild(MatSelectionList) public roleAndMembershipList: MatSelectionList; @ViewChild('delegationObjects') public delegationObjects: MatStep; @@ -111,6 +121,9 @@ export class DelegationComponent implements OnInit, OnDestroy { public countReports: number; public roleClasses: PortalDelegationsGlobalRoleclasses[] = []; + public canDelegateAll: boolean = true; + public canDelegateSelected: boolean = true; + private subscriptions: Subscription[] = []; public navigationState: CollectionLoadParameters = { PageSize: 20 }; private projectConfig: QerProjectConfig; @@ -128,11 +141,13 @@ export class DelegationComponent implements OnInit, OnDestroy { private readonly permissions: QerPermissionsService, private readonly elementalBusy: EuiLoadingService, private readonly changeDetector: ChangeDetectorRef, - authenticationservice: AuthenticationService + authenticationservice: AuthenticationService, ) { this.initGlobalDelegation(); this.initSearchControl(); - this.subscriptions.push(authenticationservice.onSessionResponse.subscribe((session) => (this.uidPerson = session.UserUid))); + this.subscriptions.push( + authenticationservice.onSessionResponse.subscribe((session: ISessionState) => (this.uidPerson = session.UserUid || '')), + ); this.busyService.busyStateChanged.subscribe((value) => { this.isLoading = value; changeDetector.detectChanges(); @@ -148,7 +163,12 @@ export class DelegationComponent implements OnInit, OnDestroy { try { this.projectConfig = await this.projectConfigSvc.getConfig(); this.newDelegation = await this.delegationService.createDelegation(); - this.isManager = await this.permissions.isPersonManager(); + this.isManager = (await this.permissions.isPersonManager()) || (await this.permissions.isPersonAdmin()); + this.canDelegateAll = this.projectConfig.EnableNewDelegationSubstitute; + this.canDelegateSelected = this.projectConfig.EnableNewDelegationIndividual; + if (!this.canDelegateAll || !this.canDelegateSelected) { + this.isGlobalDelegation = this.canDelegateAll; // because if both were false, the component would not be displayed anyway + } } finally { isBusy.endBusy(); this.initialized = true; @@ -194,6 +214,7 @@ export class DelegationComponent implements OnInit, OnDestroy { */ public async saveDelegation(): Promise { let overlay = this.elementalBusy.show(); + this.isSaving = true; this.changeDetector.detectChanges(); try { if (this.isGlobalDelegation) { @@ -205,6 +226,7 @@ export class DelegationComponent implements OnInit, OnDestroy { this.state = 'done'; } finally { this.elementalBusy.hide(overlay); + this.isSaving = false; } } @@ -243,7 +265,7 @@ export class DelegationComponent implements OnInit, OnDestroy { */ public onSelectall(): void { const items = this.roleAndMembershipList.options.filter( - (elem) => this.selections.findIndex((sel) => sel.ObjectKeyToDelegate.value === elem.value.ObjectKeyToDelegate.value) === -1 + (elem) => this.selections.findIndex((sel) => sel.ObjectKeyToDelegate.value === elem.value.ObjectKeyToDelegate.value) === -1, ); items.forEach((element) => { @@ -287,7 +309,7 @@ export class DelegationComponent implements OnInit, OnDestroy { * @param cdr the cdr to check * @returns true, if the delegation is not global and the columnname is IsDelegable */ - public isShowCdr(cdr: BaseCdr): boolean { + public isShowCdr(cdr: ColumnDependentReference): boolean { return !this.isGlobalDelegation || cdr.column.ColumnName !== 'IsDelegable'; } @@ -357,11 +379,7 @@ export class DelegationComponent implements OnInit, OnDestroy { // Update date from cdr this.newDelegation.InsertValidFrom.value = this.cdrTimeSpan[0].column.GetValue(); this.newDelegation.InsertValidUntil.value = this.cdrTimeSpan[1].column.GetValue(); - - await this.delegationService.commitDelegations( - this.newDelegation, - this.selections.map((option) => option.ObjectKeyToDelegate.value) - ); + await this.delegationService.commitDelegations(this.newDelegation, this.selections); } /** @@ -387,7 +405,7 @@ export class DelegationComponent implements OnInit, OnDestroy { const delegations = await this.delegationService.getDelegatableItems( this.uidDelegator, this.newDelegation.UID_PersonReceiver.value, - this.navigationState + this.navigationState, ); this.paginatorConfig.length = delegations.totalCount; this.delegateableItems = delegations.Data; @@ -395,7 +413,7 @@ export class DelegationComponent implements OnInit, OnDestroy { this.logger.trace(this, 'Try marking items as selected'); if (this.roleAndMembershipList) { const preselectedElements = this.roleAndMembershipList.options.filter( - (elem) => this.selections.findIndex((sel) => sel.ObjectKeyToDelegate.value === elem.value.ObjectKeyToDelegate.value) !== -1 + (elem) => this.selections.findIndex((sel) => sel.ObjectKeyToDelegate.value === elem.value.ObjectKeyToDelegate.value) !== -1, ); preselectedElements.forEach((elem) => elem.toggle()); this.logger.trace(this, 'items marked es selected', preselectedElements); @@ -417,15 +435,15 @@ export class DelegationComponent implements OnInit, OnDestroy { * Inits the 'Select the identity which responsibilities you like to delegate' form */ private initSenderForm(): void { - Object.keys(this.senderFormGroup.controls).forEach((name) => this.recipientFormGroup.removeControl(name)); - this.cdrPersonSender = this.delegationService.buildSenderCdr(this.newDelegation); + this.senderFormGroup = new UntypedFormGroup({}); + this.cdrPersonSender = new BaseCdr(this.newDelegation.UID_PersonSender.Column); } /** * Inits the 'Select the identity to which you want to delegate' form */ private initRecipientForm(): void { - Object.keys(this.recipientFormGroup.controls).forEach((name) => this.recipientFormGroup.removeControl(name)); + this.recipientFormGroup = new UntypedFormGroup({}); this.cdrPersonRecipient = new BaseCdr(this.newDelegation.UID_PersonReceiver.Column); } @@ -437,7 +455,7 @@ export class DelegationComponent implements OnInit, OnDestroy { const values = [this.newDelegation.KeepMeInformed.Column, this.newDelegation.IsDelegable.Column, this.newDelegation.OrderReason.Column]; - if (this.projectConfig.ITShopConfig.VI_ITShop_EnablePWOPriorityChange) { + if (this.projectConfig.ITShopConfig?.VI_ITShop_EnablePWOPriorityChange) { values.push(this.newDelegation.PWOPriority.Column); } @@ -453,7 +471,7 @@ export class DelegationComponent implements OnInit, OnDestroy { const schema = this.delegationService.getDelegationSchema(); this.cdrTimeSpan = [schema.Columns.InsertValidFrom, schema.Columns.InsertValidUntil].map((property) => { property.IsReadOnly = false; - return new BaseCdr(this.entityService.createLocalEntityColumn(property, undefined, { ValueConstraint: { MinValue: new Date() } })); + return new BaseCdr(this.entityService.createLocalEntityColumn(property, undefined, { ValueConstraint: { MinValue: moment() } })); }); } @@ -467,7 +485,7 @@ export class DelegationComponent implements OnInit, OnDestroy { this.navigationState = { ...this.navigationState, ...{ search: value, StartIndex: 0 } }; this.paginatorConfig.index = 0; await this.navigateDelegateable(); - }) + }), ); } @@ -480,12 +498,12 @@ export class DelegationComponent implements OnInit, OnDestroy { private buildValidationFunction(): ValidatorFn { return (control: AbstractControl): { [key: string]: boolean } | null => { if (this.isGlobalDelegation) { - return this.isValidGlobalDelegation ? null : { invalid: true }; + return this.isValidGlobalDelegation() ? null : { invalid: true }; } // at least one item set? const group = control as UntypedFormGroup; - return group == null || group.get('items') == null || group.get('items').value == null || group.get('items').value.length > 0 + return group == null || group.get('items') == null || group.get('items')?.value == null || group.get('items')?.value.length > 0 ? null : { noRoleSelected: true }; }; diff --git a/imxweb/projects/qer/src/lib/delegation/delegation.module.ts b/imxweb/projects/qer/src/lib/delegation/delegation.module.ts index b7f1553f1..9c285adb5 100644 --- a/imxweb/projects/qer/src/lib/delegation/delegation.module.ts +++ b/imxweb/projects/qer/src/lib/delegation/delegation.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,33 +27,47 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MatStepperModule, } from '@angular/material/stepper'; import { MatButtonModule } from '@angular/material/button'; -import { MatRadioModule } from '@angular/material/radio'; -import { MatListModule } from '@angular/material/list'; import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatListModule } from '@angular/material/list'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { MatRadioModule } from '@angular/material/radio'; +import { MatStepperModule } from '@angular/material/stepper'; import { RouterModule, Routes } from '@angular/router'; -import { TranslateModule } from '@ngx-translate/core'; import { EuiCoreModule } from '@elemental-ui/core'; -import { MatPaginatorModule } from '@angular/material/paginator'; +import { TranslateModule } from '@ngx-translate/core'; -import { DataTableModule, RouteGuardService, ClassloggerService, CdrModule, LdsReplaceModule, MenuService, MenuItem, HELP_CONTEXTUAL, HelpContextualModule, BusyIndicatorModule, SelectedElementsModule } from 'qbm'; -import { DelegationComponent } from './delegation.component'; -import { DelegationService } from './delegation.service'; +import { MatCardModule } from '@angular/material/card'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; -import { MatCardModule } from '@angular/material/card'; +import { ProjectConfig, QerProjectConfig } from '@imx-modules/imx-api-qer'; +import { + BusyIndicatorModule, + CdrModule, + ClassloggerService, + DataTableModule, + HELP_CONTEXTUAL, + HelpContextualModule, + LdsReplaceModule, + MenuItem, + MenuService, + RouteGuardService, + SelectedElementsModule, +} from 'qbm'; +import { DelegationGuardService } from './delegation-guard.service'; +import { DelegationComponent } from './delegation.component'; +import { DelegationService } from './delegation.service'; const routes: Routes = [ { path: 'delegation', component: DelegationComponent, - canActivate: [RouteGuardService], + canActivate: [RouteGuardService, DelegationGuardService], resolve: [RouteGuardService], - data:{ - contextId: HELP_CONTEXTUAL.Delegation - } - } + data: { + contextId: HELP_CONTEXTUAL.Delegation, + }, + }, ]; @NgModule({ @@ -79,49 +93,46 @@ const routes: Routes = [ SelectedElementsModule, HelpContextualModule, BusyIndicatorModule, - SelectedElementsModule - ], - declarations: [ - DelegationComponent + SelectedElementsModule, ], - providers: [DelegationService] + declarations: [DelegationComponent], + providers: [DelegationService], }) export class DelegationModule { constructor( private readonly menuService: MenuService, - logger: ClassloggerService + logger: ClassloggerService, ) { logger.info(this, '▶️ DelegationModule loaded'); this.setupMenu(); } private setupMenu(): void { - this.menuService.addMenuFactories( - (preProps: string[], features: string[]) => { - - const items: MenuItem[] = []; + this.menuService.addMenuFactories((preProps: string[], features: string[], projectConfig: QerProjectConfig & ProjectConfig) => { + const items: MenuItem[] = []; - if (preProps.includes('ITSHOP') && preProps.includes('DELEGATION')) { - items.push( - { - id: 'QER_Responsibilities_Delegation', - route: 'delegation', - title: '#LDS#Menu Entry Delegation', - sorting: '30-10', - }, - ); - } + if ( + preProps.includes('ITSHOP') && + preProps.includes('DELEGATION') && + (projectConfig.EnableNewDelegationIndividual || projectConfig.EnableNewDelegationSubstitute) + ) { + items.push({ + id: 'QER_Responsibilities_Delegation', + route: 'delegation', + title: '#LDS#Menu Entry Delegation', + sorting: '30-10', + }); + } - if (items.length === 0) { - return null; - } - return { - id: 'ROOT_Responsibilities', - title: '#LDS#Responsibilities', - sorting: '30', - items - }; - }, - ); + if (items.length === 0) { + return; + } + return { + id: 'ROOT_Responsibilities', + title: '#LDS#Responsibilities', + sorting: '30', + items, + }; + }); } } diff --git a/imxweb/projects/qer/src/lib/delegation/delegation.service.ts b/imxweb/projects/qer/src/lib/delegation/delegation.service.ts index ae3e44fbe..84ffa49a1 100644 --- a/imxweb/projects/qer/src/lib/delegation/delegation.service.ts +++ b/imxweb/projects/qer/src/lib/delegation/delegation.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,15 +26,12 @@ import { Injectable } from '@angular/core'; -import { - CollectionLoadParameters, - EntitySchema, - ExtendedTypedEntityCollection, -} from 'imx-qbm-dbts'; -import { GlobalDelegationInput, PortalDelegable, PortalDelegations, PortalDelegationsGlobalRoleclasses } from 'imx-api-qer'; +import { GlobalDelegationInput, PortalDelegable, PortalDelegations, PortalDelegationsGlobalRoleclasses } from '@imx-modules/imx-api-qer'; +import { CollectionLoadParameters, EntitySchema, ExtendedTypedEntityCollection } from '@imx-modules/imx-qbm-dbts'; +import { TranslateService } from '@ngx-translate/core'; +import { Action, BaseCdr, EntityService, ProcessingQueueService } from 'qbm'; import { QerApiService } from '../qer-api-client.service'; -import { BaseCdr, EntityService } from 'qbm'; @Injectable({ providedIn: 'root', @@ -43,6 +40,8 @@ export class DelegationService { constructor( private readonly qerApiService: QerApiService, private readonly entityService: EntityService, + private readonly queueService: ProcessingQueueService, + private readonly translate: TranslateService, ) {} public getDelegationSchema(): EntitySchema { @@ -56,7 +55,7 @@ export class DelegationService { public async getDelegatableItems( uidUser: string, uidRecipient: string, - parameter: CollectionLoadParameters + parameter: CollectionLoadParameters, ): Promise> { return this.qerApiService.typedClient.PortalDelegable.Get(uidUser, uidRecipient, parameter); } @@ -74,38 +73,73 @@ export class DelegationService { const fkProviderItems = this.qerApiService.client.getFkProviderItems('portal/delegations').map((item) => ({ ...item, load: (_, parameters = {}) => this.qerApiService.client.portal_person_reports_get({ ...parameters, OnlyDirect: true }), - getDataModel: async (entity) => item.getDataModel(entity), - getFilterTree: async (entity, parentKey) => item.getFilterTree(entity, parentKey), + getDataModel: async (entity) => item.getDataModel?.(entity) || {}, + getFilterTree: async (entity, parentKey) => item.getFilterTree?.(entity, parentKey) || {}, })); const column = this.entityService.createLocalEntityColumn( schema.Columns.UID_PersonSender, fkProviderItems, undefined, - async (_, newValue: string) => await entity.UID_PersonSender.Column.PutValue(newValue) + async (_, newValue: string) => await entity.UID_PersonSender.Column.PutValue(newValue), ); return new BaseCdr(column); } - public async commitDelegations(reference: PortalDelegations, objectKeys: string[]): Promise { - reference.ObjectKeyDelegated.value = objectKeys[0]; + public async commitDelegations(reference: PortalDelegations, delegations: PortalDelegable[]): Promise { + reference.ObjectKeyDelegated.value = delegations[0].ObjectKeyToDelegate.value; - if (objectKeys.length > 1) { - objectKeys.shift(); - for (const objectKey of objectKeys) { - const element = await this.createDelegation(); - for (const key in this.qerApiService.typedClient.PortalDelegations.GetSchema().Columns) { - if (!key.startsWith('__') && reference[key].GetMetadata().CanEdit()) { - await element[key].Column.PutValueStruct({ - DataValue: reference[key].value, - DisplayValue: reference[key].Column.GetDisplayValue(), - }); + // If there is more delegations here, then use the queuing service + if (delegations.length > this.queueService.actionThreshold) { + const actions = delegations.map((delegation, index) => { + let action: () => Promise; + // Skip first, it is handled above and in the group action, append group action to final action + if (index == 0) { + action = async () => {}; + } else { + action = async () => { + const element = await this.createDelegation(); + for (const key in this.qerApiService.typedClient.PortalDelegations.GetSchema().Columns) { + if (!key.startsWith('__') && reference[key].GetMetadata().CanEdit()) { + await element[key].Column.PutValueStruct({ + DataValue: reference[key].value, + DisplayValue: reference[key].Column.GetDisplayValue(), + }); + } + } + element.ObjectKeyDelegated.value = delegation.ObjectKeyToDelegate.value; + await element.GetEntity().Commit(false); + }; + } + const title = [reference.GetEntity().GetSchema().DisplaySingular, delegation.GetEntity().GetDisplay()].join(': '); + return new Action(title, '', action); + }); + + actions.push( + new Action(this.translate.instant('#LDS#Completing delegations'), '', async () => await reference.GetEntity().Commit(false)), + ); + + const groupTitle = [reference.GetEntity().GetSchema().Display, reference.GetEntity().GetDisplay().trim()].join(' '); + this.queueService.submitGroupAction(groupTitle, actions); + } else { + // Classic approach to loop over other delegates + if (delegations.length > 1) { + delegations.shift(); + for (const delegation of delegations) { + const element = await this.createDelegation(); + for (const key in this.qerApiService.typedClient.PortalDelegations.GetSchema().Columns) { + if (!key.startsWith('__') && reference[key].GetMetadata().CanEdit()) { + await element[key].Column.PutValueStruct({ + DataValue: reference[key].value, + DisplayValue: reference[key].Column.GetDisplayValue(), + }); + } } + element.ObjectKeyDelegated.value = delegation.ObjectKeyToDelegate.value; + await element.GetEntity().Commit(false); } - element.ObjectKeyDelegated.value = objectKey; - await element.GetEntity().Commit(false); } + await reference.GetEntity().Commit(false); } - await reference.GetEntity().Commit(false); } } diff --git a/imxweb/projects/qer/src/lib/dynamic-exclusion-dialog/dynamic-exclusion-dialog.component.html b/imxweb/projects/qer/src/lib/dynamic-exclusion-dialog/dynamic-exclusion-dialog.component.html index b343747b6..86d6f2377 100644 --- a/imxweb/projects/qer/src/lib/dynamic-exclusion-dialog/dynamic-exclusion-dialog.component.html +++ b/imxweb/projects/qer/src/lib/dynamic-exclusion-dialog/dynamic-exclusion-dialog.component.html @@ -1,23 +1,19 @@ -

    +

    #LDS#Heading Specify Reason for Exclusion -

    +

    - - - - - #LDS#Reason - - + + + #LDS#Reason + -
    -
    +
    diff --git a/imxweb/projects/qer/src/lib/dynamic-exclusion-dialog/dynamic-exclusion-dialog.component.scss b/imxweb/projects/qer/src/lib/dynamic-exclusion-dialog/dynamic-exclusion-dialog.component.scss deleted file mode 100644 index 820cdcf6d..000000000 --- a/imxweb/projects/qer/src/lib/dynamic-exclusion-dialog/dynamic-exclusion-dialog.component.scss +++ /dev/null @@ -1,3 +0,0 @@ -.mat-form-field { - width: 100%; -} diff --git a/imxweb/projects/qer/src/lib/dynamic-exclusion-dialog/dynamic-exclusion-dialog.component.ts b/imxweb/projects/qer/src/lib/dynamic-exclusion-dialog/dynamic-exclusion-dialog.component.ts index 0d2fee141..3f216518b 100644 --- a/imxweb/projects/qer/src/lib/dynamic-exclusion-dialog/dynamic-exclusion-dialog.component.ts +++ b/imxweb/projects/qer/src/lib/dynamic-exclusion-dialog/dynamic-exclusion-dialog.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,26 +31,26 @@ import { MatDialogRef } from '@angular/material/dialog'; @Component({ selector: 'imx-dynamic-exclusion-dialog', templateUrl: './dynamic-exclusion-dialog.component.html', - styleUrls: ['./dynamic-exclusion-dialog.component.scss'] }) export class DynamicExclusionDialogComponent implements OnInit { - public dynamicExclusionForm: UntypedFormGroup; - constructor(private formBuilder: UntypedFormBuilder, public dialogRef: MatDialogRef) {} + constructor( + private formBuilder: UntypedFormBuilder, + public dialogRef: MatDialogRef, + ) {} public ngOnInit(): void { this.dynamicExclusionForm = this.formBuilder.group({ - description: [''] + description: [''], }); } public save(): void { - this.dialogRef.close(this.dynamicExclusionForm.get('description').value); + this.dialogRef.close(this.dynamicExclusionForm.get('description')?.value); } public cancel(): void { this.dialogRef.close(undefined); } - } diff --git a/imxweb/projects/qer/src/lib/dynamic-exclusion-dialog/dynamic-exclusion-dialog.module.ts b/imxweb/projects/qer/src/lib/dynamic-exclusion-dialog/dynamic-exclusion-dialog.module.ts index 3578b0969..96b01421c 100644 --- a/imxweb/projects/qer/src/lib/dynamic-exclusion-dialog/dynamic-exclusion-dialog.module.ts +++ b/imxweb/projects/qer/src/lib/dynamic-exclusion-dialog/dynamic-exclusion-dialog.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,17 +24,18 @@ * */ -import { CommonModule } from "@angular/common"; -import { NgModule } from "@angular/core"; -import { FormsModule, ReactiveFormsModule } from "@angular/forms"; -import { RouterModule } from "@angular/router"; -import { EuiCoreModule, EuiMaterialModule } from "@elemental-ui/core"; -import { TranslateModule } from "@ngx-translate/core"; -import { CdrModule, DataSourceToolbarModule, DataTableModule, LdsReplaceModule, FkAdvancedPickerModule } from "qbm"; -import { DynamicExclusionDialogComponent } from "./dynamic-exclusion-dialog.component"; +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { RouterModule } from '@angular/router'; +import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; +import { TranslateModule } from '@ngx-translate/core'; +import { CdrModule, DataSourceToolbarModule, DataTableModule, LdsReplaceModule, FkAdvancedPickerModule } from 'qbm'; +import { DynamicExclusionDialogComponent } from './dynamic-exclusion-dialog.component'; @NgModule({ - imports: [CommonModule, + imports: [ + CommonModule, FormsModule, ReactiveFormsModule, EuiCoreModule, @@ -45,9 +46,8 @@ import { DynamicExclusionDialogComponent } from "./dynamic-exclusion-dialog.comp DataTableModule, LdsReplaceModule, FkAdvancedPickerModule, - RouterModule + RouterModule, ], - declarations: [DynamicExclusionDialogComponent] + declarations: [DynamicExclusionDialogComponent], }) -export class DynamicExclusionDialogModule { -} \ No newline at end of file +export class DynamicExclusionDialogModule {} diff --git a/imxweb/projects/qer/src/lib/filter-sqlwizard/filter-sqlwizard.service.ts b/imxweb/projects/qer/src/lib/filter-sqlwizard/filter-sqlwizard.service.ts new file mode 100644 index 000000000..045365ee8 --- /dev/null +++ b/imxweb/projects/qer/src/lib/filter-sqlwizard/filter-sqlwizard.service.ts @@ -0,0 +1,45 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Injectable } from '@angular/core'; +import { CollectionLoadParameters, EntityCollectionData, FilterProperty } from '@imx-modules/imx-qbm-dbts'; +import { SqlWizardApiService } from 'qbm'; +import { QerApiService } from '../qer-api-client.service'; + +@Injectable() +export class FilterSqlWizardService extends SqlWizardApiService { + constructor(private readonly api: QerApiService) { + super(); + } + + public async getFilterProperties(table: string): Promise { + return (await this.api.client.portal_sqlwizard_tables_columns_get(table)).Properties || []; + } + + public async getCandidates(parentTable: string, options?: CollectionLoadParameters): Promise { + return await this.api.v2Client.portal_sqlwizard_candidates_get(parentTable, options); + } +} diff --git a/imxweb/projects/qer/src/lib/guards/application-guard.service.ts b/imxweb/projects/qer/src/lib/guards/application-guard.service.ts index f83045c5b..d628dd841 100644 --- a/imxweb/projects/qer/src/lib/guards/application-guard.service.ts +++ b/imxweb/projects/qer/src/lib/guards/application-guard.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,22 +25,22 @@ */ import { Injectable, OnDestroy } from '@angular/core'; -import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router'; +import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; import { Observable, Subscription } from 'rxjs'; -import { AppConfigService, AuthenticationService, ISessionState } from "qbm"; +import { AppConfigService, AuthenticationService, ISessionState } from 'qbm'; @Injectable({ providedIn: 'root', }) -export class ApplicationGuardService implements CanActivate, OnDestroy { +export class ApplicationGuardService implements OnDestroy { private onSessionResponse: Subscription; constructor( private readonly authentication: AuthenticationService, private readonly appConfig: AppConfigService, - private readonly router: Router - ) { } + private readonly router: Router, + ) {} public canActivate(route: ActivatedRouteSnapshot, _: RouterStateSnapshot): Observable { return new Observable((observer) => { @@ -48,7 +48,7 @@ export class ApplicationGuardService implements CanActivate, OnDestroy { if (sessionState.IsLoggedIn) { const isPortal = this.appConfig?.Config?.WebAppIdentifier?.toLocaleLowerCase() === 'portal'; if (!isPortal) { - this.router.navigate([this.appConfig.Config.routeConfig.start], { queryParams: {} }); + this.router.navigate([this.appConfig.Config?.routeConfig?.start], { queryParams: {} }); } observer.next(isPortal ? true : false); observer.complete(); diff --git a/imxweb/projects/qer/src/lib/guards/data-explorer-guard.service.ts b/imxweb/projects/qer/src/lib/guards/data-explorer-guard.service.ts deleted file mode 100644 index 9e0c493c0..000000000 --- a/imxweb/projects/qer/src/lib/guards/data-explorer-guard.service.ts +++ /dev/null @@ -1,87 +0,0 @@ -/* - * ONE IDENTITY LLC. PROPRIETARY INFORMATION - * - * This software is confidential. One Identity, LLC. or one of its affiliates or - * subsidiaries, has supplied this software to you under terms of a - * license agreement, nondisclosure agreement or both. - * - * You may not copy, disclose, or use this software except in accordance with - * those terms. - * - * - * Copyright 2023 One Identity LLC. - * ALL RIGHTS RESERVED. - * - * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR - * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, OR - * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE - * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE - * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING - * THIS SOFTWARE OR ITS DERIVATIVES. - * - */ - -import { Injectable, OnDestroy } from '@angular/core'; -import { CanActivate, Router } from '@angular/router'; -import { Observable, Subscription } from 'rxjs'; - -import { AppConfigService, AuthenticationService, ISessionState, SystemInfoService } from 'qbm'; -import { QerPermissionsService } from '../admin/qer-permissions.service'; - -@Injectable({ - providedIn: 'root', -}) -export class DataExplorerGuardService implements CanActivate, OnDestroy { - private onSessionResponse: Subscription; - - constructor( - private readonly qerPermissionService: QerPermissionsService, - private readonly authentication: AuthenticationService, - private readonly appConfig: AppConfigService, - private readonly router: Router, - private readonly systemInfoService: SystemInfoService - ) {} - - public canActivate(): Observable { - return new Observable((observer) => { - this.onSessionResponse = this.authentication.onSessionResponse.subscribe(async (sessionState: ISessionState) => { - if (sessionState.IsLoggedIn) { - const userIsAdmin = await this.qerPermissionService.isPersonAdmin(); - const userIsAuditor = await this.qerPermissionService.isAuditor(); - const userIsResourceAdmin = await this.qerPermissionService.isResourceAdmin(); - const userIsRoleAdmin = await this.qerPermissionService.isRoleAdmin(); - const userIsRoleStatistics = await this.qerPermissionService.isRoleStatistics(); - const userIsStructStatistics = await this.qerPermissionService.isStructStatistics(); - const userIsStructAdmin = await this.qerPermissionService.isStructAdmin(); - const userIsTsbNameSpaceAdminBase = await this.qerPermissionService.isTsbNameSpaceAdminBase(); - const systemInfo = await this.systemInfoService.get(); - const isItShop = systemInfo.PreProps.includes('ITSHOP'); - const isActive = - (isItShop && (userIsAdmin || userIsAuditor)) || - userIsResourceAdmin || - userIsAuditor || - userIsRoleAdmin || - userIsRoleStatistics || - userIsStructStatistics || - userIsStructAdmin || - (isItShop && userIsTsbNameSpaceAdminBase); - - if (!isActive) { - this.router.navigate([this.appConfig.Config.routeConfig.start], { queryParams: {} }); - } - observer.next(isActive); - observer.complete(); - } - }); - }); - } - - public ngOnDestroy(): void { - if (this.onSessionResponse) { - this.onSessionResponse.unsubscribe(); - } - } -} diff --git a/imxweb/projects/qer/src/lib/guards/feature-guard.service.ts b/imxweb/projects/qer/src/lib/guards/feature-guard.service.ts index 3cd287be3..19137027e 100644 --- a/imxweb/projects/qer/src/lib/guards/feature-guard.service.ts +++ b/imxweb/projects/qer/src/lib/guards/feature-guard.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,23 +25,23 @@ */ import { Injectable, OnDestroy } from '@angular/core'; -import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router'; +import { ActivatedRouteSnapshot, Router } from '@angular/router'; import { Observable, Subscription, of } from 'rxjs'; -import { QerPermissionsService } from '../admin/qer-permissions.service'; import { AppConfigService, AuthenticationService, ISessionState } from 'qbm'; +import { QerPermissionsService } from '../admin/qer-permissions.service'; @Injectable({ providedIn: 'root', }) -export class FeatureGuardService implements CanActivate, OnDestroy { +export class FeatureGuardService implements OnDestroy { private onSessionResponse: Subscription; constructor( private readonly qerPermissionService: QerPermissionsService, private readonly authentication: AuthenticationService, private readonly appConfig: AppConfigService, - private readonly router: Router + private readonly router: Router, ) {} public canActivate(route: ActivatedRouteSnapshot): Observable { @@ -51,7 +51,7 @@ export class FeatureGuardService implements CanActivate, OnDestroy { if (sessionState.IsLoggedIn) { const hasFeature = await this.qerPermissionService.hasFeatures(route.data.features); if (!hasFeature) { - this.router.navigate([this.appConfig.Config.routeConfig.start], { queryParams: {} }); + this.router.navigate([this.appConfig.Config?.routeConfig?.start], { queryParams: {} }); } observer.next(hasFeature); observer.complete(); diff --git a/imxweb/projects/qer/src/lib/guards/itshop-pattern-guard.service.ts b/imxweb/projects/qer/src/lib/guards/itshop-pattern-guard.service.ts index f7632df3e..f136a4fb4 100644 --- a/imxweb/projects/qer/src/lib/guards/itshop-pattern-guard.service.ts +++ b/imxweb/projects/qer/src/lib/guards/itshop-pattern-guard.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { Injectable, OnDestroy } from '@angular/core'; -import { CanActivate, Router } from '@angular/router'; +import { Router } from '@angular/router'; import { Observable, Subscription } from 'rxjs'; import { AppConfigService, AuthenticationService, ISessionState } from 'qbm'; @@ -37,23 +37,23 @@ import { ProjectConfigurationService } from '../project-configuration/project-co @Injectable({ providedIn: 'root', }) -export class ItshopPatternGuardService implements CanActivate, OnDestroy { +export class ItshopPatternGuardService implements OnDestroy { private onSessionResponse: Subscription; constructor( private readonly projectConfig: ProjectConfigurationService, private readonly authentication: AuthenticationService, private readonly appConfig: AppConfigService, - private readonly router: Router - ) { } + private readonly router: Router, + ) {} public canActivate(): Observable { return new Observable((observer) => { this.onSessionResponse = this.authentication.onSessionResponse.subscribe(async (sessionState: ISessionState) => { if (sessionState.IsLoggedIn) { - const isRequestTemplateEnabled = (await this.projectConfig.getConfig()).ITShopConfig.VI_ITShop_ProductSelectionFromTemplate; + const isRequestTemplateEnabled = (await this.projectConfig.getConfig()).ITShopConfig?.VI_ITShop_ProductSelectionFromTemplate; if (!isRequestTemplateEnabled) { - this.router.navigate([this.appConfig.Config.routeConfig.start], { queryParams: {} }); + this.router.navigate([this.appConfig.Config?.routeConfig?.start], { queryParams: {} }); } observer.next(isRequestTemplateEnabled); observer.complete(); diff --git a/imxweb/projects/qer/src/lib/guards/manager-guard.service.ts b/imxweb/projects/qer/src/lib/guards/manager-guard.service.ts index 6258f9194..2e04c4ef1 100644 --- a/imxweb/projects/qer/src/lib/guards/manager-guard.service.ts +++ b/imxweb/projects/qer/src/lib/guards/manager-guard.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { Injectable, OnDestroy } from '@angular/core'; -import { CanActivate, Router } from '@angular/router'; +import { Router } from '@angular/router'; import { Observable, Subscription } from 'rxjs'; import { AppConfigService, AuthenticationService, ISessionState } from 'qbm'; @@ -34,15 +34,15 @@ import { QerPermissionsService } from '../admin/qer-permissions.service'; @Injectable({ providedIn: 'root', }) -export class ManagerGuardService implements CanActivate, OnDestroy { +export class ManagerGuardService implements OnDestroy { private onSessionResponse: Subscription; constructor( private readonly qerPermissionService: QerPermissionsService, private readonly authentication: AuthenticationService, private readonly appConfig: AppConfigService, - private readonly router: Router - ) { } + private readonly router: Router, + ) {} public canActivate(): Observable { return new Observable((observer) => { @@ -50,7 +50,7 @@ export class ManagerGuardService implements CanActivate, OnDestroy { if (sessionState.IsLoggedIn) { const userIsManager = await this.qerPermissionService.isPersonManager(); if (!userIsManager) { - this.router.navigate([this.appConfig.Config.routeConfig.start], { queryParams: {} }); + this.router.navigate([this.appConfig.Config?.routeConfig?.start], { queryParams: {} }); } observer.next(userIsManager); observer.complete(); diff --git a/imxweb/projects/qer/src/lib/guards/person-admin-guard.service.spec.ts b/imxweb/projects/qer/src/lib/guards/person-admin-guard.service.spec.ts index c40672dfd..69c144948 100644 --- a/imxweb/projects/qer/src/lib/guards/person-admin-guard.service.spec.ts +++ b/imxweb/projects/qer/src/lib/guards/person-admin-guard.service.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -34,7 +34,6 @@ import { MockBuilder, MockRender } from 'ng-mocks'; import { Router, RouterModule } from '@angular/router'; import { QbmDefaultMocks } from '../../../../qbm/src/default-mocks.spec'; - describe('PersonAdminGuardService', () => { let service: PersonAdminGuardService; let isPersonAdmin = false; diff --git a/imxweb/projects/qer/src/lib/guards/person-admin-guard.service.ts b/imxweb/projects/qer/src/lib/guards/person-admin-guard.service.ts index 993aad640..a6f7c34b2 100644 --- a/imxweb/projects/qer/src/lib/guards/person-admin-guard.service.ts +++ b/imxweb/projects/qer/src/lib/guards/person-admin-guard.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { Injectable, OnDestroy } from '@angular/core'; -import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router'; +import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; import { Observable, Subscription } from 'rxjs'; import { AppConfigService, AuthenticationService, ISessionState } from 'qbm'; @@ -34,15 +34,15 @@ import { QerPermissionsService } from '../admin/qer-permissions.service'; @Injectable({ providedIn: 'root', }) -export class PersonAdminGuardService implements CanActivate, OnDestroy { +export class PersonAdminGuardService implements OnDestroy { private onSessionResponse: Subscription; constructor( private readonly qerPermissionService: QerPermissionsService, private readonly authentication: AuthenticationService, private readonly appConfig: AppConfigService, - private readonly router: Router - ) { } + private readonly router: Router, + ) {} public canActivate(route: ActivatedRouteSnapshot, _: RouterStateSnapshot): Observable { return new Observable((observer) => { @@ -50,7 +50,7 @@ export class PersonAdminGuardService implements CanActivate, OnDestroy { if (sessionState.IsLoggedIn) { const userIsAdmin = await this.qerPermissionService.isPersonAdmin(); if (!userIsAdmin) { - this.router.navigate([this.appConfig.Config.routeConfig.start], { queryParams: {} }); + this.router.navigate([this.appConfig.Config?.routeConfig?.start], { queryParams: {} }); } observer.next(userIsAdmin ? true : false); observer.complete(); diff --git a/imxweb/projects/qer/src/lib/guards/rule-admin-guard.service.ts b/imxweb/projects/qer/src/lib/guards/rule-admin-guard.service.ts index 98f04904d..04f669ec3 100644 --- a/imxweb/projects/qer/src/lib/guards/rule-admin-guard.service.ts +++ b/imxweb/projects/qer/src/lib/guards/rule-admin-guard.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,23 +25,23 @@ */ import { Injectable, OnDestroy } from '@angular/core'; -import { CanActivate, Router } from '@angular/router'; +import { Router } from '@angular/router'; import { Observable, Subscription } from 'rxjs'; import { AppConfigService, AuthenticationService, ISessionState } from 'qbm'; import { QerPermissionsService } from '../admin/qer-permissions.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) -export class RuleAdminGuardService implements CanActivate, OnDestroy{ +export class RuleAdminGuardService implements OnDestroy { private onSessionResponse: Subscription; constructor( private readonly qerPermissionService: QerPermissionsService, private readonly authentication: AuthenticationService, private readonly appConfig: AppConfigService, - private readonly router: Router - ) { } + private readonly router: Router, + ) {} public canActivate(): Observable { return new Observable((observer) => { @@ -49,7 +49,7 @@ export class RuleAdminGuardService implements CanActivate, OnDestroy{ if (sessionState.IsLoggedIn) { const userIsRuleAdmin = await this.qerPermissionService.isRuleAdmin(); if (!userIsRuleAdmin) { - this.router.navigate([this.appConfig.Config.routeConfig.start], { queryParams: {} }); + this.router.navigate([this.appConfig.Config?.routeConfig?.start], { queryParams: {} }); } observer.next(userIsRuleAdmin); observer.complete(); diff --git a/imxweb/projects/qer/src/lib/guards/shop-admin-guard.service.ts b/imxweb/projects/qer/src/lib/guards/shop-admin-guard.service.ts index 2182bc734..b6259f595 100644 --- a/imxweb/projects/qer/src/lib/guards/shop-admin-guard.service.ts +++ b/imxweb/projects/qer/src/lib/guards/shop-admin-guard.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { Injectable, OnDestroy } from '@angular/core'; -import { CanActivate, Router } from '@angular/router'; +import { Router } from '@angular/router'; import { Observable, Subscription } from 'rxjs'; import { AppConfigService, AuthenticationService, ISessionState } from 'qbm'; @@ -34,15 +34,15 @@ import { QerPermissionsService } from '../admin/qer-permissions.service'; @Injectable({ providedIn: 'root', }) -export class ShopAdminGuardService implements CanActivate, OnDestroy { +export class ShopAdminGuardService implements OnDestroy { private onSessionResponse: Subscription; constructor( private readonly qerPermissionService: QerPermissionsService, private readonly authentication: AuthenticationService, private readonly appConfig: AppConfigService, - private readonly router: Router - ) { } + private readonly router: Router, + ) {} public canActivate(): Observable { return new Observable((observer) => { @@ -50,7 +50,7 @@ export class ShopAdminGuardService implements CanActivate, OnDestroy { if (sessionState.IsLoggedIn) { const userIsShopAdmin = await this.qerPermissionService.isShopAdmin(); if (!userIsShopAdmin) { - this.router.navigate([this.appConfig.Config.routeConfig.start], { queryParams: {} }); + this.router.navigate([this.appConfig.Config?.routeConfig?.start], { queryParams: {} }); } observer.next(userIsShopAdmin); observer.complete(); diff --git a/imxweb/projects/qer/src/lib/guards/shop-guard.service.ts b/imxweb/projects/qer/src/lib/guards/shop-guard.service.ts index cfec1a340..8af8d26a9 100644 --- a/imxweb/projects/qer/src/lib/guards/shop-guard.service.ts +++ b/imxweb/projects/qer/src/lib/guards/shop-guard.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { Injectable, OnDestroy } from '@angular/core'; -import { CanActivate, Router } from '@angular/router'; +import { Router } from '@angular/router'; import { Observable, Subscription } from 'rxjs'; import { AppConfigService, AuthenticationService, ISessionState } from 'qbm'; @@ -34,15 +34,15 @@ import { QerPermissionsService } from '../admin/qer-permissions.service'; @Injectable({ providedIn: 'root', }) -export class ShopGuardService implements CanActivate, OnDestroy { +export class ShopGuardService implements OnDestroy { private onSessionResponse: Subscription; constructor( private readonly qerPermissionService: QerPermissionsService, private readonly authentication: AuthenticationService, private readonly appConfig: AppConfigService, - private readonly router: Router - ) { } + private readonly router: Router, + ) {} public canActivate(): Observable { return new Observable((observer) => { @@ -51,7 +51,7 @@ export class ShopGuardService implements CanActivate, OnDestroy { const userIsShopStatistics = await this.qerPermissionService.isShopStatistics(); const userIsShopAdmin = await this.qerPermissionService.isShopAdmin(); if (!userIsShopStatistics && !userIsShopAdmin) { - this.router.navigate([this.appConfig.Config.routeConfig.start], { queryParams: {} }); + this.router.navigate([this.appConfig.Config?.routeConfig?.start], { queryParams: {} }); } observer.next(userIsShopStatistics || userIsShopAdmin); observer.complete(); diff --git a/imxweb/projects/qer/src/lib/guards/shop-statistics-guard.service.ts b/imxweb/projects/qer/src/lib/guards/shop-statistics-guard.service.ts index dc47a8b71..a5643456f 100644 --- a/imxweb/projects/qer/src/lib/guards/shop-statistics-guard.service.ts +++ b/imxweb/projects/qer/src/lib/guards/shop-statistics-guard.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { Injectable, OnDestroy } from '@angular/core'; -import { CanActivate, Router } from '@angular/router'; +import { Router } from '@angular/router'; import { Observable, Subscription } from 'rxjs'; import { AppConfigService, AuthenticationService, ISessionState } from 'qbm'; @@ -34,15 +34,15 @@ import { QerPermissionsService } from '../admin/qer-permissions.service'; @Injectable({ providedIn: 'root', }) -export class ShopStatisticsGuardService implements CanActivate, OnDestroy { +export class ShopStatisticsGuardService implements OnDestroy { private onSessionResponse: Subscription; constructor( private readonly qerPermissionService: QerPermissionsService, private readonly authentication: AuthenticationService, private readonly appConfig: AppConfigService, - private readonly router: Router - ) { } + private readonly router: Router, + ) {} public canActivate(): Observable { return new Observable((observer) => { @@ -50,7 +50,7 @@ export class ShopStatisticsGuardService implements CanActivate, OnDestroy { if (sessionState.IsLoggedIn) { const userIsShopStatistics = await this.qerPermissionService.isShopStatistics(); if (!userIsShopStatistics) { - this.router.navigate([this.appConfig.Config.routeConfig.start], { queryParams: {} }); + this.router.navigate([this.appConfig.Config?.routeConfig?.start], { queryParams: {} }); } observer.next(userIsShopStatistics); observer.complete(); diff --git a/imxweb/projects/qer/src/lib/guards/statistics-guard.service.ts b/imxweb/projects/qer/src/lib/guards/statistics-guard.service.ts index 325dea081..713e37b76 100644 --- a/imxweb/projects/qer/src/lib/guards/statistics-guard.service.ts +++ b/imxweb/projects/qer/src/lib/guards/statistics-guard.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { Injectable, OnDestroy } from '@angular/core'; -import { CanActivate, Router } from '@angular/router'; +import { Router } from '@angular/router'; import { Observable, Subscription } from 'rxjs'; import { AppConfigService, AuthenticationService, ISessionState } from 'qbm'; @@ -34,15 +34,15 @@ import { QerPermissionsService } from '../admin/qer-permissions.service'; @Injectable({ providedIn: 'root', }) -export class StatisticsGuardService implements CanActivate, OnDestroy { +export class StatisticsGuardService implements OnDestroy { private onSessionResponse: Subscription; constructor( private readonly qerPermissionService: QerPermissionsService, private readonly authentication: AuthenticationService, private readonly appConfig: AppConfigService, - private readonly router: Router - ) { } + private readonly router: Router, + ) {} public canActivate(): Observable { return new Observable((observer) => { @@ -50,7 +50,7 @@ export class StatisticsGuardService implements CanActivate, OnDestroy { if (sessionState.IsLoggedIn) { const userIsStatistics = await this.qerPermissionService.isStatistics(); if (!userIsStatistics) { - this.router.navigate([this.appConfig.Config.routeConfig.start], { queryParams: {} }); + this.router.navigate([this.appConfig.Config?.routeConfig?.start], { queryParams: {} }); } observer.next(userIsStatistics); observer.complete(); diff --git a/imxweb/projects/qer/src/lib/guards/struct-admin-guard.service.ts b/imxweb/projects/qer/src/lib/guards/struct-admin-guard.service.ts index 3817d5627..b6939afcc 100644 --- a/imxweb/projects/qer/src/lib/guards/struct-admin-guard.service.ts +++ b/imxweb/projects/qer/src/lib/guards/struct-admin-guard.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { Injectable, OnDestroy } from '@angular/core'; -import { CanActivate, Router } from '@angular/router'; +import { Router } from '@angular/router'; import { Observable, Subscription } from 'rxjs'; import { AppConfigService, AuthenticationService, ISessionState } from 'qbm'; @@ -34,15 +34,15 @@ import { QerPermissionsService } from '../admin/qer-permissions.service'; @Injectable({ providedIn: 'root', }) -export class StructAdminGuardService implements CanActivate, OnDestroy { +export class StructAdminGuardService implements OnDestroy { private onSessionResponse: Subscription; constructor( private readonly qerPermissionService: QerPermissionsService, private readonly authentication: AuthenticationService, private readonly appConfig: AppConfigService, - private readonly router: Router - ) { } + private readonly router: Router, + ) {} public canActivate(): Observable { return new Observable((observer) => { @@ -50,7 +50,7 @@ export class StructAdminGuardService implements CanActivate, OnDestroy { if (sessionState.IsLoggedIn) { const userIsStructAdmin = await this.qerPermissionService.isStructAdmin(); if (!userIsStructAdmin) { - this.router.navigate([this.appConfig.Config.routeConfig.start], { queryParams: {} }); + this.router.navigate([this.appConfig.Config?.routeConfig?.start], { queryParams: {} }); } observer.next(userIsStructAdmin); observer.complete(); diff --git a/imxweb/projects/qer/src/lib/helper-alert-content/helper-alert-content.interface.ts b/imxweb/projects/qer/src/lib/helper-alert-content/helper-alert-content.interface.ts index efa4bbd6a..95e1b098f 100644 --- a/imxweb/projects/qer/src/lib/helper-alert-content/helper-alert-content.interface.ts +++ b/imxweb/projects/qer/src/lib/helper-alert-content/helper-alert-content.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,6 +25,6 @@ */ export interface HelperAlertContent { - infoItems?: { description: string; value?: any; }[]; + infoItems?: { description: string; value?: any }[]; loading: boolean; } diff --git a/imxweb/projects/qer/src/lib/identities/create-new-identity/create-new-identity.component.html b/imxweb/projects/qer/src/lib/identities/create-new-identity/create-new-identity.component.html index fb22eb368..0e0d7277a 100644 --- a/imxweb/projects/qer/src/lib/identities/create-new-identity/create-new-identity.component.html +++ b/imxweb/projects/qer/src/lib/identities/create-new-identity/create-new-identity.component.html @@ -2,86 +2,129 @@
    - + {{ '#LDS#Unique data' | translate }} - + - -
    + +

    #LDS#There is already an identity linked to this central user account.

    -

    #LDS#You cannot create the identity using the specified central user account. Please enter a different name for the central user account.

    +

    {{ LdsKey }}

    -
    - -
    + +

    #LDS#There is at least one identity with the same first and last name.

    #LDS#You can view these identities or proceed.

    -
    - -
    + +

    #LDS#There is at least one identity with the same email address.

    #LDS#You can view these identities, change the email address, or proceed.

    -
    - + {{ '#LDS#Personal data' | translate }} - + - + {{ '#LDS#Organizational information' | translate }} - + + - + {{ '#LDS#Location information' | translate }} - +
    -
    -
    diff --git a/imxweb/projects/qer/src/lib/identities/create-new-identity/create-new-identity.component.scss b/imxweb/projects/qer/src/lib/identities/create-new-identity/create-new-identity.component.scss index 4af4d5ab4..678da4d90 100644 --- a/imxweb/projects/qer/src/lib/identities/create-new-identity/create-new-identity.component.scss +++ b/imxweb/projects/qer/src/lib/identities/create-new-identity/create-new-identity.component.scss @@ -1,27 +1,9 @@ @import '@elemental-ui/core/src/styles/_eui_palette.scss'; -.imx-info { - ::ng-deep .eui-alert { - width: auto; - margin-bottom: 10px; - } - ::ng-deep .eui-alert .eui-alert-header { - width: 100%; - } -} - -.imx-alert-button { - align-self: flex-end; -} - -.imx-flex-alert { - display: flex; - flex-direction: column; -} .eui-dark-theme { :host { background-color: $color-gray-80; - .eui-sidesheet-actions.mat-card.eui-sidesheet-actions--white{ + .eui-sidesheet-actions.mat-mdc-card.eui-sidesheet-actions--white { background-color: $color-gray-70; } } @@ -30,7 +12,7 @@ .eui-contrast-theme { :host { background-color: $color-gray-100; - .eui-sidesheet-actions.mat-card.eui-sidesheet-actions--white{ + .eui-sidesheet-actions.mat-mdc-card.eui-sidesheet-actions--white { background-color: $color-gray-90; } } diff --git a/imxweb/projects/qer/src/lib/identities/create-new-identity/create-new-identity.component.ts b/imxweb/projects/qer/src/lib/identities/create-new-identity/create-new-identity.component.ts index 74c70ab56..f58612790 100644 --- a/imxweb/projects/qer/src/lib/identities/create-new-identity/create-new-identity.component.ts +++ b/imxweb/projects/qer/src/lib/identities/create-new-identity/create-new-identity.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,30 +25,29 @@ */ import { Component, Inject, OnDestroy } from '@angular/core'; -import { FormArray, UntypedFormGroup } from '@angular/forms'; -import { EuiLoadingService, EuiSidesheetRef, EuiSidesheetService, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { UntypedFormGroup } from '@angular/forms'; +import { EUI_SIDESHEET_DATA, EuiLoadingService, EuiSidesheetRef, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; import { Subscription } from 'rxjs'; -import { PortalPersonReports, QerProjectConfig } from 'imx-api-qer'; -import { BaseCdr, ColumnDependentReference, ConfirmationService, SnackBarService, CdrFactoryService } from 'qbm'; +import { PortalPersonReports, QerProjectConfig } from '@imx-modules/imx-api-qer'; +import { CollectionLoadParameters } from '@imx-modules/imx-qbm-dbts'; +import { BaseCdr, calculateSidesheetWidth, CdrFactoryService, ColumnDependentReference, ConfirmationService, SnackBarService } from 'qbm'; import { IdentitiesService } from '../identities.service'; import { DuplicateCheckParameter } from './duplicate-check-parameter.interface'; import { DuplicatesSidesheetComponent } from './duplicates-sidesheet/duplicates-sidesheet.component'; -import { CollectionLoadParameters } from 'imx-qbm-dbts'; @Component({ selector: 'imx-create-new-identity', templateUrl: './create-new-identity.component.html', - styleUrls: ['./create-new-identity.component.scss'] + styleUrls: ['./create-new-identity.component.scss'], }) export class CreateNewIdentityComponent implements OnDestroy { - public identityForm = new UntypedFormGroup({}); - public cdrListPersonal: ColumnDependentReference[] = []; - public cdrListOrganizational: ColumnDependentReference[] = []; - public cdrListLocality: ColumnDependentReference[] = []; - public cdrListIdentifier: ColumnDependentReference[] = []; + public cdrListPersonal: (ColumnDependentReference | undefined)[] = []; + public cdrListOrganizational: (ColumnDependentReference | undefined)[] = []; + public cdrListLocality: (ColumnDependentReference | undefined)[] = []; + public cdrListIdentifier: (ColumnDependentReference | undefined)[] = []; public nameIsOff = 0; public accountIsOff = 0; public mailIsOff = 0; @@ -56,9 +55,10 @@ export class CreateNewIdentityComponent implements OnDestroy { private subscriptions: Subscription[] = []; constructor( - @Inject(EUI_SIDESHEET_DATA) public data: { - selectedIdentity: PortalPersonReports, - projectConfig: QerProjectConfig, + @Inject(EUI_SIDESHEET_DATA) + public data: { + selectedIdentity: PortalPersonReports; + projectConfig: QerProjectConfig; }, private readonly busyService: EuiLoadingService, private readonly snackbar: SnackBarService, @@ -67,28 +67,28 @@ export class CreateNewIdentityComponent implements OnDestroy { private readonly translate: TranslateService, private readonly confirm: ConfirmationService, private readonly cdrFactoryService: CdrFactoryService, - private readonly identityService: IdentitiesService) { + private readonly identityService: IdentitiesService, + ) { this.setup(); } public ngOnDestroy(): void { - this.subscriptions.forEach(s => s.unsubscribe()); + this.subscriptions.forEach((s) => s.unsubscribe()); } public async submit(): Promise { if (this.identityForm.valid) { - - let canSubmit = true; if (this.mailIsOff || this.nameIsOff) { canSubmit = await this.confirm.confirm({ Title: '#LDS#Heading Create Identity With Same Properties', - Message: this.mailIsOff && this.nameIsOff ? - '#LDS#There is at least one identity with the same properties. Are you sure you want to create the identity?' - : this.mailIsOff ? - '#LDS#There is at least one identity with the same email address. Are you sure you want to create the identity?' : - '#LDS#There is at least one identity with the same first and last name. Are you sure you want to create the identity?', - identifier: 'create-new-identity-confirm-saving' + Message: + this.mailIsOff && this.nameIsOff + ? '#LDS#There is at least one identity with the same properties. Are you sure you want to create the identity?' + : this.mailIsOff + ? '#LDS#There is at least one identity with the same email address. Are you sure you want to create the identity?' + : '#LDS#There is at least one identity with the same first and last name. Are you sure you want to create the identity?', + identifier: 'create-new-identity-confirm-saving', }); } @@ -120,99 +120,113 @@ export class CreateNewIdentityComponent implements OnDestroy { default: duplicateParameter = { firstName: this.data.selectedIdentity.GetEntity().GetColumn('FirstName').GetValue(), - lastName: this.data.selectedIdentity.GetEntity().GetColumn('LastName').GetValue() + lastName: this.data.selectedIdentity.GetEntity().GetColumn('LastName').GetValue(), }; break; } - await this.sidesheetService.open(DuplicatesSidesheetComponent, { - title: await this.translate.get('#LDS#Heading View Identities With Same Properties').toPromise(), - padding: '0px', - width: 'max(600px, 60%)', - icon: 'contactinfo', - testId: 'duplicate-identities-sidesheet', - data: { - projectConfig: this.data.projectConfig, - get: async (param: CollectionLoadParameters) => this.identityService.getDuplicates(param), - getFilter: (filter: DuplicateCheckParameter) => this.identityService.buildFilterForduplicates(filter), - duplicateParameter, - entitySchema: this.identityService.personAllSchema, - } - }).afterClosed().toPromise(); + await this.sidesheetService + .open(DuplicatesSidesheetComponent, { + title: await this.translate.get('#LDS#Heading View Identities With Same Properties').toPromise(), + padding: '0px', + width: calculateSidesheetWidth(), + icon: 'contactinfo', + testId: 'duplicate-identities-sidesheet', + data: { + projectConfig: this.data.projectConfig, + get: async (param: CollectionLoadParameters) => this.identityService.getDuplicates(param), + getFilter: (filter: DuplicateCheckParameter) => this.identityService.buildFilterForduplicates(filter), + duplicateParameter, + entitySchema: this.identityService.personAllSchema, + }, + }) + .afterClosed() + .toPromise(); } - public update(cdr: ColumnDependentReference, list: ColumnDependentReference[]): void { - const index = list.findIndex(elem => elem.column.ColumnName === cdr.column.ColumnName); - if (index === -1) { return; } + public update(cdr: ColumnDependentReference | undefined, list: (ColumnDependentReference | undefined)[]): void { + const cdrColumn = cdr?.column; + if (!cdrColumn || !!list.length) { + return; + } + const index = list.findIndex((elem) => elem?.column?.ColumnName === (cdrColumn?.ColumnName ?? '')); + if (index === -1) { + return; + } - this.identityForm.removeControl(cdr.column.ColumnName); - list.splice(index, 1, new BaseCdr(cdr.column)); + this.identityForm.removeControl(cdrColumn?.ColumnName ?? ''); + list.splice(index, 1, new BaseCdr(cdrColumn)); } - public async checkValues(name: string): Promise { + public async checkValues(name: string | undefined): Promise { switch (name) { case 'FirstName': case 'LastName': - this.nameIsOff = (await this.identityService.getDuplicates( - { - filter: this.identityService.buildFilterForduplicates( - { - firstName: this.data.selectedIdentity.GetEntity().GetColumn('FirstName').GetValue(), - lastName: this.data.selectedIdentity.GetEntity().GetColumn('LastName').GetValue(), - } - ), PageSize: -1 - } - )).totalCount; + this.nameIsOff = ( + await this.identityService.getDuplicates({ + filter: this.identityService.buildFilterForduplicates({ + firstName: this.data.selectedIdentity.GetEntity().GetColumn('FirstName').GetValue(), + lastName: this.data.selectedIdentity.GetEntity().GetColumn('LastName').GetValue(), + }), + PageSize: -1, + }) + ).totalCount; break; case 'CentralAccount': - this.accountIsOff = (await this.identityService.getDuplicates( - { - filter: this.identityService.buildFilterForduplicates( - { - centralAccount: this.data.selectedIdentity.GetEntity().GetColumn('CentralAccount').GetValue() - } - ), PageSize: -1 - } - )).totalCount; + this.accountIsOff = ( + await this.identityService.getDuplicates({ + filter: this.identityService.buildFilterForduplicates({ + centralAccount: this.data.selectedIdentity.GetEntity().GetColumn('CentralAccount').GetValue(), + }), + PageSize: -1, + }) + ).totalCount; break; case 'DefaultEmailAddress': - this.mailIsOff = (await this.identityService.getDuplicates( - { - filter: this.identityService.buildFilterForduplicates( - { - defaultEmailAddress: this.data.selectedIdentity.GetEntity().GetColumn('DefaultEmailAddress').GetValue() - } - ), PageSize: -1 - } - )).totalCount; + this.mailIsOff = ( + await this.identityService.getDuplicates({ + filter: this.identityService.buildFilterForduplicates({ + defaultEmailAddress: this.data.selectedIdentity.GetEntity().GetColumn('DefaultEmailAddress').GetValue(), + }), + PageSize: -1, + }) + ).totalCount; break; } } private setup(): void { - - this.subscriptions.push(this.sidesheetRef.closeClicked().subscribe(async () => { - if (this.identityForm.dirty) { - const close = await this.confirm.confirmLeaveWithUnsavedChanges(); - if (close) { + this.subscriptions.push( + this.sidesheetRef.closeClicked().subscribe(async () => { + if (this.identityForm.dirty) { + const close = await this.confirm.confirmLeaveWithUnsavedChanges(); + if (close) { + this.sidesheetRef.close(false); + } + } else { this.sidesheetRef.close(false); } - } else { - this.sidesheetRef.close(false); - } - })); + }), + ); const identifierColumns = ['FirstName', 'LastName', 'CentralAccount', 'DefaultEmailAddress']; - this.cdrListIdentifier = this.cdrFactoryService.buildCdrFromColumnList(this.data.selectedIdentity.GetEntity(),identifierColumns); + this.cdrListIdentifier = this.cdrFactoryService.buildCdrFromColumnList(this.data.selectedIdentity.GetEntity(), identifierColumns); - const personalColumns = this.data.projectConfig.PersonConfig.VI_Employee_MasterData_Attributes - .filter(personal => !identifierColumns.includes(personal)); - this.cdrListPersonal = this.cdrFactoryService.buildCdrFromColumnList(this.data.selectedIdentity.GetEntity(),personalColumns); + const personalColumns = (this.data.projectConfig.PersonConfig?.VI_Employee_MasterData_Attributes || []).filter( + (personal) => !identifierColumns.includes(personal), + ); + this.cdrListPersonal = this.cdrFactoryService.buildCdrFromColumnList(this.data.selectedIdentity.GetEntity(), personalColumns); - const organizationalColumns = this.data.projectConfig.PersonConfig.VI_Employee_MasterData_OrganizationalAttributes; - this.cdrListOrganizational = this.cdrFactoryService.buildCdrFromColumnList(this.data.selectedIdentity.GetEntity(),organizationalColumns); + const organizationalColumns = this.data.projectConfig.PersonConfig?.VI_Employee_MasterData_OrganizationalAttributes || []; + this.cdrListOrganizational = this.cdrFactoryService.buildCdrFromColumnList( + this.data.selectedIdentity.GetEntity(), + organizationalColumns, + ); - const localityColumns = this.data.projectConfig.PersonConfig.VI_Employee_MasterData_LocalityAttributes; - this.cdrListLocality = this.cdrFactoryService.buildCdrFromColumnList(this.data.selectedIdentity.GetEntity(),localityColumns); + const localityColumns = this.data.projectConfig.PersonConfig?.VI_Employee_MasterData_LocalityAttributes || []; + this.cdrListLocality = this.cdrFactoryService.buildCdrFromColumnList(this.data.selectedIdentity.GetEntity(), localityColumns); } + + public LdsKey = + '#LDS#You cannot create the identity using the specified central user account. Please enter a different name for the central user account.'; } diff --git a/imxweb/projects/qer/src/lib/identities/create-new-identity/duplicate-check-parameter.interface.ts b/imxweb/projects/qer/src/lib/identities/create-new-identity/duplicate-check-parameter.interface.ts index a6bb35d0d..bcee12853 100644 --- a/imxweb/projects/qer/src/lib/identities/create-new-identity/duplicate-check-parameter.interface.ts +++ b/imxweb/projects/qer/src/lib/identities/create-new-identity/duplicate-check-parameter.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { CollectionLoadParameters } from 'imx-qbm-dbts'; +import { CollectionLoadParameters } from '@imx-modules/imx-qbm-dbts'; export interface DuplicateCheckParameter { firstName?: string; diff --git a/imxweb/projects/qer/src/lib/identities/create-new-identity/duplicates-sidesheet/duplicates-sidesheet.component.html b/imxweb/projects/qer/src/lib/identities/create-new-identity/duplicates-sidesheet/duplicates-sidesheet.component.html index 85be8f551..16a24b1f2 100644 --- a/imxweb/projects/qer/src/lib/identities/create-new-identity/duplicates-sidesheet/duplicates-sidesheet.component.html +++ b/imxweb/projects/qer/src/lib/identities/create-new-identity/duplicates-sidesheet/duplicates-sidesheet.component.html @@ -1,15 +1,25 @@
    - + - + noMatchingDataText="#LDS#There are no identities matching your search." + > - - + -
    \ No newline at end of file +
    diff --git a/imxweb/projects/qer/src/lib/identities/create-new-identity/duplicates-sidesheet/duplicates-sidesheet.component.scss b/imxweb/projects/qer/src/lib/identities/create-new-identity/duplicates-sidesheet/duplicates-sidesheet.component.scss index b9b8c7b0d..463b02e4c 100644 --- a/imxweb/projects/qer/src/lib/identities/create-new-identity/duplicates-sidesheet/duplicates-sidesheet.component.scss +++ b/imxweb/projects/qer/src/lib/identities/create-new-identity/duplicates-sidesheet/duplicates-sidesheet.component.scss @@ -1,19 +1,7 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; :host { - background-color: $asher-gray; - - .imx-table-container { - display: flex; - flex-direction: column; - overflow: hidden; - - > span { - margin-bottom: 10px; - display: block; - } - } + background-color: $color-gray-2 } .eui-dark-theme { diff --git a/imxweb/projects/qer/src/lib/identities/create-new-identity/duplicates-sidesheet/duplicates-sidesheet.component.ts b/imxweb/projects/qer/src/lib/identities/create-new-identity/duplicates-sidesheet/duplicates-sidesheet.component.ts index 3e46c474c..d6b98efa3 100644 --- a/imxweb/projects/qer/src/lib/identities/create-new-identity/duplicates-sidesheet/duplicates-sidesheet.component.ts +++ b/imxweb/projects/qer/src/lib/identities/create-new-identity/duplicates-sidesheet/duplicates-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,34 +24,33 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; import { Component, Inject, OnInit } from '@angular/core'; -import { EuiLoadingService, EuiSidesheetConfig, EuiSidesheetService, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { EUI_SIDESHEET_DATA, EuiLoadingService, EuiSidesheetConfig, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { PortalPersonAll, QerProjectConfig } from 'imx-api-qer'; -import { CollectionLoadParameters, EntitySchema, FilterData, IClientProperty } from 'imx-qbm-dbts'; -import { DataSourceToolbarSettings, SettingsService } from 'qbm'; +import { QerProjectConfig } from '@imx-modules/imx-api-qer'; +import { CollectionLoadParameters, EntitySchema, FilterData, IClientProperty, TypedEntity } from '@imx-modules/imx-qbm-dbts'; +import { calculateSidesheetWidth, DataSourceToolbarSettings, SettingsService } from 'qbm'; import { AddressbookDetailComponent } from '../../../addressbook/addressbook-detail/addressbook-detail.component'; import { AddressbookService } from '../../../addressbook/addressbook.service'; import { DuplicateCheckParameter } from '../duplicate-check-parameter.interface'; @Component({ templateUrl: './duplicates-sidesheet.component.html', - styleUrls: ['./duplicates-sidesheet.component.scss'] + styleUrls: ['./duplicates-sidesheet.component.scss'], }) export class DuplicatesSidesheetComponent implements OnInit { - public dstSettings: DataSourceToolbarSettings; private displayedColumns: IClientProperty[] = []; constructor( - @Inject(EUI_SIDESHEET_DATA) public data: { - projectConfig: QerProjectConfig, - get: (param: CollectionLoadParameters) => Promise, - getFilter: (filter: DuplicateCheckParameter) => FilterData[], - duplicateParameter: DuplicateCheckParameter - entitySchema: EntitySchema, + @Inject(EUI_SIDESHEET_DATA) + public data: { + projectConfig: QerProjectConfig; + get: (param: CollectionLoadParameters) => Promise; + getFilter: (filter: DuplicateCheckParameter) => FilterData[]; + duplicateParameter: DuplicateCheckParameter; + entitySchema: EntitySchema; }, public readonly settings: SettingsService, private readonly busy: EuiLoadingService, @@ -59,47 +58,41 @@ export class DuplicatesSidesheetComponent implements OnInit { private readonly addressbookService: AddressbookService, private readonly translateService: TranslateService, ) { - this.displayedColumns = [ data.entitySchema.Columns.DefaultEmailAddress, data.entitySchema.Columns.IdentityType, data.entitySchema.Columns.UID_Department, - data.entitySchema.Columns.UID_Locality + data.entitySchema.Columns.UID_Locality, ]; - } public async ngOnInit(): Promise { await this.navigate({ PageSize: this.settings.DefaultPageSize }); } - public async navigate(parameter: CollectionLoadParameters): Promise { - const currentParameter = { ...parameter, ...{ filter: this.data.getFilter(this.data.duplicateParameter) } }; - let overlay: OverlayRef; - setTimeout(() => { - overlay = this.busy.show(); - }); + if (this.busy.overlayRefs.length === 0) { + this.busy.show(); + } try { const data = await this.data.get(currentParameter); this.dstSettings = { displayedColumns: this.displayedColumns, dataSource: data, entitySchema: this.data.entitySchema, - navigationState: currentParameter + navigationState: currentParameter, }; } finally { - setTimeout(() => { - this.busy.hide(overlay); - }); + this.busy.hide(); } } - public async onHighlightedEntityChanged(personAll: PortalPersonAll): Promise { - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busy.show()); + public async onHighlightedEntityChanged(personAll: TypedEntity): Promise { + if (this.busy.overlayRefs.length === 0) { + this.busy.show(); + } let config: EuiSidesheetConfig; @@ -108,15 +101,15 @@ export class DuplicatesSidesheetComponent implements OnInit { title: await this.translateService.get('#LDS#Heading View Identity Details').toPromise(), subTitle: personAll.GetEntity().GetDisplay(), padding: '0', - width: 'max(550px, 55%)', + width: calculateSidesheetWidth(), testId: 'duplicate-identity-detail-sidesheet', data: await this.addressbookService.getDetail( personAll, - this.data.projectConfig.PersonConfig.VI_MyData_WhitePages_DetailAttributes - ) + this.data.projectConfig.PersonConfig?.VI_MyData_WhitePages_DetailAttributes ?? [], + ), }; } finally { - setTimeout(() => this.busy.hide(overlayRef)); + this.busy.hide(); } this.sidesheet.open(AddressbookDetailComponent, config); diff --git a/imxweb/projects/qer/src/lib/identities/identities-reports.service.ts b/imxweb/projects/qer/src/lib/identities/identities-reports.service.ts index 4dad02f0b..2f9beb9d8 100644 --- a/imxweb/projects/qer/src/lib/identities/identities-reports.service.ts +++ b/imxweb/projects/qer/src/lib/identities/identities-reports.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,14 +26,22 @@ import { Overlay } from '@angular/cdk/overlay'; import { HttpClient } from '@angular/common/http'; -import { Injectable, Injector } from '@angular/core'; -import { EuiDownloadDirective, EuiSidesheetService } from '@elemental-ui/core'; +import { ElementRef, Injectable, Injector } from '@angular/core'; +import { EuiDownloadDirective, EuiDownloadService, EuiSidesheetService } from '@elemental-ui/core'; +import { PortalAdminPerson, PortalPersonReports } from '@imx-modules/imx-api-qer'; +import { CollectionLoadParameters, EntityCollectionData, ValType } from '@imx-modules/imx-qbm-dbts'; import { TranslateService } from '@ngx-translate/core'; -import { CollectionLoadParameters, EntityCollectionData, ValType } from 'imx-qbm-dbts'; -import { AppConfigService, BaseCdr, ColumnDependentReference, ElementalUiConfigService, EntityService, SettingsService } from 'qbm'; +import { + AppConfigService, + BaseCdr, + calculateSidesheetWidth, + CdrSidesheetComponent, + ColumnDependentReference, + ElementalUiConfigService, + EntityService, + SettingsService, +} from 'qbm'; import { QerApiService } from '../qer-api-client.service'; -import { CdrSidesheetComponent } from 'qbm'; -import { PortalAdminPerson, PortalPersonReports } from 'imx-api-qer'; @Injectable() export class IdentitiesReportsService { @@ -48,7 +56,8 @@ export class IdentitiesReportsService { private readonly elementalUiConfigService: ElementalUiConfigService, private readonly http: HttpClient, private readonly injector: Injector, - private readonly overlay: Overlay + private readonly overlay: Overlay, + private readonly downloadService: EuiDownloadService, ) {} public async personsReport(person: PortalPersonReports | PortalAdminPerson): Promise { @@ -60,7 +69,7 @@ export class IdentitiesReportsService { title: await this.translate.get('#LDS#Heading Download Report').toPromise(), subTitle: person.GetEntity().GetDisplay(), padding: '0px', - width: 'max(600px, 60%)', + width: calculateSidesheetWidth(), testId: 'identities-report-sidesheet', data: { cdrs: Object.values(cdrs), buttonCaption: '#LDS#Download report' }, }) @@ -87,7 +96,13 @@ export class IdentitiesReportsService { public async viewReport(url: string): Promise { // not pretty, but the download directive does not support dynamic URLs - const directive = new EuiDownloadDirective(null /* no element */, this.http, this.overlay, this.injector); + const directive = new EuiDownloadDirective( + new ElementRef('') /* no element */, + this.http, + this.overlay, + this.injector, + this.downloadService, + ); directive.downloadOptions = { ...this.elementalUiConfigService.Config.downloadOptions, fileMimeType: '', @@ -125,9 +140,9 @@ export class IdentitiesReportsService { MinLen: 0, }, undefined, - { Value: 30 } + { Value: 30 }, ), - '#LDS#Period to be considered (in days)' + '#LDS#Period to be considered (in days)', ), withSubIdentites: new BaseCdr( this.entityService.createLocalEntityColumn( @@ -137,9 +152,9 @@ export class IdentitiesReportsService { MinLen: 0, }, undefined, - { Value: false } + { Value: false }, ), - '#LDS#Include subidentities' + '#LDS#Include subidentities', ), }; } diff --git a/imxweb/projects/qer/src/lib/identities/identities.component.html b/imxweb/projects/qer/src/lib/identities/identities.component.html index a7bf89e12..9275f97e0 100644 --- a/imxweb/projects/qer/src/lib/identities/identities.component.html +++ b/imxweb/projects/qer/src/lib/identities/identities.component.html @@ -1,68 +1,165 @@ -
    -
    - #LDS#Identities +
    +
    +

    #LDS#Identities

    - +
    - - - + + + + {{ entitySchemaPersonReports?.Columns?.[DisplayColumns.DISPLAY_PROPERTYNAME]?.Display }} + +
    {{ item.GetEntity().GetDisplay() }}
    -
    {{ - item.DefaultEmailAddress.Column.GetDisplayValue() }}
    -
    -
    - - +
    {{ item.DefaultEmailAddress.Column.GetDisplayValue() }}
    + + + + + + {{ entitySchemaPersonReports?.Columns?.IsSecurityIncident?.Display }} + + + + + {{ entitySchemaPersonReports?.Columns?.IsSecurityIncident?.Display }} + + +
    {{ '#LDS#Security risk' | translate }}
    -
    -
    - - - - - - - - - - + + + + {{ entitySchemaPersonReports?.Columns?.UID_Department?.Display }} + +
    + + {{ item.GetEntity().GetColumn('UID_Department').GetDisplayValue() }} + +
    + +
    + + + + {{ entitySchemaPersonReports?.Columns?.XMarkedForDeletion?.Display }} + + + + + {{ entitySchemaPersonReports?.Columns?.XMarkedForDeletion?.Display }} + + +
    {{ item.XMarkedForDeletion.Column.GetDisplayValue() }}
    -
    -
    -
    - - + + + + + + {{ entitySchemaPersonReports?.Columns?.IdentityType?.Display }} + + + + + {{ entitySchemaPersonReports?.Columns?.IdentityType?.Display }} + + + + + {{ item.IdentityType.Column.GetDisplayValue() }} + + + + + + + {{ entitySchemaPersonReports?.Columns?.EmployeeType?.Display }} + + + + + {{ entitySchemaPersonReports?.Columns?.EmployeeType?.Display }} + + + + + {{ item.EmployeeType.Column.GetDisplayValue() }} + + + + + + + {{ entitySchemaPersonReports?.Columns?.IsExternal?.Display }} + + + + + {{ entitySchemaPersonReports?.Columns?.IsExternal?.Display }} + + + + + {{ item.IsExternal.Column.GetDisplayValue() }} + + + + +
    -
    - - - - +
    diff --git a/imxweb/projects/qer/src/lib/identities/identities.component.scss b/imxweb/projects/qer/src/lib/identities/identities.component.scss index fd70dcfb0..b9c939720 100644 --- a/imxweb/projects/qer/src/lib/identities/identities.component.scss +++ b/imxweb/projects/qer/src/lib/identities/identities.component.scss @@ -1,5 +1,5 @@ @import '@elemental-ui/core/src/styles/_eui_palette.scss'; -@import '../../../../../shared/scss/common-table.scss'; +@import 'base/mixins'; .data-explorer { &.data-explorer--identities { &.identities--fullscreen { @@ -14,16 +14,9 @@ } } } - -.data-explorer-bottom-button-row { - @include imx-button-bar(); -} - -.hidden { - display: none; -} -::ng-deep{ - imx-report-button, imx-report-button-ext{ +::ng-deep { + imx-report-button, + imx-report-button-ext { display: none; } } diff --git a/imxweb/projects/qer/src/lib/identities/identities.component.ts b/imxweb/projects/qer/src/lib/identities/identities.component.ts index d30d47d98..237e7ddb6 100644 --- a/imxweb/projects/qer/src/lib/identities/identities.component.ts +++ b/imxweb/projects/qer/src/lib/identities/identities.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,30 +24,38 @@ * */ -import { Component, OnInit, OnDestroy, Input, ViewChild, ViewContainerRef } from '@angular/core'; +import { Component, Input, OnDestroy, OnInit, ViewChild, ViewContainerRef } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; import { Subscription } from 'rxjs'; -import { PortalAdminPerson, PortalPersonAll, PortalPersonReports, ProjectConfig, ViewConfigData } from 'imx-api-qer'; -import { CollectionLoadParameters, DataModel, DataModelProperty, DisplayColumns, EntitySchema, IClientProperty } from 'imx-qbm-dbts'; +import { PortalAdminPerson, PortalPersonReports, ProjectConfig, QerProjectConfig, ViewConfigData } from '@imx-modules/imx-api-qer'; +import { + CollectionLoadParameters, + DataModel, + DataModelProperty, + DisplayColumns, + EntitySchema, + IClientProperty, + TypedEntityCollectionData, +} from '@imx-modules/imx-qbm-dbts'; import { AuthenticationService, BusyService, + calculateSidesheetWidth, ClassloggerService, - DataSourceToolbarExportMethod, DataSourceToolbarFilter, - DataSourceToolbarGroupData, - DataSourceToolbarSettings, DataSourceToolbarViewConfig, DataTableGroupedData, + DataViewInitParameters, + DataViewSource, ExtService, HelpContextualValues, IExtension, ImxTranslationProviderService, + ISessionState, MessageDialogComponent, - SettingsService, SideNavigationComponent, } from 'qbm'; import { QerPermissionsService } from '../admin/qer-permissions.service'; @@ -62,6 +70,7 @@ import { IdentitySidesheetComponent } from './identity-sidesheet/identity-sidesh selector: 'imx-data-explorer-identities', templateUrl: './identities.component.html', styleUrls: ['./identities.component.scss'], + providers: [DataViewSource], }) export class DataExplorerIdentitiesComponent implements OnInit, OnDestroy, SideNavigationComponent { @Input() public applyIssuesFilter = false; @@ -77,20 +86,10 @@ export class DataExplorerIdentitiesComponent implements OnInit, OnDestroy, SideN @Input() public isAdmin = false; @Input() public contextId: HelpContextualValues; - /** - * Settings needed by the DataSourceToolbarComponent - */ - public dstSettings: DataSourceToolbarSettings; - - /** - * Page size, start index, search and filtering options etc. - */ - public navigationState: CollectionLoadParameters; - /** * Selected person */ - public selectedPerson: PortalPersonAll | PortalPersonReports; + public selectedPerson: PortalAdminPerson | PortalPersonReports; /** * Details of selected person @@ -108,17 +107,16 @@ export class DataExplorerIdentitiesComponent implements OnInit, OnDestroy, SideN public groupData: { [key: string]: DataTableGroupedData } = {}; public isManagerForPersons: boolean; public isPersonAdmin: boolean; + public isCreationAllowed: boolean; public isAuditor: boolean; public extensions: IExtension[] = []; - private projectConfig: ProjectConfig; + private projectConfig: ProjectConfig & QerProjectConfig; private displayedColumns: IClientProperty[] = []; private authorityDataDeleted$: Subscription; private sessionResponse$: Subscription; public busyService = new BusyService(); - private displayedInnerColumns: IClientProperty[] = []; - private groupingInfo: DataSourceToolbarGroupData; private dataModel: DataModel; private viewConfig: DataSourceToolbarViewConfig; private get viewConfigPath(): string { @@ -128,6 +126,7 @@ export class DataExplorerIdentitiesComponent implements OnInit, OnDestroy, SideN constructor( public translateProvider: ImxTranslationProviderService, + public dataSource: DataViewSource, private readonly sideSheet: EuiSidesheetService, private readonly busyServiceElemental: EuiLoadingService, private readonly logger: ClassloggerService, @@ -139,25 +138,19 @@ export class DataExplorerIdentitiesComponent implements OnInit, OnDestroy, SideN private readonly authService: AuthenticationService, qerPermissionService: QerPermissionsService, private identityReports: IdentitiesReportsService, - settingsService: SettingsService, - private extService: ExtService + private extService: ExtService, ) { - this.navigationState = { PageSize: settingsService.DefaultPageSize, StartIndex: 0 }; - this.authorityDataDeleted$ = this.identitiesService.authorityDataDeleted.subscribe(() => this.navigate()); + this.authorityDataDeleted$ = this.identitiesService.authorityDataDeleted.subscribe(() => this.dataSource.updateState()); - this.sessionResponse$ = this.authService.onSessionResponse.subscribe(async (session) => { + this.sessionResponse$ = this.authService.onSessionResponse.subscribe(async (session: ISessionState) => { if (session.IsLoggedIn) { - (this.currentUser = session.UserUid), (this.isManagerForPersons = await qerPermissionService.isPersonManager()); + (this.currentUser = session.UserUid || ''), (this.isManagerForPersons = await qerPermissionService.isPersonManager()); this.isPersonAdmin = await qerPermissionService.isPersonAdmin(); this.isAuditor = await qerPermissionService.isAuditor(); } }); } - get isMobile(): boolean { - return document.body.offsetWidth <= 768; - } - public async ngOnInit(): Promise { this.getDynamicMenuItems(); await this.init(); @@ -184,7 +177,10 @@ export class DataExplorerIdentitiesComponent implements OnInit, OnDestroy, SideN // !!TODO - Fix mat menu dynamic report components // Create dynamic report components and call viewReport function public async showDynamicReport(extension: IExtension): Promise { - const dynamicReportComponent = this.dynamicReport.createComponent(extension.instance, { index: 0 }); + if (!extension.instance) { + return; + } + const dynamicReportComponent = this.dynamicReport.createComponent(extension.instance, { index: 0 }); dynamicReportComponent.instance.referrer = this.currentUser; dynamicReportComponent.instance.inputData = extension.inputData; await dynamicReportComponent.instance.ngOnInit(); @@ -193,17 +189,8 @@ export class DataExplorerIdentitiesComponent implements OnInit, OnDestroy, SideN } } - /** - * Occurs when the navigation state has changed - e.g. users clicks on the next page button. - * - */ - public async onNavigationStateChanged(newState: CollectionLoadParameters): Promise { - this.navigationState = newState; - await this.navigate(); - } - public async personsManagedReport(): Promise { - this.identityReports.personsManagedReport(this.currentUser, '#LDS#Download report on identities you are directly responsible for'); + this.identityReports.personsManagedReport(this.currentUser, '#LDS#View identities you are directly responsible for'); } /** @@ -211,7 +198,7 @@ export class DataExplorerIdentitiesComponent implements OnInit, OnDestroy, SideN * * @param identity Selected identity. */ - public async onIdentityChanged(identity: PortalPersonAll | PortalPersonReports): Promise { + public async onIdentityChanged(identity: PortalAdminPerson | PortalPersonReports): Promise { const overlayRef = this.busyServiceElemental.show(); try { @@ -234,7 +221,7 @@ export class DataExplorerIdentitiesComponent implements OnInit, OnDestroy, SideN await dialogRef.afterClosed().toPromise(); // reload data - return this.navigate(); + return this.dataSource.updateState(); } await this.viewIdentity(this.selectedPersonDetail); } finally { @@ -242,44 +229,12 @@ export class DataExplorerIdentitiesComponent implements OnInit, OnDestroy, SideN } } - /** - * Occurs when user triggers search. - * - * @param keywords Search keywords. - */ - public async onSearch(keywords: string): Promise { - this.logger.debug(this, `Searching for: ${keywords}`); - this.navigationState.StartIndex = 0; - this.navigationState.search = keywords; - await this.navigate(); - } - - public async onGroupingChange(groupKey: string): Promise { - const isBusy = this.busyService.beginBusy(); - - try { - const groupData = this.groupData[groupKey]; - groupData.data = this.isAdmin - ? await this.identitiesService.getAllPersonAdmin(groupData.navigationState) - : await this.identitiesService.getReportsOfManager(groupData.navigationState); - groupData.settings = { - displayedColumns: this.displayedInnerColumns, - dataSource: groupData.data, - entitySchema: this.entitySchemaPersonReports, - navigationState: groupData.navigationState, - dataModel: this.dataModel, - }; - } finally { - isBusy.endBusy(); - } - } - public async createNewIdentity(): Promise { await this.sideSheet .open(CreateNewIdentityComponent, { title: await this.translate.get('#LDS#Heading Create Identity').toPromise(), padding: '0px', - width: 'max(650px, 65%)', + width: calculateSidesheetWidth(), disableClose: true, testId: 'create-new-identity-sidesheet', icon: 'contactinfo', @@ -291,7 +246,7 @@ export class DataExplorerIdentitiesComponent implements OnInit, OnDestroy, SideN .afterClosed() .toPromise(); - return this.navigate(); + return this.dataSource.updateState(); } private async init(): Promise { @@ -300,6 +255,7 @@ export class DataExplorerIdentitiesComponent implements OnInit, OnDestroy, SideN this.entitySchemaPersonReports = this.identitiesService.personReportsSchema; try { this.projectConfig = await this.configService.getConfig(); + this.isCreationAllowed = this.projectConfig.PersonConfig?.EnableNewPerson ?? false; this.displayedColumns = [ this.entitySchemaPersonReports.Columns[DisplayColumns.DISPLAY_PROPERTYNAME], this.entitySchemaPersonReports.Columns.IsSecurityIncident, @@ -310,86 +266,45 @@ export class DataExplorerIdentitiesComponent implements OnInit, OnDestroy, SideN this.displayedColumns.push( this.entitySchemaPersonReports.Columns.IdentityType, this.entitySchemaPersonReports.Columns.EmployeeType, - this.entitySchemaPersonReports.Columns.IsExternal + this.entitySchemaPersonReports.Columns.IsExternal, ); } // Ensure this column is always added last this.displayedColumns.push(this.entitySchemaPersonReports.Columns.XMarkedForDeletion); - this.displayedInnerColumns = [this.entitySchemaPersonReports.Columns[DisplayColumns.DISPLAY_PROPERTYNAME]]; - this.dataModel = this.isAdmin ? await this.identitiesService.getDataModelAdmin() : await this.identitiesService.getDataModelReport(); - this.filterOptions = this.dataModel.Filters; - this.groupingOptions = this.getGroupableProperties(this.dataModel.Properties); - if (!this.isAdmin) { - const indexActive = this.filterOptions.findIndex((elem) => elem.Name === 'isinactive'); - if (indexActive > -1) { - this.filterOptions[indexActive].InitialValue = '0'; - } - const reports = this.filterOptions.findIndex((elem) => elem.Name === 'reports'); - if (reports > -1) { - this.filterOptions[reports].InitialValue = '0'; - } - } - - if (this.applyIssuesFilter) { - const indexWithManagerFilter = this.filterOptions.findIndex((elem) => elem.Name === 'withmanager'); - if (indexWithManagerFilter > -1) { - this.filterOptions[indexWithManagerFilter].InitialValue = '0'; - } - } this.viewConfig = await this.viewConfigService.getInitialDSTExtension(this.dataModel, this.viewConfigPath); - await this.navigate(); - } finally { - isBusy.endBusy(); - } - } - - private async navigate(): Promise { - const isBusy = this.busyService.beginBusy(); - try { - this.logger.debug(this, `Retrieving person list`); - this.logger.trace('Navigation settings', this.navigationState); - if (!this.groupingInfo && this.groupingOptions.length > 0) { - this.groupingInfo = { - groups: [ + const dataViewInitParameters: DataViewInitParameters = { + execute: this.isAdmin + ? (params: CollectionLoadParameters, signal: AbortSignal): Promise> => + this.identitiesService.getAllPersonAdmin(params, signal) + : (params: CollectionLoadParameters, signal: AbortSignal): Promise> => + this.identitiesService.getReportsOfManager(params, signal), + schema: this.entitySchemaPersonReports, + columnsToDisplay: this.displayedColumns, + dataModel: this.dataModel, + groupExecute: (column: string, params: CollectionLoadParameters, signal: AbortSignal) => { + return this.identitiesService.getGroupedAllPerson( + column, { - property: this.groupingOptions[0], - getData: async () => { - return this.identitiesService.getGroupedAllPerson('IdentityType', { - PageSize: this.navigationState.PageSize, - StartIndex: 0, - withProperties: this.navigationState.withProperties, - }); - }, + PageSize: params.PageSize, + StartIndex: 0, + ...params, }, - ], - }; - } - - this.entitySchemaPersonReports = this.identitiesService.personReportsSchema; - const data = this.isAdmin - ? await this.identitiesService.getAllPersonAdmin(this.navigationState) - : await this.identitiesService.getReportsOfManager(this.navigationState); - const exportMethod: DataSourceToolbarExportMethod = this.isAdmin - ? this.identitiesService.exportAdminPerson(this.navigationState) - : this.identitiesService.exportPerson(this.navigationState); - exportMethod.initialColumns = this.displayedColumns.map((col) => col.ColumnName); - - this.dstSettings = { - displayedColumns: this.displayedColumns, - dataSource: data, - entitySchema: this.entitySchemaPersonReports, - navigationState: this.navigationState, - filters: this.filterOptions, - groupData: this.groupingInfo, - dataModel: this.dataModel, + signal, + ); + }, + exportFunction: this.isAdmin + ? this.identitiesService.exportAdminPerson(this.dataSource.state()) + : this.identitiesService.exportPerson(this.dataSource.state()), viewConfig: this.viewConfig, - exportMethod, + highlightEntity: (identity: PortalAdminPerson | PortalPersonReports) => { + this.onIdentityChanged(identity); + }, }; - this.logger.debug(this, `Head at ${data.Data.length + this.navigationState.StartIndex} of ${data.totalCount} item(s)`); + this.dataSource.init(dataViewInitParameters); } finally { isBusy.endBusy(); } @@ -398,21 +313,17 @@ export class DataExplorerIdentitiesComponent implements OnInit, OnDestroy, SideN public async updateConfig(config: ViewConfigData): Promise { await this.viewConfigService.putViewConfig(config); this.viewConfig = await this.viewConfigService.getDSTExtensionChanges(this.viewConfigPath); - this.dstSettings.viewConfig = this.viewConfig; + this.dataSource.viewConfig.set(this.viewConfig); } public async deleteConfigById(id: string): Promise { await this.viewConfigService.deleteViewConfig(id); this.viewConfig = await this.viewConfigService.getDSTExtensionChanges(this.viewConfigPath); - this.dstSettings.viewConfig = this.viewConfig; + this.dataSource.viewConfig.set(this.viewConfig); } private async getPersonDetails(id: string): Promise { - if (id == null || id.length <= 0) { - return null; - } this.logger.debug(this, `Retrieving details for admin person with id ${id}`); - return this.isAdmin ? this.identitiesService.getAdminPerson(id) : (await this.identitiesService.getPersonInteractive(id)).Data[0]; } @@ -423,7 +334,7 @@ export class DataExplorerIdentitiesComponent implements OnInit, OnDestroy, SideN subTitle: identity.GetEntity().GetDisplay(), padding: '0px', disableClose: true, - width: 'max(768px, 70%)', + width: calculateSidesheetWidth(1250, 0.7), icon: 'contactinfo', data: { isAdmin: this.isAdmin, @@ -435,12 +346,6 @@ export class DataExplorerIdentitiesComponent implements OnInit, OnDestroy, SideN }) .afterClosed() .toPromise(); - return this.navigate(); - } - - private getGroupableProperties(identityProperties: DataModelProperty[]): DataModelProperty[] { - let groupable: DataModelProperty[] = []; - groupable = identityProperties.filter((item) => item.IsGroupable); - return groupable; + return this.dataSource.updateState(); } } diff --git a/imxweb/projects/qer/src/lib/identities/identities.module.ts b/imxweb/projects/qer/src/lib/identities/identities.module.ts index e0ed7f297..60904d27c 100644 --- a/imxweb/projects/qer/src/lib/identities/identities.module.ts +++ b/imxweb/projects/qer/src/lib/identities/identities.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,8 +24,8 @@ * */ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatExpansionModule } from '@angular/material/expansion'; import { MatIconModule } from '@angular/material/icon'; @@ -34,6 +34,8 @@ import { RouterModule, Routes } from '@angular/router'; import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; import { TranslateModule } from '@ngx-translate/core'; +import { MatMenuModule } from '@angular/material/menu'; +import { ProjectConfig } from '@imx-modules/imx-api-qbm'; import { BusyIndicatorModule, CdrModule, @@ -41,6 +43,7 @@ import { DataSourceToolbarModule, DataTableModule, DataTreeModule, + DataViewModule, DynamicTabsModule, ExtModule, HELP_CONTEXTUAL, @@ -51,23 +54,21 @@ import { ObjectHistoryModule, RouteGuardService, } from 'qbm'; -import { DataExplorerIdentitiesComponent } from './identities.component'; -import { IdentitiesService } from './identities.service'; -import { IdentitySidesheetComponent } from './identity-sidesheet/identity-sidesheet.component'; -import { IdentitiesReportsService } from './identities-reports.service'; import { isAuditor, isPersonAdmin, isPersonManager } from '../admin/qer-permissions-helper'; -import { RiskModule } from '../risk/risk.module'; import { DataExplorerRegistryService } from '../data-explorer-view/data-explorer-registry.service'; -import { AssignmentsComponent } from './identity-sidesheet/assignments/assignments.component'; -import { IdentityRoleMembershipsModule } from './identity-sidesheet/identity-role-memberships/identity-role-memberships.module'; +import { MyResponsibilitiesRegistryService } from '../my-responsibilities-view/my-responsibilities-registry.service'; +import { ObjectHyperviewModule } from '../object-hyperview/object-hyperview.module'; import { OrgChartModule } from '../org-chart/org-chart.module'; +import { RequestHistoryModule } from '../request-history/request-history.module'; +import { RiskModule } from '../risk/risk.module'; import { CreateNewIdentityComponent } from './create-new-identity/create-new-identity.component'; import { DuplicatesSidesheetComponent } from './create-new-identity/duplicates-sidesheet/duplicates-sidesheet.component'; -import { RequestHistoryModule } from '../request-history/request-history.module'; -import { ObjectHyperviewModule } from '../object-hyperview/object-hyperview.module'; -import { MyResponsibilitiesRegistryService } from '../my-responsibilities-view/my-responsibilities-registry.service'; -import { MatMenuModule } from '@angular/material/menu'; -import { ProjectConfig } from 'imx-api-qbm'; +import { IdentitiesReportsService } from './identities-reports.service'; +import { DataExplorerIdentitiesComponent } from './identities.component'; +import { IdentitiesService } from './identities.service'; +import { AssignmentsComponent } from './identity-sidesheet/assignments/assignments.component'; +import { IdentityRoleMembershipsModule } from './identity-sidesheet/identity-role-memberships/identity-role-memberships.module'; +import { IdentitySidesheetComponent } from './identity-sidesheet/identity-sidesheet.component'; const routes: Routes = [ { @@ -112,18 +113,18 @@ const routes: Routes = [ IdentityRoleMembershipsModule, RouterModule.forChild(routes), MatMenuModule, - HelpContextualModule + HelpContextualModule, + DataViewModule, ], providers: [IdentitiesService, IdentitiesReportsService], exports: [DataExplorerIdentitiesComponent], - entryComponents: [CreateNewIdentityComponent, DuplicatesSidesheetComponent], }) export class IdentitiesModule { constructor( private readonly dataExplorerRegistryService: DataExplorerRegistryService, private readonly menuService: MenuService, private readonly myResponsibilitiesRegistryService: MyResponsibilitiesRegistryService, - logger: ClassloggerService + logger: ClassloggerService, ) { logger.info(this, '▶️ IdentitiesModule loaded'); this.init(); @@ -132,50 +133,47 @@ export class IdentitiesModule { } private setupMenu(): void { - this.menuService.addMenuFactories( - (preProps: string[], features: string[], projectConfig: ProjectConfig, groups: string[]) => { - - const items: MenuItem[] = []; - if (preProps.includes('ITSHOP') && (isPersonAdmin(features) || isAuditor(groups))) { - items.push( - { - id: 'QER_DataExplorer', - navigationCommands: { commands: ['admin', 'dataexplorer'] }, - title: '#LDS#Menu Entry Data Explorer', - sorting: '40-10', - }, - ); - } - if (items.length === 0) { - return null; - } - return { - id: 'ROOT_Data', - title: '#LDS#Data administration', - sorting: '40', - items - }; - }, - ); - } - - private init(): void { + this.menuService.addMenuFactories((preProps: string[], features: string[], projectConfig: ProjectConfig, groups: string[]) => { + const items: MenuItem[] = []; + if (preProps.includes('ITSHOP') && (isPersonAdmin(features) || isPersonManager(features) || isAuditor(groups))) { + items.push({ + id: 'QER_DataExplorer', + navigationCommands: { commands: ['admin', 'dataexplorer'] }, + title: '#LDS#Menu Entry Data Explorer', + sorting: '40-10', + }); + } - this.dataExplorerRegistryService.registerFactory((preProps: string[], features: string[], projectConfig: ProjectConfig, groups: string[]) => { - if (!isPersonAdmin(features) && !isAuditor(groups)) { + if (items.length === 0) { return; } return { - instance: DataExplorerIdentitiesComponent, - sortOrder: 1, - name: 'identities', - caption: '#LDS#Identities', - icon: 'contactinfo', - contextId: HELP_CONTEXTUAL.DataExplorerIdentities + id: 'ROOT_Data', + title: '#LDS#Data administration', + sorting: '40', + items, }; }); } + private init(): void { + this.dataExplorerRegistryService.registerFactory( + (preProps: string[], features: string[], projectConfig: ProjectConfig, groups: string[]) => { + if (!isPersonAdmin(features) && !isPersonManager(features) && !isAuditor(groups)) { + return; + } + return { + instance: DataExplorerIdentitiesComponent, + sortOrder: 1, + name: 'identities', + caption: '#LDS#Identities', + icon: 'contactinfo', + contextId: HELP_CONTEXTUAL.DataExplorerIdentities, + }; + }, + ); + } + private setupMyResponsibilitiesView(): void { this.myResponsibilitiesRegistryService.registerFactory((preProps: string[], groups: string[]) => { if (!isPersonManager(groups)) { @@ -186,7 +184,7 @@ export class IdentitiesModule { sortOrder: 1, name: 'identities', caption: '#LDS#Identities', - contextId: HELP_CONTEXTUAL.MyResponsibilitiesIdentities + contextId: HELP_CONTEXTUAL.MyResponsibilitiesIdentities, }; }); } diff --git a/imxweb/projects/qer/src/lib/identities/identities.service.ts b/imxweb/projects/qer/src/lib/identities/identities.service.ts index c782f995b..f1efb9610 100644 --- a/imxweb/projects/qer/src/lib/identities/identities.service.ts +++ b/imxweb/projects/qer/src/lib/identities/identities.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,36 +27,41 @@ import { Injectable } from '@angular/core'; import { Subject } from 'rxjs'; -import { ClassloggerService, DataSourceToolbarExportMethod } from 'qbm'; +import { + PortalAdminPerson, + PortalPersonAll, + PortalPersonReports, + PortalPersonUid, + V2ApiClientMethodFactory, +} from '@imx-modules/imx-api-qer'; import { CollectionLoadParameters, - TypedEntityCollectionData, - FilterType, CompareOperator, - EntityCollectionData, - GroupInfoData, DataModel, + EntityCollectionData, EntitySchema, ExtendedTypedEntityCollection, FilterData, + FilterType, + GroupInfoData, MethodDefinition, MethodDescriptor, -} from 'imx-qbm-dbts'; -import { PortalPersonReports, PortalPersonAll, PortalAdminPerson, PortalPersonUid, ViewConfigData, V2ApiClientMethodFactory } from 'imx-api-qer'; -import { QerApiService } from '../qer-api-client.service'; + TypedEntityCollectionData, +} from '@imx-modules/imx-qbm-dbts'; +import { ClassloggerService, DataSourceToolbarExportMethod } from 'qbm'; import { QerPermissionsService } from '../admin/qer-permissions.service'; +import { QerApiService } from '../qer-api-client.service'; import { DuplicateCheckParameter } from './create-new-identity/duplicate-check-parameter.interface'; @Injectable() export class IdentitiesService { - public authorityDataDeleted: Subject = new Subject(); - constructor( private readonly qerClient: QerApiService, private readonly logger: ClassloggerService, - private readonly qerPermissions: QerPermissionsService) { } + private readonly qerPermissions: QerPermissionsService, + ) {} public get personReportsSchema(): EntitySchema { return this.qerClient.typedClient.PortalPersonReports.GetSchema(); @@ -74,14 +79,13 @@ export class IdentitiesService { return this.qerClient.typedClient.PortalAdminPerson.GetSchema(); } - - public getAttestationHelperAlertDescription(count: { total: number; forUser: number; }): { description: string; value?: any; }[] { + public getAttestationHelperAlertDescription(count: { total: number; forUser: number }): { description: string; value?: any }[] { // #LDS#There are currently no pending attestation cases. return [ { description: '#LDS#Here you can get an overview of all attestations cases for this object.' }, { description: '#LDS#Pending attestation cases: {0}', value: count.total }, - { description: '#LDS#Pending attestation cases you can approve or deny: {0}', value: count.forUser } + { description: '#LDS#Pending attestation cases you can approve or deny: {0}', value: count.forUser }, ]; } @@ -92,16 +96,22 @@ export class IdentitiesService { * * @returns Wrapped list of Persons. */ - public async getAllPerson(navigationState: CollectionLoadParameters): Promise> { + public async getAllPerson( + navigationState: CollectionLoadParameters, + signal?: AbortSignal, + ): Promise> { this.logger.debug(this, `Retrieving person list`); this.logger.trace('Navigation state', navigationState); - return this.qerClient.typedClient.PortalPersonAll.Get(navigationState); + return this.qerClient.typedClient.PortalPersonAll.Get(navigationState, { signal }); } - public async getAllPersonAdmin(navigationState: CollectionLoadParameters): Promise> { + public async getAllPersonAdmin( + navigationState: CollectionLoadParameters, + signal: AbortSignal, + ): Promise> { this.logger.debug(this, `Retrieving person list`); this.logger.trace('Navigation state', navigationState); - return this.qerClient.typedClient.PortalAdminPerson.Get(navigationState); + return this.qerClient.typedClient.PortalAdminPerson.Get(navigationState, { signal }); } /** @@ -122,13 +132,13 @@ export class IdentitiesService { getMethod: (withProperties: string, PageSize?: number) => { let method: MethodDescriptor; if (PageSize) { - method = factory.portal_admin_person_get({...navigationState, withProperties, PageSize, StartIndex: 0}) + method = factory.portal_admin_person_get({ ...navigationState, withProperties, PageSize, StartIndex: 0 }); } else { - method = factory.portal_admin_person_get({...navigationState, withProperties}) + method = factory.portal_admin_person_get({ ...navigationState, withProperties }); } return new MethodDefinition(method); - } - } + }, + }; } public exportPerson(navigationState?: CollectionLoadParameters): DataSourceToolbarExportMethod { @@ -137,13 +147,13 @@ export class IdentitiesService { getMethod: (withProperties: string, PageSize?: number) => { let method: MethodDescriptor; if (PageSize) { - method = factory.portal_person_reports_get({...navigationState, withProperties, PageSize, StartIndex: 0 }); + method = factory.portal_person_reports_get({ ...navigationState, withProperties, PageSize, StartIndex: 0 }); } else { - method = factory.portal_person_reports_get({...navigationState, withProperties}); + method = factory.portal_person_reports_get({ ...navigationState, withProperties }); } return new MethodDefinition(method); - } - } + }, + }; } /** @@ -153,14 +163,20 @@ export class IdentitiesService { * * @returns Wrapped list of Persons. */ - public async getReportsOfManager(navigationState: CollectionLoadParameters): - Promise> { + public async getReportsOfManager( + navigationState: CollectionLoadParameters, + signal: AbortSignal, + ): Promise> { this.logger.debug(this, `Retrieving reports of the manager`); this.logger.trace('Navigation state', navigationState); - return this.qerClient.typedClient.PortalPersonReports.Get(navigationState); + return this.qerClient.typedClient.PortalPersonReports.Get(navigationState, { signal }); } - public async getGroupedAllPerson(columns: string, navigationState: CollectionLoadParameters): Promise { + public async getGroupedAllPerson( + columns: string, + navigationState: CollectionLoadParameters, + signal: AbortSignal, + ): Promise { this.logger.debug(this, `Retrieving person list`); this.logger.trace('Navigation state', navigationState); @@ -170,12 +186,14 @@ export class IdentitiesService { def: '', StartIndex: navigationState.StartIndex, PageSize: navigationState.PageSize, + filter: navigationState.filter, withcount: true, - withmanager: '', - orphaned: '', - deletedintarget: '', - isinactive: '' - } + withmanager: navigationState?.withmanager || '', + orphaned: navigationState?.orphaned || '', + deletedintarget: navigationState?.deletedintarget || '', + isinactive: navigationState?.isinactive || '', + }, + { signal }, ); } @@ -219,9 +237,8 @@ export class IdentitiesService { } public buildFilterForduplicates(parameter: DuplicateCheckParameter): FilterData[] { - const filter = []; - if (parameter.firstName != null && parameter.firstName !== '' - && parameter.lastName != null && parameter.lastName !== '') { + const filter: FilterData[] = []; + if (parameter.firstName != null && parameter.firstName !== '' && parameter.lastName != null && parameter.lastName !== '') { filter.push(this.buildFilter('FirstName', parameter.firstName)); filter.push(this.buildFilter('LastName', parameter.lastName)); } @@ -237,9 +254,7 @@ export class IdentitiesService { return filter; } - public async getDuplicates(parameter: CollectionLoadParameters) - : Promise>> { - + public async getDuplicates(parameter: CollectionLoadParameters): Promise>> { if (parameter.filter?.length === 0) { return { Data: [], totalCount: 0 }; } @@ -251,7 +266,7 @@ export class IdentitiesService { CompareOp: CompareOperator.Equal, Type: FilterType.Compare, ColumnName: column, - Value1: value + Value1: value, }; } } diff --git a/imxweb/projects/qer/src/lib/identities/identity-sidesheet/assignments/assignments.component.html b/imxweb/projects/qer/src/lib/identities/identity-sidesheet/assignments/assignments.component.html index 70b213f58..c13bcd8f9 100644 --- a/imxweb/projects/qer/src/lib/identities/identity-sidesheet/assignments/assignments.component.html +++ b/imxweb/projects/qer/src/lib/identities/identity-sidesheet/assignments/assignments.component.html @@ -1,26 +1,4 @@ -
    - - -
    -
    - -
    - -
    -
    - {{ tab.inputData.label }} -
    -
    -
    -
    - -
    - -
    -
    -
    \ No newline at end of file + + + + diff --git a/imxweb/projects/qer/src/lib/identities/identity-sidesheet/assignments/assignments.component.scss b/imxweb/projects/qer/src/lib/identities/identity-sidesheet/assignments/assignments.component.scss index 9480887a7..69ac2dca7 100644 --- a/imxweb/projects/qer/src/lib/identities/identity-sidesheet/assignments/assignments.component.scss +++ b/imxweb/projects/qer/src/lib/identities/identity-sidesheet/assignments/assignments.component.scss @@ -1,156 +1,6 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; -@import '@elemental-ui/core/src/styles/_eui_palette.scss'; -@import '../../../../../../../shared/scss/side-navigation.scss'; -:host { - height: 100%; -} +@import 'base/mixins'; -.snavigation { +:host { + @include flex-column-container-fill(); height: 100%; - - .mat-sidenav-container { - height: 100%; - background-color: $asher-gray; - - .mat-sidenav { - width: 230px; - - .snavigation-side { - display: flex; - flex-direction: column; - height: 100%; - - .snavigation-side-toggle { - display: none; - padding: 16px 12px 0; - margin-bottom: 10px; - - .mat-button { - min-width: 0; - padding: 0 4px; - height: 28px; - - .mat-icon { - margin-top: -12px; - } - } - } - - .snavigation-side-content { - flex: 1; - padding: 32px; - padding-bottom: 16px; - - .snavigation-side-heading { - font-size: 14px; - font-weight: bold; - margin-bottom: 10px; - white-space: nowrap; - } - - .snavigation-item { - margin: 0 -32px; - padding: 10px 32px; - display: flex; - align-items: center; - justify-content: flex-start; - cursor: pointer; - - & > .eui-icon { - margin-right: 8px; - color: rgba($black, 0.4); - } - - & > span { - flex: 1; - } - - &:hover:not(.snavigation-item--selected) { - background-color: rgba($iris-tint, 0.5); - } - - &.snavigation-item--selected { - background-color: $iris-blue; - color: $white; - } - } - } - } - } - - .mat-sidenav-content { - padding: 20px; - position: relative; - display: flex; - flex-direction: column; - - &.snavigation--backdrop-showing { - overflow: hidden; - } - - .snavigation-backdrop { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba($black, 0.32); - z-index: 200; - } - - .mat-table tr:hover td { - background-color: rgba($black, 0.04); - cursor: pointer; - } - } - } -} - -@media only screen and (max-width: 768px) { - .snavigation .mat-sidenav-container { - .mat-sidenav { - transition: width 0.5s; - - .snavigation-side { - .snavigation-side-toggle { - display: block; - } - .snavigation-side-content { - padding: 16px; - } - } - - &:not(.snavigation-side--expanded) { - width: 58px; - - .snavigation-side-content { - display: none; - } - } - } - - .mat-sidenav-content { - padding: 16px; - } - } -} - -.eui-dark-theme { - :host{ - .snavigation .mat-sidenav-container{ - background-color: $color-gray-80; - } - } -} - -.eui-contrast-theme { - :host{ - .snavigation .mat-sidenav-container{ - background-color: $color-gray-100; - } - - .snavigation-item--selected { - border: 1px solid $color-gray-0 - } - } } diff --git a/imxweb/projects/qer/src/lib/identities/identity-sidesheet/assignments/assignments.component.ts b/imxweb/projects/qer/src/lib/identities/identity-sidesheet/assignments/assignments.component.ts index 459456b14..4aedf1605 100644 --- a/imxweb/projects/qer/src/lib/identities/identity-sidesheet/assignments/assignments.component.ts +++ b/imxweb/projects/qer/src/lib/identities/identity-sidesheet/assignments/assignments.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,90 +24,106 @@ * */ -import { Component, ComponentFactoryResolver, Injector, Input, OnInit, ViewChild, ViewContainerRef } from '@angular/core'; +import { Component, Injector, Input, OnInit, ViewChild, ViewContainerRef } from '@angular/core'; -import { DynamicTabDataProviderDirective, ExtService, TabItem } from 'qbm'; +import { FormControl, FormGroup } from '@angular/forms'; +import { EuiSelectOption } from '@elemental-ui/core'; +import { TranslateService } from '@ngx-translate/core'; +import { DynamicTabDataProviderDirective, ExtService, isMobile, TabItem } from 'qbm'; import { IdentityRoleMembershipsComponent } from '../identity-role-memberships/identity-role-memberships.component'; import { IdentityRoleMembershipsService } from '../identity-role-memberships/identity-role-memberships.service'; +interface SelectorForm { + selector: FormControl; +} + @Component({ selector: 'imx-assignments', templateUrl: './assignments.component.html', - styleUrls: ['./assignments.component.scss'] + styleUrls: ['./assignments.component.scss'], }) export class AssignmentsComponent implements OnInit { - - public currentTab: TabItem; - public dynamicTabs: TabItem[] = []; - - get isMobile(): boolean { - return document.body.offsetWidth <= 768; - } + public currentTab: TabItem | undefined; public mobileSideNavExpanded = false; public showBackdrop = false; - public contentMargin = this.isMobile ? '10px' : '230px'; + public contentMargin = isMobile() ? '10px' : '230px'; + + public options: EuiSelectOption[]; + public formGroup: FormGroup = new FormGroup({ + selector: new FormControl('', { nonNullable: true }), + }); + + private tabItems: TabItem[] = []; @Input() public parameters: { objecttable: string; objectuid: string; tableName?: string }; - @ViewChild('sideNavContent', { read: ViewContainerRef, static: true }) private sideNavContentRef: ViewContainerRef; + @ViewChild('content', { static: true, read: ViewContainerRef }) content!: ViewContainerRef; constructor( private readonly roleService: IdentityRoleMembershipsService, - private readonly componentFactoryResolver: ComponentFactoryResolver, private readonly extService: ExtService, - private readonly injector: Injector - ) { } + private readonly injector: Injector, + private readonly translate: TranslateService, + ) {} public async ngOnInit(): Promise { - const tabs: TabItem[] = []; - this.roleService.targets.forEach(target => { + this.roleService.targets.forEach((target) => { const tabitem: TabItem = { instance: IdentityRoleMembershipsComponent, inputData: { id: target, - checkVisibility: async _ => true, - label: this.roleService.getTabData(target).label + checkVisibility: async (_) => true, + label: this.roleService.getTabData(target)?.label || '', }, - sortOrder: this.roleService.getTabData(target).index + sortOrder: this.roleService.getTabData(target)?.index, }; tabs.push(tabitem); }); + this.tabItems = [ + ...(await this.extService.getFittingComponents('identityAssignment', (ext) => + ext.inputData.checkVisibility(this.parameters), + )), + ...tabs, + ].sort((a: TabItem, b: TabItem) => (a?.sortOrder || 0) - (b?.sortOrder || 0)); - this.dynamicTabs = [...(await this.extService.getFittingComponents('identityAssignment', - (ext) => ext.inputData.checkVisibility(this.parameters))), ...tabs] - .sort((a: TabItem, b: TabItem) => a.sortOrder - b.sortOrder); + this.options = this.tabItems.map((elem) => ({ display: this.translate.instant(elem.inputData.label), value: elem.inputData.id })); + this.formGroup.controls.selector.setValue(this.options[0].value); - this.updateTab(this.dynamicTabs[0]); + this.updateTab(this.options[0]); } - public updateTab(tab: TabItem): void { - this.currentTab = tab; - this.sideNavContentRef.clear(); + public updateTab(tab: EuiSelectOption | EuiSelectOption[]): void { + const id = Object.hasOwn(tab, 'value') ? (tab as EuiSelectOption).value : ''; + this.currentTab = this.tabItems.find((elem) => elem.inputData.id === id); + if (this.currentTab == null) { + return; + } + this.content?.clear(); const dataProvider = Injector.create({ - providers: [{ - provide: DynamicTabDataProviderDirective, - useValue: { data: { ...this.parameters, ...{ tablename: tab.inputData.id } } } - }], + providers: [ + { + provide: DynamicTabDataProviderDirective, + useValue: { data: { ...this.parameters, ...{ tablename: this.currentTab.inputData.id } } }, + }, + ], parent: this.injector, }); - - this.sideNavContentRef.createComponent(this.componentFactoryResolver.resolveComponentFactory(this.currentTab.instance), - undefined, dataProvider); + if (this.currentTab?.instance != null) { + this.content.createComponent(this.currentTab.instance, { injector: dataProvider }); + } } public toggleMobileExpand(): void { this.mobileSideNavExpanded = !this.mobileSideNavExpanded; - const showBackdrop = this.isMobile && this.mobileSideNavExpanded; + const showBackdrop = isMobile() && this.mobileSideNavExpanded; setTimeout( () => { this.showBackdrop = showBackdrop; }, - showBackdrop ? 0 : 500 + showBackdrop ? 0 : 500, ); } - - } diff --git a/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-role-memberships/identity-role-memberships-parameter.interface.ts b/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-role-memberships/identity-role-memberships-parameter.interface.ts index 01ccf00f8..a6d49babd 100644 --- a/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-role-memberships/identity-role-memberships-parameter.interface.ts +++ b/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-role-memberships/identity-role-memberships-parameter.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { CollectionLoadParameters, EntitySchema, EntityCollectionData } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, EntitySchema, EntityCollectionData } from '@imx-modules/imx-qbm-dbts'; export interface IdentityRoleMembershipsParameter { /** diff --git a/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-role-memberships/identity-role-memberships.component.html b/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-role-memberships/identity-role-memberships.component.html index bb3651ba7..616514d1b 100644 --- a/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-role-memberships/identity-role-memberships.component.html +++ b/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-role-memberships/identity-role-memberships.component.html @@ -1,5 +1,8 @@ - - {{ '#LDS#Here you can get an overview of the memberships of the identity. Additionally, you can view the assignment analysis for each membership.' | translate }} + + {{ + '#LDS#Here you can get an overview of the memberships of the identity. Additionally, you can view the assignment analysis for each membership.' + | translate + }}
    - - + +
    {{ item.GetEntity().GetDisplay() }}
    diff --git a/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-role-memberships/identity-role-memberships.component.scss b/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-role-memberships/identity-role-memberships.component.scss index 220b6601f..bb0fa0525 100644 --- a/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-role-memberships/identity-role-memberships.component.scss +++ b/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-role-memberships/identity-role-memberships.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; :host { overflow: hidden; @@ -7,7 +7,6 @@ height: 100%; } - .imx-memberships { flex: 1 1 auto; display: flex; @@ -22,7 +21,3 @@ overflow: auto; margin-top: 20px; } - -.helper-alert { - margin: 20px 2px; -} \ No newline at end of file diff --git a/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-role-memberships/identity-role-memberships.component.ts b/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-role-memberships/identity-role-memberships.component.ts index 991140679..61d513f9c 100644 --- a/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-role-memberships/identity-role-memberships.component.ts +++ b/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-role-memberships/identity-role-memberships.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,13 +24,19 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; import { Component, OnInit } from '@angular/core'; -import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; +import { EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { CollectionLoadParameters, DisplayColumns, EntitySchema, IClientProperty, TypedEntity, ValType } from 'imx-qbm-dbts'; -import { BusyService, DataSourceToolbarSettings, DynamicTabDataProviderDirective, MetadataService, SettingsService } from 'qbm'; +import { CollectionLoadParameters, DisplayColumns, EntitySchema, IClientProperty, TypedEntity } from '@imx-modules/imx-qbm-dbts'; +import { + BusyService, + calculateSidesheetWidth, + DataSourceToolbarSettings, + DynamicTabDataProviderDirective, + MetadataService, + SettingsService, +} from 'qbm'; import { RoleService } from '../../../role-management/role.service'; import { SourceDetectiveSidesheetComponent, @@ -48,7 +54,7 @@ export class IdentityRoleMembershipsComponent implements OnInit { public readonly DisplayColumns = DisplayColumns; public displayedColumns: IClientProperty[]; public caption: string; - public entitySchema: EntitySchema; + public entitySchema: EntitySchema | undefined; public withActions: boolean; private referrer: { objectuid: string; tablename: string }; @@ -58,7 +64,6 @@ export class IdentityRoleMembershipsComponent implements OnInit { public busyService = new BusyService(); constructor( - private readonly busyServiceElemental: EuiLoadingService, private readonly metadataService: MetadataService, private readonly roleMembershipsService: IdentityRoleMembershipsService, private readonly membershipService: RoleService, @@ -68,10 +73,14 @@ export class IdentityRoleMembershipsComponent implements OnInit { dataProvider: DynamicTabDataProviderDirective, ) { this.referrer = dataProvider.data; - this.entitySchema = this.roleMembershipsService.getSchema(this.referrer.tablename); + this.navigationState = { PageSize: this.settingService.DefaultPageSize }; + this.withActions = this.roleMembershipsService.canAnalyseAssignment(this.referrer.tablename); + this.entitySchema = this.roleMembershipsService.getSchema(this.referrer.tablename); - this.navigationState = { PageSize: this.settingService.DefaultPageSize }; + if (!this.entitySchema) { + return; + } this.displayedColumns = [ this.entitySchema.Columns.XOrigin, this.entitySchema.Columns.XDateInserted, @@ -89,7 +98,7 @@ export class IdentityRoleMembershipsComponent implements OnInit { } finally { isBusy.endBusy(); } - this.caption = this.metadataService.tables[this.referrer.tablename].Display; + this.caption = this.metadataService.tables[this.referrer.tablename]?.Display || ''; return this.getData(); } @@ -99,7 +108,7 @@ export class IdentityRoleMembershipsComponent implements OnInit { } const uidPerson = this.referrer.objectuid; - const uidRole = this.membershipService.targetMap.get(this.referrer.tablename)?.membership.GetUidRole(entity.GetEntity()); + const uidRole = this.membershipService.targetMap.get(this.referrer.tablename)?.membership?.GetUidRole(entity.GetEntity()); if (uidRole == null) { return; } @@ -113,7 +122,7 @@ export class IdentityRoleMembershipsComponent implements OnInit { title: await this.translate.get('#LDS#Heading View Assignment Analysis').toPromise(), subTitle: entity.GetEntity().GetDisplay(), padding: '0px', - width: 'max(768px, 80%)', + width: calculateSidesheetWidth(1100, 0.7), disableClose: false, testId: 'identity-role-memberships-risk-sidesheet', data, @@ -138,9 +147,13 @@ export class IdentityRoleMembershipsComponent implements OnInit { try { const dataSource = await this.roleMembershipsService.get(this.referrer.tablename, this.referrer.objectuid, this.navigationState); + if (this.entitySchema == null) { + return; + } + this.dstSettings = { displayedColumns: this.displayedColumnsWithDisplay, - dataSource, + dataSource: dataSource, entitySchema: this.entitySchema, navigationState: this.navigationState, }; diff --git a/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-role-memberships/identity-role-memberships.module.ts b/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-role-memberships/identity-role-memberships.module.ts index 8e629a37e..29dfec11e 100644 --- a/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-role-memberships/identity-role-memberships.module.ts +++ b/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-role-memberships/identity-role-memberships.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -36,14 +36,6 @@ import { MatCardModule } from '@angular/material/card'; @NgModule({ declarations: [IdentityRoleMembershipsComponent], - imports: [ - CommonModule, - EuiCoreModule, - DataSourceToolbarModule, - DataTableModule, - TranslateModule, - MatButtonModule, - MatCardModule - ] + imports: [CommonModule, EuiCoreModule, DataSourceToolbarModule, DataTableModule, TranslateModule, MatButtonModule, MatCardModule], }) -export class IdentityRoleMembershipsModule { } +export class IdentityRoleMembershipsModule {} diff --git a/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-role-memberships/identity-role-memberships.service.ts b/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-role-memberships/identity-role-memberships.service.ts index a395110ec..f5a42e64d 100644 --- a/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-role-memberships/identity-role-memberships.service.ts +++ b/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-role-memberships/identity-role-memberships.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,19 +31,24 @@ import { PortalPersonRolemembershipsDepartment, PortalPersonRolemembershipsItshoporg, PortalPersonRolemembershipsLocality, - PortalPersonRolemembershipsProfitcenter -} from 'imx-api-qer'; -import { CollectionLoadParameters, EntitySchema, TypedEntity, TypedEntityBuilder, TypedEntityCollectionData } from 'imx-qbm-dbts'; + PortalPersonRolemembershipsProfitcenter, +} from '@imx-modules/imx-api-qer'; +import { + CollectionLoadParameters, + EntitySchema, + TypedEntity, + TypedEntityBuilder, + TypedEntityCollectionData, +} from '@imx-modules/imx-qbm-dbts'; import { QerApiService } from '../../../qer-api-client.service'; import { IdentityRoleMembershipsParameter, MembershipContolInfo } from './identity-role-memberships-parameter.interface'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class IdentityRoleMembershipsService { - public readonly targetMap: Map = new Map(); - public targets = []; + public targets: string[] = []; protected readonly localityTag = 'Locality'; protected readonly profitCenterTag = 'ProfitCenter'; @@ -55,29 +60,48 @@ export class IdentityRoleMembershipsService { this.addPredefinedTargets(); } - public async get(target: string, uidPerson:string, navigationState?: CollectionLoadParameters) - : Promise> { + public async get( + target: string, + uidPerson: string, + navigationState?: CollectionLoadParameters, + ): Promise | undefined> { const targetObject = this.targetMap.get(target); - if (!targetObject) { - throw new Error(`No target object registered for this target name '${target}'`); + if (!targetObject || !targetObject.get) { + // throw new Error(`No target object registered for this target name '${target}'`); + return undefined; } - const builder = new TypedEntityBuilder(targetObject.type); - const data = await targetObject.get(uidPerson,navigationState); + const entitySchema = targetObject.entitySchema; + if (!entitySchema) { + return undefined; + } + const data = await targetObject?.get(uidPerson, navigationState); + if (!data) { + return undefined; + } - return builder.buildReadWriteEntities(data, targetObject.entitySchema); + const builder = new TypedEntityBuilder(targetObject?.type); + return builder.buildReadWriteEntities(data, entitySchema); } - public getSchema(target: string): EntitySchema { - return this.targetMap.get(target)?.entitySchema; + public getSchema(target: string): EntitySchema | undefined { + if (!this.targetMap.has(target)) { + return undefined; + } + const targetObject = this.targetMap.get(target); + + if (!targetObject) { + return undefined; + } + return targetObject.entitySchema; } public canAnalyseAssignment(target: string): boolean { return this.targetMap.get(target)?.withAnalysis === true; } - public getTabData(target: string): MembershipContolInfo { + public getTabData(target: string): MembershipContolInfo | undefined { return this.targetMap.get(target)?.controlInfo; } @@ -95,14 +119,10 @@ export class IdentityRoleMembershipsService { label: '#LDS#Menu Entry Locations', index: 40, }, - get: async (uidPerson: string, parameter: CollectionLoadParameters) => - { - return this.qerApiClient.client.portal_person_rolememberships_Locality_get( - uidPerson, - parameter - )} - , - withAnalysis: true + get: async (uidPerson: string, parameter: CollectionLoadParameters) => { + return this.qerApiClient.client.portal_person_rolememberships_Locality_get(uidPerson, parameter); + }, + withAnalysis: true, }); this.addTarget({ @@ -113,11 +133,9 @@ export class IdentityRoleMembershipsService { label: '#LDS#Menu Entry Cost centers', index: 50, }, - get: async (uidPerson:string, parameter: CollectionLoadParameters) => this.qerApiClient.client.portal_person_rolememberships_ProfitCenter_get( - uidPerson, - parameter - ), - withAnalysis: true + get: async (uidPerson: string, parameter: CollectionLoadParameters) => + this.qerApiClient.client.portal_person_rolememberships_ProfitCenter_get(uidPerson, parameter), + withAnalysis: true, }); this.addTarget({ @@ -128,11 +146,9 @@ export class IdentityRoleMembershipsService { label: '#LDS#Menu Entry Departments', index: 30, }, - get: async (uidPerson: string, parameter: CollectionLoadParameters) => this.qerApiClient.client.portal_person_rolememberships_Department_get( - uidPerson, - parameter - ), - withAnalysis: true + get: async (uidPerson: string, parameter: CollectionLoadParameters) => + this.qerApiClient.client.portal_person_rolememberships_Department_get(uidPerson, parameter), + withAnalysis: true, }); this.addTarget({ @@ -143,11 +159,9 @@ export class IdentityRoleMembershipsService { label: '#LDS#Menu Entry Application roles', index: 60, }, - get: async (uidPerson: string, parameter: CollectionLoadParameters) => this.qerApiClient.client.portal_person_rolememberships_AERole_get( - uidPerson, - parameter - ), - withAnalysis: true + get: async (uidPerson: string, parameter: CollectionLoadParameters) => + this.qerApiClient.client.portal_person_rolememberships_AERole_get(uidPerson, parameter), + withAnalysis: true, }); this.addTarget({ @@ -158,14 +172,12 @@ export class IdentityRoleMembershipsService { label: '#LDS#Heading Shops', index: 90, }, - get: async (uidPerson: string,parameter: CollectionLoadParameters) => this.qerApiClient.client.portal_person_rolememberships_ITShopOrg_get( - uidPerson, - { + get: async (uidPerson: string, parameter: CollectionLoadParameters) => + this.qerApiClient.client.portal_person_rolememberships_ITShopOrg_get(uidPerson, { ...parameter, - type: 'CU' - } - ), - withAnalysis: false + type: 'CU', + }), + withAnalysis: false, }); } } diff --git a/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-sidesheet.component.html b/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-sidesheet.component.html index 2242867bb..a67b9bcfa 100644 --- a/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-sidesheet.component.html +++ b/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-sidesheet.component.html @@ -1,18 +1,19 @@ - + #LDS#Overview
    - - + + -
    +
    + [objectUid]="data?.selectedIdentity?.GetEntity()?.GetKeys()?.[0] ?? ''" + >
    @@ -38,8 +39,12 @@
    - - + +
    @@ -53,7 +58,7 @@
    - + {{ '#LDS#This identity will be deleted.' | translate }} @@ -63,78 +68,79 @@
    -
    +
    - +
    - - - - - {{ '#LDS#Personal data' | translate }} - - - - - - - - - {{ '#LDS#Organizational information' | translate }} - - - - - - - - - {{ '#LDS#Location information' | translate }} - - - - - - + + + + + + {{ '#LDS#Personal data' | translate }} + + + + + + + + + {{ '#LDS#Organizational information' | translate }} + + + + + + + + + {{ '#LDS#Location information' | translate }} + + + + + + +
    -
    @@ -152,10 +158,11 @@ #LDS#Requests
    -
    +
    - - + + +
    @@ -164,11 +171,7 @@ -
    -
    - -
    -
    +
    @@ -182,26 +185,52 @@ (click)="initiateDelete()" data-imx-identifier="identity-sidesheet-delete" > - + #LDS#Delete - - - - - diff --git a/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-sidesheet.component.scss b/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-sidesheet.component.scss index ece80de4a..7839579f8 100644 --- a/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-sidesheet.component.scss +++ b/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-sidesheet.component.scss @@ -1,35 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; -.eui-sidesheet-content { - padding: 0; - - ::ng-deep .mat-tab-body-content { - display: flex; - flex-direction: column; - } -} - -.eui-sidesheet-actions { - .justify-start { - margin-right: auto; - - eui-icon { - font-size: 14px; - margin-right: 4px; - } - } - - button:not(:last-of-type):not(.justify-start) { - margin-right: 10px; - } -} - -.imx-helper-alert { - width: 100%; - ::ng-deep .eui-alert { - margin-bottom: 20px; - } -} +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; .imx-security-badge { font-weight: 600; @@ -43,72 +12,16 @@ overflow: auto; } - .imx-account-ext { padding: 30px 30px 0px 30px; } -.imx-toggle-container{ - margin-bottom:16px; - >* {margin-right:20px;} -} - -.governance-sidesheet__tab-content-body { - &.imx-requests, &.imx-history { - .mat-card { - flex: 1 1 auto; - display: flex; - flex-direction: column; - height: 100%; - } - } -} - .governance-sidesheet__tab-content { - .mat-progress-spinner { - margin: auto; - } - imx-hyperview { overflow: hidden; } } -.imx-nested-group{ - &.eui-sidesheet-content{ - padding: 20px; - .mat-card{ - height: 100%; - padding: 0; - .mat-card{ - box-shadow: none; - } - ::ng-deep .mat-tab-group{ - border-radius: 4px; - imx-object-hyperview{ - .imx-tab-content-body{ - padding: 0; - } - } - .mat-ink-bar{ - background-color: $color-blue-90; - } - .governance-sidesheet{ - &__tab-content-body{ - ng-component{ - height: 100%; - .mat-card{ - padding: 0; - box-shadow: none; - } - } - } - } - } - } - } -} - @media screen and (max-width: 480px) { .marked-for-delete { margin: 10px 0 5px; @@ -116,84 +29,3 @@ width: 100%; } } - -::ng-deep .mat-tab-group { - height: 100%; - flex-grow: 1; - overflow: auto; - - .mat-tab-header { - padding: 0 32px; - background-color: $white; - } - - ::ng-deep .mat-tab-body-wrapper { - height: 100%; - } - - .imx-edit-fk-open-picker { - display: none; - } -} - -.eui-sidesheet-actions { - > *:not(:first-child) { - margin-left: 10px; - } -} - -.justify-start { - margin-right: auto; - justify-self: start; -} - -//Theming - -.eui-dark-theme { - :host { - .governance-sidesheet { - background: $color-gray-80; - ::ng-deep .mat-tab-group .mat-tab-header { - background-color: $color-gray-80; - } - ::ng-deep .mat-tab-label { - background-color: $color-gray-80; - } - - } - .imx-nested-group{ - ::ng-deep .mat-tab-group{ - .mat-tab-header{ - background-color: $color-gray-70; - .mat-ink-bar{ - background-color: $color-gray-0; - } - } - } - } - } -} - -.eui-contrast-theme { - :host { - ::ng-deep .mat-tab-group .mat-tab-header { - background-color: $color-gray-90; - } - .governance-sidesheet { - background: $color-gray-100; - ::ng-deep .mat-tab-label { - background-color: $color-gray-100; - } - } - .imx-nested-group{ - ::ng-deep .mat-tab-group{ - .mat-tab-header{ - background-color: $color-gray-90; - .mat-ink-bar{ - background-color: $color-gray-0; - } - } - } - } - } -} diff --git a/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-sidesheet.component.ts b/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-sidesheet.component.ts index 7d28325ba..ce0868458 100644 --- a/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-sidesheet.component.ts +++ b/imxweb/projects/qer/src/lib/identities/identity-sidesheet/identity-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,36 +24,37 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; -import { Component, OnDestroy, Inject, OnInit, ViewChild } from '@angular/core'; -import { UntypedFormGroup, UntypedFormControl } from '@angular/forms'; +import { Component, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { FormControl, FormGroup } from '@angular/forms'; import { MatSlideToggleChange } from '@angular/material/slide-toggle'; import { MatTabGroup } from '@angular/material/tabs'; import { Router } from '@angular/router'; -import { EuiLoadingService, EuiSidesheetService, EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; -import { Subscription } from 'rxjs'; +import { EUI_SIDESHEET_DATA, EuiLoadingService, EuiSidesheetRef, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; +import { Subscription } from 'rxjs'; -import { IdentitiesService } from '../identities.service'; -import { FeatureConfig, PortalAdminPerson, PortalPersonReports, QerProjectConfig } from 'imx-api-qer'; +import { FeatureConfig, PortalAdminPerson, PortalPersonReports, QerProjectConfig } from '@imx-modules/imx-api-qer'; +import { DbObjectKey } from '@imx-modules/imx-qbm-dbts'; import { - ColumnDependentReference, - ClassloggerService, + AuthenticationService, BaseCdr, + calculateSidesheetWidth, + CdrFactoryService, + ClassloggerService, + ColumnDependentReference, + ConfirmationService, + ExtService, + ISessionState, SnackBarService, - AuthenticationService, SystemInfoService, - ConfirmationService, TabItem, - ExtService, - CdrFactoryService, } from 'qbm'; -import { DbObjectKey } from 'imx-qbm-dbts'; -import { IdentitiesReportsService } from '../identities-reports.service'; +import { FeatureConfigService } from '../../admin/feature-config.service'; import { PasscodeService } from '../../ops/passcode.service'; import { QerApiService } from '../../qer-api-client.service'; import { RiskAnalysisSidesheetComponent } from '../../risk/riskanalysis-sidesheet.component'; -import { FeatureConfigService } from '../../admin/feature-config.service'; +import { IdentitiesReportsService } from '../identities-reports.service'; +import { IdentitiesService } from '../identities.service'; @Component({ selector: 'imx-identity-sidesheet', @@ -63,21 +64,22 @@ import { FeatureConfigService } from '../../admin/feature-config.service'; export class IdentitySidesheetComponent implements OnInit, OnDestroy { @ViewChild('tabs') public tabs: MatTabGroup; - public readonly detailsFormGroup: UntypedFormGroup; - public cdrList: ColumnDependentReference[] = []; - public cdrListPersonal: ColumnDependentReference[] = []; - public cdrListOrganizational: ColumnDependentReference[] = []; - public cdrListLocality: ColumnDependentReference[] = []; + public readonly detailsFormGroup = new FormGroup({}); + public cdrList: (ColumnDependentReference | undefined)[] = []; + public cdrListPersonal: (ColumnDependentReference | undefined)[] = []; + public cdrListOrganizational: (ColumnDependentReference | undefined)[] = []; + public cdrListLocality: (ColumnDependentReference | undefined)[] = []; public valueChanges$: Subscription; - public readonly parameters: { objecttable: string; objectuid: string }; + public readonly parameters: { objecttable: string; objectuid: string; display: string }; public canAnalyzeRisk = false; - public isActiveFormControl = new UntypedFormControl(); - public isSecurityIncidentFormControl = new UntypedFormControl(); + public isActiveFormControl = new FormControl(false, { nonNullable: true }); + public isSecurityIncidentFormControl = new FormControl(false, { nonNullable: true }); public dynamicTabs: TabItem[] = []; private readonly subscriptions: Subscription[] = []; private currentUserUid: string; private featureConfig: FeatureConfig; + private isInactiveHasChanged = false; constructor( @Inject(EUI_SIDESHEET_DATA) @@ -100,11 +102,11 @@ export class IdentitySidesheetComponent implements OnInit, OnDestroy { private readonly router: Router, private readonly systemInfoService: SystemInfoService, private readonly translate: TranslateService, - private readonly extService: ExtService, + private readonly extService: ExtService, private readonly featureConfigService: FeatureConfigService, private readonly cdrFactoryService: CdrFactoryService, authentication: AuthenticationService, - confirm: ConfirmationService + confirm: ConfirmationService, ) { this.subscriptions.push( this.sidesheetRef.closeClicked().subscribe(async (result) => { @@ -116,20 +118,24 @@ export class IdentitySidesheetComponent implements OnInit, OnDestroy { } else { this.sidesheetRef.close(result); } - }) + }), ); - this.subscriptions.push(authentication.onSessionResponse.subscribe((sessionState) => (this.currentUserUid = sessionState.UserUid))); + this.subscriptions.push( + authentication.onSessionResponse.subscribe((sessionState: ISessionState) => (this.currentUserUid = sessionState.UserUid || '')), + ); - this.detailsFormGroup = new UntypedFormGroup({}); this.parameters = { - objecttable: PortalPersonReports.GetEntitySchema().TypeName, + objecttable: PortalPersonReports.GetEntitySchema().TypeName ?? '', objectuid: data.selectedIdentity.GetEntity().GetKeys()[0], + display: data.selectedIdentity.GetEntity().GetDisplay(), }; this.systemInfoService .get() - .then((i) => (this.canAnalyzeRisk = i.PreProps.includes('RISKINDEX') && data.selectedIdentity.RiskIndexCalculated.value > 0)); + .then( + (i) => (this.canAnalyzeRisk = (i.PreProps?.includes('RISKINDEX') && data.selectedIdentity.RiskIndexCalculated.value > 0) || false), + ); } get isIdentityMarkedForDelete(): boolean { @@ -167,7 +173,7 @@ export class IdentitySidesheetComponent implements OnInit, OnDestroy { public async personsManagedReport(): Promise { this.reports.personsManagedReport( this.data.selectedIdentity.GetEntity().GetKeys()[0], - '#LDS#Download report on identities this identity is directly responsible for' + '#LDS#View identities this identity is directly responsible for', ); } @@ -202,7 +208,11 @@ export class IdentitySidesheetComponent implements OnInit, OnDestroy { try { await this.data.selectedIdentity.GetEntity().Commit(true); this.detailsFormGroup.markAsPristine(); - this.snackbar.open({ key: '#LDS#The changes have been successfully saved.' }); + this.snackbar.open({ + key: this.isInactiveHasChanged + ? '#LDS#Your changes have been successfully saved. It may take some time for the changes to take effect.' + : '#LDS#The changes have been successfully saved.', + }); this.closeSidesheet(); } finally { this.busyService.hide(overlayRef); @@ -243,7 +253,7 @@ export class IdentitySidesheetComponent implements OnInit, OnDestroy { title: await this.translate.get('#LDS#Heading Analyze Risk').toPromise(), subTitle: this.data.selectedIdentity.GetEntity().GetDisplay(), padding: '0px', - width: '60%', + width: calculateSidesheetWidth(), testId: 'identity-sidesheet-analyze-risk-sidesheet', data: { objectKey: new DbObjectKey('Person', this.data.selectedIdentity.GetEntity().GetKeys()[0]).ToXmlString() }, }); @@ -251,12 +261,13 @@ export class IdentitySidesheetComponent implements OnInit, OnDestroy { public async generatePasscode(): Promise { let passcode; - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.busyService.show())); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } try { passcode = await this.passcodeService.getPasscodeWithPortalLogin(this.data.selectedIdentity.GetEntity().GetKeys()[0]); } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } if (!passcode) { return; @@ -264,8 +275,8 @@ export class IdentitySidesheetComponent implements OnInit, OnDestroy { return this.passcodeService.showPasscode( passcode, this.data.selectedIdentity.GetEntity().GetDisplay(), - null, - await this.passcodeService.getValidationDuration() + '', + await this.passcodeService.getValidationDuration(), ); } @@ -289,14 +300,19 @@ export class IdentitySidesheetComponent implements OnInit, OnDestroy { } } - public update(cdr: ColumnDependentReference, list: ColumnDependentReference[]): void { - const index = list.findIndex((elem) => elem.column.ColumnName === cdr.column.ColumnName); + public update(cdr: ColumnDependentReference | undefined, list: (ColumnDependentReference | undefined)[]): void { + const cdrColumn = cdr?.column; + if (!cdrColumn || !list.length) { + return; + } + const index = list.findIndex((elem) => elem?.column?.ColumnName === (cdrColumn?.ColumnName ?? '')); if (index === -1) { return; } - this.detailsFormGroup.removeControl(cdr.column.ColumnName); - list.splice(index, 1, new BaseCdr(cdr.column)); + this.isInactiveHasChanged = cdrColumn?.ColumnName === 'IsTemporaryDeactivated'; + this.detailsFormGroup.removeControl(cdrColumn?.ColumnName ?? ''); + list.splice(index, 1, new BaseCdr(cdrColumn)); } private closeSidesheet(): void { @@ -305,39 +321,57 @@ export class IdentitySidesheetComponent implements OnInit, OnDestroy { private async setup(): Promise { // Resolve an issue where the mat-tab navigation arrows could appear on first load - this.subscriptions.push( - this.sidesheetRef.componentInstance.onOpen().subscribe(() => { - // Recalculate header - this.tabs.updatePagination(); - }) - ); + if (this.sidesheetRef?.componentInstance) { + this.subscriptions.push( + this.sidesheetRef.componentInstance?.onOpen().subscribe(() => { + // Recalculate header + this.tabs.updatePagination(); + }), + ); + } // Handle the IsInActive column outside the context of a CDR editor so the UI can invert the meaning to make more sense to the user // This should be inversed on the api data response at some point, but until then we handle it in the UI this.isActiveFormControl.setValue(!this.data.selectedIdentity.IsInActive.value); + if (!this.data.canEdit || !this.data.selectedIdentity.IsInActive.GetMetadata().CanEdit()) { + this.isActiveFormControl.disable(); + } this.detailsFormGroup.addControl(this.data.selectedIdentity.IsInActive.Column.ColumnName, this.isActiveFormControl); - const personalColumns = this.data.projectConfig.PersonConfig.VI_Employee_MasterData_Attributes; - this.cdrListPersonal = this.cdrFactoryService.buildCdrFromColumnList(this.data.selectedIdentity.GetEntity(), personalColumns, !this.data.canEdit); + this.isSecurityIncidentFormControl.setValue(this.data.selectedIdentity.IsSecurityIncident.value); + if (!this.data.canEdit || !this.data.selectedIdentity.IsSecurityIncident.GetMetadata().CanEdit()) { + this.isSecurityIncidentFormControl.disable(); + } + this.detailsFormGroup.addControl(this.data.selectedIdentity.IsSecurityIncident.Column.ColumnName, this.isSecurityIncidentFormControl); + this.detailsFormGroup.markAsPristine(); - const organizationalColumns = this.data.projectConfig.PersonConfig.VI_Employee_MasterData_OrganizationalAttributes; - this.cdrListOrganizational = this.cdrFactoryService.buildCdrFromColumnList( + const personalColumns = this.data.projectConfig.PersonConfig?.VI_Employee_MasterData_Attributes || []; + this.cdrListPersonal = this.cdrFactoryService.buildCdrFromColumnList( this.data.selectedIdentity.GetEntity(), - organizationalColumns, !this.data.canEdit + personalColumns, + !this.data.canEdit, ); - const localityColumns = this.data.projectConfig.PersonConfig.VI_Employee_MasterData_LocalityAttributes; - this.cdrListLocality = this.cdrFactoryService.buildCdrFromColumnList(this.data.selectedIdentity.GetEntity(), localityColumns, !this.data.canEdit); + const organizationalColumns = this.data.projectConfig.PersonConfig?.VI_Employee_MasterData_OrganizationalAttributes || []; + this.cdrListOrganizational = this.cdrFactoryService.buildCdrFromColumnList( + this.data.selectedIdentity.GetEntity(), + organizationalColumns, + !this.data.canEdit, + ); - this.isSecurityIncidentFormControl.setValue(this.data.selectedIdentity.IsSecurityIncident.value); - this.detailsFormGroup.addControl(this.data.selectedIdentity.IsSecurityIncident.Column.ColumnName, this.isSecurityIncidentFormControl); + const localityColumns = this.data.projectConfig.PersonConfig?.VI_Employee_MasterData_LocalityAttributes || []; + this.cdrListLocality = this.cdrFactoryService.buildCdrFromColumnList( + this.data.selectedIdentity.GetEntity(), + localityColumns, + !this.data.canEdit, + ); this.busyService.show(); try { this.featureConfig = await this.featureConfigService.getFeatureConfig(); this.dynamicTabs = ( await this.extService.getFittingComponents('identitySidesheet', (ext) => ext.inputData.checkVisibility(this.parameters)) - ).sort((tab1: TabItem, tab2: TabItem) => tab1.sortOrder - tab2.sortOrder); + ).sort((tab1: TabItem, tab2: TabItem) => (tab1.sortOrder ?? 0) - (tab2.sortOrder ?? 0)); } finally { this.busyService.hide(); } diff --git a/imxweb/projects/qer/src/lib/identities/test/common-test-mocks.spec.ts b/imxweb/projects/qer/src/lib/identities/test/common-test-mocks.spec.ts index b967a11bb..573b04420 100644 --- a/imxweb/projects/qer/src/lib/identities/test/common-test-mocks.spec.ts +++ b/imxweb/projects/qer/src/lib/identities/test/common-test-mocks.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,97 +24,94 @@ * */ -import { IEntityColumn, IEntity } from 'imx-qbm-dbts'; +import { IEntityColumn, IEntity } from '@imx-modules/imx-qbm-dbts'; import { ISessionState } from 'qbm'; import { Subject } from 'rxjs'; export class IdentitiesCommonTestData { - public static mockConfigService: any = { getConfig: () => { return Promise.resolve({ PersonConfig: { VI_MyData_WhitePages_ResultAttributes: { - Columns: ['col1', 'col2'] + Columns: ['col1', 'col2'], }, VI_PersonalData_Fields: { - Columns: ['col1', 'col2'] + Columns: ['col1', 'col2'], }, VI_MyData_WhitePages_DetailAttributes: { - Columns: ['col1', 'col2'] - } - } + Columns: ['col1', 'col2'], + }, + }, }); - } + }, }; public static mockAppConfigService: any = { Config: { Title: '', routeConfig: { - start: 'dashboard' - } + start: 'dashboard', + }, }, client: { imx_multilanguage_getcaptions_get: () => Promise.resolve({}), - imx_multilanguage_translations_get: () => Promise.resolve({}) - } + imx_multilanguage_translations_get: () => Promise.resolve({}), + }, }; public static mockAuthenticationServiceStub = { onSessionResponse: new Subject(), - update: jasmine.createSpy('update') + update: jasmine.createSpy('update'), }; public static mockSessionService: any = { TypedClient: { PortalTargetsystemUnsGroup: { - Get: () => Promise.resolve({}) + Get: () => Promise.resolve({}), }, PortalTargetsystemUnsAccount: { - Get: () => Promise.resolve({}) + Get: () => Promise.resolve({}), }, PortalPersonAll: { - Get: () => Promise.resolve({}) + Get: () => Promise.resolve({}), }, PortalAdminPerson: { - Get: () => Promise.resolve({}) + Get: () => Promise.resolve({}), }, PortalPerson: { - Get: () => Promise.resolve({Data: ['test1', 'test2']}) + Get: () => Promise.resolve({ Data: ['test1', 'test2'] }), }, - } + }, }; public static mockEntityColumn = { ColumnName: '', GetMetadata: () => { return { - CanEdit: () => false, + CanEdit: () => false, GetDisplay: () => '', - GetMinLength: () => 0 + GetMinLength: () => 0, }; }, - GetValue: () => '' - + GetValue: () => '', } as IEntityColumn; public static mockEntityColumnWithValue = { ColumnName: '', GetMetadata: () => { return { - CanEdit: () => false, + CanEdit: () => false, GetDisplay: () => '', - GetMinLength: () => 0 + GetMinLength: () => 0, }; }, - GetValue: () => 'Test value 1' - + GetValue: () => 'Test value 1', } as IEntityColumn; public static mockEntity = { GetDisplay: () => 'Display value', GetKeys: () => ['1'], - GetColumn: (name) => IdentitiesCommonTestData.mockEntityColumn + GetColumn: (name) => IdentitiesCommonTestData.mockEntityColumn, } as IEntity; } diff --git a/imxweb/projects/qer/src/lib/itshop-config/irequestable-entitlement-type.ts b/imxweb/projects/qer/src/lib/itshop-config/irequestable-entitlement-type.ts index b7e1d322c..61d859444 100644 --- a/imxweb/projects/qer/src/lib/itshop-config/irequestable-entitlement-type.ts +++ b/imxweb/projects/qer/src/lib/itshop-config/irequestable-entitlement-type.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,17 +24,16 @@ * */ -import { EntitySchema, TypedEntity } from "imx-qbm-dbts"; +import { EntitySchema, TypedEntity } from '@imx-modules/imx-qbm-dbts'; export interface IRequestableEntitlementType { - getTableName(): string; getFkColumnName(): string; - + addEntitlementSelections(shelfId: string, values: string[]): Promise; createAssignmentEntity(shelfId: string): TypedEntity; getSchema(): EntitySchema; -} \ No newline at end of file +} diff --git a/imxweb/projects/qer/src/lib/itshop-config/request-config-members/member-selector/member-selector.component.html b/imxweb/projects/qer/src/lib/itshop-config/request-config-members/member-selector/member-selector.component.html index fb0678368..29e523ff6 100644 --- a/imxweb/projects/qer/src/lib/itshop-config/request-config-members/member-selector/member-selector.component.html +++ b/imxweb/projects/qer/src/lib/itshop-config/request-config-members/member-selector/member-selector.component.html @@ -1,6 +1,12 @@
    - +
    @@ -18,7 +24,7 @@ -
    @@ -97,8 +121,8 @@
    + - @@ -31,7 +51,10 @@ {{ '#LDS#Heading Shelves' | translate }} - + @@ -39,7 +62,7 @@ {{ '#LDS#Heading Access' | translate }}
    - + +
    diff --git a/imxweb/projects/qer/src/lib/itshop-config/request-config-sidesheet/request-config-sidesheet.component.ts b/imxweb/projects/qer/src/lib/itshop-config/request-config-sidesheet/request-config-sidesheet.component.ts index 5f42525ee..a4d64ad2d 100644 --- a/imxweb/projects/qer/src/lib/itshop-config/request-config-sidesheet/request-config-sidesheet.component.ts +++ b/imxweb/projects/qer/src/lib/itshop-config/request-config-sidesheet/request-config-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,11 +26,11 @@ import { Component, Inject, OnInit } from '@angular/core'; import { UntypedFormArray, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; -import { PortalShopConfigStructure } from 'imx-api-qer'; -import { TypedEntityCollectionData } from 'imx-qbm-dbts'; -import { BaseCdr, ClassloggerService, ColumnDependentReference, StorageService, HELPER_ALERT_KEY_PREFIX, ConfirmationService, HELP_CONTEXTUAL } from 'qbm'; +import { PortalShopConfigStructure } from '@imx-modules/imx-api-qer'; +import { TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; +import { BaseCdr, ClassloggerService, ColumnDependentReference, ConfirmationService, HELP_CONTEXTUAL } from 'qbm'; import { ACTION_DISMISS, RequestsService } from '../requests.service'; export interface RequestConfigSidesheetData { @@ -38,8 +38,6 @@ export interface RequestConfigSidesheetData { isNew?: boolean; } -const helperAlertKey = `${HELPER_ALERT_KEY_PREFIX}_requestShopDetails`; - @Component({ selector: 'imx-request-config-sidesheet', templateUrl: './request-config-sidesheet.component.html', @@ -52,16 +50,15 @@ export class RequestConfigSidesheetComponent implements OnInit { public detailsContextIds = HELP_CONTEXTUAL.ConfigurationRequestsDetail; private shelfCount: number; private memberCount: number; - private reload= false; + private reload = false; constructor( formBuilder: UntypedFormBuilder, public requestsService: RequestsService, @Inject(EUI_SIDESHEET_DATA) public data: RequestConfigSidesheetData, - private readonly storageService: StorageService, private readonly logger: ClassloggerService, private readonly sidesheetRef: EuiSidesheetRef, - confirm: ConfirmationService + confirm: ConfirmationService, ) { this.detailsFormGroup = new UntypedFormGroup({ formArray: formBuilder.array([]) }); sidesheetRef.closeClicked().subscribe(async () => { @@ -74,7 +71,7 @@ export class RequestConfigSidesheetComponent implements OnInit { get selectedRequestConfigKey(): string { const keys = this.data?.requestConfig?.GetEntity()?.GetKeys(); - return keys?.length ? keys[0] : undefined; + return !!keys?.length ? keys[0] : ''; } get requestConfigContainsShelves(): boolean { @@ -93,10 +90,6 @@ export class RequestConfigSidesheetComponent implements OnInit { return display; } - get showHelperAlert(): boolean { - return !this.storageService.isHelperAlertDismissed(helperAlertKey); - } - get formArray(): UntypedFormArray { return this.detailsFormGroup.get('formArray') as UntypedFormArray; } @@ -137,10 +130,6 @@ export class RequestConfigSidesheetComponent implements OnInit { } } - public onHelperDismissed(): void { - this.storageService.storeHelperAlertDismissal(helperAlertKey); - } - private async createNew(): Promise { this.requestsService.handleOpenLoader(); try { diff --git a/imxweb/projects/qer/src/lib/itshop-config/request-config.module.ts b/imxweb/projects/qer/src/lib/itshop-config/request-config.module.ts index 8fd7f1f6e..c305b966c 100644 --- a/imxweb/projects/qer/src/lib/itshop-config/request-config.module.ts +++ b/imxweb/projects/qer/src/lib/itshop-config/request-config.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,8 +24,9 @@ * */ +import { CommonModule } from '@angular/common'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RouterModule, Routes } from '@angular/router'; import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; import { TranslateModule } from '@ngx-translate/core'; @@ -35,6 +36,7 @@ import { ClassloggerService, DataSourceToolbarModule, DataTableModule, + DataViewModule, FkAdvancedPickerModule, HELP_CONTEXTUAL, HelpContextualModule, @@ -43,25 +45,25 @@ import { MenuItem, MenuService, RouteGuardService, - SelectedElementsModule + SelectedElementsModule, } from 'qbm'; -import { RequestsComponent } from './requests/requests.component'; -import { RequestConfigSidesheetComponent } from './request-config-sidesheet/request-config-sidesheet.component'; -import { RequestShelfSidesheetComponent } from './request-shelf-sidesheet/request-shelf-sidesheet.component'; -import { RequestShelvesComponent } from './request-shelves/request-shelves.component'; -import { RequestConfigMembersComponent } from './request-config-members/request-config-members.component'; -import { RequestShelfEntitlementsComponent } from './request-shelf-entitlements/request-shelf-entitlements.component'; -import { RequestsEntitySelectorComponent } from './requests-selector/requests-entity-selector.component'; -import { DynamicExclusionDialogModule } from '../dynamic-exclusion-dialog/dynamic-exclusion-dialog.module'; -import { MemberSelectorComponent } from './request-config-members/member-selector/member-selector.component'; +import { MatTableModule } from '@angular/material/table'; import { isShopAdmin, isShopStatistics } from '../admin/qer-permissions-helper'; -import { CREATE_SHELF_TOKEN } from './request-shelves/request-shelf-token'; -import { ObjectHyperviewModule } from '../object-hyperview/object-hyperview.module'; +import { DynamicExclusionDialogModule } from '../dynamic-exclusion-dialog/dynamic-exclusion-dialog.module'; import { ShopGuardService } from '../guards/shop-guard.service'; import { JustificationModule } from '../justification/justification.module'; +import { ObjectHyperviewModule } from '../object-hyperview/object-hyperview.module'; +import { MemberSelectorComponent } from './request-config-members/member-selector/member-selector.component'; import { ReasonSidesheetComponent } from './request-config-members/reason-sidesheet/reason-sidesheet.component'; - +import { RequestConfigMembersComponent } from './request-config-members/request-config-members.component'; +import { RequestConfigSidesheetComponent } from './request-config-sidesheet/request-config-sidesheet.component'; +import { RequestShelfEntitlementsComponent } from './request-shelf-entitlements/request-shelf-entitlements.component'; +import { RequestShelfSidesheetComponent } from './request-shelf-sidesheet/request-shelf-sidesheet.component'; +import { CREATE_SHELF_TOKEN } from './request-shelves/request-shelf-token'; +import { RequestShelvesComponent } from './request-shelves/request-shelves.component'; +import { RequestsEntitySelectorComponent } from './requests-selector/requests-entity-selector.component'; +import { RequestsComponent } from './requests/requests.component'; const routes: Routes = [ { @@ -69,9 +71,9 @@ const routes: Routes = [ component: RequestsComponent, canActivate: [RouteGuardService, ShopGuardService], resolve: [RouteGuardService], - data:{ - contextId: HELP_CONTEXTUAL.ConfigurationRequests - } + data: { + contextId: HELP_CONTEXTUAL.ConfigurationRequests, + }, }, ]; @@ -85,7 +87,7 @@ const routes: Routes = [ RequestConfigMembersComponent, RequestShelfEntitlementsComponent, RequestsEntitySelectorComponent, - ReasonSidesheetComponent + ReasonSidesheetComponent, ], imports: [ CommonModule, @@ -106,46 +108,43 @@ const routes: Routes = [ JustificationModule, RouterModule.forChild(routes), HelpContextualModule, + MatTableModule, + DataViewModule, ], - providers: [{provide: CREATE_SHELF_TOKEN, useValue: RequestShelfSidesheetComponent}], + providers: [{ provide: CREATE_SHELF_TOKEN, useValue: RequestShelfSidesheetComponent }], schemas: [CUSTOM_ELEMENTS_SCHEMA], }) export class RequestConfigModule { - constructor( private readonly menuService: MenuService, - logger: ClassloggerService) { + logger: ClassloggerService, + ) { logger.info(this, '▶︝ RequestConfigModule loaded'); this.setupMenu(); } private setupMenu(): void { - this.menuService.addMenuFactories( - (preProps: string[], features: string[]) => { - - const items: MenuItem[] = []; + this.menuService.addMenuFactories((preProps: string[], features: string[]) => { + const items: MenuItem[] = []; - if (isShopAdmin(features) || isShopStatistics(features)) { - items.push( - { - id: 'QER_Setup_ITShop', - route: 'configuration/requests', - title: '#LDS#Menu Entry Shops', - sorting: '60-20', - }, - ); - } + if (isShopAdmin(features) || isShopStatistics(features)) { + items.push({ + id: 'QER_Setup_ITShop', + route: 'configuration/requests', + title: '#LDS#Menu Entry Shops', + sorting: '60-20', + }); + } - if (items.length === 0) { - return null; - } - return { - id: 'ROOT_Setup', - title: '#LDS#Setup', - sorting: '60', - items - }; - }, - ); + if (items.length === 0) { + return; + } + return { + id: 'ROOT_Setup', + title: '#LDS#Setup', + sorting: '60', + items, + }; + }); } } diff --git a/imxweb/projects/qer/src/lib/itshop-config/request-shelf-entitlements/request-shelf-entitlements.component.html b/imxweb/projects/qer/src/lib/itshop-config/request-shelf-entitlements/request-shelf-entitlements.component.html index 406250a0f..82785987e 100644 --- a/imxweb/projects/qer/src/lib/itshop-config/request-shelf-entitlements/request-shelf-entitlements.component.html +++ b/imxweb/projects/qer/src/lib/itshop-config/request-shelf-entitlements/request-shelf-entitlements.component.html @@ -15,7 +15,7 @@ - +
    {{ item.GetEntity().GetDisplay() }}
    {{ entitlementTypes.get(item.GetEntity().GetKeys().toString()) }}
    - +
    {{ item.GetEntity().GetColumn('XOrigin').GetDisplayValue() }}
    @@ -39,7 +39,7 @@ >
    - +
    {{ item.GetEntity().GetColumn('XDateInserted').GetValue() | date }}
    @@ -47,13 +47,11 @@
    -
    -
    -
    +
    + - +
    diff --git a/imxweb/projects/qer/src/lib/itshop-config/request-shelf-entitlements/request-shelf-entitlements.component.ts b/imxweb/projects/qer/src/lib/itshop-config/request-shelf-entitlements/request-shelf-entitlements.component.ts index 3cb8e8237..df7318dba 100644 --- a/imxweb/projects/qer/src/lib/itshop-config/request-shelf-entitlements/request-shelf-entitlements.component.ts +++ b/imxweb/projects/qer/src/lib/itshop-config/request-shelf-entitlements/request-shelf-entitlements.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,20 +28,26 @@ import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angu import { EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { PortalRolesEntitlements } from 'imx-api-qer'; -import { CollectionLoadParameters, DbObjectKey, DisplayColumns, EntitySchema, IClientProperty } from 'imx-qbm-dbts'; +import { PortalRolesEntitlements } from '@imx-modules/imx-api-qer'; import { - DataSourceToolbarSettings, - DataSourceToolbarFilter, + CollectionLoadParameters, + DbObjectKey, + DisplayColumns, + EntitySchema, + IClientProperty, + TypedEntity, +} from '@imx-modules/imx-qbm-dbts'; +import { + BusyService, ClassloggerService, + DataSourceToolbarFilter, + DataSourceToolbarSettings, DataTableComponent, - StorageService, - HELPER_ALERT_KEY_PREFIX, - SettingsService, - MetadataService, DynamicMethodService, - BusyService, HELP_CONTEXTUAL, + MetadataService, + SettingsService, + calculateSidesheetWidth, } from 'qbm'; import { QerApiService } from '../../qer-api-client.service'; import { RequestsEntitySelectorComponent } from '../requests-selector/requests-entity-selector.component'; @@ -66,7 +72,7 @@ export class RequestShelfEntitlementsComponent implements OnInit { public dstSettings: DataSourceToolbarSettings; public navigationState: CollectionLoadParameters; public filterOptions: DataSourceToolbarFilter[] = []; - public selectedEntitlements: PortalRolesEntitlements[] = []; + public selectedEntitlements: TypedEntity[] = []; public productContextIds = HELP_CONTEXTUAL.ConfigurationRequestsShelvesProduct; private displayedColumns: IClientProperty[]; @@ -87,7 +93,7 @@ export class RequestShelfEntitlementsComponent implements OnInit { private readonly metadata: MetadataService, private readonly sidesheet: EuiSidesheetService, private readonly translate: TranslateService, - private readonly dynamicMethodService: DynamicMethodService + private readonly dynamicMethodService: DynamicMethodService, ) { this.navigationState = { PageSize: this.settingsService.DefaultPageSize, StartIndex: 0 }; this.entitySchema = qerApiService.typedClient.PortalRolesEntitlements.GetSchema(); @@ -98,10 +104,6 @@ export class RequestShelfEntitlementsComponent implements OnInit { ]; } - get isMobile(): boolean { - return document.body.offsetWidth <= 768; - } - public async ngOnInit(): Promise { await this.navigate(); } @@ -113,7 +115,7 @@ export class RequestShelfEntitlementsComponent implements OnInit { await this.navigate(); } - public onEntitlementSelected(selected: PortalRolesEntitlements[]): void { + public onEntitlementSelected(selected: TypedEntity[]): void { this.logger.debug(this, `Selected entitlements changed`); this.logger.trace(`New entitlement selections`, selected); this.selectedEntitlements = selected; @@ -135,7 +137,7 @@ export class RequestShelfEntitlementsComponent implements OnInit { title: await this.translate.get('#LDS#Heading Add Products').toPromise(), subTitle: this.shopDisplay, padding: '0px', - width: 'max(55%,550px)', + width: calculateSidesheetWidth(), testId: 'request-shelf-entitlements-add-products', data: { shelfId: this.shelfId, @@ -151,16 +153,16 @@ export class RequestShelfEntitlementsComponent implements OnInit { public async removeEntitlements(): Promise { const isBusy = this.busyService.beginBusy(); try { - const promises = []; + const promises: Promise[] = []; // TODO what if only some succeed? - this.selectedEntitlements.forEach((ent) => { + this.selectedEntitlements.forEach((ent: PortalRolesEntitlements) => { const entitlementKey = DbObjectKey.FromXml(ent.ObjectKeyElement.value); promises.push( this.dynamicMethodService.delete( this.qerApiService.apiClient, '/portal/shop/config/entitlements/' + this.shelfId + '/' + entitlementKey.TableName + '/' + entitlementKey.Keys[0], - {} - ) + {}, + ), ); }); await Promise.all(promises); @@ -177,7 +179,7 @@ export class RequestShelfEntitlementsComponent implements OnInit { private async processEntitlementSelections(values: string[]): Promise { const isBusy = this.busyService.beginBusy(); try { - await this.requestsService.selectedEntitlementType.addEntitlementSelections(this.shelfId, values); + await this.requestsService.selectedEntitlementType?.addEntitlementSelections(this.shelfId, values); this.requestsService.openSnackbar('#LDS#The products have been successfully added.', ACTION_DISMISS); await this.navigate(); } finally { @@ -207,7 +209,7 @@ export class RequestShelfEntitlementsComponent implements OnInit { navigationState: this.navigationState, filters: this.filterOptions, }; - this.logger.debug(this, `Head at ${data.Data.length + this.navigationState.StartIndex} of ${data.totalCount} item(s)`); + this.logger.debug(this, `Head at ${data.Data.length + (this.navigationState?.StartIndex || 0)} of ${data.totalCount} item(s)`); data.Data.forEach(async (item: PortalRolesEntitlements) => { const objKey = DbObjectKey.FromXml(item.ObjectKeyElement.value); @@ -215,10 +217,10 @@ export class RequestShelfEntitlementsComponent implements OnInit { var display: string; if (!this.entitlementTypes.has(objKey.TableName)) { const metadata = await this.metadata.GetTableMetadata(objKey.TableName); - this.entitlementTypes.set(uid, metadata.DisplaySingular); - display = metadata.DisplaySingular; + this.entitlementTypes.set(uid, metadata?.DisplaySingular || ''); + display = metadata?.DisplaySingular || ''; } else { - display = this.entitlementTypes.get(objKey.TableName); + display = this.entitlementTypes.get(objKey.TableName) || ''; } this.entitlementTypes.set(uid, display); diff --git a/imxweb/projects/qer/src/lib/itshop-config/request-shelf-sidesheet/request-shelf-sidesheet.component.html b/imxweb/projects/qer/src/lib/itshop-config/request-shelf-sidesheet/request-shelf-sidesheet.component.html index 09a2e875f..881063ef2 100644 --- a/imxweb/projects/qer/src/lib/itshop-config/request-shelf-sidesheet/request-shelf-sidesheet.component.html +++ b/imxweb/projects/qer/src/lib/itshop-config/request-shelf-sidesheet/request-shelf-sidesheet.component.html @@ -1,22 +1,32 @@ - + {{ '#LDS#Heading Details' | translate }}
    - +
    - + -
    -
    diff --git a/imxweb/projects/qer/src/lib/itshop-config/request-shelves/request-shelves.component.ts b/imxweb/projects/qer/src/lib/itshop-config/request-shelves/request-shelves.component.ts index c3fe4d70c..14f6e506a 100644 --- a/imxweb/projects/qer/src/lib/itshop-config/request-shelves/request-shelves.component.ts +++ b/imxweb/projects/qer/src/lib/itshop-config/request-shelves/request-shelves.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,18 +29,17 @@ import { Component, EventEmitter, Inject, Input, OnInit, Output } from '@angular import { EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { PortalShopConfigStructure } from 'imx-api-qer'; -import { CollectionLoadParameters, IClientProperty, DisplayColumns, EntitySchema } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, DisplayColumns, EntitySchema, IClientProperty, TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { - DataSourceToolbarSettings, - DataSourceToolbarFilter, - ClassloggerService, - HELPER_ALERT_KEY_PREFIX, - SettingsService, BusyService, + ClassloggerService, + DataSourceToolbarFilter, + DataSourceToolbarSettings, + HELP_CONTEXTUAL, HelpContextualComponent, HelpContextualService, - HELP_CONTEXTUAL + SettingsService, + calculateSidesheetWidth, } from 'qbm'; import { RequestsService } from '../requests.service'; import { CREATE_SHELF_TOKEN } from './request-shelf-token'; @@ -59,7 +58,7 @@ export class RequestShelvesComponent implements OnInit { public dstSettings: DataSourceToolbarSettings; public navigationState: CollectionLoadParameters; public filterOptions: DataSourceToolbarFilter[] = []; - public busyService= new BusyService(); + public busyService = new BusyService(); public shelvesContextIds = HELP_CONTEXTUAL.ConfigurationRequestsShelves; private displayedColumns: IClientProperty[] = []; @@ -71,7 +70,7 @@ export class RequestShelvesComponent implements OnInit { private readonly translate: TranslateService, public readonly requestsService: RequestsService, private readonly settingsService: SettingsService, - private readonly helpContextualService: HelpContextualService + private readonly helpContextualService: HelpContextualService, ) { this.navigationState = { PageSize: this.settingsService.DefaultPageSize, StartIndex: 0 }; this.entitySchemaShopStructure = requestsService.shopStructureSchema; @@ -93,7 +92,7 @@ export class RequestShelvesComponent implements OnInit { await this.navigate(); } - public async onRequestShelfChanged(requestConfig: PortalShopConfigStructure): Promise { + public async onRequestShelfChanged(requestConfig: TypedEntity): Promise { this.logger.debug(this, `Selected request shelf changed`); this.logger.trace(`New request shelf selected`, requestConfig); this.viewRequestShelf(requestConfig); @@ -117,9 +116,9 @@ export class RequestShelvesComponent implements OnInit { this.viewRequestShelf(newRequestShelf, true); } - private async viewRequestShelf(requestConfig: PortalShopConfigStructure, isNew: boolean = false): Promise { + private async viewRequestShelf(requestConfig: TypedEntity, isNew: boolean = false): Promise { const header = await this.translate.get(isNew ? '#LDS#Heading Create Shelf' : '#LDS#Heading Edit Shelf').toPromise(); - if(isNew){ + if (isNew) { this.helpContextualService.setHelpContextId(HELP_CONTEXTUAL.ConfigurationRequestsShelvesCreate); } const result = await this.sideSheet @@ -128,13 +127,13 @@ export class RequestShelvesComponent implements OnInit { subTitle: isNew ? '' : requestConfig.GetEntity().GetDisplay(), padding: '0px', disableClose: true, - width: '55%', + width: calculateSidesheetWidth(), testId: isNew ? 'request-shelves-create-shelf-sidesheet' : 'request-shelves-edit-shelf-sidesheet', data: { requestConfig, isNew, }, - headerComponent: isNew ? HelpContextualComponent : undefined + headerComponent: isNew ? HelpContextualComponent : undefined, }) .afterClosed() .toPromise(); @@ -160,7 +159,7 @@ export class RequestShelvesComponent implements OnInit { navigationState: this.navigationState, filters: this.filterOptions, }; - this.logger.debug(this, `Head at ${data.Data.length + this.navigationState.StartIndex} of ${data.totalCount} item(s)`); + this.logger.debug(this, `Head at ${data.Data.length + (this.navigationState?.StartIndex || 0)} of ${data.totalCount} item(s)`); } finally { isBusy.endBusy(); } diff --git a/imxweb/projects/qer/src/lib/itshop-config/requestable-entitlement-type.service.ts b/imxweb/projects/qer/src/lib/itshop-config/requestable-entitlement-type.service.ts index bcb0b0410..0edaed319 100644 --- a/imxweb/projects/qer/src/lib/itshop-config/requestable-entitlement-type.service.ts +++ b/imxweb/projects/qer/src/lib/itshop-config/requestable-entitlement-type.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,28 +24,21 @@ * */ -import { Injectable } from "@angular/core"; -import { QerApiService } from "../qer-api-client.service"; -import { IRequestableEntitlementType } from "./irequestable-entitlement-type"; -import { ResourceEntitlementType } from "./resource-entitlement-type"; +import { Injectable } from '@angular/core'; +import { QerApiService } from '../qer-api-client.service'; +import { IRequestableEntitlementType } from './irequestable-entitlement-type'; +import { ResourceEntitlementType } from './resource-entitlement-type'; @Injectable({ providedIn: 'root' }) export class RequestableEntitlementTypeService { - constructor(qerApi: QerApiService) { const types = [ - new ResourceEntitlementType("QERResource", - qerApi.typedClient.PortalShopConfigEntitlementsQerresource), - new ResourceEntitlementType("QERReuse", - qerApi.typedClient.PortalShopConfigEntitlementsQerreuse), - new ResourceEntitlementType("QERReuseUS", - qerApi.typedClient.PortalShopConfigEntitlementsQerreuseus), - new ResourceEntitlementType("QERAssign", - qerApi.typedClient.PortalShopConfigEntitlementsQerassign) - ]; - this.typeProviders = [ - () => Promise.resolve(this.enableResourceTypes ? types : []) + new ResourceEntitlementType('QERResource', qerApi.typedClient.PortalShopConfigEntitlementsQerresource), + new ResourceEntitlementType('QERReuse', qerApi.typedClient.PortalShopConfigEntitlementsQerreuse), + new ResourceEntitlementType('QERReuseUS', qerApi.typedClient.PortalShopConfigEntitlementsQerreuseus), + new ResourceEntitlementType('QERAssign', qerApi.typedClient.PortalShopConfigEntitlementsQerassign), ]; + this.typeProviders = [() => Promise.resolve(this.enableResourceTypes ? types : [])]; } private typeProviders: (() => Promise)[]; @@ -55,12 +48,11 @@ export class RequestableEntitlementTypeService { public enableResourceTypes: boolean = true; async GetTypes(): Promise { - const all = await Promise.all(this.typeProviders.map(x => x())); + const all = await Promise.all(this.typeProviders.map((x) => x())); return all.reduce((x, y) => x.concat(y)); } Register(typeProvider: () => Promise) { this.typeProviders.push(typeProvider); } - -} \ No newline at end of file +} diff --git a/imxweb/projects/qer/src/lib/itshop-config/requestable-entl-type.ts b/imxweb/projects/qer/src/lib/itshop-config/requestable-entl-type.ts index 6ac303856..d7f98a6fe 100644 --- a/imxweb/projects/qer/src/lib/itshop-config/requestable-entl-type.ts +++ b/imxweb/projects/qer/src/lib/itshop-config/requestable-entl-type.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,16 +24,16 @@ * */ -import { ApiClient, EntitySchema, TypedEntity } from "imx-qbm-dbts"; -import { DynamicMethodService, GenericTypedEntity } from "qbm"; -import { IRequestableEntitlementType } from "./irequestable-entitlement-type"; +import { ApiClient, EntitySchema, TypedEntity } from '@imx-modules/imx-qbm-dbts'; +import { DynamicMethodService, GenericTypedEntity } from 'qbm'; +import { IRequestableEntitlementType } from './irequestable-entitlement-type'; export class RequestableEntitlementType implements IRequestableEntitlementType { - - constructor(private readonly tableName: string, + constructor( + private readonly tableName: string, private readonly apiClient: ApiClient, private readonly fkColumnName: string, - private readonly dynamicMethodService: DynamicMethodService + private readonly dynamicMethodService: DynamicMethodService, ) { this.schema = this.createAssignmentEntity('dummy').GetEntity().GetSchema(); } @@ -53,27 +53,35 @@ export class RequestableEntitlementType implements IRequestableEntitlementType { } public addEntitlementSelections(shelfId: string, values: string[]): Promise { - const promises = []; - values.forEach(value => { + const promises: Promise[] = []; + values.forEach((value) => { const e = this.createAssignmentEntity(shelfId).GetEntity(); - promises.push(e.GetColumn(this.fkColumnName).PutValue(value) - .then(() => e.Commit())); + promises.push( + e + .GetColumn(this.fkColumnName) + .PutValue(value) + .then(() => e.Commit()), + ); }); return Promise.all(promises); } public createAssignmentEntity(shelfId: string): TypedEntity { - const entityColl = this.dynamicMethodService.createEntity(this.apiClient, { - path: '/portal/shop/config/entitlements/' + shelfId + '/' + this.tableName, - type: GenericTypedEntity, - schemaPath: 'portal/shop/config/entitlements/{UID_ITShopOrg}/' + this.tableName, - }, { - Columns: { - "UID_ITShopOrg": { - Value: shelfId - } - } - }); + const entityColl = this.dynamicMethodService.createEntity( + this.apiClient, + { + path: '/portal/shop/config/entitlements/' + shelfId + '/' + this.tableName, + type: GenericTypedEntity, + schemaPath: 'portal/shop/config/entitlements/{UID_ITShopOrg}/' + this.tableName, + }, + { + Columns: { + UID_ITShopOrg: { + Value: shelfId, + }, + }, + }, + ); return entityColl.Data[0]; } -} \ No newline at end of file +} diff --git a/imxweb/projects/qer/src/lib/itshop-config/requests-selector/requests-entity-selector.component.html b/imxweb/projects/qer/src/lib/itshop-config/requests-selector/requests-entity-selector.component.html index 84785a9a6..5d290e01a 100644 --- a/imxweb/projects/qer/src/lib/itshop-config/requests-selector/requests-entity-selector.component.html +++ b/imxweb/projects/qer/src/lib/itshop-config/requests-selector/requests-entity-selector.component.html @@ -22,7 +22,7 @@
    - diff --git a/imxweb/projects/qer/src/lib/itshop-config/requests-selector/requests-entity-selector.component.ts b/imxweb/projects/qer/src/lib/itshop-config/requests-selector/requests-entity-selector.component.ts index f515a9086..9d8cf4474 100644 --- a/imxweb/projects/qer/src/lib/itshop-config/requests-selector/requests-entity-selector.component.ts +++ b/imxweb/projects/qer/src/lib/itshop-config/requests-selector/requests-entity-selector.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,8 +27,8 @@ import { Component, Inject, OnInit } from '@angular/core'; import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; -import { EntityCollectionData, FkProviderItem, IEntity, TypedEntity } from 'imx-qbm-dbts'; -import { BusyService, MetadataService } from 'qbm'; +import { EntityCollectionData, FilterTreeData, FkProviderItem, IEntity, TypedEntity } from '@imx-modules/imx-qbm-dbts'; +import { BusyService, FkCandidatesData, MetadataService, TypedEntityFkData } from 'qbm'; import { IRequestableEntitlementType } from '../irequestable-entitlement-type'; import { RequestableEntitlementTypeService } from '../requestable-entitlement-type.service'; import { RequestsService } from '../requests.service'; @@ -55,7 +55,7 @@ export class RequestsEntitySelectorComponent implements OnInit { }, public readonly requestsService: RequestsService, private metadata: MetadataService, - private requestTypeService: RequestableEntitlementTypeService + private requestTypeService: RequestableEntitlementTypeService, ) { requestsService.selectedEntitlementType = null; this.ReinitData(); @@ -63,13 +63,15 @@ export class RequestsEntitySelectorComponent implements OnInit { public async ngOnInit(): Promise { const isBusy = this.busyService.beginBusy(); - try{ - const rtypes = await this.requestTypeService.GetTypes(); - for (const type of rtypes) { - const display = (await this.metadata.GetTableMetadata(type.getTableName())).Display; - this.displays.set(type.getTableName(), display); - } - this.types = rtypes.sort((a, b) => (this.displays.get(a.getTableName()) > this.displays.get(b.getTableName()) ? 1 : -1)); + try { + const rtypes = await this.requestTypeService.GetTypes(); + for (const type of rtypes) { + const display = (await this.metadata.GetTableMetadata(type.getTableName()))?.Display; + this.displays.set(type.getTableName(), display); + } + this.types = rtypes.sort((a, b) => + (this.displays.get(a.getTableName()) || '') > (this.displays.get(b.getTableName()) || '') ? 1 : -1, + ); } finally { isBusy.endBusy(); } @@ -86,24 +88,25 @@ export class RequestsEntitySelectorComponent implements OnInit { this.selectedItems = items; } - public data; + public data: FkCandidatesData | TypedEntityFkData; private fkEntity: IEntity; private fk: FkProviderItem; /** Sets the data object to trigger the changes event on the Fk candidate selector*/ private ReinitData() { this.data = { - get: (parameters) => { + Get: (parameters: any) => { if (!this.fk) { return this.empty; } return this.fk.load(this.fkEntity, { ...parameters, ...{ UID_ITShopOrg: this.sidesheetData.shelfId } }); }, - GetFilterTree: (parentKey) => { + GetFilterTree: (parentKey: string) => { + const emptyResult: FilterTreeData = { Elements: [] }; if (!this.fk) { - return { Elements: [] }; + return Promise.resolve(emptyResult); } - return this.fk.getFilterTree(this.fkEntity, parentKey); + return this.fk.getFilterTree?.(this.fkEntity, parentKey) || Promise.resolve(emptyResult); }, isMultiValue: true, }; @@ -112,9 +115,12 @@ export class RequestsEntitySelectorComponent implements OnInit { public async optionSelected(newType: IRequestableEntitlementType) { this.fkEntity = newType.createAssignmentEntity(this.sidesheetData.shelfId).GetEntity(); const property = newType.getSchema().Columns[newType.getFkColumnName()]; - this.fk = this.fkEntity + const fkProviderItem = this.fkEntity .GetFkCandidateProvider() - .getProviderItem(property.FkRelation.ParentColumnName, property.FkRelation.ParentTableName); + .getProviderItem(property.FkRelation?.ParentColumnName || '', property.FkRelation?.ParentTableName || ''); + if (fkProviderItem) { + this.fk = fkProviderItem; + } this.ReinitData(); this.selectedItems = []; } @@ -123,10 +129,10 @@ export class RequestsEntitySelectorComponent implements OnInit { if (selected) { this.selectedItems = [selected]; } - const selectedValues = []; + const selectedValues: string[] = []; this.selectedItems.forEach((typedEntity) => { const keys = typedEntity.GetEntity().GetKeys(); - const val = keys?.length ? keys[0] : undefined; + const val = !!keys?.length ? keys[0] : ''; selectedValues.push(val); }); this.dialogRef.close(selectedValues); diff --git a/imxweb/projects/qer/src/lib/itshop-config/requests.service.ts b/imxweb/projects/qer/src/lib/itshop-config/requests.service.ts index ed716dfd4..545b465a5 100644 --- a/imxweb/projects/qer/src/lib/itshop-config/requests.service.ts +++ b/imxweb/projects/qer/src/lib/itshop-config/requests.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,20 +24,20 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; import { Injectable } from '@angular/core'; import { EuiLoadingService } from '@elemental-ui/core'; -import { TranslateService } from '@ngx-translate/core'; -import { PortalRolesExclusions, PortalShopConfigMembers, PortalShopConfigStructure } from 'imx-api-qer'; +import { PortalRolesExclusions, PortalShopConfigMembers, PortalShopConfigStructure } from '@imx-modules/imx-api-qer'; import { CollectionLoadParameters, CompareOperator, EntityCollectionData, EntitySchema, + FilterData, FilterType, + TypedEntity, TypedEntityCollectionData, -} from 'imx-qbm-dbts'; +} from '@imx-modules/imx-qbm-dbts'; import { ClassloggerService, SnackBarService } from 'qbm'; import { QerApiService } from '../qer-api-client.service'; import { IRequestableEntitlementType } from './irequestable-entitlement-type'; @@ -55,17 +55,15 @@ export interface SelectedShopStructureData { }) export class RequestsService { public shelvesBlockedDeleteStatus: { [shelfId: string]: boolean } = {}; - private busyIndicator: OverlayRef; constructor( private readonly qerApiClient: QerApiService, private readonly logger: ClassloggerService, private readonly snackbar: SnackBarService, - private readonly translate: TranslateService, - private readonly busyService: EuiLoadingService + private readonly busyService: EuiLoadingService, ) {} - public selectedEntitlementType: IRequestableEntitlementType; + public selectedEntitlementType: IRequestableEntitlementType | null; public get shopStructureSchema(): EntitySchema { return this.qerApiClient.typedClient.PortalShopConfigStructure.GetSchema(); @@ -81,26 +79,31 @@ export class RequestsService { public async getShopStructures( navigationState: CollectionLoadParameters, - parentId: string = '' + parentId: string = '', + signal?: AbortSignal, ): Promise> { - let params: any = navigationState; + let params: CollectionLoadParameters = navigationState; if (!params) { params = {}; } - if (parentId == '') { - params.filter = [ - { - ColumnName: 'ITShopInfo', - CompareOp: CompareOperator.Equal, - Type: FilterType.Compare, - Value1: 'SH', - }, - ]; + if (parentId === '') { + const itShopColumnFilter: { filter: FilterData[] } = { + filter: [ + { + ColumnName: 'ITShopInfo', + CompareOp: CompareOperator.Equal, + Type: FilterType.Compare, + Value1: 'SH', + }, + ], + }; + const filter = itShopColumnFilter.filter.concat(params.filter ?? []); + params = { ...params, filter }; } params.ParentKey = parentId; this.logger.debug(this, `Retrieving shop config structures`); this.logger.trace('Navigation state', navigationState); - return this.qerApiClient.typedClient.PortalShopConfigStructure.Get(params); + return this.qerApiClient.typedClient.PortalShopConfigStructure.Get(params, { signal }); } public createRequestConfigEntity(): PortalShopConfigStructure { @@ -117,7 +120,7 @@ export class RequestsService { public getRequestConfigMembers( customerNodeId: string, - navigationState: CollectionLoadParameters + navigationState: CollectionLoadParameters, ): Promise> { return this.qerApiClient.typedClient.PortalShopConfigMembers.Get(customerNodeId, navigationState); } @@ -134,13 +137,13 @@ export class RequestsService { public createRequestConfigMember( customerNodeId: string, - newMember: PortalShopConfigMembers + newMember: PortalShopConfigMembers, ): Promise> { return this.qerApiClient.typedClient.PortalShopConfigMembers.Post(customerNodeId, newMember); } public addMultipleRequestConfigMembers(values: string[], customerNodeId: string): Promise { - const promises = []; + const promises: Promise[] = []; values.forEach((value) => { const entity = this.qerApiClient.typedClient.PortalShopConfigMembers.createEntity(); entity.UID_Person.value = value; @@ -152,13 +155,13 @@ export class RequestsService { public removeRequestConfigMembers( customerNodeId: string, uidDynamicGroup: string, - members: PortalShopConfigMembers[], - description?: string + members: TypedEntity[], + description?: string, ): Promise { - const promises = []; - members.forEach((member) => { + const promises: Promise[] = []; + members.forEach((member: PortalShopConfigMembers) => { // If the member is managed by a dynamic group, add an exclusion - // tslint:disable-next-line:no-bitwise + // eslint-disable-next-line no-bitwise if ((member.XOrigin?.value & 4) > 0) { const exclusionData = this.qerApiClient.typedClient.PortalRolesExclusions.createEntity(); exclusionData.UID_Person.value = member.UID_Person.value; @@ -172,9 +175,9 @@ export class RequestsService { return Promise.all(promises); } - public removeRequestConfigMemberExclusions(uidDynamicGroup: string, exclusions: PortalRolesExclusions[]): Promise { - const promises = []; - exclusions.forEach((exclusion) => { + public removeRequestConfigMemberExclusions(uidDynamicGroup: string, exclusions: TypedEntity[]): Promise { + const promises: Promise[] = []; + exclusions.forEach((exclusion: PortalRolesExclusions) => { const memberUid = exclusion.UID_Person?.value; promises.push(this.removeDynamicRoleExclusion(uidDynamicGroup, memberUid)); }); @@ -209,18 +212,13 @@ export class RequestsService { } public handleOpenLoader(): void { - if (!this.busyIndicator) { - this.busyIndicator = this.busyService.show(); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); } } public handleCloseLoader(): void { - if (this.busyIndicator) { - setTimeout(() => { - this.busyService.hide(this.busyIndicator); - this.busyIndicator = undefined; - }); - } + this.busyService.hide(); } public LdsSpecifyMembers = '#LDS#Here you can specify who can request the products assigned to the shop.'; diff --git a/imxweb/projects/qer/src/lib/itshop-config/requests/requests.component.html b/imxweb/projects/qer/src/lib/itshop-config/requests/requests.component.html index 16bf2df10..50fe3b931 100644 --- a/imxweb/projects/qer/src/lib/itshop-config/requests/requests.component.html +++ b/imxweb/projects/qer/src/lib/itshop-config/requests/requests.component.html @@ -1,49 +1,38 @@ -
    -

    +
    +

    {{ requestsService.LdsHeadingShops | translate }} -

    -
    +

    +
    +
    -
    - - - - - -
    {{ item.GetEntity().GetDisplay() }}
    -
    {{ item.Description.Column.GetDisplayValue() }}
    -
    -
    - -
    - -
    + + + + {{ entitySchemaShopStructure?.Columns?.[DisplayColumns.DISPLAY_PROPERTYNAME]?.Display }} + + +
    {{ item.GetEntity().GetDisplay() }}
    +
    {{ item.Description.Column.GetDisplayValue() }}
    + +
    + + + {{ entitySchemaShopStructure?.Columns?.UID_OrgAttestator?.Display }} + + +
    {{ item.UID_OrgAttestator.Column.GetDisplayValue() }}
    + +
    +
    +
    -
    - diff --git a/imxweb/projects/qer/src/lib/itshop-config/requests/requests.component.scss b/imxweb/projects/qer/src/lib/itshop-config/requests/requests.component.scss index a9483dc9d..ca9b7f650 100644 --- a/imxweb/projects/qer/src/lib/itshop-config/requests/requests.component.scss +++ b/imxweb/projects/qer/src/lib/itshop-config/requests/requests.component.scss @@ -1,5 +1,5 @@ @import '@elemental-ui/core/src/styles/_palette.scss'; -@import '../../../../../../shared/scss/common-table.scss'; +@import 'base/mixins'; :host { display: flex; flex-direction: column; @@ -13,46 +13,13 @@ overflow: hidden; } - div.imx-table-container { - @include imx-flex-fill-control-hidden-overflow(); - - .imx-requests-table { - flex-grow: 1; - overflow: auto; - } - } - .heading-wrapper { display: flex; margin-bottom: 16px; - h1 { + h2 { margin: 6px 0 40px; } - - .helper-alert { - margin-bottom: 15px; - } - - .alert-wrapper { - margin: 0 0 0 auto; - align-self: flex-end; - width: 50%; - } - } - - ::ng-deep { - .imx-data-table-row-highlighted { - background-color: inherit; - } - - .mat-row:hover { - cursor: pointer; - background-color: rgba($black-c, 0.2); - } - } - .request-config__action-buttons { - @include imx-button-bar(); } } @@ -60,23 +27,6 @@ :host { .heading-wrapper { display: block; - - .alert-wrapper { - margin: 0 0 20px 0; - width: 100%; - } - } - } -} - -.eui-contrast-theme { - :host { - .request-config { - &.request-config--full-page { - .request-config__action-buttons { - background-color: $color-gray-90; - } - } } } } diff --git a/imxweb/projects/qer/src/lib/itshop-config/requests/requests.component.ts b/imxweb/projects/qer/src/lib/itshop-config/requests/requests.component.ts index 22fb74b25..ca5af5df5 100644 --- a/imxweb/projects/qer/src/lib/itshop-config/requests/requests.component.ts +++ b/imxweb/projects/qer/src/lib/itshop-config/requests/requests.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,19 +28,24 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { PortalShopConfigStructure } from 'imx-api-qer'; -import { CollectionLoadParameters, IClientProperty, DisplayColumns, EntitySchema } from 'imx-qbm-dbts'; +import { PortalShopConfigStructure } from '@imx-modules/imx-api-qer'; +import { + CollectionLoadParameters, + DisplayColumns, + EntitySchema, + IClientProperty, + TypedEntityCollectionData, +} from '@imx-modules/imx-qbm-dbts'; import { - DataSourceToolbarSettings, - DataSourceToolbarFilter, - ClassloggerService, - StorageService, - HELPER_ALERT_KEY_PREFIX, - SettingsService, BusyService, + calculateSidesheetWidth, + DataViewInitParameters, + DataViewSource, + HELP_CONTEXTUAL, HelpContextualComponent, HelpContextualService, - HELP_CONTEXTUAL + HELPER_ALERT_KEY_PREFIX, + StorageService, } from 'qbm'; import { RequestConfigSidesheetComponent } from '../request-config-sidesheet/request-config-sidesheet.component'; import { RequestsService } from '../requests.service'; @@ -51,6 +56,7 @@ const helperAlertKey = `${HELPER_ALERT_KEY_PREFIX}_requestShop`; selector: 'imx-requests', templateUrl: './requests.component.html', styleUrls: ['./requests.component.scss'], + providers: [DataViewSource], }) export class RequestsComponent implements OnInit, OnDestroy { public get showHelperAlert(): boolean { @@ -59,9 +65,6 @@ export class RequestsComponent implements OnInit, OnDestroy { public readonly entitySchemaShopStructure: EntitySchema; public readonly DisplayColumns = DisplayColumns; - public dstSettings: DataSourceToolbarSettings; - public navigationState: CollectionLoadParameters; - public filterOptions: DataSourceToolbarFilter[] = []; public busyService = new BusyService(); @@ -69,14 +72,12 @@ export class RequestsComponent implements OnInit, OnDestroy { constructor( private readonly sidesheet: EuiSidesheetService, - private readonly logger: ClassloggerService, private readonly translate: TranslateService, private readonly storageService: StorageService, public readonly requestsService: RequestsService, - private readonly settingsService: SettingsService, - private readonly helpContextualService: HelpContextualService + private readonly helpContextualService: HelpContextualService, + public dataSource: DataViewSource, ) { - this.navigationState = { PageSize: this.settingsService.DefaultPageSize, StartIndex: 0 }; this.entitySchemaShopStructure = requestsService.shopStructureSchema; } @@ -85,7 +86,7 @@ export class RequestsComponent implements OnInit, OnDestroy { this.entitySchemaShopStructure.Columns[DisplayColumns.DISPLAY_PROPERTYNAME], this.entitySchemaShopStructure.Columns.UID_OrgAttestator, ]; - await this.navigate(); + await this.initDataTable(); } public ngOnDestroy(): void { @@ -96,39 +97,15 @@ export class RequestsComponent implements OnInit, OnDestroy { this.storageService.storeHelperAlertDismissal(helperAlertKey); } - public async onSearch(keywords: string): Promise { - this.logger.debug(this, `Searching for: ${keywords}`); - this.navigationState.StartIndex = 0; - this.navigationState.search = keywords; - await this.navigate(); - } - - public onRequestShopSelected(requestConfig: PortalShopConfigStructure): void { - this.logger.debug(this, `Selected shop changed`); - this.logger.trace(`New shop selected`, requestConfig); - this.viewRequestShop(requestConfig); - } - public async createRequestConfig(): Promise { const newRequestConfig = this.requestsService.createRequestConfigEntity(); newRequestConfig.ITShopInfo.value = 'SH'; this.viewRequestShop(newRequestConfig, true); } - /** - * Occurs when the navigation state has changed - e.g. users clicks on the next page button. - * - */ - public async onNavigationStateChanged(newState?: CollectionLoadParameters): Promise { - if (newState) { - this.navigationState = newState; - } - await this.navigate(); - } - private async viewRequestShop(requestConfig: PortalShopConfigStructure, isNew: boolean = false): Promise { const key = isNew ? this.requestsService.LdsHeadingCreateShop : this.requestsService.LdsHeadingEditShop; - if(isNew){ + if (isNew) { this.helpContextualService.setHelpContextId(HELP_CONTEXTUAL.ConfigurationRequestsCreate); } const result = await this.sidesheet @@ -137,38 +114,32 @@ export class RequestsComponent implements OnInit, OnDestroy { subTitle: isNew ? '' : requestConfig.GetEntity().GetDisplay(), padding: '0px', disableClose: true, - width: 'max(60%,600px)', + width: calculateSidesheetWidth(), testId: isNew ? 'requests-config-create-shop-sidesheet' : 'requests-config-edit-shop-sidesheet', data: { requestConfig, isNew, }, - headerComponent: isNew ? HelpContextualComponent : undefined + headerComponent: isNew ? HelpContextualComponent : undefined, }) .afterClosed() .toPromise(); // After the sidesheet closes, reload the current data to refresh any changes that might have been made if (result) { - this.navigate(); + this.dataSource.updateState(); } } - private async navigate(): Promise { - const isBusy = this.busyService.beginBusy(); - const getParams: any = this.navigationState; - - try { - const data = await this.requestsService.getShopStructures(getParams, ''); - this.dstSettings = { - displayedColumns: this.displayedColumns, - dataSource: data, - entitySchema: this.entitySchemaShopStructure, - navigationState: this.navigationState, - filters: this.filterOptions, - }; - this.logger.debug(this, `Head at ${data.Data.length + this.navigationState.StartIndex} of ${data.totalCount} item(s)`); - } finally { - isBusy?.endBusy(); - } + private initDataTable(): void { + const dataViewInitParameters: DataViewInitParameters = { + execute: (params: CollectionLoadParameters, signal: AbortSignal): Promise> => + this.requestsService.getShopStructures(params, '', signal), + schema: this.entitySchemaShopStructure, + columnsToDisplay: this.displayedColumns, + highlightEntity: (entity: PortalShopConfigStructure) => { + this.viewRequestShop(entity); + }, + }; + this.dataSource.init(dataViewInitParameters); } } diff --git a/imxweb/projects/qer/src/lib/itshop-config/resource-entitlement-type.ts b/imxweb/projects/qer/src/lib/itshop-config/resource-entitlement-type.ts index c655fd24e..a6a61b6a6 100644 --- a/imxweb/projects/qer/src/lib/itshop-config/resource-entitlement-type.ts +++ b/imxweb/projects/qer/src/lib/itshop-config/resource-entitlement-type.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,40 +24,46 @@ * */ -import { TypedEntity, CollectionLoadParameters, ExtendedTypedEntityCollection, EntitySchema, EntityData } from "imx-qbm-dbts"; -import { IRequestableEntitlementType } from "./irequestable-entitlement-type"; +import { CollectionLoadParameters, EntityData, EntitySchema, ExtendedTypedEntityCollection, TypedEntity } from '@imx-modules/imx-qbm-dbts'; +import { IRequestableEntitlementType } from './irequestable-entitlement-type'; export class ResourceEntitlementType implements IRequestableEntitlementType { - - constructor(private readonly resourceType: string, + constructor( + private readonly resourceType: string, private type: { GetSchema(): EntitySchema; createEntity(initialData?: EntityData): TypedEntity; Get(shelfUid: string, navigationState: CollectionLoadParameters): Promise>; Post(shelfId: string, entitlement: TypedEntity): Promise; - }) { } + }, + ) {} getFkColumnName(): string { - return "UID_" + this.getTableName(); + return 'UID_' + this.getTableName(); } public addEntitlementSelections(shelfId: string, values: string[]): Promise { - const promises = []; + const promises: Promise[] = []; values.forEach((value) => { const entitlement = this.type.createEntity(); - entitlement.GetEntity().GetColumn("UID_" + this.resourceType).PutValue(value); + entitlement + .GetEntity() + .GetColumn('UID_' + this.resourceType) + .PutValue(value); promises.push(this.type.Post(shelfId, entitlement)); }); return Promise.all(promises); } - public getTableName(): string { return this.resourceType }; + public getTableName(): string { + return this.resourceType; + } public createAssignmentEntity(shelfId: string): TypedEntity { const e = this.type.createEntity({ Columns: { - "UID_ITShopOrg": { Value: shelfId } - } + UID_ITShopOrg: { Value: shelfId }, + }, }); return e; } @@ -65,5 +71,4 @@ export class ResourceEntitlementType implements IRequestableEntitlementType { getSchema(): EntitySchema { return this.type.GetSchema(); } - -} \ No newline at end of file +} diff --git a/imxweb/projects/qer/src/lib/itshop-config/test/requests-configuration-mocks.ts b/imxweb/projects/qer/src/lib/itshop-config/test/requests-configuration-mocks.ts index 7ca398eb0..cc5136573 100644 --- a/imxweb/projects/qer/src/lib/itshop-config/test/requests-configuration-mocks.ts +++ b/imxweb/projects/qer/src/lib/itshop-config/test/requests-configuration-mocks.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,8 +24,8 @@ * */ -import { PortalShopConfigMembers, PortalShopConfigStructure } from 'imx-api-qer'; -import { CollectionLoadParameters, FkProviderItem, IEntity, IEntityColumn, TypedEntityCollectionData } from 'imx-qbm-dbts'; +import { PortalShopConfigMembers, PortalShopConfigStructure } from '@imx-modules/imx-api-qer'; +import { CollectionLoadParameters, FkProviderItem, IEntity, IEntityColumn, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { of } from 'rxjs'; function mockData(): TypedEntityCollectionData { @@ -38,18 +38,17 @@ const mockEntityColumn = { return { CanEdit: () => false, GetDisplay: () => '', - GetMinLength: () => 0 + GetMinLength: () => 0, }; }, - GetValue: () => '' - + GetValue: () => '', } as IEntityColumn; const mockRequestEntity = { GetDisplay: () => 'Display value', GetKeys: () => ['testId'], GetColumn: (name) => mockEntityColumn, - Commit: (bool) => Promise.resolve() + Commit: (bool) => Promise.resolve(), } as IEntity; const mockfkProviderItem = { @@ -58,7 +57,7 @@ const mockfkProviderItem = { parameterNames: [], load: () => Promise.resolve(undefined), getDataModel: () => Promise.resolve({}), - getFilterTree: async () => ({}) + getFilterTree: async () => ({}), } as FkProviderItem; const mockEntityWithFk = { @@ -67,11 +66,11 @@ const mockEntityWithFk = { GetColumn: (name) => mockEntityColumn, GetFkCandidateProvider: () => { return { getProviderItem: (colName, table) => mockfkProviderItem }; - } + }, } as IEntity; const mockNewMemberEntity: any = { - GetEntity: () => mockEntityWithFk + GetEntity: () => mockEntityWithFk, } as PortalShopConfigMembers; export class RequestsConfigurationCommonMocks { @@ -145,10 +144,12 @@ export class RequestsConfigurationCommonMocks { }; public static readonly storage = {}; public static mockStorageService: any = { - isHelperAlertDismissed: jasmine.createSpy('isHelperAlertDismissed').and.callFake( - (key: string) => RequestsConfigurationCommonMocks.storage[key]), - storeHelperAlertDismissal: jasmine.createSpy('storeHelperAlertDismissal').and.callFake( - (key: string) => RequestsConfigurationCommonMocks.storage[key] = true), + isHelperAlertDismissed: jasmine + .createSpy('isHelperAlertDismissed') + .and.callFake((key: string) => RequestsConfigurationCommonMocks.storage[key]), + storeHelperAlertDismissal: jasmine + .createSpy('storeHelperAlertDismissal') + .and.callFake((key: string) => (RequestsConfigurationCommonMocks.storage[key] = true)), }; public static mockDialogRef = { afterClosed: () => of(undefined) }; diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/duplicate-pattern-items/duplicate-pattern-item.ts b/imxweb/projects/qer/src/lib/itshop-pattern/duplicate-pattern-items/duplicate-pattern-item.ts index dfd0e67b1..426d051f1 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/duplicate-pattern-items/duplicate-pattern-item.ts +++ b/imxweb/projects/qer/src/lib/itshop-pattern/duplicate-pattern-items/duplicate-pattern-item.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,8 +28,7 @@ * Class representing the service items that could not be added to the itshop pattern. */ export class DuplicatePatternItem { - - // tslint:disable-next-line: variable-name + // eslint-disable-next-line @typescript-eslint/naming-convention,no-underscore-dangle,id-blacklist,id-match public readonly Display: string; constructor(display: string) { diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/duplicate-pattern-items/duplicate-pattern-items.component.html b/imxweb/projects/qer/src/lib/itshop-pattern/duplicate-pattern-items/duplicate-pattern-items.component.html index 0e86d932b..20566d90f 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/duplicate-pattern-items/duplicate-pattern-items.component.html +++ b/imxweb/projects/qer/src/lib/itshop-pattern/duplicate-pattern-items/duplicate-pattern-items.component.html @@ -1,12 +1,12 @@ -

    - {{'#LDS#Heading Products Cannot Be Added to Product Bundle' | translate}} -

    +

    + {{ '#LDS#Heading Products Cannot Be Added to Product Bundle' | translate }} +

    - {{ description1 | translate}} + {{ description1 | translate }}
    - {{ description2 | translate}} + {{ description2 | translate }}
    @@ -18,11 +18,11 @@

    - +
    -
    +
    diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/duplicate-pattern-items/duplicate-pattern-items.component.scss b/imxweb/projects/qer/src/lib/itshop-pattern/duplicate-pattern-items/duplicate-pattern-items.component.scss index 542a60e72..18b3745c8 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/duplicate-pattern-items/duplicate-pattern-items.component.scss +++ b/imxweb/projects/qer/src/lib/itshop-pattern/duplicate-pattern-items/duplicate-pattern-items.component.scss @@ -1,9 +1,6 @@ -.mat-dialog-content{ - overflow: hidden; - display: flex; - flex-direction: column; +@import 'base/mixins'; + +.mat-mdc-dialog-content { + @include flex-column-container($overflow: hidden); - .mat-table { - margin-top: 20px; - } } diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/duplicate-pattern-items/duplicate-pattern-items.component.ts b/imxweb/projects/qer/src/lib/itshop-pattern/duplicate-pattern-items/duplicate-pattern-items.component.ts index 51b99925c..90c66098f 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/duplicate-pattern-items/duplicate-pattern-items.component.ts +++ b/imxweb/projects/qer/src/lib/itshop-pattern/duplicate-pattern-items/duplicate-pattern-items.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -35,25 +35,22 @@ import { DuplicatePatternItem } from './duplicate-pattern-item'; @Component({ selector: 'imx-duplicate-pattern-items', templateUrl: './duplicate-pattern-items.component.html', - styleUrls: ['./duplicate-pattern-items.component.scss'] + styleUrls: ['./duplicate-pattern-items.component.scss'], }) export class DuplicatePatternItemsComponent { - public get columnNames(): string[] { - return this.displayedColumns.map(c => c.name); + return this.displayedColumns.map((c) => c.name); } public description1 = '#LDS#Each product can be added to the product bundle only once.'; public description2 = '#LDS#The following products have already been added to the product bundle and cannot be added again.'; - public readonly displayedColumns = [ - { name: 'Display', title: '#LDS#Product' } - ]; + public readonly displayedColumns = [{ name: 'Display', title: '#LDS#Product' }]; constructor( - @Inject(MAT_DIALOG_DATA) public readonly data: { - duplicatePatternItems: DuplicatePatternItem[] - } - ) { - } + @Inject(MAT_DIALOG_DATA) + public readonly data: { + duplicatePatternItems: DuplicatePatternItem[]; + }, + ) {} } diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-add-products/itshop-pattern-add-products.component.html b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-add-products/itshop-pattern-add-products.component.html index b20591b3f..946da0132 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-add-products/itshop-pattern-add-products.component.html +++ b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-add-products/itshop-pattern-add-products.component.html @@ -1,31 +1,49 @@
    - + {{ description | translate }}
    - + - {{'#LDS#Show my products' | translate}} + {{ '#LDS#Show my products' | translate }} - {{'#LDS#Show products of another identity' | translate}} + {{ '#LDS#Show products of another identity' | translate }} - +
    - +
    -
    -
    diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-add-products/itshop-pattern-add-products.component.scss b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-add-products/itshop-pattern-add-products.component.scss index 660388a0c..2536a3f77 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-add-products/itshop-pattern-add-products.component.scss +++ b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-add-products/itshop-pattern-add-products.component.scss @@ -1,40 +1,16 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; -:host { - - eui-alert { - margin-bottom: 10px; - } +@import 'base/mixins'; +:host { ::ng-deep imx-data-tiles { padding-left: 10px; } - ::ng-deep .mat-tab-body-content { - display: flex; - flex-direction: column; - } - - .eui-sidesheet-content { - display: flex; - flex-direction: column; - - >.mat-card { - display: flex; - flex-direction: column; - height: 100%; - } - } - .eui-sidesheet-actions { button:not(:last-of-type) { margin-right: 10px; } } - .mat-radio-button { - height: 70px; - margin-right: 20px; - } .imx-recipients { height: 100px; @@ -49,20 +25,14 @@ } } -.eui-dark-theme{ - :host{ +.eui-dark-theme { + :host { background-color: $color-gray-80; - .eui-sidesheet-actions{ - background-color: $color-gray-70; - } } } -.eui-contrast-theme{ - :host{ +.eui-contrast-theme { + :host { background-color: $color-gray-100; - .eui-sidesheet-actions{ - background-color: $color-gray-90; - } } } diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-add-products/itshop-pattern-add-products.component.spec.ts b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-add-products/itshop-pattern-add-products.component.spec.ts index 65b570479..121f4ac9d 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-add-products/itshop-pattern-add-products.component.spec.ts +++ b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-add-products/itshop-pattern-add-products.component.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -39,11 +39,11 @@ describe('ItshopPatternAddProductsComponent', () => { beforeEach(() => { return MockBuilder([ItshopPatternAddProductsComponent, TranslateModule.forRoot()]) - .mock(ItshopPatternModule) + .mock(ItshopPatternModule) .mock(EuiSidesheetRef, { close: jasmine.createSpy('close'), closeClicked: jasmine.createSpy('closeClicked').and.returnValue(of(undefined)), - }) + }); }); beforeEach(() => { diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-add-products/itshop-pattern-add-products.component.ts b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-add-products/itshop-pattern-add-products.component.ts index a337cb7d1..8b9c4c98c 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-add-products/itshop-pattern-add-products.component.ts +++ b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-add-products/itshop-pattern-add-products.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,15 +25,23 @@ */ import { Component, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core'; -import { MatSelectChange } from '@angular/material/select'; -import { EuiLoadingService, EuiSidesheetRef, EuiSidesheetService, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { MatRadioChange } from '@angular/material/radio'; +import { EUI_SIDESHEET_DATA, EuiLoadingService, EuiSidesheetRef, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; import { Subscription } from 'rxjs'; -import { PortalShopCategories, PortalShopServiceitems, QerProjectConfig } from 'imx-api-qer'; -import { EntityValue, IWriteValue, LocalProperty } from 'imx-qbm-dbts'; +import { PortalShopCategories, PortalShopServiceitems, QerProjectConfig } from '@imx-modules/imx-api-qer'; +import { EntityValue, IWriteValue, LocalProperty, TypedEntity } from '@imx-modules/imx-qbm-dbts'; -import { AuthenticationService, BaseCdr, ColumnDependentReference, DataTileMenuItem, EntityService } from 'qbm'; +import { + AuthenticationService, + BaseCdr, + calculateSidesheetWidth, + ColumnDependentReference, + DataTileMenuItem, + EntityService, + ISessionState, +} from 'qbm'; import { PersonService } from '../../person/person.service'; import { ProductDetailsSidesheetComponent } from '../../product-selection/product-details-sidesheet/product-details-sidesheet.component'; @@ -51,10 +59,9 @@ import { PatternItemCandidate } from '../pattern-item-candidate.interface'; @Component({ selector: 'imx-itshop-pattern-add-products', templateUrl: './itshop-pattern-add-products.component.html', - styleUrls: ['./itshop-pattern-add-products.component.scss'] + styleUrls: ['./itshop-pattern-add-products.component.scss'], }) export class ItshopPatternAddProductsComponent implements OnInit, OnDestroy { - @ViewChild(ServiceitemListComponent) public serviceitemListComponent: ServiceitemListComponent; public readonly dataSourceView = { selected: 'cardlist' }; @@ -65,17 +72,18 @@ export class ItshopPatternAddProductsComponent implements OnInit, OnDestroy { public canRequestForSomebodyElse: boolean; public recipientType: 'self' | 'others' = 'self'; - public description = '#LDS#Here you can add products to this product bundle that are available for you. You can also select another identity to view the products available for that identity.'; + public description = + '#LDS#Here you can add products to this product bundle that are available for you. You can also select another identity to view the products available for that identity.'; public serviceItemActions: DataTileMenuItem[] = [ { name: 'details', - description: '#LDS#Details' + description: '#LDS#Details', }, { name: 'addToTemplate', - description: '#LDS#Add' - } + description: '#LDS#Add', + }, ]; private projectConfig: QerProjectConfig; @@ -83,8 +91,9 @@ export class ItshopPatternAddProductsComponent implements OnInit, OnDestroy { private userUid: string; constructor( - @Inject(EUI_SIDESHEET_DATA) public data: { - shoppingCartPatternUid: string + @Inject(EUI_SIDESHEET_DATA) + public data: { + shoppingCartPatternUid: string; }, private readonly busyIndicator: EuiLoadingService, private readonly entityService: EntityService, @@ -96,12 +105,11 @@ export class ItshopPatternAddProductsComponent implements OnInit, OnDestroy { private readonly sideSheetRef: EuiSidesheetRef, private readonly translate: TranslateService, private readonly userModelService: UserModelService, - authentication: AuthenticationService + authentication: AuthenticationService, ) { - this.authSubscription = authentication.onSessionResponse.subscribe(elem => { - this.userUid = elem.UserUid; + this.authSubscription = authentication.onSessionResponse.subscribe((session: ISessionState) => { + this.userUid = session.UserUid || ''; }); - } public async ngOnInit(): Promise { @@ -113,11 +121,11 @@ export class ItshopPatternAddProductsComponent implements OnInit, OnDestroy { this.authSubscription.unsubscribe(); } - public onSelectionChanged(items: PortalShopServiceitems[]): void { - this.selectedItems = items; + public onSelectionChanged(items: TypedEntity[]): void { + this.selectedItems = items as PortalShopServiceitems[]; } - public async handlePatternItemAction(action: { name: string, item: PortalShopServiceitems }): Promise { + public async handlePatternItemAction(action: { name: string; item: PortalShopServiceitems }): Promise { if (action.name === 'addToTemplate') { this.addTemplateItem([action.item]); } @@ -127,49 +135,53 @@ export class ItshopPatternAddProductsComponent implements OnInit, OnDestroy { } public async requestDetails(item: PortalShopServiceitems): Promise { - await this.sidesheet.open(ProductDetailsSidesheetComponent, { - title: await this.translate.get('#LDS#Heading View Product Details').toPromise(), - subTitle: item.GetEntity().GetDisplay(), - padding: '0px', - width: 'max(700px, 60%)', - testId: 'product-details-sidesheet', - data: { - item, - projectConfig: this.projectConfig - } - }).afterClosed().toPromise(); + await this.sidesheet + .open(ProductDetailsSidesheetComponent, { + title: await this.translate.get('#LDS#Heading View Product Details').toPromise(), + subTitle: item.GetEntity().GetDisplay(), + padding: '0px', + width: calculateSidesheetWidth(1000), + testId: 'product-details-sidesheet', + data: { + item, + projectConfig: this.projectConfig, + }, + }) + .afterClosed() + .toPromise(); } public async addTemplateItem(serviceItems: PortalShopServiceitems[]): Promise { - const newPatternItems = serviceItems.map(item => { - return { - uidAccProduct: item.GetEntity().GetKeys()[0], - display: item.GetEntity().GetDisplay() + const newPatternItems = serviceItems.map((item) => { + return { + uidAccProduct: item.GetEntity().GetKeys()[0], + display: item.GetEntity().GetDisplay(), } as PatternItemCandidate; }); - setTimeout(() => this.busyIndicator.show()); + if (this.busyIndicator.overlayRefs.length === 0) { + this.busyIndicator.show(); + } try { - const assignedPatterns = await this.patternCreateService.assignItemsToPattern( - newPatternItems, this.data.shoppingCartPatternUid); - + const assignedPatterns = await this.patternCreateService.assignItemsToPattern(newPatternItems, this.data.shoppingCartPatternUid); + if (assignedPatterns > 0) { this.sideSheetRef.close(assignedPatterns); } } finally { - setTimeout(() => this.busyIndicator.hide()); - } + this.busyIndicator.hide(); + } } public async openCategoryTree(): Promise { const sidesheetRef = this.sidesheet.open(CategoryTreeComponent, { title: await this.translate.get('#LDS#Heading Select Service Category').toPromise(), - width: '600px', + width: calculateSidesheetWidth(600, 0.4), testId: 'categorytree-sidesheet', data: { selectedServiceCategory: this.selectedCategory, recipients: this.recipients, - showImage: false - } + showImage: false, + }, }); sidesheetRef.afterClosed().subscribe((category: PortalShopCategories) => { @@ -186,23 +198,22 @@ export class ItshopPatternAddProductsComponent implements OnInit, OnDestroy { this.selectedCategory = selectedCategory; } - public async selectedRecipientTypeChanged(arg: MatSelectChange): Promise { + public async selectedRecipientTypeChanged(arg: MatRadioChange): Promise { if (arg.value === 'self') { await this.recipients.Column.PutValueStruct({ DataValue: this.userUid, - DisplayValue: await this.getPersonDisplay(this.userUid) + DisplayValue: await this.getPersonDisplay(this.userUid), }); } else { await this.recipients.Column.PutValueStruct({ DataValue: '', - DisplayValue: '' + DisplayValue: '', }); } this.onRecipientsChanged(); } public async onRecipientsChanged(recipient?: string): Promise { - if (this.serviceitemListComponent) { this.serviceitemListComponent.deselectAll(); this.serviceitemListComponent.getData(); @@ -218,33 +229,23 @@ export class ItshopPatternAddProductsComponent implements OnInit, OnDestroy { recipientsProperty.FkRelation = this.qerClient.typedClient.PortalCartitem.GetSchema().Columns.UID_PersonOrdered.FkRelation; const dummyCartItemEntity = this.qerClient.typedClient.PortalCartitem.createEntity().GetEntity(); - const fkProviderItems = this.qerClient.client.getFkProviderItems('portal/cartitem').map(item => ({ + const fkProviderItems = this.qerClient.client.getFkProviderItems('portal/cartitem').map((item) => ({ ...item, load: (_, parameters = {}) => item.load(dummyCartItemEntity, parameters), - getDataModel: async (entity) => - item.getDataModel(entity), - getFilterTree: async (entity, parentKey) => item.getFilterTree(entity, parentKey) + getDataModel: async (entity) => item.getDataModel?.(entity) || {}, + getFilterTree: async (entity, parentKey) => item.getFilterTree?.(entity, parentKey) || {}, })); - const column = this.entityService.createLocalEntityColumn( - recipientsProperty, - fkProviderItems, - { Value: this.userUid } - ); + const column = this.entityService.createLocalEntityColumn(recipientsProperty, fkProviderItems, { Value: this.userUid }); this.recipients = new EntityValue(column); // preset recipient to the current user await this.recipients.Column.PutValueStruct({ DataValue: this.userUid, - DisplayValue: await this.getPersonDisplay(this.userUid) + DisplayValue: await this.getPersonDisplay(this.userUid), }); - - this.cartItemRecipients = new BaseCdr( - this.recipients.Column, - '#LDS#Identity' - ); - + this.cartItemRecipients = new BaseCdr(this.recipients.Column, '#LDS#Identity'); } private async getPersonDisplay(uid: string): Promise { @@ -254,5 +255,4 @@ export class ItshopPatternAddProductsComponent implements OnInit, OnDestroy { } return uid; } - } diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-changed.enum.ts b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-changed.enum.ts index 2db032e3a..c14481735 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-changed.enum.ts +++ b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-changed.enum.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,9 +27,9 @@ /** * The list of types a {@link ServiceCategory|PortalServicecategories} could changed. */ - export enum ItShopPatternChangedType { +export enum ItShopPatternChangedType { Saved, Deleted, TogglePublic, - CreateCopy + CreateCopy, } diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-create-sidesheet/itshop-pattern-create-sidesheet.component.html b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-create-sidesheet/itshop-pattern-create-sidesheet.component.html index aa9456278..bc49290ff 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-create-sidesheet/itshop-pattern-create-sidesheet.component.html +++ b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-create-sidesheet/itshop-pattern-create-sidesheet.component.html @@ -1,19 +1,23 @@
    - - {{ '#LDS#Private product bundle' | translate}} + + {{ '#LDS#Private product bundle' | translate }} - - {{'#LDS#Public product bundle' | translate}} + + {{ '#LDS#Public product bundle' | translate }} - - +
    -
    -
    diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-create-sidesheet/itshop-pattern-create-sidesheet.component.scss b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-create-sidesheet/itshop-pattern-create-sidesheet.component.scss index 75a636e6d..0bbbf69b6 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-create-sidesheet/itshop-pattern-create-sidesheet.component.scss +++ b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-create-sidesheet/itshop-pattern-create-sidesheet.component.scss @@ -1,41 +1,13 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; -.eui-sidesheet-content { - display: flex; - flex-direction: column; -} :host { - .eui-badge ::ng-deep .eui-badge-content { - font-size: 12px; - line-height: 12px; - margin-bottom: 20px; - } - - .imx-helper-alert { - width: 100%; - - ::ng-deep .eui-alert { - margin-bottom: 20px; - } - } - .imx-sidesheet-content { display: flex; flex-direction: column; height: 100%; } - .eui-sidesheet-actions { - .justify-start { - margin-right: auto; - } - - button:not(:last-of-type):not(.justify-start) { - margin-right: 10px; - } - } - .imx-errormessages { margin-bottom: 10px; @@ -45,22 +17,16 @@ } } -.eui-dark-theme{ - :host{ +.eui-dark-theme { + :host { background-color: $color-gray-80; color: $color-gray-2; - .eui-sidesheet-actions{ - background-color: $color-gray-70; - } } } -.eui-contrast-theme{ - :host{ +.eui-contrast-theme { + :host { background-color: $color-gray-100; color: $color-gray-2; - .eui-sidesheet-actions{ - background-color: $color-gray-90; - } } } diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-create-sidesheet/itshop-pattern-create-sidesheet.component.ts b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-create-sidesheet/itshop-pattern-create-sidesheet.component.ts index 9cc87c4a7..5fdb25fdc 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-create-sidesheet/itshop-pattern-create-sidesheet.component.ts +++ b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-create-sidesheet/itshop-pattern-create-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,17 +29,10 @@ import { UntypedFormArray, UntypedFormBuilder, UntypedFormGroup } from '@angular import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; import { Subscription } from 'rxjs'; -import { PortalItshopPatternPrivate, } from 'imx-api-qer'; -import { TypedEntity } from 'imx-qbm-dbts'; +import { PortalItshopPatternPrivate } from '@imx-modules/imx-api-qer'; +import { TypedEntity } from '@imx-modules/imx-qbm-dbts'; -import { - BaseCdr, - ClassloggerService, - ColumnDependentReference, - ConfirmationService, - DataTableComponent, - ErrorService, -} from 'qbm'; +import { BaseCdr, ClassloggerService, ColumnDependentReference, ConfirmationService, DataTableComponent, ErrorService } from 'qbm'; import { ItshopPatternService } from '../itshop-pattern.service'; import { ItShopPatternChangedType } from '../itshop-pattern-changed.enum'; @@ -49,10 +42,9 @@ import { ItShopPatternChangedType } from '../itshop-pattern-changed.enum'; @Component({ selector: 'imx-itshop-pattern-create-sidesheet', templateUrl: './itshop-pattern-create-sidesheet.component.html', - styleUrls: ['./itshop-pattern-create-sidesheet.component.scss'] + styleUrls: ['./itshop-pattern-create-sidesheet.component.scss'], }) export class ItshopPatternCreateSidesheetComponent implements OnInit, OnDestroy { - public get formArray(): UntypedFormArray { return this.detailsFormGroup.get('formArray') as UntypedFormArray; } @@ -66,22 +58,25 @@ export class ItshopPatternCreateSidesheetComponent implements OnInit, OnDestroy constructor( formBuilder: UntypedFormBuilder, - @Inject(EUI_SIDESHEET_DATA) public data: { - pattern: PortalItshopPatternPrivate + @Inject(EUI_SIDESHEET_DATA) + public data: { + pattern: PortalItshopPatternPrivate; }, private readonly patternService: ItshopPatternService, private readonly sideSheetRef: EuiSidesheetRef, private readonly logger: ClassloggerService, errorService: ErrorService, - confirmation: ConfirmationService + confirmation: ConfirmationService, ) { this.detailsFormGroup = new UntypedFormGroup({ formArray: formBuilder.array([]) }); this.closeSubscription = this.sideSheetRef.closeClicked().subscribe(async () => { - if (await confirmation.confirm({ - Title: '#LDS#Heading Cancel Creating', - Message: '#LDS#Are you sure you want to cancel creating the product bundle?' - })) { + if ( + await confirmation.confirm({ + Title: '#LDS#Heading Cancel Creating', + Message: '#LDS#Are you sure you want to cancel creating the product bundle?', + }) + ) { this.sideSheetRef.close(); } }); @@ -115,11 +110,6 @@ export class ItshopPatternCreateSidesheetComponent implements OnInit, OnDestroy } private async setupDetailsTab(): Promise { - - this.cdrList = [ - new BaseCdr(this.data.pattern.Ident_ShoppingCartPattern.Column), - new BaseCdr(this.data.pattern.Description.Column), - ]; - + this.cdrList = [new BaseCdr(this.data.pattern.Ident_ShoppingCartPattern.Column), new BaseCdr(this.data.pattern.Description.Column)]; } } diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-create-sidesheet/itshop-pattern-create.service.ts b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-create-sidesheet/itshop-pattern-create.service.ts index c5e2dd83a..6d5c166f8 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-create-sidesheet/itshop-pattern-create.service.ts +++ b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-create-sidesheet/itshop-pattern-create.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,21 +30,28 @@ import { MatDialog } from '@angular/material/dialog'; import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { PortalItshopPatternItem, PortalItshopPatternPrivate } from 'imx-api-qer'; - -import { ClassloggerService, HELP_CONTEXTUAL, HelpContextualComponent, HelpContextualService, SnackBarService, UserMessageService } from 'qbm'; +import { PortalItshopPatternItem, PortalItshopPatternPrivate } from '@imx-modules/imx-api-qer'; + +import { + calculateSidesheetWidth, + ClassloggerService, + HELP_CONTEXTUAL, + HelpContextualComponent, + HelpContextualService, + SnackBarService, + UserMessageService, +} from 'qbm'; import { QerApiService } from '../../qer-api-client.service'; import { DuplicatePatternItem } from '../duplicate-pattern-items/duplicate-pattern-item'; import { DuplicatePatternItemsComponent } from '../duplicate-pattern-items/duplicate-pattern-items.component'; import { ItShopPatternChangedType } from '../itshop-pattern-changed.enum'; -import { ItshopPatternCreateSidesheetComponent } from './itshop-pattern-create-sidesheet.component'; import { PatternItemCandidate } from '../pattern-item-candidate.interface'; +import { ItshopPatternCreateSidesheetComponent } from './itshop-pattern-create-sidesheet.component'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ItshopPatternCreateService { - private busyIndicator: OverlayRef; private busyIndicatorCounter = 0; @@ -58,11 +65,10 @@ export class ItshopPatternCreateService { private readonly translate: TranslateService, private readonly errorHandler: ErrorHandler, private readonly snackBar: SnackBarService, - private readonly helpContextualService: HelpContextualService - ) { } + private readonly helpContextualService: HelpContextualService, + ) {} public async saveNewPatternAndItems(pattern: PortalItshopPatternPrivate, patternItems: PortalItshopPatternItem[]): Promise { - this.handleOpenLoader(); try { const uidPattern = await this.postPattern(pattern); @@ -71,16 +77,17 @@ export class ItshopPatternCreateService { if (patternItems && patternItems.length > 0) { await this.createPatternItems(patternItems, uidPattern, false); } - } finally { this.handleCloseLoader(); } - this.snackBar.open({ - key: '#LDS#The product bundle has been successfully created.', - parameters: [pattern.GetEntity().GetDisplay()] - }, '#LDS#Close'); - + this.snackBar.open( + { + key: '#LDS#The product bundle has been successfully created.', + parameters: [pattern.GetEntity().GetDisplay()], + }, + '#LDS#Close', + ); } /** @@ -93,32 +100,32 @@ export class ItshopPatternCreateService { return pattern.GetEntity()?.GetKeys()?.join(','); } - public async createPatternItems(selection: any, uidPattern: string, showResultInSnackbar: boolean = true): Promise { - - const newAssignedObjects = (await this.handlePromiseLoader( - Promise.all( - selection.map(async (selectedItem: { XObjectKey: { value: string; }; }) => { - const patternItem = this.qerClient.typedClient.PortalItshopPatternItem.createEntity(); - patternItem.UID_ShoppingCartPattern.value = uidPattern; - patternItem.UID_AccProduct.value = selectedItem.XObjectKey.value; - await patternItem.GetEntity().Commit(true); - }) + const newAssignedObjects = ( + await this.handlePromiseLoader( + Promise.all( + selection.map(async (selectedItem: { XObjectKey: { value: string } }) => { + const patternItem = this.qerClient.typedClient.PortalItshopPatternItem.createEntity(); + patternItem.UID_ShoppingCartPattern.value = uidPattern; + patternItem.UID_AccProduct.value = selectedItem.XObjectKey.value; + await patternItem.GetEntity().Commit(true); + }), + ), ) - )).length; + ).length; if (newAssignedObjects > 0 && showResultInSnackbar) { - this.snackBar.open({ - key: '#LDS#{0} identities have been successfully assigned.', - parameters: [newAssignedObjects] - }, '#LDS#Close'); + this.snackBar.open( + { + key: '#LDS#{0} identities have been successfully assigned.', + parameters: [newAssignedObjects], + }, + '#LDS#Close', + ); } return newAssignedObjects; } - public async assignItemsToPattern( - items: PatternItemCandidate[], - uidPattern: string = ''): Promise { - + public async assignItemsToPattern(items: PatternItemCandidate[], uidPattern: string = ''): Promise { if (uidPattern.length === 0) { // create new pattern const pattern = await this.createNewPattern(false); @@ -141,10 +148,9 @@ export class ItshopPatternCreateService { newAssignedObjects++; } catch (exception) { // 810303 == the combination of the fields Role/organization, Service item, Product bundle must be unique. - if (exception?.dataItems.length && exception.dataItems[0].Number === 810303 ) { + if (exception?.dataItems.length && exception.dataItems[0].Number === 810303) { duplicateItems.push(new DuplicatePatternItem(item.display)); - } - else { + } else { this.errorHandler.handleError(exception); } } @@ -153,8 +159,8 @@ export class ItshopPatternCreateService { if (duplicateItems.length > 0) { const dialogRef = this.dialogService.open(DuplicatePatternItemsComponent, { data: { - duplicatePatternItems: duplicateItems - } + duplicatePatternItems: duplicateItems, + }, }); await dialogRef.beforeClosed().toPromise(); @@ -162,55 +168,53 @@ export class ItshopPatternCreateService { return newAssignedObjects; } - public async createNewPattern(showSnackbar: boolean): Promise { + public async createNewPattern(showSnackbar: boolean): Promise { const newPattern = this.createNewPatternEntity(); this.logger.trace(this, 'new itshop pattern created', newPattern); if (newPattern) { this.helpContextualService.setHelpContextId(HELP_CONTEXTUAL.RequestTemplatesCreate); - const result = await this.sidesheet.open(ItshopPatternCreateSidesheetComponent, { - title: await this.translate.get('#LDS#Heading Create Product Bundle').toPromise(), - panelClass: 'imx-sidesheet', - disableClose: true, - padding: '0', - width: '600px', - testId: 'pattern-create-sidesheet', - data: { - pattern: newPattern - }, - headerComponent: HelpContextualComponent - }).afterClosed().toPromise(); + const result = await this.sidesheet + .open(ItshopPatternCreateSidesheetComponent, { + title: await this.translate.get('#LDS#Heading Create Product Bundle').toPromise(), + panelClass: 'imx-sidesheet', + disableClose: true, + padding: '0', + width: calculateSidesheetWidth(600, 0.4), + testId: 'pattern-create-sidesheet', + data: { + pattern: newPattern, + }, + headerComponent: HelpContextualComponent, + }) + .afterClosed() + .toPromise(); if (result === ItShopPatternChangedType.Saved) { - if (showSnackbar) { const snackBarMessage = '#LDS#The product bundle has been successfully created.'; this.snackBar.open({ key: snackBarMessage }); } return newPattern; } - } - else { + } else { this.messageService.subject.next({ - text: '#LDS#The sample could not be created. Please reload the page and try again.' + text: '#LDS#The sample could not be created. Please reload the page and try again.', }); - return null; + return undefined; } } public handleOpenLoader(): void { if (this.busyIndicatorCounter === 0) { - setTimeout(() => this.busyIndicator = this.busyService.show()); + this.busyService.show(); } this.busyIndicatorCounter++; } public handleCloseLoader(): void { if (this.busyIndicatorCounter === 1) { - setTimeout(() => { - this.busyService.hide(this.busyIndicator); - this.busyIndicator = undefined; - }); + this.busyService.hide(this.busyIndicator); } this.busyIndicatorCounter--; } diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-item-edit/itshop-pattern-item-edit-parameter.interface.ts b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-item-edit/itshop-pattern-item-edit-parameter.interface.ts index 8d754abdc..cc9d4d416 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-item-edit/itshop-pattern-item-edit-parameter.interface.ts +++ b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-item-edit/itshop-pattern-item-edit-parameter.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,10 +24,10 @@ * */ -import { PortalShopServiceitems, ProjectConfig, QerProjectConfig } from "imx-api-qer" +import { PortalShopServiceitems, ProjectConfig, QerProjectConfig } from '@imx-modules/imx-api-qer'; export interface ItshopPatternItemEditParameter { patternItemUid: string; - serviceItem: PortalShopServiceitems; + serviceItem: PortalShopServiceitems; projectConfig: QerProjectConfig & ProjectConfig; } diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-item-edit/itshop-pattern-item-edit.component.html b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-item-edit/itshop-pattern-item-edit.component.html index 858d77fee..3f9631bf5 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-item-edit/itshop-pattern-item-edit.component.html +++ b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-item-edit/itshop-pattern-item-edit.component.html @@ -1,6 +1,6 @@
    - - {{ detailsInfoText | translate}} + + {{ detailsInfoText | translate }} @@ -16,18 +16,25 @@
    - + [attr.data-imx-identifier]="'citshop-pattern-item-edit-' + column.ColumnName" + >
    -
    -
    -
    @@ -39,7 +46,7 @@ -
    +

    {{ '#LDS#There are currently no request parameters specified for this product.' | translate }}

    diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-item-edit/itshop-pattern-item-edit.component.scss b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-item-edit/itshop-pattern-item-edit.component.scss index 0a81cdc9b..bf41727f7 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-item-edit/itshop-pattern-item-edit.component.scss +++ b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-item-edit/itshop-pattern-item-edit.component.scss @@ -1,22 +1,10 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; - -@mixin imx-flex-column-container { - display: flex; - flex-direction: column; -} +@import '@elemental-ui/core/src/styles/_palette.scss'; +@import 'base/mixins'; .eui-sidesheet-content { - @include imx-flex-column-container(); - - .mat-card { - margin-bottom: 20px; - } -} - -.imx-helper-alert { - width: 100%; + @include flex-column-container(); - ::ng-deep .eui-alert { + .mat-mdc-card { margin-bottom: 20px; } } @@ -28,7 +16,7 @@ margin-top: 20px; } -.imx-param-no-results { +.imx-no-results { text-align: center; margin: 20px 0; flex: 1 1 auto; @@ -36,14 +24,9 @@ flex-direction: column; justify-content: center; - .eui-icon { - font-size: 100px; - color: rgba($black-c, 0.55); - } - p { margin: 0; font-size: 18px; color: $black-9; } -} \ No newline at end of file +} diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-item-edit/itshop-pattern-item-edit.component.spec.ts b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-item-edit/itshop-pattern-item-edit.component.spec.ts index d901a76b2..299d0db4a 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-item-edit/itshop-pattern-item-edit.component.spec.ts +++ b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-item-edit/itshop-pattern-item-edit.component.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -39,13 +39,13 @@ describe('ItshopPatternItemEditComponent', () => { beforeEach(() => { return MockBuilder([ItshopPatternItemEditComponent, TranslateModule.forRoot()]) - .mock(ItshopPatternService) - .mock(EuiSidesheetRef) - .beforeCompileComponents(testBed => { - testBed.configureTestingModule({ - schemas: [CUSTOM_ELEMENTS_SCHEMA] + .mock(ItshopPatternService) + .mock(EuiSidesheetRef) + .beforeCompileComponents((testBed) => { + testBed.configureTestingModule({ + schemas: [CUSTOM_ELEMENTS_SCHEMA], + }); }); - }); }); beforeEach(() => { @@ -56,7 +56,7 @@ describe('ItshopPatternItemEditComponent', () => { afterAll(() => { clearStylesFromDOM(); - }) + }); it('should create', () => { expect(component).toBeTruthy(); diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-item-edit/itshop-pattern-item-edit.component.ts b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-item-edit/itshop-pattern-item-edit.component.ts index ad3ca5092..478b167ea 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-item-edit/itshop-pattern-item-edit.component.ts +++ b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-item-edit/itshop-pattern-item-edit.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,10 +26,10 @@ import { Component, Inject, OnInit } from '@angular/core'; import { FormGroup } from '@angular/forms'; -import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; -import { PortalItshopPatternItem } from 'imx-api-qer'; +import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; +import { PortalItshopPatternItem } from '@imx-modules/imx-api-qer'; -import { DisplayColumns, IEntityColumn } from 'imx-qbm-dbts'; +import { DisplayColumns, IEntityColumn } from '@imx-modules/imx-qbm-dbts'; import { BaseReadonlyCdr, ClassloggerService, ColumnDependentReference } from 'qbm'; import { ExtendedEntityWrapper } from '../../parameter-data/extended-entity-wrapper.interface'; @@ -39,14 +39,15 @@ import { ItshopPatternItemEditParameter } from './itshop-pattern-item-edit-param @Component({ selector: 'imx-itshop-pattern-item-edit', templateUrl: './itshop-pattern-item-edit.component.html', - styleUrls: ['./itshop-pattern-item-edit.component.scss'] + styleUrls: ['./itshop-pattern-item-edit.component.scss'], }) export class ItshopPatternItemEditComponent implements OnInit { public readonly requestParamForm = new FormGroup({}); public columns: IEntityColumn[]; public cdrList: ColumnDependentReference[] = []; - public detailsInfoText = '#LDS#Here you can see the details of the product. Additionally, you can pre-fill the values of the request parameters.'; + public detailsInfoText = + '#LDS#Here you can see the details of the product. Additionally, you can pre-fill the values of the request parameters.'; public loadingParams = true; public hasParams: boolean = true; @@ -58,19 +59,19 @@ export class ItshopPatternItemEditComponent implements OnInit { private readonly sideSheetRef: EuiSidesheetRef, private readonly patternService: ItshopPatternService, private readonly logger: ClassloggerService, - ) { } + ) {} public async ngOnInit(): Promise { - const properties = this.data.projectConfig.ITShopConfig.AccProductProperties; + const properties = this.data.projectConfig.ITShopConfig?.AccProductProperties || []; this.cdrList = [ new BaseReadonlyCdr(this.data.serviceItem.GetEntity().GetColumn(DisplayColumns.DISPLAY_PROPERTYNAME)), new BaseReadonlyCdr(this.data.serviceItem.TableName.Column), new BaseReadonlyCdr(this.data.serviceItem.Tags.Column), - ...properties.map((prop: string) => new BaseReadonlyCdr(this.data.serviceItem.GetEntity().GetColumn(prop))) + ...properties.map((prop: string) => new BaseReadonlyCdr(this.data.serviceItem.GetEntity().GetColumn(prop))), ]; this.entityWrapper = await this.patternService.getInteractivePatternitem(this.data.patternItemUid); - this.columns = this.entityWrapper.parameterCategoryColumns.map(item => item.column); + this.columns = this.entityWrapper.parameterCategoryColumns.map((item) => item.column); this.hasParams = this.entityWrapper.parameterCategoryColumns?.length > 0; this.loadingParams = false; } @@ -83,6 +84,6 @@ export class ItshopPatternItemEditComponent implements OnInit { } finally { this.patternService.handleCloseLoader(); } - this.sideSheetRef.close(true) + this.sideSheetRef.close(true); } } diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-sidesheet/itshop-pattern-sidesheet.component.html b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-sidesheet/itshop-pattern-sidesheet.component.html index cf465f7f8..93197272e 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-sidesheet/itshop-pattern-sidesheet.component.html +++ b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-sidesheet/itshop-pattern-sidesheet.component.html @@ -1,86 +1,120 @@ -
    - - - -
    - - {{ (data.canEditAndDelete ? editableDetailsInfoText : detailsInfoText) | translate}} - - - - {{ '#LDS#Private product bundle' | translate}} - - - {{'#LDS#Public product bundle' | translate}} - - - - -
    -
    -
    - - -
    - - {{ (data.canEditAndDelete ? editableProductsInfoText : productsInfoText) | translate}} - - -
    - - - - - - - - -
    -
    -
    -
    -
    -
    -
    + + + +
    + + {{ (data.canEditAndDelete ? editableDetailsInfoText : detailsInfoText) | translate }} + + + + {{ '#LDS#Private product bundle' | translate }} + + + {{ '#LDS#Public product bundle' | translate }} + + + +
    +
    +
    + + +
    + + {{ (data.canEditAndDelete ? editableProductsInfoText : productsInfoText) | translate }} + + +
    + + + + + + + +
    +
    +
    +
    +
    +
    - - - -
    -
    - -
    diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-sidesheet/itshop-pattern-sidesheet.component.scss b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-sidesheet/itshop-pattern-sidesheet.component.scss index 6c6f3f27c..9f6c8f5ad 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-sidesheet/itshop-pattern-sidesheet.component.scss +++ b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-sidesheet/itshop-pattern-sidesheet.component.scss @@ -1,106 +1,13 @@ @import '@elemental-ui/core/src/styles/_eui_palette.scss'; - -@mixin imx-flex-column-container { - display: flex; - flex-direction: column; -} - -.eui-sidesheet-content { - @include imx-flex-column-container(); - padding: 0; -} - -:host { - ::ng-deep .mat-tab-body-content { - display: flex; - } - - .eui-badge ::ng-deep .eui-badge-content { - font-size: 12px; - line-height: 12px; - margin-bottom: 20px; - } - - .imx-helper-alert { - width: 100%; - - ::ng-deep .eui-alert { - margin-bottom: 20px; - } - } - - .eui-sidesheet-actions { - .justify-start { - margin-right: auto; - } - - .mat-stroked-button eui-icon { - font-size: 14px; - margin-right: 4px; - } - - button:not(:last-of-type):not(.justify-start) { - margin-right: 10px; - } - } - - ::ng-deep .mat-tab-body-wrapper { - flex: 1 1 auto; - - .mat-tab-body { - display: flex; - flex-direction: column; - flex: 1 1 auto; - } - } - - ::ng-deep .mat-tab-body-content { - display: flex; - flex-direction: column; - } - - ::ng-deep .mat-tab-group { - flex-grow: 1; - overflow: auto; - } -} - -.imx-tab-content { - flex: 1; - padding: 32px; - overflow: auto; - @include imx-flex-column-container(); - - .mat-card { - @include imx-flex-column-container(); - overflow: hidden; - - .imx-table-container { - overflow: auto; - @include imx-flex-column-container(); - } - } -} - -// Theming -:host { - ::ng-deep .mat-tab-group .mat-tab-header { - background-color: $color-gray-0; - } -} +@import 'base/mixins'; .eui-dark-theme { :host { background-color: $color-gray-80; color: $color-gray-0; - .eui-sidesheet-actions.mat-card.eui-sidesheet-actions--white{ + .eui-sidesheet-actions.mat-mdc-card.eui-sidesheet-actions--white { background-color: $color-gray-70; } - ::ng-deep .mat-tab-group { - .mat-tab-header { - background-color: $color-gray-80; - } - } } } @@ -108,14 +15,8 @@ :host { background-color: $color-gray-100; color: $color-gray-0; - .eui-sidesheet-actions.mat-card.eui-sidesheet-actions--white{ + .eui-sidesheet-actions.mat-mdc-card.eui-sidesheet-actions--white { background-color: $color-gray-90; } - ::ng-deep .mat-tab-group { - .mat-tab-header { - background-color: $color-gray-100; - } - } } } - diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-sidesheet/itshop-pattern-sidesheet.component.spec.ts b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-sidesheet/itshop-pattern-sidesheet.component.spec.ts index f8e54903b..c4360a0e2 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-sidesheet/itshop-pattern-sidesheet.component.spec.ts +++ b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-sidesheet/itshop-pattern-sidesheet.component.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { FormArray, FormsModule, ReactiveFormsModule, UntypedFormArray, UntypedFormBuilder } from '@angular/forms'; import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; import { TranslateModule } from '@ngx-translate/core'; import { of } from 'rxjs'; @@ -64,7 +64,7 @@ describe('ItshopPatternSidesheetComponent', () => { }; const patterServiceStub = { - togglePublic: jasmine.createSpy('makePublic').and.returnValue({}), + togglePublic: jasmine.createSpy('togglePublic').and.returnValue({}), createCopy: jasmine.createSpy('createCopy').and.returnValue({}), handleOpenLoader: jasmine.createSpy('handleOpenLoader').and.callThrough(), handleCloseLoader: jasmine.createSpy('handleCloseLoader').and.callThrough(), @@ -72,13 +72,17 @@ describe('ItshopPatternSidesheetComponent', () => { delete: jasmine.createSpy('delete').and.callThrough(), getPatternItems: jasmine.createSpy('getPatternItems').and.callThrough(), }; + const formBuilder = { + array: jasmine.createSpy('togglePublic').and.returnValue(new FormArray([])), + }; beforeEach(() => { - return MockBuilder([ItshopPatternSidesheetComponent, TranslateModule.forRoot(),FormsModule,ReactiveFormsModule]) + return MockBuilder([ItshopPatternSidesheetComponent, TranslateModule.forRoot(), FormsModule, ReactiveFormsModule]) .mock(ItshopPatternModule, { exportAll: true }) .mock(ItshopPatternService, patterServiceStub) .mock(EuiSidesheetRef, mockSidesheetRef) - .mock(EUI_SIDESHEET_DATA, sidesheetData ); + .mock(EUI_SIDESHEET_DATA, sidesheetData) + .mock(UntypedFormBuilder, formBuilder); }); beforeEach(() => { diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-sidesheet/itshop-pattern-sidesheet.component.ts b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-sidesheet/itshop-pattern-sidesheet.component.ts index 531a4c81c..61ee7ed43 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-sidesheet/itshop-pattern-sidesheet.component.ts +++ b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern-sidesheet/itshop-pattern-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,17 +27,24 @@ import { Component, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { UntypedFormArray, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { MatTabChangeEvent } from '@angular/material/tabs'; -import { EuiSidesheetRef, EuiSidesheetService, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { EUI_SIDESHEET_DATA, EuiSidesheetRef, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; import { Subscription } from 'rxjs'; -import { PortalItshopPatternItem, PortalItshopPatternPrivate, PortalShopServiceitems, ProjectConfig, QerProjectConfig } from 'imx-api-qer'; -import { CollectionLoadParameters, CompareOperator, DisplayColumns, FilterType, TypedEntity } from 'imx-qbm-dbts'; +import { + PortalItshopPatternItem, + PortalItshopPatternPrivate, + PortalShopServiceitems, + ProjectConfig, + QerProjectConfig, +} from '@imx-modules/imx-api-qer'; +import { CollectionLoadParameters, CompareOperator, DisplayColumns, FilterType, TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { BaseCdr, - BusyService, BaseReadonlyCdr, + BusyService, + calculateSidesheetWidth, ClassloggerService, ColumnDependentReference, ConfirmationService, @@ -46,20 +53,19 @@ import { DataTableComponent, SnackBarService, } from 'qbm'; -import { ItshopPatternService } from '../itshop-pattern.service'; -import { ItShopPatternChangedType } from '../itshop-pattern-changed.enum'; +import { ProjectConfigurationService } from '../../project-configuration/project-configuration.service'; +import { ServiceItemsService } from '../../service-items/service-items.service'; import { ItshopPatternAddProductsComponent } from '../itshop-pattern-add-products/itshop-pattern-add-products.component'; +import { ItShopPatternChangedType } from '../itshop-pattern-changed.enum'; import { ItshopPatternItemEditComponent } from '../itshop-pattern-item-edit/itshop-pattern-item-edit.component'; -import { ServiceItemsService } from '../../service-items/service-items.service'; -import { ProjectConfigurationService } from '../../project-configuration/project-configuration.service'; +import { ItshopPatternService } from '../itshop-pattern.service'; @Component({ selector: 'imx-itshop-pattern-sidesheet', templateUrl: './itshop-pattern-sidesheet.component.html', - styleUrls: ['./itshop-pattern-sidesheet.component.scss'] + styleUrls: ['./itshop-pattern-sidesheet.component.scss'], }) export class ItshopPatternSidesheetComponent implements OnInit, OnDestroy { - public get formArray(): UntypedFormArray { return this.detailsFormGroup.get('formArray') as UntypedFormArray; } @@ -69,7 +75,7 @@ export class ItshopPatternSidesheetComponent implements OnInit, OnDestroy { public busyService = new BusyService(); public dstWrapper: DataSourceWrapper; - public dstSettings: DataSourceToolbarSettings; + public dstSettings: DataSourceToolbarSettings | undefined; public selectedPatternItems: PortalItshopPatternItem[] = []; public adminMode: boolean; public selectedTabIndex = 0; @@ -78,8 +84,8 @@ export class ItshopPatternSidesheetComponent implements OnInit, OnDestroy { public editableDetailsInfoText = '#LDS#Here you can edit the details of this product bundle.'; public productsInfoText = '#LDS#Here you can get an overview of all products assigned to this product bundle.'; - public editableProductsInfoText = '#LDS#Here you can get an overview of all products assigned to this product bundle. Additionally, you can add and remove products.'; - + public editableProductsInfoText = + '#LDS#Here you can get an overview of all products assigned to this product bundle. Additionally, you can add and remove products.'; @ViewChild(DataTableComponent) public table: DataTableComponent; @@ -88,11 +94,12 @@ export class ItshopPatternSidesheetComponent implements OnInit, OnDestroy { constructor( formBuilder: UntypedFormBuilder, - @Inject(EUI_SIDESHEET_DATA) public data: { - pattern: PortalItshopPatternPrivate - isMyPattern: boolean, - adminMode: boolean, - canEditAndDelete: boolean + @Inject(EUI_SIDESHEET_DATA) + public data: { + pattern: PortalItshopPatternPrivate; + isMyPattern: boolean; + adminMode: boolean; + canEditAndDelete: boolean; }, private readonly translate: TranslateService, private readonly patternService: ItshopPatternService, @@ -102,15 +109,14 @@ export class ItshopPatternSidesheetComponent implements OnInit, OnDestroy { private readonly sideSheetRef: EuiSidesheetRef, private readonly snackBar: SnackBarService, private readonly logger: ClassloggerService, - private confirmation: ConfirmationService + private confirmation: ConfirmationService, ) { this.detailsFormGroup = new UntypedFormGroup({ formArray: formBuilder.array([]) }); this.closeSubscription = this.sideSheetRef.closeClicked().subscribe(async () => { - if (!this.detailsFormGroup.dirty - || await confirmation.confirmLeaveWithUnsavedChanges()) { - this.data.pattern.GetEntity().DiscardChanges(); - this.sideSheetRef.close(); + if (!this.detailsFormGroup.dirty || (await confirmation.confirmLeaveWithUnsavedChanges())) { + this.data.pattern.GetEntity().DiscardChanges(); + this.sideSheetRef.close(); } }); } @@ -144,7 +150,7 @@ export class ItshopPatternSidesheetComponent implements OnInit, OnDestroy { const parameters = { ...parameter, - ...filteredState + ...filteredState, }; this.dstSettings = await this.dstWrapper.getDstSettings(parameters); } finally { @@ -161,9 +167,9 @@ export class ItshopPatternSidesheetComponent implements OnInit, OnDestroy { } } - public onSelectionChanged(items: PortalItshopPatternItem[]): void { + public onSelectionChanged(items: TypedEntity[]): void { this.logger.trace(this, 'selection changed', items); - this.selectedPatternItems = items; + this.selectedPatternItems = items as PortalItshopPatternItem[]; } public async togglePublic(): Promise { @@ -172,10 +178,12 @@ export class ItshopPatternSidesheetComponent implements OnInit, OnDestroy { } public async delete(): Promise { - if (await this.confirmation.confirm({ - Title: '#LDS#Heading Delete Product Bundle', - Message: '#LDS#Are you sure you want to delete the product bundle?' - })) { + if ( + await this.confirmation.confirm({ + Title: '#LDS#Heading Delete Product Bundle', + Message: '#LDS#Are you sure you want to delete the product bundle?', + }) + ) { if (await this.patternService.delete([this.data.pattern])) { this.sideSheetRef.close(ItShopPatternChangedType.Deleted); } @@ -190,18 +198,20 @@ export class ItshopPatternSidesheetComponent implements OnInit, OnDestroy { } public async addProducts(): Promise { - - const result = await this.sidesheet.open(ItshopPatternAddProductsComponent, { - title: await this.translate.get('#LDS#Heading Add Products To Product Bundle').toPromise(), - subTitle: this.data.pattern.Ident_ShoppingCartPattern.value, - panelClass: 'imx-sidesheet', - padding: '0', - width: 'max(768px, 70%)', - testId: 'pattern-add-products-sidesheet', - data: { - shoppingCartPatternUid: this.shoppingCartPatternUid - } - }).afterClosed().toPromise(); + const result = await this.sidesheet + .open(ItshopPatternAddProductsComponent, { + title: await this.translate.get('#LDS#Heading Add Products To Product Bundle').toPromise(), + subTitle: this.data.pattern.Ident_ShoppingCartPattern.value, + panelClass: 'imx-sidesheet', + padding: '0', + width: calculateSidesheetWidth(1000), + testId: 'pattern-add-products-sidesheet', + data: { + shoppingCartPatternUid: this.shoppingCartPatternUid, + }, + }) + .afterClosed() + .toPromise(); if (result) { const snackBarMessage = '#LDS#The selected products have been successfully added to the product bundle.'; @@ -211,8 +221,7 @@ export class ItshopPatternSidesheetComponent implements OnInit, OnDestroy { } public selectedItemsCanBeDeleted(): boolean { - return this.selectedPatternItems != null - && this.selectedPatternItems.length > 0; + return this.selectedPatternItems != null && this.selectedPatternItems.length > 0; } public async createPrivateCopy(): Promise { @@ -234,14 +243,13 @@ export class ItshopPatternSidesheetComponent implements OnInit, OnDestroy { } } - public async onHighlightedEntityChanged(selectedItem: PortalItshopPatternItem): Promise { - await this.editPatternItem(selectedItem); + public async onHighlightedEntityChanged(selectedItem: TypedEntity): Promise { + await this.editPatternItem(selectedItem as PortalItshopPatternItem); } public async editPatternItem(selectedItem: PortalItshopPatternItem): Promise { - let projectConfig: QerProjectConfig & ProjectConfig; - let serviceItem: PortalShopServiceitems; + let serviceItem: PortalShopServiceitems | undefined; this.patternService.handleOpenLoader(); try { @@ -251,35 +259,32 @@ export class ItshopPatternSidesheetComponent implements OnInit, OnDestroy { this.patternService.handleCloseLoader(); } - this.sidesheet.open(ItshopPatternItemEditComponent, - { - title: await this.translate.get('#LDS#Heading View Product Details').toPromise(), - subTitle: selectedItem.GetEntity().GetDisplay(), - padding: '0px', - width: '600px', - testId: 'itshop-pattern-item-edit-sidesheet', - data: { - patternItemUid: selectedItem.GetEntity().GetKeys().join(''), - serviceItem, - projectConfig - } - } - ); + this.sidesheet.open(ItshopPatternItemEditComponent, { + title: await this.translate.get('#LDS#Heading View Product Details').toPromise(), + subTitle: selectedItem.GetEntity().GetDisplay(), + padding: '0px', + width: calculateSidesheetWidth(600, 0.4), + testId: 'itshop-pattern-item-edit-sidesheet', + data: { + patternItemUid: selectedItem.GetEntity().GetKeys().join(''), + serviceItem, + projectConfig, + }, + }); } private async setupDetailsTab(): Promise { - if (this.data.canEditAndDelete) { this.cdrList = [ new BaseCdr(this.data.pattern.Ident_ShoppingCartPattern.Column), new BaseCdr(this.data.pattern.Description.Column), - new BaseCdr(this.data.pattern.UID_Person.Column) + new BaseCdr(this.data.pattern.UID_Person.Column), ]; } else { this.cdrList = [ new BaseReadonlyCdr(this.data.pattern.Ident_ShoppingCartPattern.Column), new BaseReadonlyCdr(this.data.pattern.Description.Column), - new BaseReadonlyCdr(this.data.pattern.UID_Person.Column) + new BaseReadonlyCdr(this.data.pattern.UID_Person.Column), ]; } } @@ -287,11 +292,9 @@ export class ItshopPatternSidesheetComponent implements OnInit, OnDestroy { private setupProductsTab(): void { const entitySchema = this.patternService.itshopPatternItemSchema; this.dstWrapper = new DataSourceWrapper( - state => this.patternService.getPatternItems(state), - [ - entitySchema.Columns[DisplayColumns.DISPLAY_PROPERTYNAME] - ], - entitySchema + (state) => this.patternService.getPatternItems(state), + [entitySchema.Columns[DisplayColumns.DISPLAY_PROPERTYNAME]], + entitySchema, ); } } diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern.component.html b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern.component.html index 1b3cd3bfc..d3820aa40 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern.component.html +++ b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern.component.html @@ -1,56 +1,63 @@
    -
    -

    +
    +

    {{ '#LDS#Heading Product Bundles' | translate }} -

    +

    +
    - -
    - - - - - -
    {{ item.Ident_ShoppingCartPattern.Column.GetDisplayValue() }}
    -
    - {{ item.Description.Column.GetDisplayValue() }} -
    -
    -
    - - - -
    - -
    + + + + + {{ entitySchema?.Columns?.[DisplayColumns.DISPLAY_PROPERTYNAME]?.Display }} + + +
    {{ item.Ident_ShoppingCartPattern.Column.GetDisplayValue() }}
    +
    + {{ item.Description.Column.GetDisplayValue() }} +
    + +
    + + + + {{ entitySchema?.Columns?.UID_Person?.Display }} + + + + + {{ item.UID_Person.Column.GetDisplayValue() }} + + + + + + + {{ entitySchema?.Columns?.IsPublicPattern?.Display }} + + + + + {{ item.IsPublicPattern.Column.GetDisplayValue() }} + + + +
    +
    -
    - -
    - -
    diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern.component.scss b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern.component.scss index 8fdc7838c..bfcf0660a 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern.component.scss +++ b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern.component.scss @@ -1,68 +1,24 @@ -@import '../../../../../shared/scss/common-table.scss'; -@mixin imx-flex-column-container { - display: flex; - flex-direction: column; -} - -@mixin imx-flex-row-container { - display: flex; - flex-direction: row; -} - -.eui-sidesheet-content { - @include imx-flex-column-container(); -} +@import 'base/mixins'; .heading-wrapper { - @include imx-flex-row-container(); + @include flex-row-container(); flex: 0 0 auto; - - .helper-alert { - display: flex; - margin-bottom: 15px; - } } :host { - @include imx-flex-column-container(); - overflow: hidden; - height: 100%; - max-width: 100%; + @include flex-column-container($overflow: hidden, $height: 100%, $max-width: 100%); } .imx-pattern-page { background-color: inherit; - @include imx-flex-fill-control-hidden-overflow(); + @include flex-column-container-fill(); - .mat-card { - @include imx-flex-fill-control-hidden-overflow(); - } - - div.imx-table-container { - @include imx-flex-column-container(); - overflow: hidden; - height: inherit; - - .imx-patterns-table { - flex-grow: 1; - overflow: auto; - } - } - - .imx-button-bar { - @include imx-button-bar(); - } } @media screen and (max-width: 768px) { .imx-pattern-page { .heading-wrapper { display: block; - - .alert-wrapper { - margin: 0 0 20px 0; - width: 100%; - } } } } diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern.component.ts b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern.component.ts index a10f0d3e5..5349069ad 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern.component.ts +++ b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,25 +29,27 @@ import { EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; import { Subscription } from 'rxjs'; -import { PortalItshopPatternAdmin, PortalItshopPatternPrivate } from 'imx-api-qer'; -import { CollectionLoadParameters, DisplayColumns, TypedEntity } from 'imx-qbm-dbts'; +import { PortalItshopPatternAdmin, PortalItshopPatternPrivate } from '@imx-modules/imx-api-qer'; +import { CollectionLoadParameters, DisplayColumns, EntitySchema, TypedEntity, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { AuthenticationService, ClassloggerService, ConfirmationService, - DataSourceToolbarSettings, - DataSourceWrapper, DataTableComponent, + DataViewInitParameters, + DataViewSource, HELP_CONTEXTUAL, HelpContextualValues, + ISessionState, SnackBarService, + calculateSidesheetWidth, } from 'qbm'; import { QerPermissionsService } from '../admin/qer-permissions.service'; +import { ItShopPatternChangedType } from './itshop-pattern-changed.enum'; +import { ItshopPatternCreateService } from './itshop-pattern-create-sidesheet/itshop-pattern-create.service'; import { ItshopPatternSidesheetComponent } from './itshop-pattern-sidesheet/itshop-pattern-sidesheet.component'; import { ItshopPatternService } from './itshop-pattern.service'; -import { ItshopPatternCreateService } from './itshop-pattern-create-sidesheet/itshop-pattern-create.service'; -import { ItShopPatternChangedType } from './itshop-pattern-changed.enum'; /** * Component that shows a list of all product bundles (internal names are itshop pattern and request templates) of the current user @@ -56,21 +58,21 @@ import { ItShopPatternChangedType } from './itshop-pattern-changed.enum'; @Component({ selector: 'imx-itshop-pattern', templateUrl: './itshop-pattern.component.html', - styleUrls: ['./itshop-pattern.component.scss'] + styleUrls: ['./itshop-pattern.component.scss'], + providers: [DataViewSource], }) export class ItshopPatternComponent implements OnInit, OnDestroy { - /** * the wrapper component for the {@link DataSourceToolbar|dataSourceToolbar}. */ - public dstWrapper: DataSourceWrapper; + // public dstWrapper: DataSourceWrapper; - public dstSettings: DataSourceToolbarSettings; + // public dstSettings: DataSourceToolbarSettings | undefined; /** * The list of all selected product bundles. */ - public selectedPatterns: PortalItshopPatternAdmin[] = []; + public selectedPatterns: (PortalItshopPatternPrivate | PortalItshopPatternAdmin)[] = []; /** * Indicates wether the component should be shown for shop admins or not. @@ -80,9 +82,11 @@ export class ItshopPatternComponent implements OnInit, OnDestroy { @ViewChild(DataTableComponent) public table: DataTableComponent; public readonly status = { - enabled: (pattern: PortalItshopPatternAdmin): boolean => this.canBeEditedAndDeleted(pattern) + enabled: (pattern: PortalItshopPatternAdmin): boolean => this.canBeEditedAndDeleted(pattern), }; public helpContextId: HelpContextualValues; + public entitySchema: EntitySchema; + public readonly DisplayColumns = DisplayColumns; private readonly subscriptions: Subscription[] = []; private currentUserUid: string; @@ -96,12 +100,11 @@ export class ItshopPatternComponent implements OnInit, OnDestroy { private readonly translate: TranslateService, private readonly logger: ClassloggerService, private readonly confirmationService: ConfirmationService, - authentication: AuthenticationService + authentication: AuthenticationService, + public dataSource: DataViewSource, ) { this.subscriptions.push( - authentication.onSessionResponse.subscribe((sessionState) => - this.currentUserUid = sessionState.UserUid - ) + authentication.onSessionResponse.subscribe((sessionState: ISessionState) => (this.currentUserUid = sessionState.UserUid || '')), ); } @@ -110,24 +113,9 @@ export class ItshopPatternComponent implements OnInit, OnDestroy { try { this.adminMode = await this.qerPermissionService.isShopAdmin(); - this.helpContextId = this.adminMode ? HELP_CONTEXTUAL.RequestTemplates: HELP_CONTEXTUAL.RequestTemplatesUser; + this.helpContextId = this.adminMode ? HELP_CONTEXTUAL.RequestTemplates : HELP_CONTEXTUAL.RequestTemplatesUser; - const entitySchema = this.adminMode - ? this.patternService.itshopPatternAdminSchema - : this.patternService.itshopPatternPrivateSchema; - this.dstWrapper = new DataSourceWrapper( - state => this.adminMode - ? this.patternService.getPublicPatterns(state) - : this.patternService.getPrivatePatterns(state), - [ - entitySchema.Columns[DisplayColumns.DISPLAY_PROPERTYNAME], - entitySchema.Columns.UID_Person, - entitySchema.Columns.IsPublicPattern, - ], - entitySchema, - undefined, - 'itshop-pattern' - ); + this.entitySchema = this.adminMode ? this.patternService.itshopPatternAdminSchema : this.patternService.itshopPatternPrivateSchema; } finally { this.patternService.handleCloseLoader(); } @@ -135,84 +123,96 @@ export class ItshopPatternComponent implements OnInit, OnDestroy { } public ngOnDestroy(): void { - this.subscriptions.forEach(s => s.unsubscribe()); + this.subscriptions.forEach((s) => s.unsubscribe()); } - public isMyPattern(pattern: PortalItshopPatternPrivate | PortalItshopPatternAdmin): boolean { + public isMyPattern(entity: TypedEntity): boolean { + const pattern = entity as PortalItshopPatternPrivate | PortalItshopPatternAdmin; return this.currentUserUid === pattern.UID_Person.value; } - public canBeEditedAndDeleted(pattern: PortalItshopPatternPrivate | PortalItshopPatternAdmin): boolean { + public canBeEditedAndDeleted(pattern: TypedEntity): boolean { return this.isMyPattern(pattern) || this.adminMode; } public async delete(selectedPattern?: PortalItshopPatternPrivate | PortalItshopPatternAdmin): Promise { - if (await this.confirmationService.confirm({ - Title: '#LDS#Heading Delete Product Bundles', - Message: '#LDS#Are you sure you want to delete the selected product bundles?' - })) { + if ( + await this.confirmationService.confirm({ + Title: '#LDS#Heading Delete Product Bundles', + Message: '#LDS#Are you sure you want to delete the selected product bundles?', + }) + ) { await this.patternService.delete(selectedPattern ? [selectedPattern] : this.selectedPatterns, this.adminMode); - this.getData(); - this.table.clearSelection(); + this.dataSource.selection.clear(); + this.dataSource.updateState(); } } - public async publish(selectedPatterns: PortalItshopPatternPrivate[] | PortalItshopPatternAdmin[]): Promise { + public async publish(selectedPatterns: TypedEntity[]): Promise { await this.patternService.makePublic(selectedPatterns, true); - this.getData(); - this.table.clearSelection(); + this.dataSource.selection.clear(); + this.dataSource.updateState(); } - public async unpublish(selectedPatterns: PortalItshopPatternPrivate[] | PortalItshopPatternAdmin[]): Promise { + public async unpublish(selectedPatterns: TypedEntity[]): Promise { await this.patternService.makePublic(selectedPatterns, false); - this.getData(); - this.table.clearSelection(); + this.dataSource.selection.clear(); + this.dataSource.updateState(); } public async createNewPattern(): Promise { if (await this.patternCreateService.createNewPattern(true)) { - this.getData(); + this.dataSource.updateState(); } } - public async getData(parameter?: CollectionLoadParameters): Promise { - this.patternService.handleOpenLoader(); - try { - const parameters = { - ...parameter, - ...{ OrderBy: 'Ident_ShoppingCartPattern asc' } - }; - this.dstSettings = await this.dstWrapper.getDstSettings(parameters); - } finally { - this.patternService.handleCloseLoader(); - } + public async getData(): Promise { + this.dataSource.itemStatus = this.status; + const dataViewInitParameters: DataViewInitParameters = { + execute: this.adminMode + ? (params: CollectionLoadParameters, signal: AbortSignal): Promise> => + this.patternService.getPublicPatterns(params, signal) + : (params: CollectionLoadParameters, signal: AbortSignal): Promise> => + this.patternService.getPrivatePatterns(params, signal), + schema: this.entitySchema, + columnsToDisplay: [ + this.entitySchema.Columns[DisplayColumns.DISPLAY_PROPERTYNAME], + this.entitySchema.Columns.UID_Person, + this.entitySchema.Columns.IsPublicPattern, + ], + highlightEntity: (entity: PortalItshopPatternPrivate | PortalItshopPatternAdmin) => { + this.viewDetails(entity); + }, + selectionChange: (selection: (PortalItshopPatternPrivate | PortalItshopPatternAdmin)[]) => { + this.logger.trace(this, 'selection changed', selection); + this.selectedPatterns = selection; + }, + }; + this.dataSource.init(dataViewInitParameters); } public selectedItemsCanBePublished(): boolean { - return this.selectedPatterns != null - && this.selectedPatterns.length > 0 - && this.selectedPatterns.every(item => this.isMyPattern(item) && !item.IsPublicPattern.value); + return ( + this.selectedPatterns != null && + this.selectedPatterns.length > 0 && + this.selectedPatterns.every((item: PortalItshopPatternAdmin) => this.isMyPattern(item) && !item.IsPublicPattern.value) + ); } public selectedItemsCanBeUnpublished(): boolean { - return this.selectedPatterns != null - && this.selectedPatterns.length > 0 - && this.selectedPatterns.every(item => this.isMyPattern(item) && item.IsPublicPattern.value); + return ( + this.selectedPatterns != null && + this.selectedPatterns.length > 0 && + this.selectedPatterns.every((item: PortalItshopPatternAdmin) => this.isMyPattern(item) && item.IsPublicPattern.value) + ); } public selectedItemsCanBeDeleted(): boolean { - return this.selectedPatterns != null - && this.selectedPatterns.length > 0 - && this.selectedPatterns.every(item => this.canBeEditedAndDeleted(item)); - } - - public onSelectionChanged(items: PortalItshopPatternAdmin[]): void { - this.logger.trace(this, 'selection changed', items); - this.selectedPatterns = items; - } - - public async onHighlightedEntityChanged(selectedPattern: PortalItshopPatternPrivate | PortalItshopPatternAdmin): Promise { - await this.viewDetails(selectedPattern); + return ( + this.selectedPatterns != null && + this.selectedPatterns.length > 0 && + this.selectedPatterns.every((item) => this.canBeEditedAndDeleted(item)) + ); } private async viewDetails(selectedPattern: PortalItshopPatternPrivate | PortalItshopPatternAdmin): Promise { @@ -224,25 +224,28 @@ export class ItshopPatternComponent implements OnInit, OnDestroy { (pattern) => pattern.GetEntity().GetKeys()[0] === selectedPattern.GetEntity().GetKeys()[0], ); - const title = await this.translate.get(canEditAndDelete - ? '#LDS#Heading Edit Product Bundle' - : '#LDS#Heading View Product Bundle Details').toPromise(); - - const result = await this.sidesheet.open(ItshopPatternSidesheetComponent, { - title, - subTitle: pattern.Ident_ShoppingCartPattern.value, - panelClass: 'imx-sidesheet', - disableClose: true, - padding: '0', - width: '600px', - testId: 'pattern-details-sidesheet', - data: { - pattern, - isMyPattern, - adminMode: this.adminMode, - canEditAndDelete - } - }).afterClosed().toPromise(); + const title = await this.translate + .get(canEditAndDelete ? '#LDS#Heading Edit Product Bundle' : '#LDS#Heading View Product Bundle Details') + .toPromise(); + + const result = await this.sidesheet + .open(ItshopPatternSidesheetComponent, { + title, + subTitle: pattern?.Ident_ShoppingCartPattern.value, + panelClass: 'imx-sidesheet', + disableClose: true, + padding: '0', + width: calculateSidesheetWidth(), + testId: 'pattern-details-sidesheet', + data: { + pattern, + isMyPattern, + adminMode: this.adminMode, + canEditAndDelete, + }, + }) + .afterClosed() + .toPromise(); if (result === ItShopPatternChangedType.Saved) { const snackBarMessage = '#LDS#The product bundle has been successfully saved.'; @@ -251,6 +254,5 @@ export class ItshopPatternComponent implements OnInit, OnDestroy { } else if (result) { this.getData(); } - } } diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern.module.ts b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern.module.ts index 1e6fc1785..650c8bf0f 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern.module.ts +++ b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,31 +31,32 @@ import { RouterModule, Routes } from '@angular/router'; import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; import { TranslateModule } from '@ngx-translate/core'; -import { ProjectConfig } from 'imx-api-qbm'; -import { QerProjectConfig } from 'imx-api-qer'; +import { ProjectConfig } from '@imx-modules/imx-api-qbm'; +import { QerProjectConfig } from '@imx-modules/imx-api-qer'; import { CdrModule, ClassloggerService, DataSourceToolbarModule, DataTableModule, + DataViewModule, HELP_CONTEXTUAL, HelpContextualModule, MenuItem, MenuService, RouteGuardService, SelectedElementsModule, - UserMessageModule + UserMessageModule, } from 'qbm'; -import { ItshopPatternComponent } from './itshop-pattern.component'; -import { ItshopPatternSidesheetComponent } from './itshop-pattern-sidesheet/itshop-pattern-sidesheet.component'; -import { ItshopPatternCreateSidesheetComponent } from './itshop-pattern-create-sidesheet/itshop-pattern-create-sidesheet.component'; -import { ItshopPatternAddProductsComponent } from './itshop-pattern-add-products/itshop-pattern-add-products.component'; +import { ItshopPatternGuardService } from '../guards/itshop-pattern-guard.service'; import { ServiceItemsModule } from '../service-items/service-items.module'; -import { DuplicatePatternItemsComponent } from './duplicate-pattern-items/duplicate-pattern-items.component'; import { UserModule } from '../user/user.module'; -import { ItshopPatternGuardService } from '../guards/itshop-pattern-guard.service'; +import { DuplicatePatternItemsComponent } from './duplicate-pattern-items/duplicate-pattern-items.component'; +import { ItshopPatternAddProductsComponent } from './itshop-pattern-add-products/itshop-pattern-add-products.component'; +import { ItshopPatternCreateSidesheetComponent } from './itshop-pattern-create-sidesheet/itshop-pattern-create-sidesheet.component'; import { ItshopPatternItemEditComponent } from './itshop-pattern-item-edit/itshop-pattern-item-edit.component'; +import { ItshopPatternSidesheetComponent } from './itshop-pattern-sidesheet/itshop-pattern-sidesheet.component'; +import { ItshopPatternComponent } from './itshop-pattern.component'; const routes: Routes = [ { @@ -63,10 +64,10 @@ const routes: Routes = [ component: ItshopPatternComponent, canActivate: [RouteGuardService, ItshopPatternGuardService], resolve: [RouteGuardService], - data:{ - contextId: HELP_CONTEXTUAL.RequestTemplates - } - } + data: { + contextId: HELP_CONTEXTUAL.RequestTemplates, + }, + }, ]; @NgModule({ @@ -76,7 +77,7 @@ const routes: Routes = [ ItshopPatternCreateSidesheetComponent, ItshopPatternAddProductsComponent, DuplicatePatternItemsComponent, - ItshopPatternItemEditComponent + ItshopPatternItemEditComponent, ], imports: [ CdrModule, @@ -94,47 +95,43 @@ const routes: Routes = [ UserModule, SelectedElementsModule, HelpContextualModule, - ] + DataViewModule, + ], }) export class ItshopPatternModule { - constructor( private readonly menuService: MenuService, - logger: ClassloggerService + logger: ClassloggerService, ) { logger.info(this, '▶️ ItshopPatternModule loaded'); this.setupMenu(); } private setupMenu(): void { - this.menuService.addMenuFactories( - (preProps: string[], features: string[], projectConfig: QerProjectConfig & ProjectConfig) => { - const items: MenuItem[] = []; - const requestTemplatesEnabled = projectConfig.ITShopConfig.VI_ITShop_ProductSelectionFromTemplate; + this.menuService.addMenuFactories((preProps: string[], features: string[], projectConfig: QerProjectConfig & ProjectConfig) => { + const items: MenuItem[] = []; + const requestTemplatesEnabled = projectConfig.ITShopConfig?.VI_ITShop_ProductSelectionFromTemplate || false; - if (preProps.includes('ITSHOP') && requestTemplatesEnabled) { - items.push( - { - id: 'QER_Request_RequestTemplates', - navigationCommands: { - commands: ['itshop', 'requesttemplates'] - }, - title: '#LDS#Menu Entry Product bundles', - sorting: '10-50', - } - ); - } + if (preProps.includes('ITSHOP') && requestTemplatesEnabled) { + items.push({ + id: 'QER_Request_RequestTemplates', + navigationCommands: { + commands: ['itshop', 'requesttemplates'], + }, + title: '#LDS#Menu Entry Product bundles', + sorting: '10-50', + }); + } - if (items.length === 0) { - return null; - } - return { - id: 'ROOT_Request', - title: '#LDS#Requests', - sorting: '10', - items - }; + if (items.length === 0) { + return; } - ); + return { + id: 'ROOT_Request', + title: '#LDS#Requests', + sorting: '10', + items, + }; + }); } } diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern.service.ts b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern.service.ts index c31ec87ad..b3450a77a 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern.service.ts +++ b/imxweb/projects/qer/src/lib/itshop-pattern/itshop-pattern.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,8 +28,19 @@ import { OverlayRef } from '@angular/cdk/overlay'; import { ErrorHandler, Injectable } from '@angular/core'; import { EuiLoadingService } from '@elemental-ui/core'; -import { PortalItshopPatternAdmin, PortalItshopPatternItem, PortalItshopPatternPrivate } from 'imx-api-qer'; -import { CollectionLoadParameters, CompareOperator, EntitySchema, ExtendedTypedEntityCollection, FilterType, FkProviderItem, IFkCandidateProvider, InteractiveEntityWriteData, ParameterData, TypedEntity } from 'imx-qbm-dbts'; +import { PortalItshopPatternAdmin, PortalItshopPatternItem, PortalItshopPatternPrivate } from '@imx-modules/imx-api-qer'; +import { + CollectionLoadParameters, + CompareOperator, + EntitySchema, + ExtendedTypedEntityCollection, + FilterType, + FkProviderItem, + IFkCandidateProvider, + InteractiveEntityWriteData, + ParameterData, + TypedEntity, +} from '@imx-modules/imx-qbm-dbts'; import { ClassloggerService, SnackBarService } from 'qbm'; import { ExtendedEntityWrapper } from '../parameter-data/extended-entity-wrapper.interface'; @@ -37,10 +48,9 @@ import { QerApiService } from '../qer-api-client.service'; import { RequestParametersService } from '../shopping-cart/cart-item-edit/request-parameters.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ItshopPatternService { - private busyIndicator: OverlayRef; private busyIndicatorCounter = 0; @@ -50,7 +60,8 @@ export class ItshopPatternService { private readonly logger: ClassloggerService, private readonly busyService: EuiLoadingService, private readonly errorHandler: ErrorHandler, - private readonly snackBar: SnackBarService) { } + private readonly snackBar: SnackBarService, + ) {} public get itshopPatternAdminSchema(): EntitySchema { return this.qerClient.typedClient.PortalItshopPatternAdmin.GetSchema(); @@ -64,17 +75,18 @@ export class ItshopPatternService { return this.qerClient.typedClient.PortalItshopPatternItem.GetSchema(); } - /** * Retrieves all private itshop patterns of a person. * * @returns A list of {@link PortalItshopPatternPrivate} entities. */ - public async getPrivatePatterns(navigationState?: CollectionLoadParameters): - Promise> { + public async getPrivatePatterns( + navigationState?: CollectionLoadParameters, + signal?: AbortSignal, + ): Promise> { this.logger.debug(this, `Retrieving private itshop patterns`); this.logger.trace('Navigation state', navigationState); - return this.qerClient.typedClient.PortalItshopPatternPrivate.Get(navigationState); + return this.qerClient.typedClient.PortalItshopPatternPrivate.Get(navigationState, { signal }); } /** @@ -102,8 +114,9 @@ export class ItshopPatternService { * * @returns A list of {@link PortalItshopPatternAdmin} entities. */ - public async getPatternItems(navigationState?: CollectionLoadParameters): - Promise> { + public async getPatternItems( + navigationState?: CollectionLoadParameters, + ): Promise> { this.logger.debug(this, `Retrieving public itshop patterns`); this.logger.trace('Navigation state', navigationState); return this.qerClient.typedClient.PortalItshopPatternItem.Get(navigationState); @@ -114,11 +127,13 @@ export class ItshopPatternService { * * @returns A list of {@link PortalItshopPatternAdmin} entities. */ - public async getPublicPatterns(navigationState?: CollectionLoadParameters): - Promise> { + public async getPublicPatterns( + navigationState?: CollectionLoadParameters, + signal?: AbortSignal, + ): Promise> { this.logger.debug(this, `Retrieving public itshop patterns`); this.logger.trace('Navigation state', navigationState); - return this.qerClient.typedClient.PortalItshopPatternAdmin.Get(navigationState); + return this.qerClient.typedClient.PortalItshopPatternAdmin.Get(navigationState, { signal }); } /** @@ -144,7 +159,7 @@ export class ItshopPatternService { * @param selectedPatterns the list of itshop pattern that should be delete. * @returns true if at least on pattern was successfully deleted. */ - public async delete(selectedPatterns: PortalItshopPatternPrivate[], adminMode?: boolean): Promise { + public async delete(selectedPatterns: TypedEntity[], adminMode?: boolean): Promise { let deleteCount = 0; this.handleOpenLoader(); try { @@ -155,9 +170,10 @@ export class ItshopPatternService { } if (deleteCount > 0) { - const message = deleteCount > 1 - ? '#LDS#The selected product bundles have been successfully deleted.' - : '#LDS#The product bundle has been successfully deleted.'; + const message = + deleteCount > 1 + ? '#LDS#The selected product bundles have been successfully deleted.' + : '#LDS#The product bundle has been successfully deleted.'; this.snackBar.open({ key: message }); } } finally { @@ -202,16 +218,17 @@ export class ItshopPatternService { const typedEntity = collection.Data[index]; + const columns = this.requestParametersService.createInteractiveParameterCategoryColumns( + { + Parameters: typedEntity.extendedDataRead?.Parameters, + index, + }, + (parameter) => this.getFkProviderItemsInteractive(typedEntity, parameter), + typedEntity, + ); return { typedEntity, - parameterCategoryColumns: this.requestParametersService.createInteractiveParameterCategoryColumns( - { - Parameters: typedEntity.extendedDataRead?.Parameters, - index - }, - parameter => this.getFkProviderItemsInteractive(typedEntity, parameter), - typedEntity - ) + parameterCategoryColumns: columns ?? [], }; } @@ -219,68 +236,64 @@ export class ItshopPatternService { return entityWrapper.typedEntity.GetEntity().Commit(true); } - public getFkProviderItemsInteractive( interactiveEntity: { InteractiveEntityWriteData: InteractiveEntityWriteData }, - parameterData: ParameterData + parameterData: ParameterData, ): IFkCandidateProvider { - const qerClient = this.qerClient; - return new class implements IFkCandidateProvider { + return new (class implements IFkCandidateProvider { getProviderItem(_columnName, fkTableName) { - if (parameterData.Property.FkRelation) { - return this.getFkProviderItemInteractive(interactiveEntity, parameterData.Property.ColumnName, parameterData.Property.FkRelation.ParentTableName); + if (parameterData.Property?.FkRelation != null) { + return this.getFkProviderItemInteractive( + interactiveEntity, + parameterData.Property?.ColumnName || '', + parameterData.Property?.FkRelation.ParentTableName || '', + ); } - if (parameterData.Property.ValidReferencedTables) { - const t = parameterData.Property.ValidReferencedTables.map(parentTableRef => - this.getFkProviderItemInteractive(interactiveEntity, parameterData.Property.ColumnName, parentTableRef.TableName) - ).filter(t => t.fkTableName == fkTableName); - if (t.length == 1) - return t[0]; - return null; + if (parameterData.Property?.ValidReferencedTables != null) { + const t = parameterData.Property?.ValidReferencedTables.map((parentTableRef) => + this.getFkProviderItemInteractive(interactiveEntity, parameterData.Property?.ColumnName || '', parentTableRef.TableName || ''), + ).filter((t) => t.fkTableName == fkTableName); + if (t.length == 1) return t[0]; + return undefined; } - return null; + return undefined; } private getFkProviderItemInteractive( interactiveEntity: { InteractiveEntityWriteData: InteractiveEntityWriteData }, columnName: string, - fkTableName: string + fkTableName: string, ): FkProviderItem { return { columnName, fkTableName, - parameterNames: [ - 'OrderBy', - 'StartIndex', - 'PageSize', - 'filter', - 'search' - ], + parameterNames: ['OrderBy', 'StartIndex', 'PageSize', 'filter', 'search'], load: async (__, parameters?) => { return qerClient.client.portal_itshop_pattern_item_interactive_parameter_candidates_post( columnName, fkTableName, interactiveEntity.InteractiveEntityWriteData, - parameters + parameters, ); }, getDataModel: async () => ({}), getFilterTree: async (__, parentkey) => { return qerClient.client.portal_itshop_pattern_item_interactive_parameter_candidates_filtertree_post( - columnName, fkTableName, interactiveEntity.InteractiveEntityWriteData, { parentkey: parentkey } + columnName, + fkTableName, + interactiveEntity.InteractiveEntityWriteData, + { parentkey: parentkey }, ); - } + }, }; } - - } + })(); } - /** * Toogle the IsPublicPattern value of a {@link PortalItshopPatternPrivate} and commit the changes to the server * @param uid the uid of itshop pattern that should be toggled. @@ -305,24 +318,23 @@ export class ItshopPatternService { return reload; } - public async makePublic(selectedPatterns: PortalItshopPatternAdmin[], shouldBePublic: boolean): Promise { + public async makePublic(selectedPatterns: TypedEntity[], shouldBePublic: boolean): Promise { let commitCount = 0; this.handleOpenLoader(); try { - for (const pattern of selectedPatterns) { + for (const pattern of selectedPatterns as PortalItshopPatternAdmin[]) { pattern.IsPublicPattern.value = shouldBePublic; if (await this.tryCommit(pattern)) { commitCount++; } } const message = shouldBePublic - ? (commitCount === 1 + ? commitCount === 1 ? '#LDS#The product bundle has been shared successfully. The product bundle is now available for all users.' - : '#LDS#The product bundles have been shared successfully. {0} product bundles are now available for all users.') - : (commitCount === 1 + : '#LDS#The product bundles have been shared successfully. {0} product bundles are now available for all users.' + : commitCount === 1 ? '#LDS#Sharing of the product bundle has been successfully undone. The product bundle is now only available for yourself.' - : '#LDS#Sharing of the product bundles has been successfully undone. {0} product bundles are now only available for yourself.' - ); + : '#LDS#Sharing of the product bundles has been successfully undone. {0} product bundles are now only available for yourself.'; this.snackBar.open({ key: message, parameters: [commitCount] }); } finally { this.handleCloseLoader(); @@ -332,17 +344,14 @@ export class ItshopPatternService { public handleOpenLoader(): void { if (this.busyIndicatorCounter === 0) { - setTimeout(() => this.busyIndicator = this.busyService.show()); + this.busyIndicator = this.busyService.show(); } this.busyIndicatorCounter++; } public handleCloseLoader(): void { if (this.busyIndicatorCounter === 1) { - setTimeout(() => { - this.busyService.hide(this.busyIndicator); - this.busyIndicator = undefined; - }); + this.busyService.hide(this.busyIndicator); } this.busyIndicatorCounter--; } @@ -373,7 +382,7 @@ export class ItshopPatternService { private async tryDeleteProducts(uid: string): Promise { try { - await this.qerClient.typedClient.PortalItshopPatternItem.Delete(uid); + await this.qerClient.typedClient.PortalItshopPatternItem.Delete(uid); return true; } catch (error) { this.errorHandler.handleError(error); diff --git a/imxweb/projects/qer/src/lib/itshop-pattern/pattern-item-candidate.interface.ts b/imxweb/projects/qer/src/lib/itshop-pattern/pattern-item-candidate.interface.ts index 474b0a8d1..55c32121c 100644 --- a/imxweb/projects/qer/src/lib/itshop-pattern/pattern-item-candidate.interface.ts +++ b/imxweb/projects/qer/src/lib/itshop-pattern/pattern-item-candidate.interface.ts @@ -1,5 +1,3 @@ - - /* * ONE IDENTITY LLC. PROPRIETARY INFORMATION * @@ -11,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,7 +28,6 @@ * Interface for the object that is used to create a new pattern item. */ export interface PatternItemCandidate { - /** * the display of the pattern item candidate. */ @@ -41,5 +38,3 @@ export interface PatternItemCandidate { */ uidAccProduct: string; } - - diff --git a/imxweb/projects/qer/src/lib/itshop/decision-history.service.ts b/imxweb/projects/qer/src/lib/itshop/decision-history.service.ts index 5ed6f0948..9242719e2 100644 --- a/imxweb/projects/qer/src/lib/itshop/decision-history.service.ts +++ b/imxweb/projects/qer/src/lib/itshop/decision-history.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,7 +27,7 @@ import { Injectable } from '@angular/core'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class DecisionHistoryService { public getDecisionTypeCssClass(decisiontype: string): string { @@ -36,10 +36,14 @@ export class DecisionHistoryService { case 'cancel': case 'abort': case 'unsubscribe': - case 'reject': return 'imx-negative'; - case 'grant': return 'imx-positive'; - case 'query': return 'imx-question'; - default: return 'imx-info'; + case 'reject': + return 'imx-negative'; + case 'grant': + return 'imx-positive'; + case 'query': + return 'imx-question'; + default: + return 'imx-info'; } } @@ -100,5 +104,6 @@ export class DecisionHistoryService { case 'Reset': return '#LDS#DisplayPersonHead_Reset'; } + return '#LDS#No data'; } } diff --git a/imxweb/projects/qer/src/lib/itshop/image.service.spec.ts b/imxweb/projects/qer/src/lib/itshop/image.service.spec.ts index 243647579..32b02d05c 100644 --- a/imxweb/projects/qer/src/lib/itshop/image.service.spec.ts +++ b/imxweb/projects/qer/src/lib/itshop/image.service.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -39,17 +39,17 @@ describe('ImageService', () => { providers: [ { provide: QerApiService, - useValue: {} + useValue: {}, }, { provide: AppConfigService, useClass: class { public Config = { - BaseUrl: '' + BaseUrl: '', }; - } - } - ] + }, + }, + ], }); service = TestBed.inject(ImageService); }); diff --git a/imxweb/projects/qer/src/lib/itshop/image.service.ts b/imxweb/projects/qer/src/lib/itshop/image.service.ts index f141d7ff0..04fa746b5 100644 --- a/imxweb/projects/qer/src/lib/itshop/image.service.ts +++ b/imxweb/projects/qer/src/lib/itshop/image.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,13 +26,19 @@ import { Injectable } from '@angular/core'; -import { ITShopConfig, PortalItshopPattern, PortalItshopPatternItem, PortalServicecategories, PortalShopServiceitems, V2ApiClientMethodFactory } from 'imx-api-qer'; -import { MethodDefinition, MethodDescriptor } from 'imx-qbm-dbts'; +import { + ITShopConfig, + PortalItshopPatternItem, + PortalServicecategories, + PortalShopServiceitems, + V2ApiClientMethodFactory, +} from '@imx-modules/imx-api-qer'; +import { MethodDefinition, MethodDescriptor, TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { AppConfigService, CdrFactoryService } from 'qbm'; import { QerApiService } from '../qer-api-client.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ImageService { private readonly methodFactory = new V2ApiClientMethodFactory(); @@ -40,18 +46,22 @@ export class ImageService { constructor( private readonly api: QerApiService, private readonly config: AppConfigService, - ) { } + ) {} /** Returns the URL to the image for the specified service item. */ - public getPath(item: PortalShopServiceitems | PortalServicecategories | PortalItshopPatternItem): string { - const imageValue = item.ImageRef?.value ?? CdrFactoryService.tryGetColumn(item.GetEntity(), 'ImageRef')?.GetValue(); + public getPath(item: TypedEntity): string { + const imageValue = + (item as PortalShopServiceitems | PortalServicecategories | PortalItshopPatternItem).ImageRef?.value ?? + CdrFactoryService.tryGetColumn(item.GetEntity(), 'ImageRef')?.GetValue(); const tokens = imageValue?.split('/'); if (tokens?.length > 1) { - let path: MethodDescriptor; + let path: MethodDescriptor; if (tokens[0] === 'category') { path = this.methodFactory.portal_shop_categoryimage_get(tokens[1]); } else if (tokens[0] === 'product') { path = this.methodFactory.portal_shop_serviceitemimage_get(tokens[1]); + } else { + return ''; } if (path) { @@ -59,13 +69,13 @@ export class ImageService { return this.config.BaseUrl + new MethodDefinition(path).path; } } - return null; + return ''; } /** * @deprecated Use getPath() */ - public async get(serviceItem: PortalShopServiceitems, config: ITShopConfig): Promise { + public async get(serviceItem: PortalShopServiceitems, config: ITShopConfig): Promise { const tokens = serviceItem.ImageRef.value?.split('/'); if (tokens?.length > 1) { if (tokens[0] === 'category') { @@ -79,6 +89,6 @@ export class ImageService { } } - return null; + return undefined; } } diff --git a/imxweb/projects/qer/src/lib/itshop/itshop-request.service.ts b/imxweb/projects/qer/src/lib/itshop/itshop-request.service.ts index 0a2e9b263..76ad6e29d 100644 --- a/imxweb/projects/qer/src/lib/itshop/itshop-request.service.ts +++ b/imxweb/projects/qer/src/lib/itshop/itshop-request.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,9 +26,9 @@ import { Injectable } from '@angular/core'; -import { PwoData } from 'imx-api-qer'; -import { IEntity, IEntityColumn, ParameterData, WriteExtTypedEntity } from 'imx-qbm-dbts'; -import { AuthenticationService } from 'qbm'; +import { PwoData } from '@imx-modules/imx-api-qer'; +import { IEntity, IEntityColumn, ParameterData, WriteExtTypedEntity } from '@imx-modules/imx-qbm-dbts'; +import { AuthenticationService, ISessionState } from 'qbm'; import { Approval } from '../itshopapprove/approval'; import { ExtendedCollectionData } from '../parameter-data/extended-collection-data.interface'; import { ParameterDataService } from '../parameter-data/parameter-data.service'; @@ -42,17 +42,17 @@ export class ItshopRequestService { constructor( private readonly parameterDataService: ParameterDataService, private readonly itshopService: ItshopService, - authentication: AuthenticationService + authentication: AuthenticationService, ) { - authentication.onSessionResponse.subscribe((session) => (this.currentUser = session.UserUid)); + authentication.onSessionResponse.subscribe((session: ISessionState) => (this.currentUser = session.UserUid || '')); } - public createParameterColumns(entity: IEntity, parameters: ParameterData[]): IEntityColumn[] { + public createParameterColumns(entity: IEntity, parameters: ParameterData[]): (IEntityColumn | undefined)[] { return this.parameterDataService.createParameterColumns( entity, parameters, (loadParameters) => this.itshopService.getRequestParameterCandidates(loadParameters), - (treeParameters) => this.itshopService.getRequestParameterFilterTree(treeParameters) + (treeParameters) => this.itshopService.getRequestParameterFilterTree(treeParameters), ); } @@ -60,7 +60,7 @@ export class ItshopRequestService { public createRequestApprovalItem( typedEntity: WriteExtTypedEntity, - extendedCollectionData: ExtendedCollectionData + extendedCollectionData: ExtendedCollectionData | undefined, ): Approval { const entity = typedEntity.GetEntity(); @@ -68,14 +68,20 @@ export class ItshopRequestService { entity, extendedCollectionData, (loadParameters) => this.itshopService.getRequestParameterCandidates(loadParameters), - (treeParameters) => this.itshopService.getRequestParameterFilterTree(treeParameters) + (treeParameters) => this.itshopService.getRequestParameterFilterTree(treeParameters), ); return new Approval({ entity, uidCurrentUser: this.currentUser, isChiefApproval: this.itshopService.isChiefApproval, - pwoData: extendedDataWrapper.data, + pwoData: { + ...extendedDataWrapper.data, + WorkflowSteps: extendedCollectionData?.WorkflowSteps, + CanRecallDecision: extendedDataWrapper.data?.CanRecallDecision || false, + CanRevokeDelegation: extendedDataWrapper.data?.CanRevokeDelegation || false, + CanAskForHelp: extendedDataWrapper.data?.CanAskForHelp || false, + }, parameterColumns: extendedDataWrapper.parameterWrapper.columns, commit: async () => { typedEntity.extendedData = extendedDataWrapper.parameterWrapper.getEntityWriteDataColumns(); diff --git a/imxweb/projects/qer/src/lib/itshop/itshop.module.ts b/imxweb/projects/qer/src/lib/itshop/itshop.module.ts index 6d6243e93..11643a130 100644 --- a/imxweb/projects/qer/src/lib/itshop/itshop.module.ts +++ b/imxweb/projects/qer/src/lib/itshop/itshop.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -52,14 +52,9 @@ import { MatExpansionModule } from '@angular/material/expansion'; NonRequestableItemsComponent, PeerGroupComponent, ServiceItemDetailComponent, - ProductEntitlementsComponent - ], - exports: [ - RequestInfoComponent, - PeerGroupComponent, - ServiceItemDetailComponent, - ProductEntitlementsComponent + ProductEntitlementsComponent, ], + exports: [RequestInfoComponent, PeerGroupComponent, ServiceItemDetailComponent, ProductEntitlementsComponent], imports: [ BusyIndicatorModule, CdrModule, @@ -77,11 +72,8 @@ import { MatExpansionModule } from '@angular/material/expansion'; ExtModule, DateModule, DataTableModule, - DataSourceToolbarModule - ], - providers: [ - ItshopService, - ShelfService + DataSourceToolbarModule, ], + providers: [ItshopService, ShelfService], }) -export class ItshopModule { } +export class ItshopModule {} diff --git a/imxweb/projects/qer/src/lib/itshop/itshop.service.ts b/imxweb/projects/qer/src/lib/itshop/itshop.service.ts index e4e79941e..073af887f 100644 --- a/imxweb/projects/qer/src/lib/itshop/itshop.service.ts +++ b/imxweb/projects/qer/src/lib/itshop/itshop.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,16 +26,15 @@ import { Injectable } from '@angular/core'; -import { ClassloggerService } from 'qbm'; import { PortalItshopApproveHistory, PortalItshopCart, - PortalItshopPersondecision, PortalItshopPeergroupMemberships, + PortalItshopPersondecision, + PortalShopServiceitems, PwoData, ServiceItemsExtendedData, - PortalShopServiceitems, -} from 'imx-api-qer'; +} from '@imx-modules/imx-api-qer'; import { ApiRequestOptions, CollectionLoadParameters, @@ -47,10 +46,11 @@ import { FilterType, TypedEntityBuilder, TypedEntityCollectionData, -} from 'imx-qbm-dbts'; +} from '@imx-modules/imx-qbm-dbts'; +import { ClassloggerService } from 'qbm'; +import { ServiceItemParameters } from '../new-request/new-request-product/service-item-parameters'; import { ParameterDataLoadParameters } from '../parameter-data/parameter-data-load-parameters.interface'; import { QerApiService } from '../qer-api-client.service'; -import { ServiceItemParameters } from '../new-request/new-request-product/service-item-parameters'; @Injectable({ providedIn: 'root', @@ -64,7 +64,10 @@ export class ItshopService { private readonly historyBuilder = new TypedEntityBuilder(PortalItshopApproveHistory); - constructor(private readonly qerClient: QerApiService, private readonly logger: ClassloggerService) {} + constructor( + private readonly qerClient: QerApiService, + private readonly logger: ClassloggerService, + ) {} public async get( parameters: CollectionLoadParameters & { @@ -74,12 +77,12 @@ export class ItshopService { UID_AccProductParent?: string; UID_PersonReference?: string; UID_PersonPeerGroup?: string; - } + }, ): Promise> { return this.qerClient.typedClient.PortalShopServiceitems.Get(parameters); } - public async getServiceItem(serviceItemUid: string, isSkippable?: boolean): Promise { + public async getServiceItem(serviceItemUid: string, isSkippable?: boolean): Promise { const serviceItemCollection = await this.get({ IncludeChildCategories: false, filter: [ @@ -93,7 +96,7 @@ export class ItshopService { }); if (serviceItemCollection == null || serviceItemCollection.Data == null || serviceItemCollection.Data.length === 0) { if (isSkippable) { - return null; + return undefined; } throw new Error('getServiceItem - service item not found'); } @@ -102,8 +105,8 @@ export class ItshopService { } public async getPeerGroupMemberships( - parameters: (CollectionLoadParameters | ServiceItemParameters), - requestOpts?: ApiRequestOptions + parameters: CollectionLoadParameters | ServiceItemParameters, + requestOpts?: ApiRequestOptions, ): Promise> { return this.qerClient.typedClient.PortalItshopPeergroupMemberships.Get(parameters, requestOpts); } @@ -112,38 +115,38 @@ export class ItshopService { if (pwoData?.WorkflowHistory) { return this.historyBuilder.buildReadWriteEntities( pwoData.WorkflowHistory, - this.qerClient.typedClient.PortalItshopApproveHistory.GetSchema() + this.qerClient.typedClient.PortalItshopApproveHistory.GetSchema(), ).Data; } - return undefined; + return []; } public async getRequestParameterCandidates(parameters: ParameterDataLoadParameters): Promise { return this.qerClient.client.portal_itshop_requests_parameter_candidates_post( parameters.columnName, - parameters.fkTableName, - parameters.diffData, + parameters.fkTableName || '', + parameters.diffData || {}, { OrderBy: parameters.OrderBy, StartIndex: parameters.StartIndex, PageSize: parameters.PageSize, filter: parameters.filter, search: parameters.search, - ParentKey: parameters.ParentKey - } + ParentKey: parameters.ParentKey, + }, ); } public async getRequestParameterFilterTree(parameters: ParameterDataLoadParameters): Promise { return this.qerClient.client.portal_itshop_requests_parameter_candidates_filtertree_post( parameters.columnName, - parameters.fkTableName, - parameters.diffData, + parameters.fkTableName || '', + parameters.diffData || {}, { filter: parameters.filter, - parentkey: parameters.ParentKey - } + parentkey: parameters.ParentKey, + }, ); } diff --git a/imxweb/projects/qer/src/lib/itshop/non-requestable-items/non-requestable-items.component.html b/imxweb/projects/qer/src/lib/itshop/non-requestable-items/non-requestable-items.component.html index 99c18d6eb..d0c2446ba 100644 --- a/imxweb/projects/qer/src/lib/itshop/non-requestable-items/non-requestable-items.component.html +++ b/imxweb/projects/qer/src/lib/itshop/non-requestable-items/non-requestable-items.component.html @@ -1,10 +1,9 @@ -

    {{'#LDS#Heading Products Cannot Be Requested' | translate}}

    +

    {{ '#LDS#Heading Products Cannot Be Requested' | translate }}

    - {{'#LDS#The following products could not be added to the shopping cart for the following recipients.' | translate}} + {{ '#LDS#The following products could not be added to the shopping cart for the following recipients.' | translate }}

    - +
    - +
    {{ column.title | translate }} @@ -14,9 +13,9 @@

    {{'#LDS#Heading Products Cannot Be Requested' | translate}}

    -
    - -
    \ No newline at end of file +
    + +
    diff --git a/imxweb/projects/qer/src/lib/itshop/non-requestable-items/non-requestable-items.component.scss b/imxweb/projects/qer/src/lib/itshop/non-requestable-items/non-requestable-items.component.scss index e110d11b2..248a268c5 100644 --- a/imxweb/projects/qer/src/lib/itshop/non-requestable-items/non-requestable-items.component.scss +++ b/imxweb/projects/qer/src/lib/itshop/non-requestable-items/non-requestable-items.component.scss @@ -1 +1 @@ -// add styles if necessary \ No newline at end of file +// add styles if necessary diff --git a/imxweb/projects/qer/src/lib/itshop/non-requestable-items/non-requestable-items.component.spec.ts b/imxweb/projects/qer/src/lib/itshop/non-requestable-items/non-requestable-items.component.spec.ts index fcacabb0f..e796fbf02 100644 --- a/imxweb/projects/qer/src/lib/itshop/non-requestable-items/non-requestable-items.component.spec.ts +++ b/imxweb/projects/qer/src/lib/itshop/non-requestable-items/non-requestable-items.component.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -35,33 +35,28 @@ describe('NonRequestableItemsComponent', () => { let fixture: ComponentFixture; const mockMatDialogRef = { - close: jasmine.createSpy('close') + close: jasmine.createSpy('close'), }; const dialogData = { - nonRequestableProductsForPersons: [] - } + nonRequestableProductsForPersons: [], + }; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - declarations: [ - NonRequestableItemsComponent - ], - imports: [ - MatTableModule, - ], + declarations: [NonRequestableItemsComponent], + imports: [MatTableModule], providers: [ { provide: MatDialogRef, - useValue: mockMatDialogRef + useValue: mockMatDialogRef, }, { provide: MAT_DIALOG_DATA, - useValue: dialogData - } - ] - }) - .compileComponents(); + useValue: dialogData, + }, + ], + }).compileComponents(); })); beforeEach(() => { diff --git a/imxweb/projects/qer/src/lib/itshop/non-requestable-items/non-requestable-items.component.ts b/imxweb/projects/qer/src/lib/itshop/non-requestable-items/non-requestable-items.component.ts index b20d2249e..7e603ccc5 100644 --- a/imxweb/projects/qer/src/lib/itshop/non-requestable-items/non-requestable-items.component.ts +++ b/imxweb/projects/qer/src/lib/itshop/non-requestable-items/non-requestable-items.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,32 +26,27 @@ import { Component, Inject } from '@angular/core'; import { MAT_DIALOG_DATA } from '@angular/material/dialog'; -import { RequestableProductForPerson } from 'imx-api-qer'; +import { RequestableProductForPerson } from '@imx-modules/imx-api-qer'; @Component({ selector: 'imx-non-requestable-items', templateUrl: './non-requestable-items.component.html', - styleUrls: ['./non-requestable-items.component.scss'] + styleUrls: ['./non-requestable-items.component.scss'], }) export class NonRequestableItemsComponent { - public get columnNames(): string[] { - return this.displayedColumns.map(c => c.name); + return this.displayedColumns.map((c) => c.name); } public readonly displayedColumns = [ { name: 'Display', title: '#LDS#Product' }, - { name: 'DisplayRecipient', title: '#LDS#Recipient' } + { name: 'DisplayRecipient', title: '#LDS#Recipient' }, ]; constructor( - @Inject(MAT_DIALOG_DATA) public readonly data: { - nonRequestableProductsForPersons: RequestableProductForPerson[] - } - ) { } - - - - - + @Inject(MAT_DIALOG_DATA) + public readonly data: { + nonRequestableProductsForPersons: RequestableProductForPerson[]; + }, + ) {} } diff --git a/imxweb/projects/qer/src/lib/itshop/peer-group/peer-group.component.html b/imxweb/projects/qer/src/lib/itshop/peer-group/peer-group.component.html index ee2c776fc..a9ecce5b0 100644 --- a/imxweb/projects/qer/src/lib/itshop/peer-group/peer-group.component.html +++ b/imxweb/projects/qer/src/lib/itshop/peer-group/peer-group.component.html @@ -1,4 +1,7 @@
    - {{ '#LDS#Assigned to {0} of {1} other identities of the peer group ({2}%)' - | translate | ldsReplace:item.CountInPeerGroup.value:peerGroupSize:getPeerGroupPercentage() }} + {{ + '#LDS#Assigned to {0} of {1} other identities of the peer group ({2}%)' + | translate + | ldsReplace: item.CountInPeerGroup.value : peerGroupSize : getPeerGroupPercentage() + }}
    diff --git a/imxweb/projects/qer/src/lib/itshop/peer-group/peer-group.component.ts b/imxweb/projects/qer/src/lib/itshop/peer-group/peer-group.component.ts index e66d07e6d..8a593e29c 100644 --- a/imxweb/projects/qer/src/lib/itshop/peer-group/peer-group.component.ts +++ b/imxweb/projects/qer/src/lib/itshop/peer-group/peer-group.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,15 +26,15 @@ import { Component, Input } from '@angular/core'; -import { IReadValue } from 'imx-qbm-dbts'; +import { IReadValue } from '@imx-modules/imx-qbm-dbts'; @Component({ selector: 'imx-peer-group', templateUrl: './peer-group.component.html', - styleUrls: ['./peer-group.component.scss'] + styleUrls: ['./peer-group.component.scss'], }) export class PeerGroupComponent { - @Input() public item: { CountInPeerGroup: IReadValue; }; + @Input() public item: { CountInPeerGroup: IReadValue }; @Input() public peerGroupSize: number; @Input() public width = '100%'; diff --git a/imxweb/projects/qer/src/lib/itshop/request-info/approver-container.spec.ts b/imxweb/projects/qer/src/lib/itshop/request-info/approver-container.spec.ts index 78ad9b628..c7618ec1f 100644 --- a/imxweb/projects/qer/src/lib/itshop/request-info/approver-container.spec.ts +++ b/imxweb/projects/qer/src/lib/itshop/request-info/approver-container.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,8 +24,8 @@ * */ -import { ITShopConfig, PwoData } from 'imx-api-qer'; -import { EntityData } from 'imx-qbm-dbts'; +import { ITShopConfig, PwoData } from '@imx-modules/imx-api-qer'; +import { EntityData } from '@imx-modules/imx-qbm-dbts'; import { ApproverContainer } from './approver-container'; describe('ApproverContainer', () => { @@ -34,15 +34,15 @@ describe('ApproverContainer', () => { decisionLevel: level, qerWorkingMethod: uidWorkingMethode, pwoData: buildPwoData(steps, data), - approvers + approvers, }; } function buildPwoData(steps?: EntityData[], data?: EntityData[]): PwoData { return { WorkflowSteps: steps == null ? {} : { TotalCount: steps.length, IsLimitReached: true, Entities: steps }, - WorkflowData: data == null ? {} : { TotalCount: data.length, IsLimitReached: true, Entities: data } - } as PwoData + WorkflowData: data == null ? {} : { TotalCount: data.length, IsLimitReached: true, Entities: data }, + } as PwoData; } function buildStep(uidWorkingMethode?: string, level?: number, positive?: number, uidstep?: string): EntityData { @@ -52,8 +52,8 @@ describe('ApproverContainer', () => { LevelNumber: { Value: level }, PositiveSteps: { Value: positive }, }, - Keys: uidstep == null ? [] : [uidstep] - } + Keys: uidstep == null ? [] : [uidstep], + }; } function buildData(uidWorkingStep?: string, uidPersonHead?: string): EntityData { @@ -72,11 +72,11 @@ describe('ApproverContainer', () => { it('should create', () => { const test = new ApproverContainer( {} as { - decisionLevel: number, - qerWorkingMethod: string, - pwoData: PwoData, - approvers: string[] - } + decisionLevel: number; + qerWorkingMethod: string; + pwoData: PwoData; + approvers: string[]; + }, ); expect(test).toBeTruthy(); }); @@ -84,66 +84,107 @@ describe('ApproverContainer', () => { for (const testcase of [ { descriprion: 'with null values', pwo: {}, personNow: [], personsFuture: [] }, { - descriprion: 'with empty values', pwo: buildPwo('uidQerWorkingMethode', 0, [], [], []), personNow: [], personsFuture: [] + descriprion: 'with empty values', + pwo: buildPwo('uidQerWorkingMethode', 0, [], [], []), + personNow: [], + personsFuture: [], }, { - descriprion: 'with single step', pwo: buildPwo('uidQerWorkingMethode', 0, - [buildStep('uidQerWorkingMethode', 0, 0, 'uidStep')], [buildData('uidStep', 'person')], ['person']), - personNow: ['person'], personsFuture: [] - }, { - descriprion: 'with multiple steps (only 1 match)', pwo: buildPwo('uidQerWorkingMethode', 0, + descriprion: 'with single step', + pwo: buildPwo( + 'uidQerWorkingMethode', + 0, + [buildStep('uidQerWorkingMethode', 0, 0, 'uidStep')], + [buildData('uidStep', 'person')], + ['person'], + ), + personNow: ['person'], + personsFuture: [], + }, + { + descriprion: 'with multiple steps (only 1 match)', + pwo: buildPwo( + 'uidQerWorkingMethode', + 0, [buildStep('uidQerWorkingMethode', 0, 0, 'uidStep'), buildStep('uidQerWorkingMethode', 1, 2, 'uidStep2')], - [buildData('uidStep', 'person')], ['person']), personNow: ['person'], personsFuture: [] - }, { - descriprion: 'with multiple steps (but only 1 approver)', pwo: buildPwo('uidQerWorkingMethode', 0, + [buildData('uidStep', 'person')], + ['person'], + ), + personNow: ['person'], + personsFuture: [], + }, + { + descriprion: 'with multiple steps (but only 1 approver)', + pwo: buildPwo( + 'uidQerWorkingMethode', + 0, [buildStep('uidQerWorkingMethode', 0, 0, 'uidStep'), buildStep('uidQerWorkingMethode', 1, 1, 'uidStep2')], - [buildData('uidStep', 'person'), buildData('uidStep2', 'person2')], ['person']), - personNow: ['person'], personsFuture: [] - }, { - descriprion: 'with multiple steps (future and now)', pwo: buildPwo('uidQerWorkingMethode', 0, + [buildData('uidStep', 'person'), buildData('uidStep2', 'person2')], + ['person'], + ), + personNow: ['person'], + personsFuture: [], + }, + { + descriprion: 'with multiple steps (future and now)', + pwo: buildPwo( + 'uidQerWorkingMethode', + 0, [buildStep('uidQerWorkingMethode', 0, 1, 'uidStep'), buildStep('uidQerWorkingMethode', 1, 1, 'uidStep2')], - [buildData('uidStep', 'person'), buildData('uidStep2', 'person2')], ['person', 'person2']), - personNow: ['person'], personsFuture: ['person2'] - }, { - descriprion: 'with multiple steps (multiple future and now)', pwo: buildPwo('uidQerWorkingMethode', 0, - [buildStep('uidQerWorkingMethode', 0, 1, 'uidStep'), - buildStep('uidQerWorkingMethode', 1, 1, 'uidStep2'), - buildStep('uidQerWorkingMethode', 2, 1, 'uidStep3')], - [buildData('uidStep', 'person'), - buildData('uidStep', 'person2'), - buildData('uidStep2', 'person2'), - buildData('uidStep3', 'person3')], - ['person', 'person2', 'person3']), - personNow: ['person', 'person2'], personsFuture: ['person2', 'person3'] - } - + [buildData('uidStep', 'person'), buildData('uidStep2', 'person2')], + ['person', 'person2'], + ), + personNow: ['person'], + personsFuture: ['person2'], + }, + { + descriprion: 'with multiple steps (multiple future and now)', + pwo: buildPwo( + 'uidQerWorkingMethode', + 0, + [ + buildStep('uidQerWorkingMethode', 0, 1, 'uidStep'), + buildStep('uidQerWorkingMethode', 1, 1, 'uidStep2'), + buildStep('uidQerWorkingMethode', 2, 1, 'uidStep3'), + ], + [ + buildData('uidStep', 'person'), + buildData('uidStep', 'person2'), + buildData('uidStep2', 'person2'), + buildData('uidStep3', 'person3'), + ], + ['person', 'person2', 'person3'], + ), + personNow: ['person', 'person2'], + personsFuture: ['person2', 'person3'], + }, ]) { it(`can initialize ${testcase.descriprion}`, () => { const container = new ApproverContainer( testcase.pwo as { - decisionLevel: number, - qerWorkingMethod: string, - pwoData: PwoData, - approvers: string[] - } + decisionLevel: number; + qerWorkingMethod: string; + pwoData: PwoData; + approvers: string[]; + }, ); expect(container.approverNow.length).toEqual(testcase.personNow.length); for (const approver of testcase.personNow) { - const result = container.approverNow.map(app => app.Columns.UID_PersonHead.Value); + const result = container.approverNow.map((app) => app.Columns.UID_PersonHead.Value); expect(result.includes(approver)).toBeTruthy(); } expect(container.approverFuture.length).toEqual(testcase.personsFuture.length); for (const approver of testcase.personsFuture) { - const result = container.approverFuture.map(app => app.Columns.UID_PersonHead.Value); + const result = container.approverFuture.map((app) => app.Columns.UID_PersonHead.Value); expect(result.includes(approver)).toBeTruthy(); } - }) + }); } it('can initialize with multiple approvers (now)', () => { - const stepUid = 'uidStep0' + const stepUid = 'uidStep0'; const personsSteps = { person1: stepUid, @@ -158,26 +199,24 @@ describe('ApproverContainer', () => { const positiveSteps = 1; - const steps = [ - buildStep(uidQerWorkingMethod, level, positiveSteps, stepUid) - ]; + const steps = [buildStep(uidQerWorkingMethod, level, positiveSteps, stepUid)]; const data = []; - persons.forEach(person => data.push(buildData(personsSteps[person], person))) + persons.forEach((person) => data.push(buildData(personsSteps[person], person))); const container = new ApproverContainer( { decisionLevel: level, qerWorkingMethod: uidQerWorkingMethod, pwoData: buildPwoData(steps, data), - approvers: persons + approvers: persons, }, - { VI_ITShop_CurrentApproversCanBeSeen: true } as ITShopConfig + { VI_ITShop_CurrentApproversCanBeSeen: true } as ITShopConfig, ); expect(container.approverNow.length).toEqual(persons.length); persons.forEach((approver, index) => expect(container.approverNow[index].Columns.UID_PersonHead.DisplayValue).toEqual(approver)); - }) + }); it('can initialize with multiple future steps', () => { const personsFutureSteps = { @@ -196,28 +235,30 @@ describe('ApproverContainer', () => { const steps = [ buildStep(uidQerWorkingMethod, level, positiveSteps, 'uidStep0'), buildStep(uidQerWorkingMethod, level + positiveSteps, positiveSteps, personsFutureSteps.person1), - buildStep(uidQerWorkingMethod, level + positiveSteps * 2, positiveSteps, personsFutureSteps.person2) + buildStep(uidQerWorkingMethod, level + positiveSteps * 2, positiveSteps, personsFutureSteps.person2), ]; const data = [buildData('uidStep', 'person')]; - personsFuture.forEach(person => data.push(buildData(personsFutureSteps[person], person))) + personsFuture.forEach((person) => data.push(buildData(personsFutureSteps[person], person))); const container = new ApproverContainer( { decisionLevel: level, qerWorkingMethod: uidQerWorkingMethod, pwoData: buildPwoData(steps, data), - approvers: undefined + approvers: undefined, }, - { VI_ITShop_NextApproverCanBeSeen: true } as ITShopConfig + { VI_ITShop_NextApproverCanBeSeen: true } as ITShopConfig, ); expect(container.approverFuture.length).toEqual(personsFuture.length); - personsFuture.forEach((approver, index) => expect(container.approverFuture[index].Columns.UID_PersonHead.DisplayValue).toEqual(approver)); - }) + personsFuture.forEach((approver, index) => + expect(container.approverFuture[index].Columns.UID_PersonHead.DisplayValue).toEqual(approver), + ); + }); it('can initialize with auto steps', () => { - const stepUids = ['0', '1'] + const stepUids = ['0', '1']; const uidQerWorkingMethod = 'uidQerWorkingMethod'; @@ -225,22 +266,18 @@ describe('ApproverContainer', () => { const positiveSteps = 1; - const steps = stepUids.map((stepUid, index) => - buildStep(uidQerWorkingMethod, level + index * positiveSteps, positiveSteps, stepUid) - ); + const steps = stepUids.map((stepUid, index) => buildStep(uidQerWorkingMethod, level + index * positiveSteps, positiveSteps, stepUid)); - const data = stepUids.map(stepUid => buildData(stepUid)); + const data = stepUids.map((stepUid) => buildData(stepUid)); - const container = new ApproverContainer( - { - decisionLevel: level, - qerWorkingMethod: uidQerWorkingMethod, - pwoData: buildPwoData(steps, data), - approvers: ['some approver'] - } - ); + const container = new ApproverContainer({ + decisionLevel: level, + qerWorkingMethod: uidQerWorkingMethod, + pwoData: buildPwoData(steps, data), + approvers: ['some approver'], + }); expect(container.approverNow.length).toEqual(0); expect(container.approverFuture.length).toEqual(0); - }) + }); }); diff --git a/imxweb/projects/qer/src/lib/itshop/request-info/approver-container.ts b/imxweb/projects/qer/src/lib/itshop/request-info/approver-container.ts index f36954ceb..b88437e81 100644 --- a/imxweb/projects/qer/src/lib/itshop/request-info/approver-container.ts +++ b/imxweb/projects/qer/src/lib/itshop/request-info/approver-container.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { EntityCollectionData, EntityData, MultiValueProperty } from 'imx-qbm-dbts'; +import { EntityCollectionData, EntityData, MultiValueProperty } from '@imx-modules/imx-qbm-dbts'; import { ClassloggerService } from 'qbm'; import { OrderedWorkingStep } from './ordered-working-step.interface'; @@ -37,6 +37,8 @@ export class ApproverContainer { */ public approverNow: EntityData[] = []; + public canSeeSteps: boolean; + /** * List of next approvers */ @@ -58,31 +60,34 @@ export class ApproverContainer { VI_ITShop_CurrentApproversCanBeSeen?: boolean; VI_ITShop_NextApproverCanBeSeen?: boolean; } = {}, - private logger?: ClassloggerService + private logger?: ClassloggerService, ) { - this.isInWorkflow = this.request.isInWorkflow; + this.isInWorkflow = this.request.isInWorkflow ?? false; if (this.config?.VI_ITShop_NextApproverCanBeSeen || this.config?.VI_ITShop_CurrentApproversCanBeSeen) { - this.initApproverContainer(this.config.VI_ITShop_CurrentApproversCanBeSeen, this.config.VI_ITShop_NextApproverCanBeSeen); + this.initApproverContainer( + this.config.VI_ITShop_CurrentApproversCanBeSeen ?? false, + this.config.VI_ITShop_NextApproverCanBeSeen ?? false, + ); } else { this.initApproverContainer(true, true); } } public getApproverSortedByStep(future = true): { display: string; data: EntityData[] }[] { - const ret = []; + const ret: { display: string; data: EntityData[] }[] = []; const steps = [ ...new Set( - this.request.pwoData.WorkflowSteps?.Entities.sort((a, b) => a.Columns.LevelNumber.Value - b.Columns.LevelNumber.Value).map( + this.request.pwoData.WorkflowSteps?.Entities?.sort((a, b) => a.Columns?.LevelNumber.Value - b.Columns?.LevelNumber.Value).map( (elem) => - elem.Columns.UID_QERWorkingStep.Value + + elem.Columns?.UID_QERWorkingStep.Value + MultiValueProperty.DefaultSeparator + - (elem.Columns.Ident_PWODecisionStep.DisplayValue ?? elem.Columns.Ident_PWODecisionStep.Value) - ) + (elem.Columns?.Ident_PWODecisionStep.DisplayValue ?? elem.Columns?.Ident_PWODecisionStep.Value), + ), ), ]; - this.logger.trace(this, 'following approval steps are available', steps); + this.logger?.trace(this, 'following approval steps are available', steps); steps.forEach((element) => { const uid = element.split(MultiValueProperty.DefaultSeparator)[0]; @@ -90,18 +95,18 @@ export class ApproverContainer { const approver = future ? this.approverFuture?.filter( (workflowData, index, newArray) => - workflowData.Columns.UID_QERWorkingStep.Value === uid && - newArray.findIndex((checkData) => checkData.Columns.UID_PersonHead.Value === workflowData.Columns.UID_PersonHead.Value) === - index + workflowData.Columns?.UID_QERWorkingStep.Value === uid && + newArray.findIndex((checkData) => checkData.Columns?.UID_PersonHead.Value === workflowData.Columns?.UID_PersonHead.Value) === + index, ) : this.approverNow?.filter( (workflowData, index, newArray) => - workflowData.Columns.UID_QERWorkingStep.Value === uid && - newArray.findIndex((checkData) => checkData.Columns.UID_PersonHead.Value === workflowData.Columns.UID_PersonHead.Value) === - index + workflowData.Columns?.UID_QERWorkingStep.Value === uid && + newArray.findIndex((checkData) => checkData.Columns?.UID_PersonHead.Value === workflowData.Columns?.UID_PersonHead.Value) === + index, ); - this.logger.trace(this, `analysing ${future ? 'future' : 'current'} step ${uid} (${display}):`, approver); + this.logger?.trace(this, `analysing ${future ? 'future' : 'current'} step ${uid} (${display}):`, approver); if (approver?.length > 0) { ret.push({ display: display, data: approver }); @@ -120,20 +125,33 @@ export class ApproverContainer { const orderedWorkingSteps = this.buildOrderedWorkingSteps(); this.logger?.trace(this, 'working steps with order', orderedWorkingSteps); + this.canSeeSteps = !this.request.pwoData?.WorkflowSteps?.Entities ? false : this.request.pwoData?.WorkflowSteps?.Entities.length > 0; + if (canSeeCurrent) { const currentSteps = orderedWorkingSteps.filter((step) => step.order === 1); this.logger?.trace(this, 'current steps', currentSteps); this.approverNow = - this.request == null || this.request.pwoData == null || this.request.pwoData.WorkflowData == null || currentSteps.length === 0 + this.request == null || + this.request.pwoData == null || + this.request.pwoData.WorkflowData == null || + this.request.pwoData.WorkflowData.Entities == null || + currentSteps.length === 0 ? [] - : this.request.pwoData.WorkflowData.Entities.filter( + : this.request.pwoData.WorkflowData?.Entities?.filter( (data) => - !this.isDecided(data,this.request.pwoData.WorkflowData.Entities.filter((elem) => elem.Columns.Decision?.Value !== '')) && - data.Columns.UID_PersonHead.Value && - currentSteps.some((step) => data.Columns.UID_QERWorkingStep.Value === step.uidWorkingStep) && - this.request.approvers.includes(data.Columns.UID_PersonHead.Value) - ).sort((data1, data2) => data1.Columns.UID_PersonHead.DisplayValue.localeCompare(data2.Columns.UID_PersonHead.DisplayValue)); + !this.isDecided( + data, + !this.request.pwoData.WorkflowData?.Entities + ? [] + : this.request.pwoData.WorkflowData?.Entities?.filter((elem) => elem.Columns?.Decision?.Value !== ''), + ) && + data.Columns?.UID_PersonHead.Value && + currentSteps.some((step) => data.Columns?.UID_QERWorkingStep.Value === step.uidWorkingStep) && + this.request.approvers.includes(data.Columns?.UID_PersonHead.Value), + ).sort((data1, data2) => + (data1.Columns?.UID_PersonHead.DisplayValue || '').localeCompare(data2.Columns?.UID_PersonHead.DisplayValue || ''), + ); this.logger?.trace(this, 'personWantsOrg should be approved by', this.approverNow); } @@ -142,13 +160,21 @@ export class ApproverContainer { this.logger?.trace(this, 'future steps', futureSteps); this.approverFuture = - this.request == null || this.request.pwoData == null || this.request.pwoData.WorkflowData == null || futureSteps == null + this.request == null || + this.request.pwoData == null || + this.request.pwoData.WorkflowData == null || + this.request.pwoData.WorkflowData.Entities == null || + futureSteps == null ? [] - : this.request.pwoData.WorkflowData.Entities.filter( + : this.request.pwoData.WorkflowData?.Entities?.filter( (data) => - data.Columns.UID_PersonHead.Value && - futureSteps.map((step) => step.uidWorkingStep).includes(data.Columns.UID_QERWorkingStep.Value) - ).sort((data1, data2) => data1.Columns.UID_PersonHead.DisplayValue.localeCompare(data2.Columns.UID_PersonHead.DisplayValue)); + data.Columns?.UID_PersonHead.Value && + futureSteps.map((step) => step.uidWorkingStep).includes(data.Columns?.UID_QERWorkingStep.Value), + ).sort((data1, data2) => + (!data1.Columns?.UID_PersonHead?.DisplayValue ? '' : data1.Columns.UID_PersonHead.DisplayValue).localeCompare( + !data2.Columns?.UID_PersonHead?.DisplayValue ? '' : data2.Columns.UID_PersonHead.DisplayValue, + ), + ); this.logger?.trace(this, 'personWantsOrg should be approved in the future by', this.approverFuture); } } @@ -156,11 +182,11 @@ export class ApproverContainer { /* * Checks, if a workflowData item is already decided (by any person in the same sub step) */ - private isDecided(data: EntityData, decidedEntries:EntityData[]): boolean { + private isDecided(data: EntityData, decidedEntries: EntityData[]): boolean { return decidedEntries.some( (elem) => - elem.Columns.LevelNumber.Value === data.Columns.LevelNumber.Value && - elem.Columns.SubLevelNumber.Value === data.Columns.SubLevelNumber.Value + elem.Columns?.LevelNumber.Value === data.Columns?.LevelNumber.Value && + elem.Columns?.SubLevelNumber.Value === data.Columns?.SubLevelNumber.Value, ); } @@ -173,13 +199,13 @@ export class ApproverContainer { const currentLevel = this.request.decisionLevel; const workingMethod = this.request.qerWorkingMethod; - const stepsForWorkingMethod = this.request.pwoData.WorkflowSteps.Entities.filter( - (elem) => elem.Columns.UID_QERWorkingMethod.Value === workingMethod - ); + const stepsForWorkingMethod = !this.request.pwoData.WorkflowSteps?.Entities + ? [] + : this.request.pwoData.WorkflowSteps?.Entities?.filter((elem) => elem.Columns?.UID_QERWorkingMethod.Value === workingMethod); this.logger?.trace(this, `calculate steps for method ${workingMethod}`, stepsForWorkingMethod); const startSteps = stepsForWorkingMethod.filter( - (n, i, arr) => arr.findIndex((elem) => elem.Keys[0] === n.Keys[0] && elem.Columns.LevelNumber.Value === currentLevel) === i + (n, i, arr) => arr.findIndex((elem) => elem.Keys?.[0] === n.Keys?.[0] && elem.Columns?.LevelNumber.Value === currentLevel) === i, ); this.logger?.trace(this, 'starting with step', startSteps); @@ -191,11 +217,11 @@ export class ApproverContainer { orderedSteps.push( ...startSteps.map((elem) => ({ - uidWorkingStep: elem.Keys[0], + uidWorkingStep: !elem.Keys ? '' : elem.Keys?.[0], decisionLevel: currentLevel, - positiveSteps: elem.Columns.PositiveSteps.Value, + positiveSteps: elem.Columns?.PositiveSteps.Value, order: 1, - })) + })), ); this.logger?.debug(this, 'first working steps are added'); @@ -210,7 +236,7 @@ export class ApproverContainer { (join) => join.orderedStep != null && join.orderedStep.positiveSteps !== 0 && - orderedSteps.findIndex((workunit) => workunit.uidWorkingStep === join.workingStep.Keys[0]) === -1 + orderedSteps.findIndex((workunit) => workunit.uidWorkingStep === join.workingStep.Keys?.[0]) === -1, ); this.logger?.debug(this, 'new steps has been filtered'); @@ -219,11 +245,11 @@ export class ApproverContainer { this.logger?.debug(this, 'at least one new step has been calculated'); orderedSteps = orderedSteps.concat( filteredJoin.map((join) => ({ - decisionLevel: join.workingStep.Columns.LevelNumber.Value, + decisionLevel: join.workingStep.Columns?.LevelNumber.Value, order: join.orderedStep.order + 1, - positiveSteps: join.workingStep.Columns.PositiveSteps.Value, - uidWorkingStep: join.workingStep.Keys[0], - })) + positiveSteps: join.workingStep.Columns?.PositiveSteps.Value, + uidWorkingStep: !join.workingStep.Keys ? '' : join.workingStep.Keys?.[0], + })), ); this.logger?.trace(this, 'new step list', orderedSteps); } @@ -233,14 +259,14 @@ export class ApproverContainer { private joinOrderedStepsWithOthers( workingSteps: EntityData[], - orderedSteps: OrderedWorkingStep[] + orderedSteps: OrderedWorkingStep[], ): { orderedStep: OrderedWorkingStep; workingStep: EntityData }[] { let join: { orderedStep: OrderedWorkingStep; workingStep: EntityData }[] = []; for (const workingStep of workingSteps) { this.logger?.trace(this, 'working step for join', workingStep); const filteredOrderedSteps = orderedSteps.filter( - (step) => workingStep.Columns.LevelNumber.Value === step.decisionLevel + step.positiveSteps + (step) => workingStep.Columns?.LevelNumber.Value === step.decisionLevel + step.positiveSteps, ); this.logger?.trace(this, 'filtered order steps', filteredOrderedSteps); @@ -249,7 +275,7 @@ export class ApproverContainer { filteredOrderedSteps.map((orderedStep) => ({ orderedStep, workingStep, - })) + })), ); } } diff --git a/imxweb/projects/qer/src/lib/itshop/request-info/decision-history.component.html b/imxweb/projects/qer/src/lib/itshop/request-info/decision-history.component.html index fc48b2b70..dc2d1fb40 100644 --- a/imxweb/projects/qer/src/lib/itshop/request-info/decision-history.component.html +++ b/imxweb/projects/qer/src/lib/itshop/request-info/decision-history.component.html @@ -1,6 +1,9 @@
      -
    • +
    • @@ -8,16 +11,24 @@ step.approveHistory.DecisionType.Column.GetDisplayValue() }} - - {{ step.approveHistory.XDateInserted.value | localizedDate }} + {{ step.approveHistory.XDateInserted.value ?? step.approveHistory.DateHead.value | localizedDate }} + - {{ step.approveHistory.Ident_PWODecisionStep?.Column?.GetDisplayValue() }} + + {{ step.approveHistory.Ident_PWODecisionStep?.Column?.GetDisplayValue() }} + - {{ - '#LDS#Approval decision by chief approval team' | translate - }} + {{ '#LDS#Approval decision by chief approval team' | translate }}
      @@ -35,15 +46,26 @@
    • -
    • +
    • - {{ '#LDS#The next approval step is currently being calculated. The request might also be waiting on a pending approval decision of another request.' | translate }} + {{ + '#LDS#The next approval step is currently being calculated. The request might also be waiting on a pending approval decision of another request.' + | translate + }}
    • @@ -60,7 +82,7 @@ {{ approvalStep.display }}
    • - {{ pnow.Columns.UID_PersonHead.DisplayValue }} + {{ pnow.Columns?.UID_PersonHead?.DisplayValue }}
    @@ -70,7 +92,11 @@
  • @@ -87,7 +113,7 @@ {{ approvalStep.display }}
  • - {{ pnext.Columns.UID_PersonHead.DisplayValue }} + {{ pnext.Columns?.UID_PersonHead?.DisplayValue }}
    diff --git a/imxweb/projects/qer/src/lib/itshop/request-info/decision-history.component.scss b/imxweb/projects/qer/src/lib/itshop/request-info/decision-history.component.scss index 64aef0737..933dbc567 100644 --- a/imxweb/projects/qer/src/lib/itshop/request-info/decision-history.component.scss +++ b/imxweb/projects/qer/src/lib/itshop/request-info/decision-history.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; :host { display: flex; @@ -7,7 +7,7 @@ } .chief-approval-badge { - margin-bottom: .5em; + margin-bottom: 0.5em; } .imx-reason { @@ -25,7 +25,7 @@ letter-spacing: 0.1em; } -.mat-card-title { +.mat-mdc-card-title { margin-bottom: 15px; :first-child { font-weight: bold; @@ -47,7 +47,7 @@ li.imx-event::before { background-color: $white; height: 24px; width: 28px; - content: ""; + content: ''; float: left; margin-top: -2px; } @@ -61,7 +61,7 @@ li.imx-event.imx-positive::before { font-family: Cadence-Icon; font-size: 24px; color: $aspen-green; - content: "\e049"; + content: '\e049'; } li.imx-event.imx-negative { @@ -73,7 +73,7 @@ li.imx-event.imx-negative::before { font-family: Cadence-Icon; font-size: 24px; color: $phoenix-red; - content: "\e05c"; + content: '\e05c'; } li.imx-event.imx-question { @@ -85,7 +85,7 @@ li.imx-event.imx-question::before { font-family: Cadence-Icon; font-size: 24px; color: $iris-blue; - content: "\e009"; + content: '\e009'; } li.imx-event.imx-info { @@ -97,7 +97,7 @@ li.imx-event.imx-info::before { font-family: Cadence-Icon; font-size: 24px; color: $iris-blue; - content: "\e010"; + content: '\e010'; } li.imx-event.imx-pending { @@ -109,19 +109,18 @@ li.imx-event.imx-pending::before { font-family: Cadence-Icon; font-size: 24px; color: $black-b; - content: "\e009"; + content: '\e009'; } -.mat-card-header { - margin-left: -16px; +.mat-mdc-card-header { margin-bottom: 10px; - .mat-card-title { + .mat-mdc-card-title { font-size: 1.05em; margin-bottom: 0; } - .mat-card-subtitle.imx-approveHistory-pwodecisionstep { + .mat-mdc-card-subtitle.imx-approveHistory-pwodecisionstep { margin: 5px 0; } } @@ -149,10 +148,10 @@ div[step] { } div[step-caption]{ margin-bottom: 10px; - span[caption]{ + span[caption] { font-weight: 600; font-size: 16px; - } + } } div[approver] { @@ -161,7 +160,12 @@ div[approver] { .eui-dark-theme { :host { - li.imx-event.imx-info::before , li.imx-event.imx-pending::before, li.imx-event.imx-question::before, li.imx-event.imx-positive::before , li.imx-event.imx-negative::before, li.imx-event.imx-info::before{ + li.imx-event.imx-info::before, + li.imx-event.imx-pending::before, + li.imx-event.imx-question::before, + li.imx-event.imx-positive::before, + li.imx-event.imx-negative::before, + li.imx-event.imx-info::before { background-color: $color-gray-70; } } @@ -169,7 +173,12 @@ div[approver] { .eui-contrast-theme { :host { - li.imx-event.imx-info::before , li.imx-event.imx-pending::before, li.imx-event.imx-question::before, li.imx-event.imx-positive::before , li.imx-event.imx-negative::before, li.imx-event.imx-info::before{ + li.imx-event.imx-info::before, + li.imx-event.imx-pending::before, + li.imx-event.imx-question::before, + li.imx-event.imx-positive::before, + li.imx-event.imx-negative::before, + li.imx-event.imx-info::before { background-color: $color-gray-90; } } diff --git a/imxweb/projects/qer/src/lib/itshop/request-info/decision-history.component.ts b/imxweb/projects/qer/src/lib/itshop/request-info/decision-history.component.ts index db45595da..528940f0c 100644 --- a/imxweb/projects/qer/src/lib/itshop/request-info/decision-history.component.ts +++ b/imxweb/projects/qer/src/lib/itshop/request-info/decision-history.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,8 +24,8 @@ * */ -import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; -import { EntityData } from 'imx-qbm-dbts'; +import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; +import { EntityData } from '@imx-modules/imx-qbm-dbts'; import { DecisionHistoryService } from '../decision-history.service'; import { ApproverContainer } from './approver-container'; @@ -34,20 +34,18 @@ import { WorkflowHistoryItemWrapper } from './workflow-history-item-wrapper'; @Component({ selector: 'imx-decision-history', templateUrl: './decision-history.component.html', - styleUrls: ['./decision-history.component.scss'] + styleUrls: ['./decision-history.component.scss'], }) export class DecisionHistoryComponent implements OnChanges { @Input() public approverContainer: ApproverContainer; @Input() public workflow: WorkflowHistoryItemWrapper[]; - public approverNow:{ display: string; data: EntityData[] }[] = []; - public approverFuture:{ display: string; data: EntityData[] }[] = []; - constructor( - public readonly decisionHistory: DecisionHistoryService - ) { } + public approverNow: { display: string; data: EntityData[] }[] = []; + public approverFuture: { display: string; data: EntityData[] }[] = []; + constructor(public readonly decisionHistory: DecisionHistoryService) {} public ngOnChanges(changes: SimpleChanges): void { - if(changes.approverContainer){ + if (changes.approverContainer) { this.approverFuture = this.approverContainer?.getApproverSortedByStep(); this.approverNow = this.approverContainer?.getApproverSortedByStep(false); } diff --git a/imxweb/projects/qer/src/lib/itshop/request-info/itshop-request-data.spec.ts b/imxweb/projects/qer/src/lib/itshop/request-info/itshop-request-data.spec.ts index ef6c5c472..0f9fb0624 100644 --- a/imxweb/projects/qer/src/lib/itshop/request-info/itshop-request-data.spec.ts +++ b/imxweb/projects/qer/src/lib/itshop/request-info/itshop-request-data.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,8 +24,8 @@ * */ -import { IClientProperty } from 'imx-qbm-dbts'; -import { ItshopRequestData } from "./itshop-request-data"; +import { IClientProperty } from '@imx-modules/imx-qbm-dbts'; +import { ItshopRequestData } from './itshop-request-data'; describe('ItshopRequestData', () => { function createParameterData(value, name) { @@ -34,29 +34,23 @@ describe('ItshopRequestData', () => { it('should init request parameters', () => { const propertyColumnName = 'DisplayOrg'; - const properties = { }; + const properties = {}; properties[propertyColumnName] = { ColumnName: propertyColumnName, - GetValue: () => 'some value' + GetValue: () => 'some value', }; const parameters = [ - [ - createParameterData('some value 1.0', '1'), - createParameterData('some value 2.0', '2'), - ], - [ - createParameterData('some value 1.1', '3'), - createParameterData('some value 2.1', '4'), - ] + [createParameterData('some value 1.0', '1'), createParameterData('some value 2.0', '2')], + [createParameterData('some value 1.1', '3'), createParameterData('some value 2.1', '4')], ]; const extendedCollectionData = { index: 0, Parameters: { parameterCategory1: [[parameters[0][0]], [parameters[1][0]]], - parameterCategory2: [[parameters[0][1]], [parameters[1][1]]] - } + parameterCategory2: [[parameters[0][1]], [parameters[1][1]]], + }, }; const request = new ItshopRequestData(extendedCollectionData); diff --git a/imxweb/projects/qer/src/lib/itshop/request-info/itshop-request-data.ts b/imxweb/projects/qer/src/lib/itshop/request-info/itshop-request-data.ts index b4204f83c..68204c6b4 100644 --- a/imxweb/projects/qer/src/lib/itshop/request-info/itshop-request-data.ts +++ b/imxweb/projects/qer/src/lib/itshop/request-info/itshop-request-data.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,8 +24,8 @@ * */ -import { PwoData, PwoExtendedData } from 'imx-api-qer'; -import { ParameterData } from 'imx-qbm-dbts'; +import { PwoData, PwoExtendedData } from '@imx-modules/imx-api-qer'; +import { ParameterData } from '@imx-modules/imx-qbm-dbts'; export class ItshopRequestData { public readonly parameters: ParameterData[] = []; @@ -33,10 +33,10 @@ export class ItshopRequestData { constructor(extendedCollectionData: { index: number } & PwoExtendedData) { if (extendedCollectionData.Parameters) { - Object.keys(extendedCollectionData.Parameters).forEach(parameterCategoryName => { - const parameterCategory = extendedCollectionData.Parameters[parameterCategoryName]; + Object.keys(extendedCollectionData.Parameters).forEach((parameterCategoryName) => { + const parameterCategory = extendedCollectionData.Parameters?.[parameterCategoryName]; if (parameterCategory && parameterCategory[extendedCollectionData.index]) { - parameterCategory[extendedCollectionData.index].forEach(parameterData => this.parameters.push(parameterData)); + parameterCategory[extendedCollectionData.index].forEach((parameterData) => this.parameters.push(parameterData)); } }); } diff --git a/imxweb/projects/qer/src/lib/itshop/request-info/itshop-request-entity-wrapper.interface.ts b/imxweb/projects/qer/src/lib/itshop/request-info/itshop-request-entity-wrapper.interface.ts index 12463f9b8..dc7cb4f6f 100644 --- a/imxweb/projects/qer/src/lib/itshop/request-info/itshop-request-entity-wrapper.interface.ts +++ b/imxweb/projects/qer/src/lib/itshop/request-info/itshop-request-entity-wrapper.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,8 +24,8 @@ * */ -import { IEntity, IEntityColumn } from 'imx-qbm-dbts'; -import { PwoData } from 'imx-api-qer'; +import { PwoData } from '@imx-modules/imx-api-qer'; +import { IEntity, IEntityColumn } from '@imx-modules/imx-qbm-dbts'; export interface ItshopRequestEntityWrapper { entity: IEntity; @@ -33,5 +33,5 @@ export interface ItshopRequestEntityWrapper { uidCurrentUser?: string; isChiefApproval?: boolean; parameterColumns: IEntityColumn[]; - commit?: () => Promise; + commit: () => Promise; } diff --git a/imxweb/projects/qer/src/lib/itshop/request-info/ordered-working-step.interface.ts b/imxweb/projects/qer/src/lib/itshop/request-info/ordered-working-step.interface.ts index dd3a794f0..46c034edb 100644 --- a/imxweb/projects/qer/src/lib/itshop/request-info/ordered-working-step.interface.ts +++ b/imxweb/projects/qer/src/lib/itshop/request-info/ordered-working-step.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qer/src/lib/itshop/request-info/request-info.component.html b/imxweb/projects/qer/src/lib/itshop/request-info/request-info.component.html index f907175e6..fa2dfc73e 100644 --- a/imxweb/projects/qer/src/lib/itshop/request-info/request-info.component.html +++ b/imxweb/projects/qer/src/lib/itshop/request-info/request-info.component.html @@ -1,5 +1,5 @@ - +
    @@ -8,12 +8,12 @@ - + - #LDS#Information on the associated service item - + #LDS#Information on the associated service item + @@ -22,7 +22,7 @@ -
    +
    @@ -30,14 +30,17 @@ -
    +
    - + @@ -45,5 +48,4 @@ --> - diff --git a/imxweb/projects/qer/src/lib/itshop/request-info/request-info.component.scss b/imxweb/projects/qer/src/lib/itshop/request-info/request-info.component.scss index 0f79b45f7..24e4b29e6 100644 --- a/imxweb/projects/qer/src/lib/itshop/request-info/request-info.component.scss +++ b/imxweb/projects/qer/src/lib/itshop/request-info/request-info.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; :host { display: flex; flex-direction: column; @@ -15,26 +15,6 @@ align-items: center; } - .mat-tab-group { - height: 100%; - overflow: hidden; - - ::ng-deep .mat-tab-body-wrapper { - flex: 1 1 auto; - - .mat-tab-body-content { - height: 100%; - overflow: hidden; - display: flex; - flex-direction: column; - } - } - } - - ::ng-deep .eui-alert { - margin-bottom: 20px; - } - .imx-additional-info { margin: 0 20px 20px; @@ -51,10 +31,15 @@ .imx-request-entitlements, .imx-request-history, .imx-request-item-detail { - margin: 20px; + padding: 20px; overflow: auto; } + .imx-request-history { + padding-bottom: 3px; + padding-right: 3px; + } + .imx-request-entitlements { display: flex; flex-direction: column; @@ -69,32 +54,8 @@ .imx-request-item-detail mat-card { background-color: transparent; } - imx-ext{ + imx-ext { height: 100%; overflow: auto; } } - -:host { - ::ng-deep .mat-tab-labels { - background-color: $color-gray-0; - } -} - -.eui-dark-theme { - :host { - background-color: $color-gray-80; - ::ng-deep .mat-tab-labels { - background-color: $color-gray-80; - } - } -} - -.eui-contrast-theme { - :host { - background-color: $color-gray-90; - ::ng-deep .mat-tab-labels { - background-color: $color-gray-90; - } - } -} diff --git a/imxweb/projects/qer/src/lib/itshop/request-info/request-info.component.ts b/imxweb/projects/qer/src/lib/itshop/request-info/request-info.component.ts index a1c44e0a3..ea8f836e8 100644 --- a/imxweb/projects/qer/src/lib/itshop/request-info/request-info.component.ts +++ b/imxweb/projects/qer/src/lib/itshop/request-info/request-info.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,18 +24,18 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; -import { Component, OnInit, Input, OnDestroy } from '@angular/core';import { Subscription } from 'rxjs'; +import { Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { Subscription } from 'rxjs'; +import { PortalShopServiceitems, QerProjectConfig } from '@imx-modules/imx-api-qer'; +import moment from 'moment'; import { BaseReadonlyCdr, BusyService, ClassloggerService, ColumnDependentReference, ExtService, IExtension } from 'qbm'; import { ProjectConfigurationService } from '../../project-configuration/project-configuration.service'; -import { ApproverContainer } from './approver-container'; +import { DecisionHistoryService } from '../decision-history.service'; import { ItshopService } from '../itshop.service'; +import { ApproverContainer } from './approver-container'; import { RequestParameterDataEntity } from './request-parameter-data-entity.interface'; import { WorkflowHistoryItemWrapper } from './workflow-history-item-wrapper'; -import { DecisionHistoryService } from '../decision-history.service'; -import { PortalShopServiceitems, QerProjectConfig } from 'imx-api-qer'; - @Component({ templateUrl: './request-info.component.html', @@ -48,13 +48,13 @@ export class RequestInfoComponent implements OnInit, OnDestroy { @Input() public userId: string; @Input() public isApproval: boolean; - public parameters: BaseReadonlyCdr[]; + public parameters: (BaseReadonlyCdr | undefined)[]; public propertyInfo: ColumnDependentReference[]; public approverContainer: ApproverContainer; public workflow: WorkflowHistoryItemWrapper[]; public readonly ruleViolationDetailId = 'cpl.ruleViolationDetail'; public extensions: IExtension[] = []; - public serviceItem: PortalShopServiceitems; + public serviceItem: PortalShopServiceitems | undefined; public projectConfig: QerProjectConfig; public isRoleAssignment: boolean; public isLoading = false; @@ -67,26 +67,29 @@ export class RequestInfoComponent implements OnInit, OnDestroy { private readonly logger: ClassloggerService, private readonly itshopService: ItshopService, private readonly decisionHistory: DecisionHistoryService, - private readonly ext: ExtService + private readonly ext: ExtService, ) { this.extensions = this.ext.Registry[this.ruleViolationDetailId]; this.subscriptions.push( this.busyService.busyStateChanged.subscribe((state: boolean) => { this.isLoading = state; - }) + }), ); } public async ngOnInit(): Promise { - const isBusy = this.busyService.beginBusy(); try { this.projectConfig = await this.projectConfigService.getConfig(); this.propertyInfo = this.request == null || this.request.propertyInfo == null ? [] : this.request.propertyInfo.filter((elem) => this.isForView(elem)); - this.parameters = this.request.parameterColumns.map((column) => new BaseReadonlyCdr(column)); + this.parameters = this.request.parameterColumns + .map((column) => { + return column != null ? new BaseReadonlyCdr(column) : undefined; + }) + .filter((elem) => elem != null); this.approverContainer = new ApproverContainer( { @@ -95,22 +98,30 @@ export class RequestInfoComponent implements OnInit, OnDestroy { isInWorkflow: ['OrderProduct', 'OrderUnsubscribe', 'OrderProlongate'].includes(this.request.UiOrderState.value), pwoData: this.request.pwoData, approvers: (await this.itshopService.getApprovers(this.request.GetEntity().GetKeys()[0])).Data.map( - (elem) => elem.UID_Person.value + (elem) => elem.UID_Person.value, ), }, this.projectConfig.ITShopConfig, - this.logger + this.logger, ); this.workflow = this.itshopService .createTypedHistory(this.request.pwoData) - .map((item) => new WorkflowHistoryItemWrapper(item, this.decisionHistory)); + .map((item) => new WorkflowHistoryItemWrapper(item, this.decisionHistory)) + .sort((item1, item2) => { + if (item1.approveHistory.XDateInserted.value && item2.approveHistory.XDateInserted.value) + return moment(item1.approveHistory.XDateInserted.value).isAfter(item2.approveHistory.XDateInserted.value) ? 1 : -1; + + return moment(item1.approveHistory.DateHead.value).isAfter(item2.approveHistory.DateHead.value) ? 1 : -1; + }); this.isRoleAssignment = ['ESet', 'QERAssign'].includes(this.request.TableName.value); if (!this.isRoleAssignment) { - this.serviceItem = await this.itshopService.getServiceItem(this.request.UID_AccProduct.value, + this.serviceItem = await this.itshopService.getServiceItem( + this.request.UID_AccProduct.value, // if the service item is not in the catalog, just use null - true); + true, + ); } } finally { isBusy.endBusy(); diff --git a/imxweb/projects/qer/src/lib/itshop/request-info/request-parameter-data-entity.interface.ts b/imxweb/projects/qer/src/lib/itshop/request-info/request-parameter-data-entity.interface.ts index 63f9b9e4e..c5b108c85 100644 --- a/imxweb/projects/qer/src/lib/itshop/request-info/request-parameter-data-entity.interface.ts +++ b/imxweb/projects/qer/src/lib/itshop/request-info/request-parameter-data-entity.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,8 +24,8 @@ * */ -import { IReadValue, IEntityColumn, IEntity } from 'imx-qbm-dbts'; -import { PwoData } from 'imx-api-qer'; +import { PwoData } from '@imx-modules/imx-api-qer'; +import { IEntity, IEntityColumn, IReadValue } from '@imx-modules/imx-qbm-dbts'; import { ColumnDependentReference } from 'qbm'; export interface RequestParameterDataEntity { @@ -35,7 +35,7 @@ export interface RequestParameterDataEntity { propertyInfo: ColumnDependentReference[]; pwoData: PwoData; qerWorkingMethod?: string; - parameterColumns: IEntityColumn[]; + parameterColumns: (IEntityColumn | undefined)[]; GetEntity: () => IEntity; complianceRuleViolation?: boolean; isArchived?: boolean; diff --git a/imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/product-details.service.ts b/imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/product-details.service.ts index bf83f9719..a0eaa3404 100644 --- a/imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/product-details.service.ts +++ b/imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/product-details.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,28 +26,24 @@ import { Injectable } from '@angular/core'; -import { PortalShopServiceitemsEntitlements } from 'imx-api-qer'; -import { CollectionLoadParameters, EntitySchema, ExtendedTypedEntityCollection } from 'imx-qbm-dbts'; +import { PortalShopServiceitemsEntitlements } from '@imx-modules/imx-api-qer'; +import { CollectionLoadParameters, EntitySchema, ExtendedTypedEntityCollection } from '@imx-modules/imx-qbm-dbts'; import { QerApiService } from '../../../qer-api-client.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ProductDetailsService { - - constructor( - private readonly qerapiService: QerApiService - ) { } - + constructor(private readonly qerapiService: QerApiService) {} public get productEntitlementSchema(): EntitySchema { return this.qerapiService.typedClient.PortalShopServiceitemsEntitlements.GetSchema(); } - public async getRoleEntitlements(uidAccProduct: string, parameter?: CollectionLoadParameters): - Promise> { + public async getRoleEntitlements( + uidAccProduct: string, + parameter?: CollectionLoadParameters, + ): Promise> { return this.qerapiService.typedClient.PortalShopServiceitemsEntitlements.Get(uidAccProduct, parameter); } - - } diff --git a/imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/product-entitlements/product-entitlements.component.html b/imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/product-entitlements/product-entitlements.component.html index 8ce91349f..619d2a1e7 100644 --- a/imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/product-entitlements/product-entitlements.component.html +++ b/imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/product-entitlements/product-entitlements.component.html @@ -1,27 +1,41 @@ - + - {{helperText}} + {{ helperText }} - - + + - - + + - {{item.TargetEntitlement.Column.GetDisplayValue()}} + {{ item.TargetEntitlement.Column.GetDisplayValue() }} - + -
    {{entitlementTypes.get(item.GetEntity().GetKeys().toString())}}
    +
    {{ entitlementTypes.get(item.GetEntity().GetKeys().toString()) }}
    -
    \ No newline at end of file +
    diff --git a/imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/product-entitlements/product-entitlements.component.scss b/imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/product-entitlements/product-entitlements.component.scss index 681be1f52..71541833c 100644 --- a/imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/product-entitlements/product-entitlements.component.scss +++ b/imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/product-entitlements/product-entitlements.component.scss @@ -3,32 +3,4 @@ display: flex; flex-direction: column; flex: 1 1 auto; - - - .mat-card { - display: flex; - flex-direction: column; - flex: 1 1 auto; - margin: 3px; - } -} - -.imx-helper-alert { - margin-bottom: 15px; -} - -.eui-dark-theme { - :host { - .imx-helper-alert { - color: $color-gray-2; - } - } -} - -.eui-contrast-theme { - :host { - .imx-helper-alert { - color: $color-gray-0; - } - } } diff --git a/imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/product-entitlements/product-entitlements.component.ts b/imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/product-entitlements/product-entitlements.component.ts index 966f5248d..cc7641c2c 100644 --- a/imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/product-entitlements/product-entitlements.component.ts +++ b/imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/product-entitlements/product-entitlements.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,26 +24,25 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; import { Component, Input, OnInit } from '@angular/core'; import { EuiLoadingService } from '@elemental-ui/core'; -import { PortalShopServiceitemsEntitlements } from 'imx-api-qer'; -import { CollectionLoadParameters, DbObjectKey, DisplayColumns, EntitySchema, IClientProperty, ValType } from 'imx-qbm-dbts'; +import { PortalShopServiceitemsEntitlements } from '@imx-modules/imx-api-qer'; +import { CollectionLoadParameters, DbObjectKey, DisplayColumns, EntitySchema, ValType } from '@imx-modules/imx-qbm-dbts'; import { ClientPropertyForTableColumns, DataSourceToolbarSettings, MetadataService } from 'qbm'; import { ProductDetailsService } from '../product-details.service'; @Component({ selector: 'imx-product-entitlements', templateUrl: './product-entitlements.component.html', - styleUrls: ['./product-entitlements.component.scss'] + styleUrls: ['./product-entitlements.component.scss'], }) export class ProductEntitlementsComponent implements OnInit { - public dstSettings: DataSourceToolbarSettings; public entitySchema: EntitySchema; public DisplayColumns = DisplayColumns; public showHelperAlert = true; - public helperText = '#LDS#Here you can get an overview of the entitlements associated with the product. If you approve the request, the recipient will get the following entitlements.'; + public helperText = + '#LDS#Here you can get an overview of the entitlements associated with the product. If you approve the request, the recipient will get the following entitlements.'; @Input() public uidAccProduct: string; public entitlementTypes: Map; @@ -53,16 +52,16 @@ export class ProductEntitlementsComponent implements OnInit { constructor( private readonly busy: EuiLoadingService, private readonly metadata: MetadataService, - private readonly detailsProvider: ProductDetailsService + private readonly detailsProvider: ProductDetailsService, ) { this.entitySchema = detailsProvider.productEntitlementSchema; this.displayColumns = this.displayColumns = [ { Type: ValType.String, ColumnName: 'entitlementDisplay', - untranslatedDisplay: '#LDS#Actions' + untranslatedDisplay: '#LDS#Actions', }, - this.entitySchema.Columns.TargetEntitlement + this.entitySchema.Columns.TargetEntitlement, ]; } @@ -79,33 +78,32 @@ export class ProductEntitlementsComponent implements OnInit { } public async navigate(parameter?: CollectionLoadParameters): Promise { - let overlay: OverlayRef; - setTimeout(() => { overlay = this.busy.show(); }); + if (this.busy.overlayRefs.length === 0) { + this.busy.show(); + } try { const dataSource = await this.detailsProvider.getRoleEntitlements(this.uidAccProduct, parameter); this.entitlementTypes = new Map(); - dataSource.Data.forEach(async item => { + dataSource.Data.forEach(async (item) => { this.entitlementTypes.set(item.GetEntity().GetKeys().toString(), await this.getTypeDescription(item)); }); this.dstSettings = { dataSource, entitySchema: this.entitySchema, - navigationState: parameter, + navigationState: parameter || {}, displayedColumns: this.displayColumns, }; } finally { - setTimeout(() => { this.busy.hide(overlay); }); + this.busy.hide(); } - } private async getTypeDescription(item: PortalShopServiceitemsEntitlements): Promise { const objKey = DbObjectKey.FromXml(item.TargetEntitlement.value); const metadata = await this.metadata.GetTableMetadata(objKey.TableName); - return metadata.DisplaySingular; + return metadata?.DisplaySingular || ''; } - } diff --git a/imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/service-item-detail.component.html b/imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/service-item-detail.component.html index 996f6b5d0..47eacb93b 100644 --- a/imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/service-item-detail.component.html +++ b/imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/service-item-detail.component.html @@ -1,3 +1,3 @@ - - - + + + diff --git a/imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/service-item-detail.component.scss b/imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/service-item-detail.component.scss deleted file mode 100644 index 0578d5e4a..000000000 --- a/imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/service-item-detail.component.scss +++ /dev/null @@ -1,6 +0,0 @@ -:host { - - .mat-card { - margin:3px; - } -} diff --git a/imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/service-item-detail.component.ts b/imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/service-item-detail.component.ts index 53c1f2c84..26ef4ae7e 100644 --- a/imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/service-item-detail.component.ts +++ b/imxweb/projects/qer/src/lib/itshop/request-info/service-item-detail/service-item-detail.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,14 +25,13 @@ */ import { Component, Input, OnInit } from '@angular/core'; -import { PortalShopServiceitems, QerProjectConfig } from 'imx-api-qer'; -import { DisplayColumns } from 'imx-qbm-dbts'; -import { ColumnDependentReference, BaseReadonlyCdr } from 'qbm'; +import { PortalShopServiceitems, QerProjectConfig } from '@imx-modules/imx-api-qer'; +import { DisplayColumns } from '@imx-modules/imx-qbm-dbts'; +import { BaseReadonlyCdr, ColumnDependentReference } from 'qbm'; @Component({ selector: 'imx-service-item-detail', templateUrl: './service-item-detail.component.html', - styleUrls: ['./service-item-detail.component.scss'] }) export class ServiceItemDetailComponent implements OnInit { @Input() public serviceItem: PortalShopServiceitems; @@ -42,19 +41,16 @@ export class ServiceItemDetailComponent implements OnInit { public isRoleAssignment = true; - constructor() { } + constructor() {} public async ngOnInit(): Promise { - this.cdrList = [ new BaseReadonlyCdr(this.serviceItem.GetEntity().GetColumn(DisplayColumns.DISPLAY_PROPERTYNAME)), new BaseReadonlyCdr(this.serviceItem.TableName.Column), - new BaseReadonlyCdr(this.serviceItem.Tags.Column) + new BaseReadonlyCdr(this.serviceItem.Tags.Column), ]; - const properties = this.projectConfig.ITShopConfig.AccProductProperties; - this.cdrList = this.cdrList.concat( - properties.map(prop => new BaseReadonlyCdr(this.serviceItem.GetEntity().GetColumn(prop)))); + const properties = this.projectConfig.ITShopConfig?.AccProductProperties; + this.cdrList = this.cdrList.concat(properties?.map((prop) => new BaseReadonlyCdr(this.serviceItem.GetEntity().GetColumn(prop))) || []); } - } diff --git a/imxweb/projects/qer/src/lib/itshop/request-info/workflow-history-item-wrapper.spec.ts b/imxweb/projects/qer/src/lib/itshop/request-info/workflow-history-item-wrapper.spec.ts index 00acc560d..5a6e167be 100644 --- a/imxweb/projects/qer/src/lib/itshop/request-info/workflow-history-item-wrapper.spec.ts +++ b/imxweb/projects/qer/src/lib/itshop/request-info/workflow-history-item-wrapper.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { PortalItshopApproveHistory } from 'imx-api-qer'; +import { PortalItshopApproveHistory } from '@imx-modules/imx-api-qer'; import { DecisionHistoryService } from '../decision-history.service'; import { WorkflowHistoryItemWrapper } from './workflow-history-item-wrapper'; @@ -33,18 +33,18 @@ function createProperty(name?, value?, display?) { ColumnName: name, GetValue: () => value, GetDisplayValue: () => value, - GetMetadata: () => ({ GetDisplay: () => display || name }) + GetMetadata: () => ({ GetDisplay: () => display || name }), }; return { value: value, Column: column, - GetMetadata: () => column.GetMetadata() + GetMetadata: () => column.GetMetadata(), }; } describe('WorkflowHistoryItemWrapper', () => { - const expectedDefaultColumns: { name: string, display?: string }[] = []; + const expectedDefaultColumns: { name: string; display?: string }[] = []; for (const testcase of [ {}, @@ -63,7 +63,7 @@ describe('WorkflowHistoryItemWrapper', () => { { decisionType: 'Unsubscribe' }, { decisionType: 'Deny' }, { decisionType: 'AddAdditional' }, - { decisionType: 'AddAdditional', personRelated: 'value', expectedAdditionalColumns: [{ name: 'UID_PersonRelated' }] } + { decisionType: 'AddAdditional', personRelated: 'value', expectedAdditionalColumns: [{ name: 'UID_PersonRelated' }] }, ]) { it('can be initialized with data', () => { const expectedColumns = expectedDefaultColumns.concat(testcase.expectedAdditionalColumns || []); @@ -77,18 +77,18 @@ describe('WorkflowHistoryItemWrapper', () => { UID_PWODecisionRule: createProperty('UID_PWODecisionRule', testcase.pwoDecisionRule), UID_PersonRelated: createProperty('UID_PersonRelated', testcase.personRelated), IsFromDelegation: createProperty('IsFromDelegation', testcase.isFromDelegation), - IsDecisionBySystem: createProperty('IsDecisionBySystem') + IsDecisionBySystem: createProperty('IsDecisionBySystem'), } as PortalItshopApproveHistory; - const decision = { } as DecisionHistoryService; + const decision = {} as DecisionHistoryService; - const historyItemWrapper = new WorkflowHistoryItemWrapper(history,decision); + const historyItemWrapper = new WorkflowHistoryItemWrapper(history, decision); expect(historyItemWrapper.approveHistory).toEqual(history); expect(historyItemWrapper.columns.length).toEqual(expectedColumns.length); - expectedColumns.forEach(expectedColumn => { - const cdr = historyItemWrapper.columns.find(cdr => cdr.column.ColumnName === expectedColumn.name); + expectedColumns.forEach((expectedColumn) => { + const cdr = historyItemWrapper.columns.find((cdr) => cdr.column.ColumnName === expectedColumn.name); expect(cdr.column).toBeDefined(); if (expectedColumn.display) { expect(cdr.display).toContain(expectedColumn.display); @@ -108,7 +108,7 @@ describe('WorkflowHistoryItemWrapper', () => { { decisionType: 'Query', reason: 'value', expectedDisplay: 'Query' }, { decisionType: 'Create', reason: 'value', expectedDisplay: 'ReasonHead' }, { decisionType: 'Order', reason: 'value', expectedDisplay: 'ReasonHead' }, - ].forEach(testcase => + ].forEach((testcase) => it('should getReasonDisplay', () => { const history = { DecisionType: createProperty('DecisionType', testcase.decisionType), @@ -119,10 +119,11 @@ describe('WorkflowHistoryItemWrapper', () => { RulerLevel: {}, } as PortalItshopApproveHistory; - const decision = { getColumnDescriptionForDisplayPersonHead: orderType => orderType} as DecisionHistoryService; + const decision = { getColumnDescriptionForDisplayPersonHead: (orderType) => orderType } as DecisionHistoryService; - const historyItemWrapper = new WorkflowHistoryItemWrapper(history,decision); + const historyItemWrapper = new WorkflowHistoryItemWrapper(history, decision); expect(historyItemWrapper.getReasonDisplay()).toContain(testcase.expectedDisplay); - })); + }), + ); }); diff --git a/imxweb/projects/qer/src/lib/itshop/request-info/workflow-history-item-wrapper.ts b/imxweb/projects/qer/src/lib/itshop/request-info/workflow-history-item-wrapper.ts index d18795399..763ac3b6d 100644 --- a/imxweb/projects/qer/src/lib/itshop/request-info/workflow-history-item-wrapper.ts +++ b/imxweb/projects/qer/src/lib/itshop/request-info/workflow-history-item-wrapper.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,18 +24,20 @@ * */ -import { PortalItshopApproveHistory } from 'imx-api-qer'; -import { IReadValue } from 'imx-qbm-dbts'; +import { PortalItshopApproveHistory } from '@imx-modules/imx-api-qer'; +import { IReadValue } from '@imx-modules/imx-qbm-dbts'; import { BaseReadonlyCdr, ColumnDependentReference } from 'qbm'; import { DecisionHistoryService } from '../decision-history.service'; export class WorkflowHistoryItemWrapper { - public readonly approver: ColumnDependentReference; - public readonly columns: ColumnDependentReference[]; + public readonly approver: ColumnDependentReference | undefined; + public readonly columns: (ColumnDependentReference | undefined)[]; - constructor(public readonly approveHistory: PortalItshopApproveHistory, public readonly decisionHistory: DecisionHistoryService) { - this.approver = this.createApproverCdr(this.approveHistory), - this.columns = this.createCdrList(this.approveHistory); + constructor( + public readonly approveHistory: PortalItshopApproveHistory, + public readonly decisionHistory: DecisionHistoryService, + ) { + (this.approver = this.createApproverCdr(this.approveHistory)), (this.columns = this.createCdrList(this.approveHistory)); } public getReasonDisplay(): string { @@ -48,10 +50,12 @@ export class WorkflowHistoryItemWrapper { return this.approveHistory.ReasonHead.GetMetadata().GetDisplay(); } - private createApproverCdr(historyItem: PortalItshopApproveHistory): BaseReadonlyCdr { + private createApproverCdr(historyItem: PortalItshopApproveHistory): BaseReadonlyCdr | undefined { if (historyItem.DisplayPersonHead.value) { - return new BaseReadonlyCdr(historyItem.DisplayPersonHead.Column, - this.decisionHistory.getColumnDescriptionForDisplayPersonHead(historyItem.DecisionType.value)); + return new BaseReadonlyCdr( + historyItem.DisplayPersonHead.Column, + this.decisionHistory.getColumnDescriptionForDisplayPersonHead(historyItem.DecisionType.value), + ); } if (historyItem.IsDecisionBySystem.value) { @@ -61,7 +65,7 @@ export class WorkflowHistoryItemWrapper { return undefined; } - private createCdrList(historyItem: PortalItshopApproveHistory): BaseReadonlyCdr[] { + private createCdrList(historyItem: PortalItshopApproveHistory): (BaseReadonlyCdr | undefined)[] { const properties = [ historyItem.RulerLevel.value !== 0 ? historyItem.RulerLevel : undefined, historyItem.UID_PWODecisionRule, @@ -69,7 +73,7 @@ export class WorkflowHistoryItemWrapper { historyItem.IsFromDelegation, historyItem.ValidUntil, historyItem.ValidUntilProlongation, - historyItem.ValidUntilUnsubscribe + historyItem.ValidUntilUnsubscribe, ]; const customDisplays = { @@ -77,19 +81,25 @@ export class WorkflowHistoryItemWrapper { historyItem.DecisionType.value === 'AddInsteadOf' ? '#LDS#Delegated approver' : historyItem.DecisionType.value === 'AddAdditional' - ? '#LDS#Additional approver' - : historyItem.DecisionType.value === 'Query' - ? '#LDS#Recipient' - : undefined, + ? '#LDS#Additional approver' + : historyItem.DecisionType.value === 'Query' + ? '#LDS#Recipient' + : undefined, }; - return properties.filter(property => this.isToView(property)).map(property => - new BaseReadonlyCdr(property.Column, customDisplays[property.Column.ColumnName]) - ); + return properties + .filter((property) => property?.Column != null && this.isToView(property)) + .map((property) => { + return property?.Column == null + ? undefined + : new BaseReadonlyCdr(property?.Column, customDisplays[property?.Column.ColumnName || '']); + }); } - private isToView(property: IReadValue): boolean { - if (!property || property.value == null || property.value === '') { return false; } + private isToView(property: IReadValue | undefined): boolean { + if (!property || property.value == null || property.value === '') { + return false; + } return property.Column.ColumnName !== 'IsFromDelegation' || property.value; } } diff --git a/imxweb/projects/qer/src/lib/itshop/shelf-selection-sidesheet.model.ts b/imxweb/projects/qer/src/lib/itshop/shelf-selection-sidesheet.model.ts index 9ea3bbbbb..f4a8bc47c 100644 --- a/imxweb/projects/qer/src/lib/itshop/shelf-selection-sidesheet.model.ts +++ b/imxweb/projects/qer/src/lib/itshop/shelf-selection-sidesheet.model.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,21 +25,20 @@ */ export interface ShelfObject { - uidItShopOrg: string; - displayShelf: string; + uidItShopOrg?: string; + displayShelf?: string; } export interface PersonForProduct { uidItShopOrg?: string; - uidPerson: string; - displayPerson: string; + uidPerson?: string; + displayPerson?: string; shelfsObjects: ShelfObject[]; } export interface ShelfSelectionObject { - uidAccproduct: string; - displayAccProduct: string; + uidAccproduct?: string; + displayAccProduct?: string; personsForProduct: PersonForProduct[]; possibleShelfObjects: ShelfObject[]; } - diff --git a/imxweb/projects/qer/src/lib/itshop/shelf-selection.component.html b/imxweb/projects/qer/src/lib/itshop/shelf-selection.component.html index 1813bcd76..f96e3ade1 100644 --- a/imxweb/projects/qer/src/lib/itshop/shelf-selection.component.html +++ b/imxweb/projects/qer/src/lib/itshop/shelf-selection.component.html @@ -1,40 +1,47 @@
    - - #LDS#One or more selected products are included in several shelves. For each product, select from which shelf you want to request the product. You can select a default shelf that will be used for all recipients or select a shelf individually for each recipient. + + {{ LdsKey }}
    - - + - {{shelfSelectionObject.displayAccProduct}} + {{ shelfSelectionObject.displayAccProduct }}
    - - {{'#LDS#Select a default shelf' | translate}} - - - {{so.displayShelf}} + + {{ '#LDS#Select a default shelf' | translate }} + + + {{ so.displayShelf }}
    - {{personForProduct.displayPerson}}
    + {{ personForProduct.displayPerson }} +
    - {{'#LDS#Select a shelf' | translate}} + {{ '#LDS#Select a shelf' | translate }} - - {{so.displayShelf}} + [attr.data-imx-identifier]="'shelf-selection-person-shelf-object-' + personForProduct.uidPerson" + [formControl]="getFormControl(shelfSelectionObject, personForProduct)" + [required]="true" + > + + {{ so.displayShelf }} @@ -42,12 +49,17 @@
    -
    -
    - -
    \ No newline at end of file + +
    diff --git a/imxweb/projects/qer/src/lib/itshop/shelf-selection.component.scss b/imxweb/projects/qer/src/lib/itshop/shelf-selection.component.scss index a2ed29334..0e16a6f6b 100644 --- a/imxweb/projects/qer/src/lib/itshop/shelf-selection.component.scss +++ b/imxweb/projects/qer/src/lib/itshop/shelf-selection.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; :host { @@ -22,7 +22,7 @@ } } -.eui-sidesheet-actions.mat-card { +.eui-sidesheet-actions.mat-mdc-card { margin: 0; display: flex; justify-content: flex-end; @@ -51,18 +51,13 @@ white-space: nowrap; } -.mat-form-field { +.mat-mdc-form-field { margin-bottom: -20px; } -.imx-helper-alert { - margin-bottom: 20px; - display: block; -} - .eui-dark-theme { :host { - .eui-sidesheet-actions.mat-card { + .eui-sidesheet-actions.mat-mdc-card { background-color: $color-gray-70; } } @@ -70,7 +65,7 @@ .eui-contrast-theme { :host { - .eui-sidesheet-actions.mat-card { + .eui-sidesheet-actions.mat-mdc-card { background-color: $color-gray-90; } } diff --git a/imxweb/projects/qer/src/lib/itshop/shelf-selection.component.ts b/imxweb/projects/qer/src/lib/itshop/shelf-selection.component.ts index 1d017ef09..f6cc99446 100644 --- a/imxweb/projects/qer/src/lib/itshop/shelf-selection.component.ts +++ b/imxweb/projects/qer/src/lib/itshop/shelf-selection.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,19 +26,17 @@ import { Component, Inject } from '@angular/core'; import { UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; -import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; import { PersonForProduct, ShelfObject, ShelfSelectionObject } from './shelf-selection-sidesheet.model'; @Component({ selector: 'imx-shelf-selection', templateUrl: './shelf-selection.component.html', - styleUrls: ['./shelf-selection.component.scss'] + styleUrls: ['./shelf-selection.component.scss'], }) export class ShelfSelectionComponent { - public showHelperAlert = true; - public shelfSelectionObjects: ShelfSelectionObject[] = []; public formGroup: UntypedFormGroup; @@ -46,16 +44,17 @@ export class ShelfSelectionComponent { constructor( @Inject(EUI_SIDESHEET_DATA) public data: ShelfSelectionObject[], - public readonly sideSheetRef: EuiSidesheetRef) { + public readonly sideSheetRef: EuiSidesheetRef, + ) { this.setup(); } public optionSelected(uidItShopOrg: string, uidAccproduct: string): void { const group = this.formGroup.get(uidAccproduct) as UntypedFormGroup; - const persons = this.shelfSelectionObjects.find(elem => elem.uidAccproduct === uidAccproduct); - persons.personsForProduct.forEach(person => { - const control = group.get(person.uidPerson); - if (control?.value === '' && person.shelfsObjects.some(shelf => shelf.uidItShopOrg === uidItShopOrg)) { + const persons = this.shelfSelectionObjects.find((elem) => elem.uidAccproduct === uidAccproduct); + persons?.personsForProduct.forEach((person) => { + const control = group.get(person.uidPerson ?? ''); + if (control?.value === '' && person.shelfsObjects.some((shelf) => shelf.uidItShopOrg === uidItShopOrg)) { control.setValue(uidItShopOrg); } }); @@ -68,21 +67,23 @@ export class ShelfSelectionComponent { } public needsDefaultShelf(product: ShelfSelectionObject): boolean { - return product.personsForProduct.filter(person => person.shelfsObjects.length > 1).length > 1; + return product.personsForProduct.filter((person) => person.shelfsObjects.length > 1).length > 1; } - public getFormControl(shelfSelectionObject: ShelfSelectionObject, personForProduct: PersonForProduct): UntypedFormControl { - return (this.formGroup.get(shelfSelectionObject.uidAccproduct) as UntypedFormGroup).get(personForProduct.uidPerson) as UntypedFormControl; + return (this.formGroup.get(shelfSelectionObject.uidAccproduct ?? '') as UntypedFormGroup).get( + personForProduct.uidPerson ?? '', + ) as UntypedFormControl; } public submit(): void { - this.shelfSelectionObjects.forEach(product => { - product.personsForProduct.filter(elem => elem.shelfsObjects.length > 1).forEach(person => { - const control = this.getFormControl(product, person); - person.uidItShopOrg = control.value; - } - ); + this.shelfSelectionObjects.forEach((product) => { + product.personsForProduct + .filter((elem) => elem.shelfsObjects.length > 1) + .forEach((person) => { + const control = this.getFormControl(product, person); + person.uidItShopOrg = control.value; + }); }); this.sideSheetRef.close(this.shelfSelectionObjects); } @@ -91,14 +92,17 @@ export class ShelfSelectionComponent { this.shelfSelectionObjects = this.data; this.formGroup = new UntypedFormGroup({}); - this.shelfSelectionObjects.forEach(element => { + this.shelfSelectionObjects.forEach((element) => { const personGroup = new UntypedFormGroup({}); - element.personsForProduct.filter(elem => elem.shelfsObjects.length > 1).forEach(person => - personGroup.addControl(person.uidPerson, new UntypedFormControl(person.uidItShopOrg, Validators.required)) - ); - this.formGroup.addControl(element.uidAccproduct, personGroup); + element.personsForProduct + .filter((elem) => elem.shelfsObjects.length > 1) + .forEach((person) => + personGroup.addControl(person.uidPerson ?? '', new UntypedFormControl(person.uidItShopOrg, Validators.required)), + ); + this.formGroup.addControl(element.uidAccproduct ?? '', personGroup); }); } - + public LdsKey = + '#LDS#One or more selected products are included in several shelves. For each product, select from which shelf you want to request the product. You can select a default shelf that will be used for all recipients or select a shelf individually for each recipient.'; } diff --git a/imxweb/projects/qer/src/lib/itshop/shelf.service.ts b/imxweb/projects/qer/src/lib/itshop/shelf.service.ts index 5c60a4f5e..4e75c01e2 100644 --- a/imxweb/projects/qer/src/lib/itshop/shelf.service.ts +++ b/imxweb/projects/qer/src/lib/itshop/shelf.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,17 +26,16 @@ import { Injectable } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; -import { TranslateService } from '@ngx-translate/core'; import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; -import { OverlayRef } from '@angular/cdk/overlay'; +import { TranslateService } from '@ngx-translate/core'; -import { ClassloggerService, MessageDialogComponent } from 'qbm'; -import { RequestableProductForPerson, ServiceItemForPerson } from 'imx-api-qer'; -import { ShelfSelectionComponent } from './shelf-selection.component'; +import { RequestableProductForPerson, ServiceItemForPerson } from '@imx-modules/imx-api-qer'; +import _ from 'lodash'; +import { ClassloggerService, MessageDialogComponent, calculateSidesheetWidth } from 'qbm'; import { QerApiService } from '../qer-api-client.service'; import { NonRequestableItemsComponent } from './non-requestable-items/non-requestable-items.component'; import { PersonForProduct, ShelfObject, ShelfSelectionObject } from './shelf-selection-sidesheet.model'; -import _ from 'lodash'; +import { ShelfSelectionComponent } from './shelf-selection.component'; @Injectable({ providedIn: 'root', @@ -48,7 +47,7 @@ export class ShelfService { private readonly logger: ClassloggerService, private readonly dialogService: MatDialog, private readonly translate: TranslateService, - private readonly busyService: EuiLoadingService + private readonly busyService: EuiLoadingService, ) {} public async setShops(requestableServiceItemsForPersons: RequestableProductForPerson[]): Promise { @@ -82,8 +81,8 @@ export class ShelfService { data.some( (shelf) => elem.UidAccProduct === shelf.uidAccproduct && - shelf.personsForProduct.find((person) => person.uidPerson === elem.UidPerson)?.shelfsObjects.length > 0 - ) + !!shelf.personsForProduct.find((person) => person.uidPerson === (elem?.UidPerson || ''))?.shelfsObjects.length, + ), ); if (!(await this.setElementsWithSideSheet(requested, data))) { @@ -109,7 +108,7 @@ export class ShelfService { DisplayRecipient: person.displayPerson, }); } - }) + }), ); } @@ -138,7 +137,7 @@ export class ShelfService { const sidesheetRef = this.sideSheet.open(ShelfSelectionComponent, { title: await this.translate.get('#LDS#Heading Select Shelf').toPromise(), padding: '0px', - width: 'max(500px, 50%)', + width: calculateSidesheetWidth(800, 0.5), testId: 'shelf-selection-sidesheet', data: ssos, }); @@ -151,7 +150,9 @@ export class ShelfService { // TODO TASK 321664: This is only a temporary fix to choose to all items with the same uid, regardless of if they are optional or not. const items = requested.filter( (elem) => - elem.UidAccProduct === requestedProduct.uidAccproduct && elem.UidPerson === person.uidPerson && person.shelfsObjects.length > 0 + elem.UidAccProduct === requestedProduct.uidAccproduct && + elem.UidPerson === (person.uidPerson || '') && + person.shelfsObjects.length > 0, ); items.forEach((item) => { item.UidITShopOrg = person.uidItShopOrg; @@ -159,7 +160,7 @@ export class ShelfService { // const item = requested.find(elem => // elem.UidAccProduct === requestedProduct.uidAccproduct && elem.UidPerson === person.uidPerson); // item.UidITShopOrg = person.uidItShopOrg; - }) + }), ); return true; } @@ -168,7 +169,7 @@ export class ShelfService { // Here we set any unique products with a unique shelf, unique is defined as only one UID for AccProduct, Person for (const serviceItemForPerson of requested.filter((elem) => elem.UidITShopOrg == null || elem.UidITShopOrg === '')) { const possibleRequestableServiceItems = productsWithShops.filter( - (item) => item.UidAccProduct === serviceItemForPerson.UidAccProduct && item.UidPerson === serviceItemForPerson.UidPerson + (item) => item.UidAccProduct === serviceItemForPerson.UidAccProduct && item.UidPerson === serviceItemForPerson.UidPerson, ); if (possibleRequestableServiceItems.length === 1) { serviceItemForPerson.UidITShopOrg = possibleRequestableServiceItems[0].UidITShopOrg; @@ -183,7 +184,7 @@ export class ShelfService { private buildShelfSelection( requested: RequestableProductForPerson[], - productsWithShops: RequestableProductForPerson[] + productsWithShops: RequestableProductForPerson[], ): ShelfSelectionObject[] { // Build the shelf selection object to then ask the user to set values on return requested @@ -192,27 +193,27 @@ export class ShelfService { .map((elem) => ({ uidAccproduct: elem.uid, displayAccProduct: elem.display, - personsForProduct: this.getPersonsForProduct(requested, productsWithShops, elem.uid), - possibleShelfObjects: this.getPossibleShelfs(productsWithShops, elem.uid), + personsForProduct: this.getPersonsForProduct(requested, productsWithShops, elem.uid ?? ''), + possibleShelfObjects: this.getPossibleShelfs(productsWithShops, elem.uid ?? ''), })) .filter((elem) => this.hasMultipleShelfs(elem)) - .sort((a, b) => a.displayAccProduct.localeCompare(b.displayAccProduct)); + .sort((a, b) => (a.displayAccProduct ?? '').localeCompare(b.displayAccProduct ?? '')); } private getPersonsForProduct( requested: RequestableProductForPerson[], productsWithShops: RequestableProductForPerson[], - uid: string + uid: string, ): PersonForProduct[] { const elements = requested.filter((elem) => elem.UidAccProduct === uid); return elements .map((elem) => ({ uidPerson: elem.UidPerson, - uidItShopOrg: this.tryGetItShop(productsWithShops, uid, elem.UidPerson), + uidItShopOrg: this.tryGetItShop(productsWithShops, uid, elem.UidPerson ?? ''), displayPerson: elem.DisplayRecipient, - shelfsObjects: this.getShelfObjects(productsWithShops, uid, elem.UidPerson), + shelfsObjects: this.getShelfObjects(productsWithShops, uid, elem.UidPerson ?? ''), })) - .sort((a, b) => a.displayPerson.localeCompare(b.displayPerson)); + .sort((a, b) => (a.displayPerson ?? '').localeCompare(b.displayPerson ?? '')); } private getPossibleShelfs(productsWithShops: RequestableProductForPerson[], uid: string): ShelfObject[] { @@ -225,18 +226,22 @@ export class ShelfService { .filter(this.uniqueShelfs); } - private uniqueProducts(value: { display: string; uid: string }, index: number, self: { display: string; uid: string }[]): boolean { + private uniqueProducts( + value: { display: string | undefined; uid?: string | undefined }, + index: number, + self: { display: string; uid: string }[], + ): boolean { return self.findIndex((elem) => value.uid === elem.uid) === index; } private tryGetItShop(productsWithShops: RequestableProductForPerson[], uidAcc: string, uidPerson: string): string { - const shelf = productsWithShops.filter((elem) => elem.UidAccProduct === uidAcc && elem.UidPerson === uidPerson); - return shelf.length === 1 ? shelf[0].UidITShopOrg : ''; + const shelf = productsWithShops.filter((elem) => elem.UidAccProduct === uidAcc && (elem.UidPerson || '') === uidPerson); + return shelf.length === 1 ? shelf[0].UidITShopOrg ?? '' : ''; } private getShelfObjects(productsWithShops: RequestableProductForPerson[], uidAcc: string, uidPerson: string): ShelfObject[] { return productsWithShops - .filter((elem) => elem.UidAccProduct === uidAcc && elem.UidPerson === uidPerson) + .filter((elem) => elem.UidAccProduct === uidAcc && (elem.UidPerson || '') === uidPerson) .map((elem) => ({ displayShelf: elem.Display, uidItShopOrg: elem.UidITShopOrg })); } @@ -247,13 +252,14 @@ export class ShelfService { private async findUniqueProductsInShops(serviceItemsForPersons: ServiceItemForPerson[]): Promise { // Get all unique products within the itshop let products: RequestableProductForPerson[]; - let overlay: OverlayRef; - setTimeout(() => (overlay = this.busyService.show())); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } try { products = await this.qerClient.client.portal_itshop_findproducts_post(serviceItemsForPersons); products = _.uniqWith(products, _.isEqual); } finally { - setTimeout(() => this.busyService.hide(overlay)); + this.busyService.hide(); } return products; } diff --git a/imxweb/projects/qer/src/lib/itshop/workflow-data-wrapper.spec.ts b/imxweb/projects/qer/src/lib/itshop/workflow-data-wrapper.spec.ts index 4815e193f..175275878 100644 --- a/imxweb/projects/qer/src/lib/itshop/workflow-data-wrapper.spec.ts +++ b/imxweb/projects/qer/src/lib/itshop/workflow-data-wrapper.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,22 +24,23 @@ * */ -import { EntityCollectionData } from 'imx-qbm-dbts'; +import { EntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { WorkflowDataWrapper } from './workflow-data-wrapper'; describe('WorkflowDataWrapper', () => { - const createWorkflowCollection = (data) => ({ - Entities: data.map(item => { - const Columns = {}; - Object.keys(item).forEach(key => Columns[key] = { Value: item[key] }); - return { Columns } - }) - } as EntityCollectionData); + const createWorkflowCollection = (data) => + ({ + Entities: data.map((item) => { + const Columns = {}; + Object.keys(item).forEach((key) => (Columns[key] = { Value: item[key] })); + return { Columns }; + }), + }) as EntityCollectionData; const testcaseToString = (testcase) => { const tokens = []; - Object.keys(testcase).forEach(key => - Object.keys(testcase[key]).forEach(itemkey => tokens.push(itemkey + '="' + testcase[key][itemkey] + '"')) + Object.keys(testcase).forEach((key) => + Object.keys(testcase[key]).forEach((itemkey) => tokens.push(itemkey + '="' + testcase[key][itemkey] + '"')), ); return tokens.join(); }; @@ -50,16 +51,14 @@ describe('WorkflowDataWrapper', () => { const LevelNumber = 1; const UID_QERWorkingStep = 'some workingstep'; - const workflow = new WorkflowDataWrapper( - { - WorkflowData: createWorkflowCollection([{ UID_PersonHead, LevelNumber, UID_QERWorkingStep }]), - WorkflowSteps: createWorkflowCollection([{ UID_QERWorkingStep: 'some other workingstep', DirectSteps: '1' }]) - } - ); + const workflow = new WorkflowDataWrapper({ + WorkflowData: createWorkflowCollection([{ UID_PersonHead, LevelNumber, UID_QERWorkingStep }]), + WorkflowSteps: createWorkflowCollection([{ UID_QERWorkingStep: 'some other workingstep', DirectSteps: '1' }]), + }); const directSteps = workflow.getDirectSteps(UID_PersonHead, LevelNumber); - expect(directSteps).toBeUndefined(); + expect(directSteps).toEqual([]); }); it('returns the directSteps for the matching item', () => { @@ -67,12 +66,10 @@ describe('WorkflowDataWrapper', () => { const LevelNumber = 1; const workflowStep = { UID_QERWorkingStep: 'some workingstep', DirectSteps: '1' }; - const workflow = new WorkflowDataWrapper( - { - WorkflowData: createWorkflowCollection([{ UID_PersonHead, LevelNumber, UID_QERWorkingStep: workflowStep.UID_QERWorkingStep }]), - WorkflowSteps: createWorkflowCollection([ workflowStep ]) - } - ); + const workflow = new WorkflowDataWrapper({ + WorkflowData: createWorkflowCollection([{ UID_PersonHead, LevelNumber, UID_QERWorkingStep: workflowStep.UID_QERWorkingStep }]), + WorkflowSteps: createWorkflowCollection([workflowStep]), + }); const directSteps = workflow.getDirectSteps(UID_PersonHead, LevelNumber); @@ -84,21 +81,16 @@ describe('WorkflowDataWrapper', () => { const LevelNumber = 1; const SubLevelNumber = 0; const workflowStep = { UID_QERWorkingStep: 'some workingstep', DirectSteps: '1' }; - const workflowStepOther = { UID_QERWorkingStep: 'some other workingstep', DirectSteps: '2' };; + const workflowStepOther = { UID_QERWorkingStep: 'some other workingstep', DirectSteps: '2' }; - const workflow = new WorkflowDataWrapper( - { - WorkflowData: createWorkflowCollection([ - { UID_PersonHead, LevelNumber, SubLevelNumber: SubLevelNumber + 1, UID_QERWorkingStep: workflowStepOther.UID_QERWorkingStep }, - { UID_PersonHead, LevelNumber, SubLevelNumber, UID_QERWorkingStep: workflowStep.UID_QERWorkingStep }, - { UID_PersonHead, LevelNumber, SubLevelNumber: SubLevelNumber + 2, UID_QERWorkingStep: workflowStepOther.UID_QERWorkingStep } - ]), - WorkflowSteps: createWorkflowCollection([ - workflowStep, - workflowStepOther - ]) - } - ); + const workflow = new WorkflowDataWrapper({ + WorkflowData: createWorkflowCollection([ + { UID_PersonHead, LevelNumber, SubLevelNumber: SubLevelNumber + 1, UID_QERWorkingStep: workflowStepOther.UID_QERWorkingStep }, + { UID_PersonHead, LevelNumber, SubLevelNumber, UID_QERWorkingStep: workflowStep.UID_QERWorkingStep }, + { UID_PersonHead, LevelNumber, SubLevelNumber: SubLevelNumber + 2, UID_QERWorkingStep: workflowStepOther.UID_QERWorkingStep }, + ]), + WorkflowSteps: createWorkflowCollection([workflowStep, workflowStepOther]), + }); const directSteps = workflow.getDirectSteps(UID_PersonHead, LevelNumber); @@ -112,12 +104,10 @@ describe('WorkflowDataWrapper', () => { const LevelNumber = 1; const UID_QERWorkingStep = 'some workingstep'; - const workflow = new WorkflowDataWrapper( - { - WorkflowData: createWorkflowCollection([{ UID_PersonHead, LevelNumber, UID_QERWorkingStep }]), - WorkflowSteps: createWorkflowCollection([{ UID_QERWorkingStep: 'some other workingstep' }]) - } - ); + const workflow = new WorkflowDataWrapper({ + WorkflowData: createWorkflowCollection([{ UID_PersonHead, LevelNumber, UID_QERWorkingStep }]), + WorkflowSteps: createWorkflowCollection([{ UID_QERWorkingStep: 'some other workingstep' }]), + }); expect(workflow.isAdditionalAllowed(UID_PersonHead, LevelNumber)).toBeFalsy(); }); @@ -126,27 +116,26 @@ describe('WorkflowDataWrapper', () => { { workflowData: { IsFromDelegation: true }, workflowStep: { IsAdditionalAllowed: true } }, { workflowData: { UID_PersonAdditional: 'id of some additional person' }, workflowStep: { IsAdditionalAllowed: true } }, { workflowData: { UID_PersonInsteadOf: 'id of some other person' }, workflowStep: { IsAdditionalAllowed: true } }, - { workflowStep: { IsAdditionalAllowed: false } } - ].forEach(testcase => - it('returns false if matching item has ' + testcaseToString(testcase), () => { - const UID_PersonHead = 'some user id'; - const LevelNumber = 1; - const workflowStep = { ...{ UID_QERWorkingStep: 'some workingstep' }, ...testcase.workflowStep }; - - const workflow = new WorkflowDataWrapper( - { + { workflowStep: { IsAdditionalAllowed: false } }, + ].forEach((testcase) => + it('returns false if matching item has ' + testcaseToString(testcase), () => { + const UID_PersonHead = 'some user id'; + const LevelNumber = 1; + const workflowStep = { ...{ UID_QERWorkingStep: 'some workingstep' }, ...testcase.workflowStep }; + + const workflow = new WorkflowDataWrapper({ WorkflowData: createWorkflowCollection([ { ...{ UID_PersonHead, LevelNumber, UID_QERWorkingStep: workflowStep.UID_QERWorkingStep }, - ...testcase.workflowData - } + ...testcase.workflowData, + }, ]), - WorkflowSteps: createWorkflowCollection([ workflowStep ]) - } - ); + WorkflowSteps: createWorkflowCollection([workflowStep]), + }); - expect(workflow.isAdditionalAllowed(UID_PersonHead, LevelNumber)).toBeFalsy(); - })); + expect(workflow.isAdditionalAllowed(UID_PersonHead, LevelNumber)).toBeFalsy(); + }), + ); it('returns true if matching item', () => { const UID_PersonHead = 'some user id'; @@ -155,14 +144,12 @@ describe('WorkflowDataWrapper', () => { const UID_PersonAdditional = ''; const UID_PersonInsteadOf = ''; - const workflow = new WorkflowDataWrapper( - { - WorkflowData: createWorkflowCollection([ - { UID_PersonHead, LevelNumber, UID_QERWorkingStep: workflowStep.UID_QERWorkingStep, UID_PersonAdditional, UID_PersonInsteadOf } - ]), - WorkflowSteps: createWorkflowCollection([ workflowStep ]) - } - ); + const workflow = new WorkflowDataWrapper({ + WorkflowData: createWorkflowCollection([ + { UID_PersonHead, LevelNumber, UID_QERWorkingStep: workflowStep.UID_QERWorkingStep, UID_PersonAdditional, UID_PersonInsteadOf }, + ]), + WorkflowSteps: createWorkflowCollection([workflowStep]), + }); expect(workflow.isAdditionalAllowed(UID_PersonHead, LevelNumber)).toBeTruthy(); }); @@ -174,12 +161,10 @@ describe('WorkflowDataWrapper', () => { const LevelNumber = 1; const UID_QERWorkingStep = 'some workingstep'; - const workflow = new WorkflowDataWrapper( - { - WorkflowData: createWorkflowCollection([{ UID_PersonHead, LevelNumber, UID_QERWorkingStep }]), - WorkflowSteps: createWorkflowCollection([{ UID_QERWorkingStep: 'some other workingstep' }]) - } - ); + const workflow = new WorkflowDataWrapper({ + WorkflowData: createWorkflowCollection([{ UID_PersonHead, LevelNumber, UID_QERWorkingStep }]), + WorkflowSteps: createWorkflowCollection([{ UID_QERWorkingStep: 'some other workingstep' }]), + }); expect(workflow.isInsteadOfAllowed(UID_PersonHead, LevelNumber)).toBeFalsy(); }); @@ -188,27 +173,26 @@ describe('WorkflowDataWrapper', () => { { workflowData: { IsFromDelegation: true }, workflowStep: { IsInsteadOfAllowed: true } }, { workflowData: { UID_PersonAdditional: 'id of some additional person' }, workflowStep: { IsInsteadOfAllowed: true } }, { workflowData: { UID_PersonInsteadOf: 'id of some other person' }, workflowStep: { IsInsteadOfAllowed: true } }, - { workflowStep: { IsInsteadOfAllowed: false } } - ].forEach(testcase => - it('returns false if matching item has ' + testcaseToString(testcase), () => { - const UID_PersonHead = 'some user id'; - const LevelNumber = 1; - const workflowStep = { ...{ UID_QERWorkingStep: 'some workingstep' }, ...testcase.workflowStep }; - - const workflow = new WorkflowDataWrapper( - { + { workflowStep: { IsInsteadOfAllowed: false } }, + ].forEach((testcase) => + it('returns false if matching item has ' + testcaseToString(testcase), () => { + const UID_PersonHead = 'some user id'; + const LevelNumber = 1; + const workflowStep = { ...{ UID_QERWorkingStep: 'some workingstep' }, ...testcase.workflowStep }; + + const workflow = new WorkflowDataWrapper({ WorkflowData: createWorkflowCollection([ { ...{ UID_PersonHead, LevelNumber, UID_QERWorkingStep: workflowStep.UID_QERWorkingStep }, - ...testcase.workflowData - } + ...testcase.workflowData, + }, ]), - WorkflowSteps: createWorkflowCollection([ workflowStep ]) - } - ); + WorkflowSteps: createWorkflowCollection([workflowStep]), + }); - expect(workflow.isInsteadOfAllowed(UID_PersonHead, LevelNumber)).toBeFalsy(); - })); + expect(workflow.isInsteadOfAllowed(UID_PersonHead, LevelNumber)).toBeFalsy(); + }), + ); it('returns true if matching item', () => { const UID_PersonHead = 'some user id'; @@ -217,14 +201,12 @@ describe('WorkflowDataWrapper', () => { const UID_PersonAdditional = ''; const UID_PersonInsteadOf = ''; - const workflow = new WorkflowDataWrapper( - { - WorkflowData: createWorkflowCollection([ - { UID_PersonHead, LevelNumber, UID_QERWorkingStep: workflowStep.UID_QERWorkingStep, UID_PersonAdditional, UID_PersonInsteadOf } - ]), - WorkflowSteps: createWorkflowCollection([ workflowStep ]) - } - ); + const workflow = new WorkflowDataWrapper({ + WorkflowData: createWorkflowCollection([ + { UID_PersonHead, LevelNumber, UID_QERWorkingStep: workflowStep.UID_QERWorkingStep, UID_PersonAdditional, UID_PersonInsteadOf }, + ]), + WorkflowSteps: createWorkflowCollection([workflowStep]), + }); expect(workflow.isInsteadOfAllowed(UID_PersonHead, LevelNumber)).toBeTruthy(); }); @@ -236,12 +218,10 @@ describe('WorkflowDataWrapper', () => { const UID_PersonHead = 'some user id'; const LevelNumber = 1; - const workflow = new WorkflowDataWrapper( - { - CanRevokeDelegation, - WorkflowData: createWorkflowCollection([{ UID_PersonHead: 'some other user id', LevelNumber }]) - } - ); + const workflow = new WorkflowDataWrapper({ + CanRevokeDelegation, + WorkflowData: createWorkflowCollection([{ UID_PersonHead: 'some other user id', LevelNumber }]), + }); expect(workflow.canRevokeAdditionalApprover(UID_PersonHead, LevelNumber)).toBeFalsy(); }); @@ -252,12 +232,10 @@ describe('WorkflowDataWrapper', () => { const LevelNumber = 1; const UID_PersonAdditional = 'some other user id'; - const workflow = new WorkflowDataWrapper( - { - CanRevokeDelegation, - WorkflowData: createWorkflowCollection([{ UID_PersonHead, LevelNumber, UID_PersonAdditional }]) - } - ); + const workflow = new WorkflowDataWrapper({ + CanRevokeDelegation, + WorkflowData: createWorkflowCollection([{ UID_PersonHead, LevelNumber, UID_PersonAdditional }]), + }); expect(workflow.canRevokeAdditionalApprover(UID_PersonHead, LevelNumber)).toBeFalsy(); }); @@ -267,12 +245,10 @@ describe('WorkflowDataWrapper', () => { const UID_PersonHead = 'some user id'; const LevelNumber = 1; - const workflow = new WorkflowDataWrapper( - { - CanRevokeDelegation, - WorkflowData: createWorkflowCollection([{ UID_PersonHead, LevelNumber }]) - } - ); + const workflow = new WorkflowDataWrapper({ + CanRevokeDelegation, + WorkflowData: createWorkflowCollection([{ UID_PersonHead, LevelNumber }]), + }); expect(workflow.canRevokeAdditionalApprover(UID_PersonHead, LevelNumber)).toBeFalsy(); }); @@ -283,12 +259,10 @@ describe('WorkflowDataWrapper', () => { const LevelNumber = 1; const UID_PersonAdditional = 'some other user id'; - const workflow = new WorkflowDataWrapper( - { - CanRevokeDelegation, - WorkflowData: createWorkflowCollection([{ UID_PersonHead, LevelNumber, UID_PersonAdditional }]) - } - ); + const workflow = new WorkflowDataWrapper({ + CanRevokeDelegation, + WorkflowData: createWorkflowCollection([{ UID_PersonHead, LevelNumber, UID_PersonAdditional }]), + }); expect(workflow.canRevokeAdditionalApprover(UID_PersonHead, LevelNumber)).toBeTruthy(); }); @@ -300,12 +274,10 @@ describe('WorkflowDataWrapper', () => { const UID_PersonHead = 'some user id'; const LevelNumber = 1; - const workflow = new WorkflowDataWrapper( - { - CanRevokeDelegation, - WorkflowData: createWorkflowCollection([{ UID_PersonHead: 'some other user id', LevelNumber }]) - } - ); + const workflow = new WorkflowDataWrapper({ + CanRevokeDelegation, + WorkflowData: createWorkflowCollection([{ UID_PersonHead: 'some other user id', LevelNumber }]), + }); expect(workflow.canRevokeDelegatedApprover(UID_PersonHead, LevelNumber)).toBeFalsy(); }); @@ -316,12 +288,10 @@ describe('WorkflowDataWrapper', () => { const LevelNumber = 1; const UID_PersonInsteadOf = 'some other user id'; - const workflow = new WorkflowDataWrapper( - { - CanRevokeDelegation, - WorkflowData: createWorkflowCollection([{ UID_PersonHead, LevelNumber, UID_PersonInsteadOf }]) - } - ); + const workflow = new WorkflowDataWrapper({ + CanRevokeDelegation, + WorkflowData: createWorkflowCollection([{ UID_PersonHead, LevelNumber, UID_PersonInsteadOf }]), + }); expect(workflow.canRevokeDelegatedApprover(UID_PersonHead, LevelNumber)).toBeFalsy(); }); @@ -331,12 +301,10 @@ describe('WorkflowDataWrapper', () => { const UID_PersonHead = 'some user id'; const LevelNumber = 1; - const workflow = new WorkflowDataWrapper( - { - CanRevokeDelegation, - WorkflowData: createWorkflowCollection([{ UID_PersonHead, LevelNumber }]) - } - ); + const workflow = new WorkflowDataWrapper({ + CanRevokeDelegation, + WorkflowData: createWorkflowCollection([{ UID_PersonHead, LevelNumber }]), + }); expect(workflow.canRevokeDelegatedApprover(UID_PersonHead, LevelNumber)).toBeFalsy(); }); @@ -347,12 +315,10 @@ describe('WorkflowDataWrapper', () => { const LevelNumber = 1; const UID_PersonInsteadOf = 'some other user id'; - const workflow = new WorkflowDataWrapper( - { - CanRevokeDelegation, - WorkflowData: createWorkflowCollection([{ UID_PersonHead, LevelNumber, UID_PersonInsteadOf }]) - } - ); + const workflow = new WorkflowDataWrapper({ + CanRevokeDelegation, + WorkflowData: createWorkflowCollection([{ UID_PersonHead, LevelNumber, UID_PersonInsteadOf }]), + }); expect(workflow.canRevokeDelegatedApprover(UID_PersonHead, LevelNumber)).toBeTruthy(); }); @@ -364,12 +330,10 @@ describe('WorkflowDataWrapper', () => { const UID_PersonHead = 'some user id'; const LevelNumber = 1; - const workflow = new WorkflowDataWrapper( - { - CanRevokeDelegation, - WorkflowData: createWorkflowCollection([{ UID_PersonHead: 'some other user id', LevelNumber }]) - } - ); + const workflow = new WorkflowDataWrapper({ + CanRevokeDelegation, + WorkflowData: createWorkflowCollection([{ UID_PersonHead: 'some other user id', LevelNumber }]), + }); expect(workflow.canDenyDecision(UID_PersonHead, LevelNumber)).toBeFalsy(); }); @@ -379,11 +343,9 @@ describe('WorkflowDataWrapper', () => { const LevelNumber = 1; const IsFromDelegation = false; - const workflow = new WorkflowDataWrapper( - { - WorkflowData: createWorkflowCollection([{ UID_PersonHead, LevelNumber, IsFromDelegation }]) - } - ); + const workflow = new WorkflowDataWrapper({ + WorkflowData: createWorkflowCollection([{ UID_PersonHead, LevelNumber, IsFromDelegation }]), + }); expect(workflow.canDenyDecision(UID_PersonHead, LevelNumber)).toBeFalsy(); }); @@ -393,11 +355,9 @@ describe('WorkflowDataWrapper', () => { const LevelNumber = 1; const IsFromDelegation = false; - const workflow = new WorkflowDataWrapper( - { - WorkflowData: createWorkflowCollection([{ UID_PersonHead, LevelNumber, IsFromDelegation }]) - } - ); + const workflow = new WorkflowDataWrapper({ + WorkflowData: createWorkflowCollection([{ UID_PersonHead, LevelNumber, IsFromDelegation }]), + }); expect(workflow.canDenyDecision(UID_PersonHead, LevelNumber)).toBeFalsy(); }); @@ -408,11 +368,9 @@ describe('WorkflowDataWrapper', () => { const LevelNumber = 1; const workflowStep = { EscalationSteps: 1, LevelNumber: 2 }; - const workflow = new WorkflowDataWrapper( - { - WorkflowSteps: createWorkflowCollection([ workflowStep ]) - } - ); + const workflow = new WorkflowDataWrapper({ + WorkflowSteps: createWorkflowCollection([workflowStep]), + }); expect(workflow.canEscalateDecision(LevelNumber)).toBeFalsy(); }); @@ -421,11 +379,9 @@ describe('WorkflowDataWrapper', () => { const LevelNumber = 1; const workflowStep = { EscalationSteps: 0, LevelNumber }; - const workflow = new WorkflowDataWrapper( - { - WorkflowSteps: createWorkflowCollection([ workflowStep ]) - } - ); + const workflow = new WorkflowDataWrapper({ + WorkflowSteps: createWorkflowCollection([workflowStep]), + }); expect(workflow.canEscalateDecision(LevelNumber)).toBeFalsy(); }); @@ -434,11 +390,9 @@ describe('WorkflowDataWrapper', () => { const LevelNumber = 1; const workflowStep = { EscalationSteps: 1, LevelNumber }; - const workflow = new WorkflowDataWrapper( - { - WorkflowSteps: createWorkflowCollection([ workflowStep ]) - } - ); + const workflow = new WorkflowDataWrapper({ + WorkflowSteps: createWorkflowCollection([workflowStep]), + }); expect(workflow.canEscalateDecision(LevelNumber)).toBeTruthy(); }); diff --git a/imxweb/projects/qer/src/lib/itshop/workflow-data-wrapper.ts b/imxweb/projects/qer/src/lib/itshop/workflow-data-wrapper.ts index 7e4f9d728..4377ce1b6 100644 --- a/imxweb/projects/qer/src/lib/itshop/workflow-data-wrapper.ts +++ b/imxweb/projects/qer/src/lib/itshop/workflow-data-wrapper.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { EntityCollectionData, EntityData, MultiValue } from 'imx-qbm-dbts'; +import { EntityCollectionData, EntityData, MultiValue } from '@imx-modules/imx-qbm-dbts'; export class WorkflowDataWrapper { constructor( @@ -33,23 +33,23 @@ export class WorkflowDataWrapper { WorkflowData?: EntityCollectionData; WorkflowSteps?: EntityCollectionData; CanRevokeDelegation?: boolean; - } + }, ) {} public canEscalateDecision(decisionLevel: number): boolean { - const workflowStep = this.data?.WorkflowSteps?.Entities?.find((step) => step.Columns.LevelNumber.Value === decisionLevel)?.Columns; + const workflowStep = this.data?.WorkflowSteps?.Entities?.find((step) => step.Columns?.LevelNumber.Value === decisionLevel)?.Columns; return (workflowStep?.EscalationSteps?.Value ?? 0) !== 0; } public userAskedLastQuestion(userUid: string, decisionLevel: number): boolean { - const questionHistory = this.data.WorkflowHistory.Entities.filter( - (entityData) => entityData.Columns.DecisionLevel.Value === decisionLevel - ).sort((item1, item2) => this.ascendingDate(item1.Columns.XDateInserted?.Value, item2.Columns.XDateInserted?.Value)); + const questionHistory = this.data.WorkflowHistory?.Entities?.filter( + (entityData) => entityData.Columns?.DecisionLevel.Value === decisionLevel, + ).sort((item1, item2) => this.ascendingDate(item1.Columns?.XDateInserted?.Value, item2.Columns?.XDateInserted?.Value)); return ( - questionHistory.length > 0 && - questionHistory[0].Columns.DecisionType.Value === 'Query' && - questionHistory[0].Columns.UID_PersonHead.Value === userUid + !!questionHistory?.length && + questionHistory[0].Columns?.DecisionType.Value === 'Query' && + questionHistory[0].Columns?.UID_PersonHead.Value === userUid ); } @@ -59,13 +59,15 @@ export class WorkflowDataWrapper { public canRevokeDelegatedApprover(userUid: string, decisionLevel: number): boolean { return ( - this.data.CanRevokeDelegation && this.getWorkflowDataItem(userUid, decisionLevel)?.Columns?.UID_PersonInsteadOf?.Value?.length > 0 + (this.data.CanRevokeDelegation && !!this.getWorkflowDataItem(userUid, decisionLevel)?.Columns?.UID_PersonInsteadOf?.Value?.length) || + false ); } public canRevokeAdditionalApprover(userUid: string, decisionLevel: number): boolean { return ( - this.data.CanRevokeDelegation && this.getWorkflowDataItem(userUid, decisionLevel)?.Columns?.UID_PersonAdditional?.Value?.length > 0 + (this.data.CanRevokeDelegation && !!this.getWorkflowDataItem(userUid, decisionLevel)?.Columns?.UID_PersonAdditional?.Value?.length) || + false ); } @@ -77,10 +79,10 @@ export class WorkflowDataWrapper { if (workflowStep) { return ( - !workflowDataItem.Columns.IsFromDelegation?.Value && - !workflowDataItem.Columns.UID_PersonAdditional?.Value?.length && - !workflowDataItem.Columns.UID_PersonInsteadOf?.Value?.length && - workflowStep.Columns.IsInsteadOfAllowed?.Value + !workflowDataItem.Columns?.IsFromDelegation?.Value && + !workflowDataItem.Columns?.UID_PersonAdditional?.Value?.length && + !workflowDataItem.Columns?.UID_PersonInsteadOf?.Value?.length && + workflowStep.Columns?.IsInsteadOfAllowed?.Value ); } } @@ -96,10 +98,10 @@ export class WorkflowDataWrapper { if (workflowStep) { return ( - !workflowDataItem.Columns.IsFromDelegation?.Value && - !workflowDataItem.Columns.UID_PersonAdditional?.Value?.length && - !workflowDataItem.Columns.UID_PersonInsteadOf?.Value?.length && - workflowStep.Columns.IsAdditionalAllowed?.Value + !workflowDataItem.Columns?.IsFromDelegation?.Value && + !workflowDataItem.Columns?.UID_PersonAdditional?.Value?.length && + !workflowDataItem.Columns?.UID_PersonInsteadOf?.Value?.length && + workflowStep.Columns?.IsAdditionalAllowed?.Value ); } } @@ -114,26 +116,26 @@ export class WorkflowDataWrapper { const workflowStep = this.getWorkflowStep(workflowDataItem); if (workflowStep) { - return MultiValue.FromString(workflowStep.Columns.DirectSteps.Value) + return MultiValue.FromString(workflowStep.Columns?.DirectSteps.Value) .GetValues() .map((step) => Number(step)); } } - return undefined; + return []; } - private getWorkflowDataItem(userUid: string, decisionLevel: number): EntityData { - return this.data.WorkflowData?.Entities.filter( - (item) => item.Columns.UID_PersonHead.Value === userUid && item.Columns.LevelNumber.Value === decisionLevel + private getWorkflowDataItem(userUid: string, decisionLevel: number): EntityData | undefined { + return this.data.WorkflowData?.Entities?.filter( + (item) => item.Columns?.UID_PersonHead.Value === userUid && item.Columns?.LevelNumber.Value === decisionLevel, ) - .sort((item1, item2) => this.ascending(item1.Columns.SubLevelNumber?.Value, item2.Columns.SubLevelNumber?.Value)) + .sort((item1, item2) => this.ascending(item1.Columns?.SubLevelNumber?.Value, item2.Columns?.SubLevelNumber?.Value)) .pop(); } - private getWorkflowStep(workflowDataItem: EntityData): EntityData { - return this.data.WorkflowSteps?.Entities.filter( - (item) => item.Columns.UID_QERWorkingStep.Value === workflowDataItem.Columns.UID_QERWorkingStep.Value + private getWorkflowStep(workflowDataItem: EntityData): EntityData | undefined { + return this.data.WorkflowSteps?.Entities?.filter( + (item) => item.Columns?.UID_QERWorkingStep.Value === workflowDataItem.Columns?.UID_QERWorkingStep.Value, ).pop(); } diff --git a/imxweb/projects/qer/src/lib/itshopapprove/approval.spec.ts b/imxweb/projects/qer/src/lib/itshopapprove/approval.spec.ts index e7d1bb8e9..88a579cdd 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/approval.spec.ts +++ b/imxweb/projects/qer/src/lib/itshopapprove/approval.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,80 +24,77 @@ * */ -import { IEntity, IEntityColumn, EntityCollectionData, EntityColumnData, IClientProperty } from 'imx-qbm-dbts'; -import { ITShopConfig, PwoData } from 'imx-api-qer'; +import { ITShopConfig, PwoData } from '@imx-modules/imx-api-qer'; +import { EntityCollectionData, EntityColumnData, IClientProperty, IEntity, IEntityColumn } from '@imx-modules/imx-qbm-dbts'; import { Approval } from './approval'; describe('Approval', () => { function createColumn(value?) { return { GetMetadata: () => ({ CanEdit: () => true }), - GetValue: () => value + GetValue: () => value, } as IEntityColumn; } function createEntitySchema(columnNames) { const clientProperties = {}; - columnNames.forEach(name => - clientProperties[name] = {} as IClientProperty - ); + columnNames.forEach((name) => (clientProperties[name] = {} as IClientProperty)); return { Columns: clientProperties }; } function createEntity(columns: { [name: string]: IEntityColumn } = {}, key?) { return { GetDisplay: () => '', - GetColumn: name => columns[name] || createColumn(), + GetColumn: (name) => columns[name] || createColumn(), GetKeys: () => [key], - GetSchema: () => createEntitySchema(Object.keys(columns ?? {})) + GetSchema: () => createEntitySchema(Object.keys(columns ?? {})), } as IEntity; } [ { orderState: '', canSet: true }, { orderState: 'OrderUnsubscribe', canSet: false }, - ].forEach(testcase => + ].forEach((testcase) => it('checks if user can set ValidFrom', () => { - const approval = new Approval( - { - entity: createEntity({ - OrderState: createColumn(testcase.orderState), - ValidFrom: undefined - }), - parameterColumns: [] - } - ); + const approval = new Approval({ + commit: () => Promise.resolve(), + entity: createEntity({ + OrderState: createColumn(testcase.orderState), + ValidFrom: undefined, + }), + parameterColumns: [], + }); expect(approval.canSetValidFrom()).toEqual(testcase.canSet); - })); + }), + ); [ { orderState: '', canSet: true }, { orderState: 'OrderUnsubscribe', canSet: false }, - ].forEach(testcase => + ].forEach((testcase) => it('checks if user can set ValidUntil', () => { - const approval = new Approval( - { - entity: createEntity({ - OrderState: createColumn(testcase.orderState), - ValidUntil: undefined - }), - parameterColumns: [] - } - ); + const approval = new Approval({ + commit: () => Promise.resolve(), + entity: createEntity({ + OrderState: createColumn(testcase.orderState), + ValidUntil: undefined, + }), + parameterColumns: [], + }); expect(approval.canSetValidUntil({} as ITShopConfig)).toEqual(testcase.canSet); - })); + }), + ); it('provides decisionOffset', () => { const decisionLevel = 23; const levelNumber = 3; const expectedDecisionOffset = levelNumber - decisionLevel; - const approval = new Approval( - { - entity: createEntity({ DecisionLevel: createColumn(decisionLevel) }), - parameterColumns: [] - } - ); - approval.updateDirectDecisionTarget({ GetColumn: name => ({ LevelNumber: createColumn(levelNumber) }[name]) } as IEntity); + const approval = new Approval({ + commit: () => Promise.resolve(), + entity: createEntity({ DecisionLevel: createColumn(decisionLevel) }), + parameterColumns: [], + }); + approval.updateDirectDecisionTarget({ GetColumn: (name) => ({ LevelNumber: createColumn(levelNumber) })[name] } as IEntity); expect(approval.decisionOffset).toEqual(expectedDecisionOffset); }); @@ -107,47 +104,49 @@ describe('Approval', () => { const decisionLevel = 0; const uidWorkingStep = 'some workingstep uid'; - const approval = new Approval( - { - entity: createEntity({ + const approval = new Approval({ + commit: () => Promise.resolve(), + entity: createEntity( + { DecisionLevel: createColumn(decisionLevel), CanDelegateOrAddApprover: createColumn(true), - }, approvalKey), - parameterColumns: [], - pwoData: { - WorkflowSteps: { - TotalCount: 1, - Entities: [ - { - Columns: { - DirectSteps: { Value: '1' }, - LevelNumber:{Value: 0}, - IsAdditionalAllowed: { Value: true }, - IsInsteadOfAllowed: { Value: true }, - EscalationSteps: { Value: 1 }, - UID_QERWorkingStep: { Value: uidWorkingStep } - } as { [key: string]: EntityColumnData } - } - ] - } as EntityCollectionData, - WorkflowData: { - TotalCount: 1, - Entities: [ - { - Columns: { - LevelNumber: { Value: decisionLevel }, - UID_PersonAdditional: { Value: '' }, - UID_PersonInsteadOf: { Value: '' }, - IsFromDelegation: { Value: false }, - UID_PersonHead: { Value: userUid }, - UID_QERWorkingStep: { Value: uidWorkingStep } - } as { [key: string]: EntityColumnData } - } - ] - } as EntityCollectionData - } as PwoData - } - ); + }, + approvalKey, + ), + parameterColumns: [], + pwoData: { + WorkflowSteps: { + TotalCount: 1, + Entities: [ + { + Columns: { + DirectSteps: { Value: '1' }, + LevelNumber: { Value: 0 }, + IsAdditionalAllowed: { Value: true }, + IsInsteadOfAllowed: { Value: true }, + EscalationSteps: { Value: 1 }, + UID_QERWorkingStep: { Value: uidWorkingStep }, + } as { [key: string]: EntityColumnData }, + }, + ], + } as EntityCollectionData, + WorkflowData: { + TotalCount: 1, + Entities: [ + { + Columns: { + LevelNumber: { Value: decisionLevel }, + UID_PersonAdditional: { Value: '' }, + UID_PersonInsteadOf: { Value: '' }, + IsFromDelegation: { Value: false }, + UID_PersonHead: { Value: userUid }, + UID_QERWorkingStep: { Value: uidWorkingStep }, + } as { [key: string]: EntityColumnData }, + }, + ], + } as EntityCollectionData, + } as PwoData, + }); expect(approval.canRerouteDecision(userUid)).toEqual(true); expect(approval.canAddApprover(userUid)).toEqual(true); @@ -158,32 +157,32 @@ describe('Approval', () => { [ { value: true, expected: 1 }, { value: false, expected: 0 }, - { value: undefined, expected: 0 } - ].forEach(testcase => - it('adds IsCrossFunctional to propertyInfo only if it is "true"', () => { - const approval = new Approval( - { + { value: undefined, expected: 0 }, + ].forEach((testcase) => + it('adds IsCrossFunctional to propertyInfo only if it is "true"', () => { + const approval = new Approval({ + commit: () => Promise.resolve(), entity: createEntity({ IsCrossFunctional: { GetValue: () => testcase.value } as IEntityColumn }), - parameterColumns: [] - } - ); + parameterColumns: [], + }); - expect(approval.propertyInfo.length).toEqual(testcase.expected); - })); + expect(approval.propertyInfo.length).toEqual(testcase.expected); + }), + ); [ { value: 'some reason', expected: 1 }, { value: '', expected: 0 }, - { value: undefined, expected: 0 } - ].forEach(testcase => - it('adds OrderReason to propertyInfo only if it is non-empty', () => { - const approval = new Approval( - { + { value: undefined, expected: 0 }, + ].forEach((testcase) => + it('adds OrderReason to propertyInfo only if it is non-empty', () => { + const approval = new Approval({ + commit: () => Promise.resolve(), entity: createEntity({ OrderReason: { GetValue: () => testcase.value } as IEntityColumn }), - parameterColumns: [] - } - ); + parameterColumns: [], + }); - expect(approval.propertyInfo.length).toEqual(testcase.expected); - })); + expect(approval.propertyInfo.length).toEqual(testcase.expected); + }), + ); }); diff --git a/imxweb/projects/qer/src/lib/itshopapprove/approval.ts b/imxweb/projects/qer/src/lib/itshopapprove/approval.ts index 7a94e0681..416acb47c 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/approval.ts +++ b/imxweb/projects/qer/src/lib/itshopapprove/approval.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,8 +24,8 @@ * */ -import { PortalItshopApproveRequests, PwoData, ITShopConfig } from 'imx-api-qer'; -import { IEntity, IEntityColumn } from 'imx-qbm-dbts'; +import { ITShopConfig, PortalItshopApproveRequests, PwoData } from '@imx-modules/imx-api-qer'; +import { IEntity, IEntityColumn } from '@imx-modules/imx-qbm-dbts'; import { BaseReadonlyCdr } from 'qbm'; import { ItshopRequestEntityWrapper } from '../itshop/request-info/itshop-request-entity-wrapper.interface'; import { RequestParameterDataEntity } from '../itshop/request-info/request-parameter-data-entity.interface'; @@ -67,7 +67,7 @@ export class Approval extends PortalItshopApproveRequests implements RequestPara this.workflowWrapper = new WorkflowDataWrapper(this.pwoData); } - this.parameterColumns = entityWrapper.parameterColumns; + this.parameterColumns = entityWrapper.parameterColumns || []; this.propertyInfo = [ this.DisplayOrg, @@ -91,11 +91,11 @@ export class Approval extends PortalItshopApproveRequests implements RequestPara property.value != null && property.value !== '' && property.value !== false && - !this.parameterColumns.find((column) => column.ColumnName === property.Column.ColumnName) + !this.parameterColumns.find((column) => column.ColumnName === property.Column.ColumnName), ) .map((property) => new BaseReadonlyCdr(property.Column)); - this.currentUser = entityWrapper.uidCurrentUser; + this.currentUser = entityWrapper.uidCurrentUser ?? ''; } public async commit(): Promise { @@ -106,12 +106,12 @@ export class Approval extends PortalItshopApproveRequests implements RequestPara return this.ValidFrom.GetMetadata().CanEdit() && !this.entityWrapper.isChiefApproval && this.OrderState.value !== 'OrderUnsubscribe'; } - public canSetValidUntil(itShopConfig: ITShopConfig): boolean { + public canSetValidUntil(itShopConfig: ITShopConfig | undefined): boolean { return ( this.ValidUntil.GetMetadata().CanEdit() && !this.entityWrapper.isChiefApproval && this.OrderState.value !== 'OrderUnsubscribe' && - (!itShopConfig.VI_ITShop_ShowValidUntilQERReuse || this.TableName.value !== 'QERReuse') + (!itShopConfig?.VI_ITShop_ShowValidUntilQERReuse || this.TableName.value !== 'QERReuse') ); } diff --git a/imxweb/projects/qer/src/lib/itshopapprove/approvals-decision.enum.ts b/imxweb/projects/qer/src/lib/itshopapprove/approvals-decision.enum.ts index fb975800d..47093f06f 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/approvals-decision.enum.ts +++ b/imxweb/projects/qer/src/lib/itshopapprove/approvals-decision.enum.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,5 +28,5 @@ export enum ApprovalsDecision { none, approve, deny, - denydecision + denydecision, } diff --git a/imxweb/projects/qer/src/lib/itshopapprove/approvals-load-parameters.ts b/imxweb/projects/qer/src/lib/itshopapprove/approvals-load-parameters.ts index e1a089e88..ee5131036 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/approvals-load-parameters.ts +++ b/imxweb/projects/qer/src/lib/itshopapprove/approvals-load-parameters.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { CollectionLoadParameters } from "imx-qbm-dbts"; +import { CollectionLoadParameters } from '@imx-modules/imx-qbm-dbts'; export interface ApprovalsLoadParameters extends CollectionLoadParameters { Escalation?: boolean; diff --git a/imxweb/projects/qer/src/lib/itshopapprove/approvals-sidesheet/approvals-sidesheet.component.html b/imxweb/projects/qer/src/lib/itshopapprove/approvals-sidesheet/approvals-sidesheet.component.html index e58dae3bd..0a896f93a 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/approvals-sidesheet/approvals-sidesheet.component.html +++ b/imxweb/projects/qer/src/lib/itshopapprove/approvals-sidesheet/approvals-sidesheet.component.html @@ -1,19 +1,18 @@ -
    +
    - - #LDS#Based on the peer group analysis, it is recommended that you approve this request. + + {{ LdsKeyApprove }} - - #LDS#Based on the peer group analysis, it is recommended that you deny this request. + + {{ LdsKeyDeny }} @@ -33,8 +32,17 @@ --> - - + + + mat-stroked-button + *ngIf="data.pwo.CanAskForHelp.value" + data-imx-identifier="approvals-sidesheet-button-ask-for-helpl" + title="{{ '#LDS#Sends an inquiry about this request to an identity' | translate }}" + (click)="actionService.askForHelp([data.pwo], currentUserId)" + > + {{ '#LDS#Send inquiry' | translate }} + - + - +
    - - - -
    - - - - - - - - - - {{ pwo.UiOrderState.value === 'OrderProduct' ? ('#LDS#Requested' | translate) : '' }} - {{ - pwo.UiOrderState.value === 'OrderProlongate' - ? ('#LDS#Renewed' | translate) + - ' ' + - (pwo.ValidUntilProlongation.value ? ('#LDS#Until' | translate) + ' ' + (pwo.ValidUntilProlongation.value | shortDate) : ('#LDS#unlimited' | translate)) - : '' - }} - {{ ['OrderProduct', 'OrderProlongate'].includes(pwo.UiOrderState.value) ? '' : pwo.UiOrderState.Column.GetDisplayValue() }} - - - - - - -
    -
    - - {{ '#LDS#Priority' | translate }}: {{ pwo.PWOPriority.Column.GetDisplayValue() }} -
    -
    - - {{ '#LDS#Rule violation' | translate }} -
    -
    - - {{ '#LDS#Reserved' | translate }} -
    + + + {{ entitySchema?.Columns?.DisplayOrg?.Display }} + + + + + + + + + {{ entitySchema?.Columns?.UiOrderState?.Display }} + + + + {{ pwo.UiOrderState.value === 'OrderProduct' ? ('#LDS#Requested' | translate) : '' }} + {{ + pwo.UiOrderState.value === 'OrderProlongate' + ? ('#LDS#Renewed' | translate) + + ' ' + + (pwo.ValidUntilProlongation.value + ? ('#LDS#Until' | translate) + ' ' + (pwo.ValidUntilProlongation.value | shortDate) + : ('#LDS#unlimited' | translate)) + : '' + }} + {{ ['OrderProduct', 'OrderProlongate'].includes(pwo.UiOrderState.value) ? '' : pwo.UiOrderState.Column.GetDisplayValue() }} + + + + + + {{ entitySchema?.Columns?.OrderDate?.Display }} + + +
    {{ pwo.OrderDate?.Column.GetDisplayValue() }}
    + +
    + + + +
    +
    + + {{ '#LDS#Priority' | translate }}: {{ pwo.PWOPriority.Column.GetDisplayValue() }}
    - - - - -
    - - +
    + + {{ '#LDS#Rule violation' | translate }}
    - - - - -
    - +
    + + {{ '#LDS#Reserved' | translate }}
    - - - -
    - +
    + + + + + +
    + + +
    + +
    + + + +
    + +
    + +
    + + -
    - -
    +
    + - - + - diff --git a/imxweb/projects/qer/src/lib/itshopapprove/approvals-table.component.scss b/imxweb/projects/qer/src/lib/itshopapprove/approvals-table.component.scss index dbcd9ec0c..22fe5ab14 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/approvals-table.component.scss +++ b/imxweb/projects/qer/src/lib/itshopapprove/approvals-table.component.scss @@ -1,12 +1,12 @@ -@import '../../../../../shared/scss/common-table.scss'; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/mixins'; :host { - @include imx-flex-fill-control-hidden-overflow(); + @include flex-column-container-fill(); - .mat-stroked-button, - .mat-raised-button { - @include imx-icon-for-image-button(); + .mat-mdc-outlined-button, + .mat-mdc-unelevated-button { + @include image-button-icon(); } .recommendation-approve-icon { @@ -17,26 +17,6 @@ color: $color-red-60; } - .escalation-approver-toggle { - margin: 0.5em; - } - - .imx-table-container { - flex-grow: 1; - overflow: auto; - } - - .imx-button-bar { - @include imx-button-bar(); - } - - .imx-decision { - @include imx-button-column-right(); - > button:not(:last-of-type) { - margin-right: 5px; - } - } - @media only screen and (max-width: 1024px) { ::ng-deep .mat-column-OrderDate, ::ng-deep .mat-column-decision { @@ -47,7 +27,8 @@ .toggle-wrapper { /* so that the tooltip on the toggle is centered */ flex-grow: 0; - display: inline; + display:flex; + justify-content: space-between; } .imx-badge-container { @@ -65,51 +46,6 @@ flex-direction: column; height: 100%; } - - .imx-icons-container { - padding-right: 15px; - .table-icon { - display: flex; - align-items: center; - font-weight: 600; - height: 14px; - &:not(:first-of-type) { - margin-left: 8px; - padding-left: 8px; - } - .mat-icon, - .eui-icon { - padding-right: 6px; - } - } - } -} - -:host { - .imx-icons-container { - .table-icon { - color: $color-gray-60; - &--warning { - .eui-icon { - color: $color-orange-60; - } - } - &--primary { - .mat-icon { - color: $color-blue-60; - } - } - &--alert { - .eui-icon, - span { - color: $color-red-60; - } - } - &:not(:first-of-type) { - border-left: 1px solid $color-gray-20; - } - } - } } .eui-dark-theme { @@ -117,31 +53,6 @@ .imx-data-tree-container { background-color: $color-gray-70; } - .imx-icons-container { - .table-icon { - color: $color-gray-10; - - &--warning { - .eui-icon { - color: $color-orange-40; - } - } - &--primary { - .mat-icon { - color: $color-blue-40; - } - } - &--alert { - .eui-icon, - span { - color: $color-red-40; - } - } - &:not(:first-of-type) { - border-left: 1px solid $color-gray-10; - } - } - } } } @@ -150,30 +61,6 @@ .imx-data-tree-container { background-color: $color-gray-100; } - .imx-icons-container { - .table-icon { - color: $color-gray-0; - &--warning { - .eui-icon { - color: $color-orange-40; - } - } - &--primary { - .mat-icon { - color: $color-blue-40; - } - } - &--alert { - .eui-icon, - span { - color: $color-red-40; - } - } - &:not(:first-of-type) { - border-left: 1px solid $color-gray-10; - } - } - } } } diff --git a/imxweb/projects/qer/src/lib/itshopapprove/approvals-table.component.spec.ts b/imxweb/projects/qer/src/lib/itshopapprove/approvals-table.component.spec.ts index aa976343a..af247e0aa 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/approvals-table.component.spec.ts +++ b/imxweb/projects/qer/src/lib/itshopapprove/approvals-table.component.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,19 +25,18 @@ */ import { EuiSidesheetService } from '@elemental-ui/core'; -import { of, Subject } from 'rxjs'; import { MockBuilder, MockedComponentFixture, MockRender } from 'ng-mocks'; +import { of, Subject } from 'rxjs'; -import { IEntity, IEntityColumn } from 'imx-qbm-dbts'; +import { IEntity, IEntityColumn } from '@imx-modules/imx-qbm-dbts'; -import { clearStylesFromDOM, ExtService } from 'qbm'; -import { ApprovalsTableComponent } from './approvals-table.component'; +import { clearStylesFromDOM, DataViewSource, ExtService, FakeDataViewSource } from 'qbm'; import { ProjectConfigurationService } from '../project-configuration/project-configuration.service'; -import { ApprovalsService } from './approvals.service'; -import { Approval } from './approval'; -import { WorkflowActionService } from './workflow-action/workflow-action.service'; import { UserModelService } from '../user/user-model.service'; +import { Approval } from './approval'; +import { ApprovalsTableComponent } from './approvals-table.component'; import { ApprovalsModule } from './approvals.module'; +import { WorkflowActionService } from './workflow-action/workflow-action.service'; describe('ApprovalsTable', () => { let component: ApprovalsTableComponent; @@ -62,7 +61,7 @@ describe('ApprovalsTable', () => { getConfig: jasmine.createSpy('getConfig').and.returnValue( Promise.resolve({ ITShopConfig: {}, - }) + }), ), }; @@ -89,10 +88,10 @@ describe('ApprovalsTable', () => { .mock(ApprovalsModule) .mock(ExtService, extServiceStub as unknown) .mock(EuiSidesheetService, sideSheetTestHelper.servicestub) - .mock(UserModelService,{ getFeatures: () => Promise.resolve({}) }) - .mock(WorkflowActionService, { applied: new Subject() }) + .mock(UserModelService, { getFeatures: () => Promise.resolve({}) }) + .mock(WorkflowActionService, { applied: new Subject() }) .mock(ProjectConfigurationService, projectConfigurationServiceStub) - .mock(ApprovalsService); + .mock(DataViewSource, FakeDataViewSource); }); beforeEach(() => { @@ -151,6 +150,6 @@ describe('ApprovalsTable', () => { component.onSelectionChanged([approval]); expect(component.selectedItems.length).toBe(1); - expect(component.selectedItems[0].DocumentNumber.value).toBe('123'); + expect((component.selectedItems[0] as Approval).DocumentNumber.value).toBe('123'); }); }); diff --git a/imxweb/projects/qer/src/lib/itshopapprove/approvals-table.component.ts b/imxweb/projects/qer/src/lib/itshopapprove/approvals-table.component.ts index dca15aaa9..080532230 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/approvals-table.component.ts +++ b/imxweb/projects/qer/src/lib/itshopapprove/approvals-table.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,44 +24,54 @@ * */ -import { Component, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { Params } from '@angular/router'; import { EuiSidesheetService } from '@elemental-ui/core'; -import { Subscription } from 'rxjs'; import { TranslateService } from '@ngx-translate/core'; +import { Subscription } from 'rxjs'; -import { PwoExtendedData, RecommendationEnum, ViewConfigData } from 'imx-api-qer'; -import { ValType, ExtendedTypedEntityCollection, TypedEntity, EntitySchema, DataModel, IClientProperty } from 'imx-qbm-dbts'; +import { PwoExtendedData, RecommendationEnum, ViewConfigData } from '@imx-modules/imx-api-qer'; +import { + CollectionLoadParameters, + DataModel, + EntitySchema, + ExtendedTypedEntityCollection, + IClientProperty, + ValType, +} from '@imx-modules/imx-qbm-dbts'; import { - DataSourceToolbarSettings, - ClassloggerService, AuthenticationService, - DataTableComponent, - SettingsService, - IExtension, - ExtService, - buildAdditionalElementsString, - DataSourceToolbarViewConfig, - ClientPropertyForTableColumns, BusyService, + calculateSidesheetWidth, + ClassloggerService, + ClientPropertyForTableColumns, + DataSourceToolbarSettings, + DataSourceToolbarViewConfig, + DataViewInitParameters, + DataViewSource, + ExtService, + IExtension, + ISessionState, + SettingsService, } from 'qbm'; -import { ApprovalsSidesheetComponent } from './approvals-sidesheet/approvals-sidesheet.component'; -import { Approval } from './approval'; +import { QerPermissionsService } from '../admin/qer-permissions.service'; import { ProjectConfigurationService } from '../project-configuration/project-configuration.service'; -import { ApprovalsService } from './approvals.service'; -import { WorkflowActionService } from './workflow-action/workflow-action.service'; -import { ApprovalsLoadParameters } from './approvals-load-parameters'; -import { ApprovalsDecision } from './approvals-decision.enum'; import { UserModelService } from '../user/user-model.service'; +import { Approval } from './approval'; +import { ApprovalsDecision } from './approvals-decision.enum'; +import { ApprovalsLoadParameters } from './approvals-load-parameters'; +import { ApprovalsSidesheetComponent } from './approvals-sidesheet/approvals-sidesheet.component'; +import { ApprovalsService } from './approvals.service'; import { RecommendationSidesheetComponent } from './recommendation-sidesheet/recommendation-sidesheet.component'; -import { QerPermissionsService } from '../admin/qer-permissions.service'; +import { WorkflowActionService } from './workflow-action/workflow-action.service'; -import { ViewConfigService } from '../view-config/view-config.service'; import { isCancelPwO } from '../admin/qer-permissions-helper'; +import { ViewConfigService } from '../view-config/view-config.service'; @Component({ templateUrl: './approvals-table.component.html', selector: 'imx-approvals-table', styleUrls: ['./approvals-table.component.scss'], + providers: [DataViewSource], }) export class ApprovalsTableComponent implements OnInit, OnDestroy { public recApprove = RecommendationEnum.Approve; @@ -69,39 +79,42 @@ export class ApprovalsTableComponent implements OnInit, OnDestroy { private isChiefApprover = false; - public get tableReady() { return this.countTableLoading == 0; } - private countTableLoading = 0; - @Input() public params: Params = {}; + @Input() hideToolbar = true; public isUserEscalationApprover = false; + public abortController: AbortController = new AbortController(); + public get canWithdrawAdditionalApprover(): boolean { - return this.selectedItems.every((item) => item.canWithdrawAdditionalApprover(this.currentUserId)); + return this.selectedItems.every((item: Approval) => item.canWithdrawAdditionalApprover(this.currentUserId)); } public get canAddApprover(): boolean { - return this.selectedItems.every((item) => item.canAddApprover(this.currentUserId)); + return this.selectedItems.every((item: Approval) => item.canAddApprover(this.currentUserId)); } public get canDelegateDecision(): boolean { - return this.selectedItems.every((item) => item.canDelegateDecision(this.currentUserId)); + return this.selectedItems.every((item: Approval) => item.canDelegateDecision(this.currentUserId)); } public get canDenyApproval(): boolean { - return this.selectedItems.every((item) => item.canDenyApproval(this.currentUserId)); + return this.selectedItems.every((item: Approval) => item.canDenyApproval(this.currentUserId)); } public get canEscalateDecision(): boolean { - return this.selectedItems.every((item) => item.canEscalateDecision); + return this.selectedItems.every((item: Approval) => item.canEscalateDecision); } public get canRerouteDecision(): boolean { - return this.selectedItems.every((item) => item.canRerouteDecision(this.currentUserId)); + return this.selectedItems.every((item: Approval) => item.canRerouteDecision(this.currentUserId)); } - public get canResetReservation(): boolean{ - return this.selectedItems.every((item)=> item.canResetReservation(this.isChiefApprover)); + public get canResetReservation(): boolean { + return this.selectedItems.every((item: Approval) => item.canResetReservation(this.isChiefApprover)); } - public get canRecallInquiry(): boolean{ - return this.selectedItems.every((item)=> item.canRecallInquiry); + public get canSendInquiry(): boolean { + return this.selectedItems.every((item: Approval) => item.CanAskForHelp.value); } + public get canRecallInquiry(): boolean { + return this.selectedItems.every((item: Approval) => item.canRecallInquiry); + } public get canPerformActions(): boolean { return ( @@ -112,22 +125,19 @@ export class ApprovalsTableComponent implements OnInit, OnDestroy { this.canDenyApproval || this.canRerouteDecision || this.canEscalateDecision || - this.canRecallInquiry || this.canResetReservation) + this.canSendInquiry || + this.canRecallInquiry || + this.canResetReservation) ); } public currentUserId: string; - public dstSettings: DataSourceToolbarSettings; public readonly entitySchema: EntitySchema; public canBeDelegated = false; public selectedItems: Approval[] = []; - public approvalsCollection: ExtendedTypedEntityCollection; - public hasData = false; public busyService = new BusyService(); - @ViewChild(DataTableComponent) private readonly table: DataTableComponent; - private navigationState: ApprovalsLoadParameters; private approvalsDecision: ApprovalsDecision = ApprovalsDecision.none; private extensions: IExtension[] = []; @@ -151,7 +161,8 @@ export class ApprovalsTableComponent implements OnInit, OnDestroy { private readonly userModelService: UserModelService, authentication: AuthenticationService, private readonly ext: ExtService, - private readonly permissions : QerPermissionsService, + private readonly permissions: QerPermissionsService, + public dataSource: DataViewSource, ) { this.navigationState = { PageSize: settingsService.DefaultPageSize, StartIndex: 0 }; this.entitySchema = approvalsService.PortalItshopApproveRequestsSchema; @@ -164,27 +175,27 @@ export class ApprovalsTableComponent implements OnInit, OnDestroy { ColumnName: 'decision', Type: ValType.String, afterAdditionals: true, - untranslatedDisplay: '#LDS#Approval decision' + untranslatedDisplay: '#LDS#Approval decision', }, { ColumnName: 'recommendations', Type: ValType.String, - untranslatedDisplay: '#LDS#Recommendation' + untranslatedDisplay: '#LDS#Recommendation', }, ]; this.subscriptions.push( this.actionService.applied.subscribe(async () => { - this.getData(); - this.table.clearSelection(); - }) + this.dataSource.selection.clear(); + this.dataSource.updateState(); + }), ); this.subscriptions.push( - authentication.onSessionResponse.subscribe((state) => { - this.currentUserId = state.UserUid; + authentication.onSessionResponse.subscribe((state: ISessionState) => { + this.currentUserId = state.UserUid || ''; if (state.IsLoggedIn) { this.viewEscalation = false; } - }) + }), ); this.userModelService.getFeatures().then((featureInfo) => { this.isUserEscalationApprover = isCancelPwO(featureInfo.Features || []); @@ -193,8 +204,11 @@ export class ApprovalsTableComponent implements OnInit, OnDestroy { this.extensions = this.ext.Registry[this.UID_ComplianceRuleId]; if (this.extensions && this.extensions.length > 0) { - this.extensions[0].subject.subscribe((dstSettings: DataSourceToolbarSettings) => { - this.dstSettings = dstSettings; + this.extensions[0].subject?.subscribe((dstSettings: DataSourceToolbarSettings) => { + this.dataSource.collectionData.update((collectionData) => ({ + ...collectionData, + Data: dstSettings.dataSource?.Data as Approval[], + })); }); } } @@ -204,9 +218,20 @@ export class ApprovalsTableComponent implements OnInit, OnDestroy { const isBusy = this.busyService.beginBusy(); try { - this.dataModel = await this.approvalsService.getApprovalDataModel(); + this.dataModel = await this.approvalsService.getApprovalDataModel(this.abortController.signal); this.isChiefApprover = await this.permissions.isCancelPwO(); - this.viewConfig = await this.viewConfigService.getInitialDSTExtension(this.dataModel, this.viewConfigPath); + if (this.abortController.signal.aborted) { + return; + } + this.viewConfig = await this.viewConfigService.getInitialDSTExtension( + this.dataModel, + this.viewConfigPath, + this.abortController.signal, + ); + + if (this.abortController.signal.aborted) { + return; + } await this.getData(); this.handleDecision(); @@ -217,6 +242,8 @@ export class ApprovalsTableComponent implements OnInit, OnDestroy { public ngOnDestroy(): void { this.approvalsService.abortCall(); + this.dataSource?.abortCall(); + this.abortController.abort(); // Set service value back to false since the toggle value is stored there this.approvalsService.isChiefApproval = false; this.subscriptions.forEach((s) => s.unsubscribe()); @@ -225,13 +252,13 @@ export class ApprovalsTableComponent implements OnInit, OnDestroy { public async updateConfig(config: ViewConfigData): Promise { await this.viewConfigService.putViewConfig(config); this.viewConfig = await this.viewConfigService.getDSTExtensionChanges(this.viewConfigPath); - this.dstSettings.viewConfig = this.viewConfig; + this.dataSource.viewConfig.set(this.viewConfig); } public async deleteConfigById(id: string): Promise { await this.viewConfigService.deleteViewConfig(id); this.viewConfig = await this.viewConfigService.getDSTExtensionChanges(this.viewConfigPath); - this.dstSettings.viewConfig = this.viewConfig; + this.dataSource.viewConfig.set(this.viewConfig); } public get viewEscalation(): boolean { @@ -241,40 +268,56 @@ export class ApprovalsTableComponent implements OnInit, OnDestroy { this.approvalsService.isChiefApproval = val; } - public switchEscalation(): Promise { - // Set start index to 0 and clear selection before changing - const navigationState = { - ...this.navigationState, - ...{ - StartIndex: 0, - }, - }; - this.table.clearSelection(); - return this.getData(navigationState); + public switchEscalation(): void { + this.dataSource.selection.clear(); + this.dataSource.state.update((state) => ({ ...state, StartIndex: 0 })); + this.dataSource.updateState(); } - public getAdditionalText(entity: Approval, additional: IClientProperty[]): string { - return buildAdditionalElementsString(entity.GetEntity(), additional); + public getAdditionalText(entity: Approval): string { + return ( + this.dataSource + .additionalListColumns() + ?.map((elem: IClientProperty) => { + return `${elem?.Display || elem?.ColumnName}: ${elem?.ColumnName == null ? '-' : entity.GetEntity().GetColumn(elem.ColumnName).GetDisplayValue() || '-'}`; + }) + .filter((elem) => !!elem) + .join('; ') || '' + ); } - public async getData(parameters?: ApprovalsLoadParameters): Promise { - if (parameters) { - this.navigationState = parameters; - } - - const isBusy = this.busyService.beginBusy(); - - try { - this.approvalsCollection = await this.approvalsService.get(this.navigationState, {signal: this.approvalsService.abortController.signal}); - this.hasData = this.approvalsCollection?.totalCount > 0 || (this.navigationState.search ?? '') !== ''; - this.updateTable(); - - if (this.extensions && this.extensions[0]) { - this.extensions[0].inputData = this.dstSettings; - } - } finally { - isBusy.endBusy(); - } + public async getData(): Promise { + const dataViewInitParameters: DataViewInitParameters = { + execute: ( + params: CollectionLoadParameters, + signal: AbortSignal, + ): Promise | undefined> => { + return Promise.resolve( + this.approvalsService.get(params, { signal }).then((collectionData) => { + if (this.extensions) { + const dstSettings: DataSourceToolbarSettings = { + dataSource: collectionData, + navigationState: this.dataSource.state, + entitySchema: this.entitySchema, + extendedData: collectionData?.extendedData?.Data, + }; + this.extensions[0].inputData = dstSettings; + } + return collectionData; + }), + ); + }, + schema: this.entitySchema, + columnsToDisplay: this.displayedColumns, + dataModel: this.dataModel, + exportFunction: this.approvalsService.exportApprovalRequests(this.dataSource.state()), + viewConfig: this.viewConfig, + highlightEntity: (approval: Approval) => { + this.editPwo(approval); + }, + selectionChange: (approval: Approval[]) => this.onSelectionChanged(approval), + }; + this.dataSource.init(dataViewInitParameters); } /** @@ -289,7 +332,7 @@ export class ApprovalsTableComponent implements OnInit, OnDestroy { title: await this.translator.get('#LDS#Heading View Request Details').toPromise(), subTitle: pwo.GetEntity().GetDisplay(), padding: '0', - width: 'max(700px, 60%)', + width: calculateSidesheetWidth(1000), testId: 'approvals-sidesheet', data: { pwo, @@ -301,7 +344,7 @@ export class ApprovalsTableComponent implements OnInit, OnDestroy { .toPromise(); if (doUpdate) { - await this.getData(); + await this.dataSource.updateState(); } } @@ -318,9 +361,9 @@ export class ApprovalsTableComponent implements OnInit, OnDestroy { subTitle: pwo.GetEntity().GetDisplay(), panelClass: 'imx-sidesheet', padding: '0', - width: 'max(700px, 60%)', + width: calculateSidesheetWidth(1000), testId: 'approval-recommendation-sidesheet', - data: pwo.pwoData.Recommendation, + data: { recommendations: pwo.pwoData.Recommendation }, }) .afterClosed() .toPromise(); @@ -332,43 +375,11 @@ export class ApprovalsTableComponent implements OnInit, OnDestroy { } } - public onSearch(keywords: string): Promise { - const navigationState = { - ...this.navigationState, - ...{ - StartIndex: 0, - search: keywords, - }, - }; - - return this.getData(navigationState); - } - public onSelectionChanged(items: Approval[]): void { this.logger.trace(this, 'selection changed', items); this.selectedItems = items; } - private updateTable(): void { - if (this.approvalsCollection) { - const exportMethod = this.approvalsService.exportApprovalRequests(this.navigationState); - exportMethod.initialColumns = this.displayedColumns.map(col => col.ColumnName); - this.dstSettings = { - dataSource: this.approvalsCollection, - extendedData: this.approvalsCollection.extendedData.Data, - entitySchema: this.entitySchema, - navigationState: this.navigationState, - displayedColumns: this.displayedColumns, - dataModel: this.dataModel, - viewConfig: this.viewConfig, - filters: this.dataModel.Filters, - exportMethod - }; - } else { - this.dstSettings = undefined; - } - } - private parseParams(): void { // Cases: VI_BuildITShopLink_Approve, VI_BuildITShopLink_Deny, VI_BuildITShopLink_Reject if (this.params.uid_personwantsorg && this.params.uid_pwohelperpwo && this.params.decision) { @@ -392,23 +403,19 @@ export class ApprovalsTableComponent implements OnInit, OnDestroy { } private handleDecision(): void { - if ( - this.approvalsDecision === ApprovalsDecision.none || - this.approvalsCollection.Data == null || - this.approvalsCollection.Data.length === 0 - ) { + if (this.approvalsDecision === ApprovalsDecision.none || !!this.dataSource.collectionData()?.Data?.length) { return; } switch (this.approvalsDecision) { case ApprovalsDecision.approve: - this.actionService.approve(this.approvalsCollection.Data); + this.actionService.approve(this.dataSource.collectionData()?.Data || []); break; case ApprovalsDecision.deny: - this.actionService.deny(this.approvalsCollection.Data); + this.actionService.deny(this.dataSource.collectionData()?.Data || []); break; case ApprovalsDecision.denydecision: - this.actionService.denyDecisions(this.approvalsCollection.Data); + this.actionService.denyDecisions(this.dataSource.collectionData()?.Data || []); break; } } diff --git a/imxweb/projects/qer/src/lib/itshopapprove/approvals.component.html b/imxweb/projects/qer/src/lib/itshopapprove/approvals.component.html index 9b83e7d3e..bf8faa4ca 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/approvals.component.html +++ b/imxweb/projects/qer/src/lib/itshopapprove/approvals.component.html @@ -1,26 +1,38 @@ -
    -

    +
    +

    #LDS#Heading Pending Requests -

    +

    + + +
    - + - + - + - - + + +
    + + + + diff --git a/imxweb/projects/qer/src/lib/itshopapprove/approvals.component.scss b/imxweb/projects/qer/src/lib/itshopapprove/approvals.component.scss index 30c1daf85..edbaec5f7 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/approvals.component.scss +++ b/imxweb/projects/qer/src/lib/itshopapprove/approvals.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; :host { display: flex; @@ -15,61 +15,7 @@ overflow: hidden; } -.mat-tab-group { - height: 100%; - overflow: hidden; - - ::ng-deep .mat-tab-body-wrapper { - flex: 1 1 auto; - - .mat-tab-body-content { - height: 100%; - overflow: hidden; - display: flex; - flex-direction: column; - } - } -} - .heading-wrapper { display: flex; align-items: flex-start; - - .helper-alert { - display: flex; - margin: 0 0 20px auto; - align-self: flex-end; - flex-basis: 50%; - span { - display: block; - } - } -} - -:host { - .mat-tab-group { - ::ng-deep .mat-tab-labels { - background-color: $color-gray-0; - } - } -} - -.eui-dark-theme { - :host { - .mat-tab-group { - ::ng-deep .mat-tab-labels { - background-color: $color-gray-70; - } - } - } -} - -.eui-contrast-theme { - :host { - .mat-tab-group { - ::ng-deep .mat-tab-labels { - background-color: $color-gray-100; - } - } - } } diff --git a/imxweb/projects/qer/src/lib/itshopapprove/approvals.component.spec.ts b/imxweb/projects/qer/src/lib/itshopapprove/approvals.component.spec.ts index 7215ac024..7b59f23e3 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/approvals.component.spec.ts +++ b/imxweb/projects/qer/src/lib/itshopapprove/approvals.component.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -41,13 +41,17 @@ describe('Approvals', () => { beforeEach(() => { return MockBuilder(ApprovalsComponent, ApprovalsModule) .mock(EuiSidesheetService) - .mock(UserModelService,{getPendingItems: jasmine.createSpy('getPendingItems').and.returnValue(Promise.resolve({}))},{export:true}) + .mock( + UserModelService, + { getPendingItems: jasmine.createSpy('getPendingItems').and.returnValue(Promise.resolve({})) }, + { export: true }, + ) .mock( ActivatedRoute, { queryParams: from([]), }, - { export: true } + { export: true }, ); }); diff --git a/imxweb/projects/qer/src/lib/itshopapprove/approvals.component.ts b/imxweb/projects/qer/src/lib/itshopapprove/approvals.component.ts index c69a48a26..16e18a557 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/approvals.component.ts +++ b/imxweb/projects/qer/src/lib/itshopapprove/approvals.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,13 +24,16 @@ * */ -import { Component, OnInit } from '@angular/core'; +import { ChangeDetectorRef, Component, OnInit, ViewChild } from '@angular/core'; import { ActivatedRoute, Params } from '@angular/router'; import { first } from 'rxjs/operators'; +import { PwoExtendedData } from '@imx-modules/imx-api-qer'; +import { DataViewSource, HELP_CONTEXTUAL, HelpContextualValues } from 'qbm'; import { PendingItemsType } from '../user/pending-items-type.interface'; import { UserModelService } from '../user/user-model.service'; -import { HELP_CONTEXTUAL, HelpContextualValues } from 'qbm'; +import { Approval } from './approval'; +import { ApprovalsTableComponent } from './approvals-table.component'; @Component({ templateUrl: './approvals.component.html', @@ -42,10 +45,13 @@ export class ApprovalsComponent implements OnInit { public tabIndex = 0; public hasInquiries = false; public viewReady = false; + public dataSource: DataViewSource; + @ViewChild('approvalsTableComponent', { static: false }) public approvalsTableComponent: ApprovalsTableComponent; constructor( private readonly activatedRoute: ActivatedRoute, - private readonly usermodelService: UserModelService + private readonly usermodelService: UserModelService, + private changeDetectionRef: ChangeDetectorRef, ) {} public async ngOnInit(): Promise { @@ -54,24 +60,29 @@ export class ApprovalsComponent implements OnInit { this.hasInquiries = pendingItems.OpenInquiries > 0; const queryParams = await this.activatedRoute.queryParams.pipe(first()).toPromise(); const result = {}; - for (const [key, value] of Object.entries(queryParams)) { - result[key.toLowerCase()] = value; + if (queryParams) { + for (const [key, value] of Object.entries(queryParams)) { + result[key.toLowerCase()] = value; + } } this.params = result; if (this.params.inquiries) { this.tabIndex = 1; - this.hasInquiries = true;} + this.hasInquiries = true; + } } finally { this.viewReady = true; + this.changeDetectionRef.detectChanges(); + this.dataSource = this.approvalsTableComponent?.dataSource; } } - public get contextId(): HelpContextualValues{ - if(this.tabIndex === 0){ + public get contextId(): HelpContextualValues { + if (this.tabIndex === 0) { return HELP_CONTEXTUAL.PendingRequest; - }else{ + } else { return HELP_CONTEXTUAL.PendingRequestInquiries; } } diff --git a/imxweb/projects/qer/src/lib/itshopapprove/approvals.module.ts b/imxweb/projects/qer/src/lib/itshopapprove/approvals.module.ts index edee8dc8c..56850e22e 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/approvals.module.ts +++ b/imxweb/projects/qer/src/lib/itshopapprove/approvals.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,56 +24,56 @@ * */ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatTabsModule } from '@angular/material/tabs'; import { RouterModule, Routes } from '@angular/router'; -import { TranslateModule } from '@ngx-translate/core'; import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; -import { MatTabsModule } from '@angular/material/tabs'; +import { TranslateModule } from '@ngx-translate/core'; import { BulkPropertyEditorModule, + BusyIndicatorModule, CdrModule, ClassloggerService, DataSourceToolbarModule, DataTableModule, + DataViewModule, DateModule, EntityModule, + HelpContextualModule, LdsReplaceModule, MenuItem, MenuService, - BusyIndicatorModule, RouteGuardService, SelectedElementsModule, - HelpContextualModule, - HELP_CONTEXTUAL } from 'qbm'; -import { ApprovalsComponent } from './approvals.component'; -import { ApprovalsTableComponent } from './approvals-table.component'; -import { ApprovalsService } from './approvals.service'; -import { ApprovalsSidesheetComponent } from './approvals-sidesheet/approvals-sidesheet.component'; -import { QueryPersonComponent } from './workflow-actions/query-person.component'; -import { WorkflowActionComponent } from './workflow-action/workflow-action.component'; +import { MatCheckboxModule } from '@angular/material/checkbox'; import { ItshopModule } from '../itshop/itshop.module'; +import { JustificationModule } from '../justification/justification.module'; import { RequestHistoryModule } from '../request-history/request-history.module'; import { RequestsFeatureGuardService } from '../requests-feature-guard.service'; -import { JustificationModule } from '../justification/justification.module'; -import { WorkflowMultiActionComponent } from './workflow-action/workflow-multi-action/workflow-multi-action.component'; -import { WorkflowSingleActionComponent } from './workflow-action/workflow-single-action/workflow-single-action.component'; +import { ApprovalsSidesheetComponent } from './approvals-sidesheet/approvals-sidesheet.component'; +import { ApprovalsTableComponent } from './approvals-table.component'; +import { ApprovalsComponent } from './approvals.component'; +import { ApprovalsService } from './approvals.service'; +import { InquiriesComponent } from './inquiries/inquiries.component'; import { RecommendationSidesheetComponent } from './recommendation-sidesheet/recommendation-sidesheet.component'; import { ApprovalHistoryComponent } from './workflow-action/approval-history/approval-history.component'; import { HistoryFilterComponent } from './workflow-action/approval-history/history-filter/history-filter.component'; -import { MatCheckboxModule } from '@angular/material/checkbox'; -import { InquiriesComponent } from './inquiries/inquiries.component' +import { WorkflowActionComponent } from './workflow-action/workflow-action.component'; +import { WorkflowMultiActionComponent } from './workflow-action/workflow-multi-action/workflow-multi-action.component'; +import { WorkflowSingleActionComponent } from './workflow-action/workflow-single-action/workflow-single-action.component'; +import { QueryPersonComponent } from './workflow-actions/query-person.component'; const routes: Routes = [ { path: 'itshop/approvals', component: ApprovalsComponent, canActivate: [RouteGuardService, RequestsFeatureGuardService], - resolve: [RouteGuardService] + resolve: [RouteGuardService], }, ]; @@ -89,7 +89,7 @@ const routes: Routes = [ RecommendationSidesheetComponent, InquiriesComponent, ApprovalHistoryComponent, - HistoryFilterComponent + HistoryFilterComponent, ], imports: [ BulkPropertyEditorModule, @@ -114,52 +114,44 @@ const routes: Routes = [ TranslateModule, SelectedElementsModule, HelpContextualModule, + DataViewModule, ], - providers: [ - ApprovalsService - ], - exports: [ - RecommendationSidesheetComponent - ] + providers: [ApprovalsService], + exports: [RecommendationSidesheetComponent, WorkflowActionComponent], }) export class ApprovalsModule { constructor( private readonly menuService: MenuService, - logger: ClassloggerService + logger: ClassloggerService, ) { logger.info(this, '▶️ ApprovalsModule loaded'); this.setupMenu(); } private setupMenu(): void { - this.menuService.addMenuFactories( - (preProps: string[], features: string[]) => { - - const items: MenuItem[] = []; + this.menuService.addMenuFactories((preProps: string[], features: string[]) => { + const items: MenuItem[] = []; - if (preProps.includes('ITSHOP')) { - items.push( - { - id: 'QER_Request_PendingRequests', - navigationCommands: { - commands: ['itshop', 'approvals'] - }, - title: '#LDS#Menu Entry Pending requests', - sorting: '10-30', - }, - ); - } + if (preProps.includes('ITSHOP')) { + items.push({ + id: 'QER_Request_PendingRequests', + navigationCommands: { + commands: ['itshop', 'approvals'], + }, + title: '#LDS#Menu Entry Pending requests', + sorting: '10-30', + }); + } - if (items.length === 0) { - return null; - } - return { - id: 'ROOT_Request', - title: '#LDS#Requests', - sorting: '10', - items - }; - }, - ); + if (items.length === 0) { + return; + } + return { + id: 'ROOT_Request', + title: '#LDS#Requests', + sorting: '10', + items, + }; + }); } } diff --git a/imxweb/projects/qer/src/lib/itshopapprove/approvals.service.ts b/imxweb/projects/qer/src/lib/itshopapprove/approvals.service.ts index 91539e6c6..3543ef311 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/approvals.service.ts +++ b/imxweb/projects/qer/src/lib/itshopapprove/approvals.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,30 +26,41 @@ import { Injectable } from '@angular/core'; -import { ExtendedTypedEntityCollection, EntitySchema, DataModel, MethodDescriptor, EntityCollectionData, MethodDefinition, ApiRequestOptions } from 'imx-qbm-dbts'; import { - PortalItshopApproveRequests, - OtherApproverInput, - DirectDecisionInput, DecisionInput, - PwoExtendedData, - RecallDecisionInput, - ReasonInput, DenyDecisionInput, + DirectDecisionInput, + OtherApproverInput, + PortalItshopApproveRequests, + PwoExtendedData, PwoQueryInput, + ReasonInput, + RecallDecisionInput, V2ApiClientMethodFactory, -} from 'imx-api-qer'; -import { Approval } from './approval'; +} from '@imx-modules/imx-api-qer'; +import { + ApiRequestOptions, + DataModel, + EntityCollectionData, + EntitySchema, + ExtendedTypedEntityCollection, + MethodDefinition, + MethodDescriptor, +} from '@imx-modules/imx-qbm-dbts'; +import { DataSourceToolbarExportMethod } from 'qbm'; +import { ItshopRequestService } from '../itshop/itshop-request.service'; import { QerApiService } from '../qer-api-client.service'; +import { Approval } from './approval'; import { ApprovalsLoadParameters } from './approvals-load-parameters'; -import { ItshopRequestService } from '../itshop/itshop-request.service'; -import { DataSourceToolbarExportMethod } from 'qbm'; @Injectable() export class ApprovalsService { public abortController = new AbortController(); - constructor(private readonly apiService: QerApiService, private readonly itshopRequest: ItshopRequestService) {} + constructor( + private readonly apiService: QerApiService, + private readonly itshopRequest: ItshopRequestService, + ) {} public get PortalItshopApproveRequestsSchema(): EntitySchema { return this.apiService.typedClient.PortalItshopApproveRequests.GetSchema(); @@ -70,21 +81,27 @@ export class ApprovalsService { public async get( parameters: ApprovalsLoadParameters, - requestOpts?: ApiRequestOptions - ): Promise> { - const collection = await this.apiService.typedClient.PortalItshopApproveRequests.Get({ - Escalation: this.isChiefApproval, - ...parameters, - },requestOpts); - - return collection == null ? undefined: { - tableName: collection.tableName, - totalCount: collection.totalCount, - Data: collection.Data.map((element, index) => - this.itshopRequest.createRequestApprovalItem(element, { ...collection.extendedData, ...{ index } }) - ), - extendedData: collection.extendedData, - }; + requestOpts?: ApiRequestOptions, + ): Promise | undefined> { + const collection = await this.apiService.typedClient.PortalItshopApproveRequests.Get( + { + Escalation: this.isChiefApproval, + ...parameters, + }, + requestOpts, + ); + + return collection == null + ? undefined + : { + tableName: collection.tableName, + totalCount: collection.totalCount, + Data: collection.Data.map((element, index) => { + const parameter = collection.extendedData ? { ...collection.extendedData, ...{ index } } : undefined; + return this.itshopRequest.createRequestApprovalItem(element, parameter); + }), + extendedData: collection.extendedData, + }; } public exportApprovalRequests(parameters: ApprovalsLoadParameters): DataSourceToolbarExportMethod { @@ -106,8 +123,8 @@ export class ApprovalsService { }; } - public async getApprovalDataModel(): Promise { - return this.apiService.client.portal_itshop_approve_requests_datamodel_get(undefined); + public async getApprovalDataModel(signal?: AbortSignal): Promise { + return this.apiService.client.portal_itshop_approve_requests_datamodel_get(undefined, { signal }); } public async recallDecision(pwo: PortalItshopApproveRequests, approver: RecallDecisionInput): Promise { diff --git a/imxweb/projects/qer/src/lib/itshopapprove/decision-step.service.ts b/imxweb/projects/qer/src/lib/itshopapprove/decision-step.service.ts index f05eb8b6b..8d68b9382 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/decision-step.service.ts +++ b/imxweb/projects/qer/src/lib/itshopapprove/decision-step.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,29 +25,32 @@ */ import { Injectable } from '@angular/core'; -import { EntityColumnData, EntityData, IEntityColumn, TypedEntity, ValType } from 'imx-qbm-dbts'; -import { EntityService, AuthenticationService, ColumnDependentReference, BaseReadonlyCdr, CdrFactoryService } from 'qbm'; +import { EntityColumnData, EntityData, IEntityColumn, TypedEntity, ValType } from '@imx-modules/imx-qbm-dbts'; +import { AuthenticationService, BaseReadonlyCdr, ColumnDependentReference, EntityService, ISessionState } from 'qbm'; @Injectable({ providedIn: 'root', }) export class DecisionStepSevice { private uidUser: string; - constructor(private readonly entityService: EntityService, authentication: AuthenticationService) { - authentication.onSessionResponse.subscribe((session) => (this.uidUser = session.UserUid)); + constructor( + private readonly entityService: EntityService, + authentication: AuthenticationService, + ) { + authentication.onSessionResponse.subscribe((session: ISessionState) => (this.uidUser = session.UserUid || '')); } - public getCurrentStepCdr(entity: TypedEntity, extended: any, display: string): ColumnDependentReference { + public getCurrentStepCdr(entity: TypedEntity, extended: any, display: string): ColumnDependentReference | undefined { const steps = extended.WorkflowSteps?.Entities?.filter( (elem) => elem?.Columns?.UID_QERWorkingMethod.Value === entity.GetEntity().GetColumn('UID_QERWorkingMethod').GetValue() && - elem.Columns.LevelNumber.Value === entity.GetEntity().GetColumn('DecisionLevel').GetValue() + elem.Columns.LevelNumber.Value === entity.GetEntity().GetColumn('DecisionLevel').GetValue(), ); const step = steps.find((elem) => this.isFitting(extended.WorkflowData?.Entities, elem)); return step?.Columns.Ident_PWODecisionStep == null - ? null + ? undefined : new BaseReadonlyCdr(this.createEntityColumn(step.Columns.Ident_PWODecisionStep), display); } @@ -59,7 +62,7 @@ export class DecisionStepSevice { return workflowData.some( (elem) => elem?.Columns?.UID_QERWorkingStep.Value === step?.Columns?.UID_QERWorkingStep.Value && - elem?.Columns?.UID_PersonHead.Value === this.uidUser + elem?.Columns?.UID_PersonHead.Value === this.uidUser, ); } } diff --git a/imxweb/projects/qer/src/lib/itshopapprove/inquiries/inquiries.component.html b/imxweb/projects/qer/src/lib/itshopapprove/inquiries/inquiries.component.html index 3b5ed2aa7..317f881cc 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/inquiries/inquiries.component.html +++ b/imxweb/projects/qer/src/lib/itshopapprove/inquiries/inquiries.component.html @@ -1,65 +1,66 @@ - - + + - - -
    - - - - - - - - - {{ getInquiryText(item) }} - - - - - {{ getInquirer(item) }} - - - - - {{ getQueryDate(item) | shortDate }} - - - - - -
    - -
    -
    -
    -
    -
    - + + + {{ entitySchema.Columns.DisplayOrg?.Display }} + + + + + + + + {{ '#LDS#Query' | translate }} + + + {{ getInquiryText(item) }} + + + + + {{ '#LDS#Inquiry made by' | translate }} + + + {{ getInquirer(item) }} + + + + + {{ '#LDS#Inquiry made on' | translate }} + + + {{ (getQueryDate(item) | shortDate) || '' }} + + + + + +
    + +
    + +
    + +
    diff --git a/imxweb/projects/qer/src/lib/itshopapprove/inquiries/inquiries.component.scss b/imxweb/projects/qer/src/lib/itshopapprove/inquiries/inquiries.component.scss index eeda36bbe..e1691c119 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/inquiries/inquiries.component.scss +++ b/imxweb/projects/qer/src/lib/itshopapprove/inquiries/inquiries.component.scss @@ -1,4 +1,4 @@ -@import '../../../../../../shared/scss/common-table.scss'; +@import 'base/mixins'; :host { display: flex; @@ -7,26 +7,14 @@ flex-grow: 1; } -.imx-table-container { - flex-grow: 1; - overflow: auto; -} - -.imx-mat-card-container { - overflow: hidden; - display: flex; - flex-direction: column; - height: 100%; -} - .imx-margin-right { margin-right: 10px; } -.mat-stroked-button { - @include imx-icon-for-image-button(); +.mat-mdc-outlined-button { + @include image-button-icon(); } -.imx-button-column { - @include imx-button-column-right(); -} +.mat-mdc-card.imx-card-fill{ + margin: 0 0 3px 0; +} \ No newline at end of file diff --git a/imxweb/projects/qer/src/lib/itshopapprove/inquiries/inquiries.component.ts b/imxweb/projects/qer/src/lib/itshopapprove/inquiries/inquiries.component.ts index d704a88ff..662d3b4d0 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/inquiries/inquiries.component.ts +++ b/imxweb/projects/qer/src/lib/itshopapprove/inquiries/inquiries.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,47 +24,49 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; -import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; -import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; -import { Subscription } from 'rxjs'; +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; +import { Subscription } from 'rxjs'; -import { PwoExtendedData, ViewConfigData } from 'imx-api-qer'; -import { ValType, ExtendedTypedEntityCollection, TypedEntity, EntitySchema, DataModel, EntityData } from 'imx-qbm-dbts'; +import { PwoExtendedData, ViewConfigData } from '@imx-modules/imx-api-qer'; +import { + CollectionLoadParameters, + DataModel, + EntityData, + EntitySchema, + ExtendedTypedEntityCollection, + TypedEntity, + ValType, +} from '@imx-modules/imx-qbm-dbts'; import { - DataSourceToolbarSettings, - ClassloggerService, AuthenticationService, - DataTableComponent, - SettingsService, - SnackBarService, - ClientPropertyForTableColumns, BusyService, + calculateSidesheetWidth, + ClassloggerService, + ClientPropertyForTableColumns, DataSourceToolbarViewConfig, + DataViewInitParameters, + DataViewSource, + ISessionState, + SnackBarService, } from 'qbm'; -import { ApprovalsSidesheetComponent } from '../approvals-sidesheet/approvals-sidesheet.component'; -import { Approval } from '../approval'; import { ProjectConfigurationService } from '../../project-configuration/project-configuration.service'; +import { ViewConfigService } from '../../view-config/view-config.service'; +import { Approval } from '../approval'; +import { ApprovalsSidesheetComponent } from '../approvals-sidesheet/approvals-sidesheet.component'; import { ApprovalsService } from '../approvals.service'; import { WorkflowActionService } from '../workflow-action/workflow-action.service'; -import { ApprovalsLoadParameters } from '../approvals-load-parameters'; -import { ViewConfigService } from '../../view-config/view-config.service'; @Component({ templateUrl: './inquiries.component.html', selector: 'imx-inquiries', styleUrls: ['./inquiries.component.scss'], + providers: [DataViewSource], }) export class InquiriesComponent implements OnInit, OnDestroy { - public dstSettings: DataSourceToolbarSettings; public readonly entitySchema: EntitySchema; - public approvalsCollection: ExtendedTypedEntityCollection; - public hasData = false; - - @ViewChild(DataTableComponent) private readonly table: DataTableComponent; - private navigationState: ApprovalsLoadParameters; private displayedColumns: ClientPropertyForTableColumns[]; private readonly subscriptions: Subscription[] = []; private dataModel: DataModel; @@ -83,12 +85,11 @@ export class InquiriesComponent implements OnInit, OnDestroy { private readonly projectConfig: ProjectConfigurationService, private readonly translator: TranslateService, snackbar: SnackBarService, - settingsService: SettingsService, - authentication: AuthenticationService + authentication: AuthenticationService, + public dataSource: DataViewSource, ) { - this.navigationState = { PageSize: settingsService.DefaultPageSize, StartIndex: 0 }; this.entitySchema = approvalsService.PortalItshopApproveRequestsSchema; - this.displayedColumns = [ + (this.displayedColumns = [ { ColumnName: 'query', Type: ValType.String, @@ -110,24 +111,23 @@ export class InquiriesComponent implements OnInit, OnDestroy { Type: ValType.String, untranslatedDisplay: '#LDS#Actions', }, - ], - this.subscriptions.push( - this.actionService.applied.subscribe(async () => { - if (this.dstSettings.dataSource.totalCount === 1) { - snackbar.open({ - key: '#LDS#There are currently no inquiries.', - }); - } - this.getData(); - this.table.clearSelection(); - }) - ); + ]), + this.subscriptions.push( + this.actionService.applied.subscribe(async () => { + if (this.dataSource.collectionData().totalCount === 1) { + snackbar.open({ + key: '#LDS#There are currently no inquiries.', + }); + } + this.dataSource.selection.clear(); + this.dataSource.updateState(); + }), + ); this.approvalsService.isChiefApproval = false; - this.subscriptions.push(authentication.onSessionResponse.subscribe((session) => (this.userUid = session.UserUid))); + this.subscriptions.push(authentication.onSessionResponse.subscribe((session: ISessionState) => (this.userUid = session.UserUid || ''))); } public async ngOnInit(): Promise { - this.navigationState.forinquiry = true; const isBusy = this.busyService.beginBusy(); try { @@ -146,32 +146,35 @@ export class InquiriesComponent implements OnInit, OnDestroy { this.subscriptions.forEach((s) => s.unsubscribe()); } - public async getData(parameters?: ApprovalsLoadParameters): Promise { - if (parameters) { - this.navigationState = parameters; - } - - const isBusy = this.busyService.beginBusy(); - - try { - this.approvalsCollection = await this.approvalsService.get(this.navigationState); - this.hasData = this.approvalsCollection.totalCount > 0 || (this.navigationState.search ?? '') !== ''; - this.updateTable(); - } finally { - isBusy.endBusy(); - } + public async getData(): Promise { + const dataViewInitParameters: DataViewInitParameters = { + execute: ( + params: CollectionLoadParameters, + signal: AbortSignal, + ): Promise | undefined> => + this.approvalsService.get({ ...params, forinquiry: true }, { signal }), + schema: this.entitySchema, + columnsToDisplay: this.displayedColumns, + dataModel: this.dataModel, + exportFunction: this.approvalsService.exportApprovalRequests(this.dataSource.state()), + viewConfig: this.viewConfig, + highlightEntity: (approval: Approval) => { + this.editPwo(approval); + }, + }; + this.dataSource.init(dataViewInitParameters); } public async updateConfig(config: ViewConfigData): Promise { await this.viewConfigService.putViewConfig(config); this.viewConfig = await this.viewConfigService.getDSTExtensionChanges(this.viewConfigPath); - this.dstSettings.viewConfig = this.viewConfig; + this.dataSource.viewConfig.set(this.viewConfig); } public async deleteConfigById(id: string): Promise { await this.viewConfigService.deleteViewConfig(id); this.viewConfig = await this.viewConfigService.getDSTExtensionChanges(this.viewConfigPath); - this.dstSettings.viewConfig = this.viewConfig; + this.dataSource.viewConfig.set(this.viewConfig); } /** @@ -179,14 +182,14 @@ export class InquiriesComponent implements OnInit, OnDestroy { * * @param pwo Selected PortalItshopApproveRequests. */ - public async editPwo(pwo: Approval): Promise { + public async editPwo(pwo: TypedEntity): Promise { this.logger.trace('New selected pwo', pwo); const doUpdate = await this.sideSheet .open(ApprovalsSidesheetComponent, { title: await this.translator.get('#LDS#Heading View Request Details').toPromise(), subTitle: pwo.GetEntity().GetDisplay(), padding: '0', - width: 'max(700px, 60%)', + width: calculateSidesheetWidth(1000), testId: 'inqueries-sidesheet', data: { pwo, @@ -203,32 +206,20 @@ export class InquiriesComponent implements OnInit, OnDestroy { } public getInquiryText(pwo: Approval): string { - return this.getPwoData(pwo).Columns.ReasonHead.Value; + return this.getPwoData(pwo)?.Columns?.ReasonHead.Value || ''; } public getInquirer(pwo: Approval): string { - return this.getPwoData(pwo).Columns.DisplayPersonHead.Value; + return this.getPwoData(pwo)?.Columns?.DisplayPersonHead.Value || ''; } - public getQueryDate(pwo: Approval): Date { - return new Date(this.getPwoData(pwo).Columns.DateHead.Value); + public getQueryDate(pwo: Approval): string { + return this.getPwoData(pwo)?.Columns?.DateHead.Value ?? ''; } - public onSearch(keywords: string): Promise { - const navigationState = { - ...this.navigationState, - ...{ - StartIndex: 0, - search: keywords, - }, - }; - - return this.getData(navigationState); - } - - private getPwoData(pwo: Approval): EntityData { - const questionHistory = pwo.pwoData.WorkflowHistory.Entities.filter( - (entityData) => entityData.Columns.DecisionLevel.Value === pwo.DecisionLevel.value - ).sort((item1, item2) => this.ascendingDate(item1.Columns.XDateInserted?.Value, item2.Columns.XDateInserted?.Value)); - return questionHistory[0]; + private getPwoData(pwo: Approval): EntityData | undefined { + const questionHistory = pwo.pwoData.WorkflowHistory?.Entities?.filter( + (entityData) => entityData.Columns?.DecisionLevel.Value === pwo.DecisionLevel.value, + ).sort((item1, item2) => this.ascendingDate(item1.Columns?.XDateInserted?.Value, item2.Columns?.XDateInserted?.Value)); + return questionHistory?.[0]; } private ascendingDate(value1: Date, value2: Date): number { @@ -242,23 +233,4 @@ export class InquiriesComponent implements OnInit, OnDestroy { return 0; } - - private updateTable(): void { - if (this.approvalsCollection) { - const exportMethod = this.approvalsService.exportApprovalRequests(this.navigationState); - exportMethod.initialColumns = this.displayedColumns.map(col => col.ColumnName); - this.dstSettings = { - dataSource: this.approvalsCollection, - extendedData: this.approvalsCollection.extendedData.Data, - entitySchema: this.entitySchema, - navigationState: this.navigationState, - displayedColumns: this.displayedColumns, - dataModel: this.dataModel, - viewConfig: this.viewConfig, - exportMethod - }; - } else { - this.dstSettings = undefined; - } - } } diff --git a/imxweb/projects/qer/src/lib/itshopapprove/recommendation-sidesheet/recommendation-sidesheet.component.html b/imxweb/projects/qer/src/lib/itshopapprove/recommendation-sidesheet/recommendation-sidesheet.component.html index 0042573d4..4529668e5 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/recommendation-sidesheet/recommendation-sidesheet.component.html +++ b/imxweb/projects/qer/src/lib/itshopapprove/recommendation-sidesheet/recommendation-sidesheet.component.html @@ -1,40 +1,55 @@
    - - - {{'#LDS#Based on an analysis of currently available data, it is recommended that you approve this request.'| translate}} + + {{ + (data?.informationTexts?.approve != null + ? data?.informationTexts?.approve || '' + : '#LDS#Based on an analysis of currently available data, it is recommended that you approve this request.' + ) | translate + }} - - {{'#LDS#Based on an analysis of currently available data, it is recommended that you deny this request.'| translate}} + + {{ + (data?.informationTexts?.reject != null + ? data?.informationTexts?.reject || '' + : '#LDS#Based on an analysis of currently available data, it is recommended that you deny this request.' + ) | translate + }} - - {{'#LDS#Based on an analysis of currently available data, no definitive recommendation can be made for this request.'| translate}} + + {{ + (data?.informationTexts?.noRecord != null + ? data?.informationTexts?.noRecord || '' + : '#LDS#Based on an analysis of currently available data, no definitive recommendation can be made for this request.' + ) | translate + }}
    -

    {{'#LDS#The following factors were considered in this recommendation.' | translate}}

    +

    {{ '#LDS#The following factors were considered in this recommendation.' | translate }}

    - - + +
    - {{item.Display}} + {{ item.Display }} - {{item.Detail}} + {{ item.Detail }}
    -
    - - +
    + +
    - - - diff --git a/imxweb/projects/qer/src/lib/itshopapprove/recommendation-sidesheet/recommendation-sidesheet.component.scss b/imxweb/projects/qer/src/lib/itshopapprove/recommendation-sidesheet/recommendation-sidesheet.component.scss index afdb73fdd..d3cba8702 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/recommendation-sidesheet/recommendation-sidesheet.component.scss +++ b/imxweb/projects/qer/src/lib/itshopapprove/recommendation-sidesheet/recommendation-sidesheet.component.scss @@ -1,12 +1,8 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/mixins'; :host { - ::ng-deep .eui-alert { - margin-bottom: 20px; - } - - .mat-card-avatar { + .mat-mdc-card-avatar { display: flex; align-items: center; justify-content: center; @@ -15,20 +11,6 @@ .recommendation-item { margin-bottom: 20px; } - - .imx-icon-warning { - color: $corbin-orange; - } - - .imx-icon-ok { - color: $aspen-green - } - - .eui-sidesheet-actions { - > * { - margin-left: 5px; - } - } } .eui-dark-theme { @@ -38,7 +20,7 @@ background-color: $color-gray-80; } .eui-sidesheet-content { - .recommendation-items , .eui-alert.eui-alert-condensed .eui-alert-header{ + .recommendation-items { color: $color-gray-2; } } @@ -52,7 +34,7 @@ background-color: $color-gray-100; } .eui-sidesheet-content { - .recommendation-items , .eui-alert.eui-alert-condensed .eui-alert-header{ + .recommendation-items { color: $color-gray-0; } } diff --git a/imxweb/projects/qer/src/lib/itshopapprove/recommendation-sidesheet/recommendation-sidesheet.component.ts b/imxweb/projects/qer/src/lib/itshopapprove/recommendation-sidesheet/recommendation-sidesheet.component.ts index 8d697f19b..a4578f7ad 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/recommendation-sidesheet/recommendation-sidesheet.component.ts +++ b/imxweb/projects/qer/src/lib/itshopapprove/recommendation-sidesheet/recommendation-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,13 +25,13 @@ */ import { Component, Inject } from '@angular/core'; -import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; -import { RecommendationData, RecommendationDataItem, RecommendationEnum } from 'imx-api-qer'; +import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; +import { RecommendationData, RecommendationDataItem, RecommendationEnum } from '@imx-modules/imx-api-qer'; @Component({ selector: 'imx-recommendation-sidesheet', templateUrl: './recommendation-sidesheet.component.html', - styleUrls: ['./recommendation-sidesheet.component.scss'] + styleUrls: ['./recommendation-sidesheet.component.scss'], }) export class RecommendationSidesheetComponent { public isRecApprove: boolean; @@ -41,19 +41,19 @@ export class RecommendationSidesheetComponent { constructor( private sideSheetRef: EuiSidesheetRef, - @Inject(EUI_SIDESHEET_DATA) private data: RecommendationData + @Inject(EUI_SIDESHEET_DATA) + public data: { recommendations: RecommendationData; informationTexts?: { approve: string; reject: string; noRecord: string } }, ) { - if (data.Recommendation === RecommendationEnum.Approve) { + if (data.recommendations.Recommendation === RecommendationEnum.Approve) { this.isRecApprove = true; - } else if (data.Recommendation === RecommendationEnum.Deny) { + } else if (data.recommendations.Recommendation === RecommendationEnum.Deny) { this.isRecReject = true; } else { this.isNoRec = true; } - this.recommendationItems = data.Items; + this.recommendationItems = data.recommendations.Items || []; } - public onApprove(): void { this.sideSheetRef.close('approve'); } @@ -61,5 +61,4 @@ export class RecommendationSidesheetComponent { public onDeny(): void { this.sideSheetRef.close('deny'); } - } diff --git a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/approval-history.component.html b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/approval-history.component.html index 30079d238..76891e024 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/approval-history.component.html +++ b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/approval-history.component.html @@ -1,7 +1,7 @@ - + {{ alertText | translate }} - +
    - +
    - - - - + + + + {{ approver.caption }} - +
    -
    +
    #LDS#Heading No Filter Applied
    {{ '#LDS#Select at least one filter.' | translate }}
    diff --git a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/approval-history.component.scss b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/approval-history.component.scss index c1859530a..464b14aa0 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/approval-history.component.scss +++ b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/approval-history.component.scss @@ -1,7 +1,7 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; :host { - ::ng-deep .mat-card > :last-child:not(.mat-card-footer) { + ::ng-deep .mat-mdc-card > :last-child:not(.mat-mdc-card-footer) { margin-bottom: auto; } display: flex; @@ -10,24 +10,6 @@ height: 100%; } -.imx-table-container { - display: flex; - flex-direction: column; - overflow: hidden; - height: 100%; -} - -.mat-card { - flex: 1 1 auto; - display: flex; - flex-direction: column; - overflow: hidden; -} - -.imx-alert { - margin-bottom: 15px; -} - .imx-data-table-no-results { margin: auto; margin-top: auto; @@ -36,11 +18,6 @@ align-items: center; color: $black-9; - .eui-icon { - font-size: 100px; - color: rgba($black-c, 0.55); - } - div[headline] { margin: 0; font-size: 18px; diff --git a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/approval-history.component.ts b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/approval-history.component.ts index c886760c7..7d2629b98 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/approval-history.component.ts +++ b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/approval-history.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,19 +24,20 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; import { Component, Input, OnInit } from '@angular/core'; import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { HistoryFilterMode, ITShopConfig } from 'imx-api-qer'; -import { CollectionLoadParameters, EntityData, EntitySchema, IClientProperty, TypedEntity, ValType } from 'imx-qbm-dbts'; +import { HistoryFilterMode, ITShopConfig } from '@imx-modules/imx-api-qer'; +import { CollectionLoadParameters, EntityData, EntitySchema, TypedEntity, ValType } from '@imx-modules/imx-qbm-dbts'; import { AuthenticationService, + calculateSidesheetWidth, ClassloggerService, ClientPropertyForTableColumns, DataSourceToolbarFilter, DataSourceToolbarSettings, + ISessionState, } from 'qbm'; import { ProjectConfigurationService } from '../../../project-configuration/project-configuration.service'; import { ItshopRequest } from '../../../request-history/itshop-request'; @@ -53,7 +54,7 @@ export class ApprovalHistoryComponent implements OnInit { @Input() public approval: Approval; public dstSettings: DataSourceToolbarSettings; public entitySchema: EntitySchema; - // tslint:disable-next-line: no-bitwise + // eslint-disable-next-line no-bitwise public filtermode: HistoryFilterMode = HistoryFilterMode.SameAccProduct | HistoryFilterMode.SameStep; public filters: DataSourceToolbarFilter[] = []; public hasFilterApplied = true; @@ -64,7 +65,7 @@ export class ApprovalHistoryComponent implements OnInit { private navigationState: CollectionLoadParameters = { OrderBy: 'OrderDate' }; private currentUser: string; private displayedColumns: ClientPropertyForTableColumns[]; - private itShopConfig: ITShopConfig; + private itShopConfig: ITShopConfig | undefined; constructor( private readonly approvalHistoryservice: ApprovalHistoryService, @@ -73,25 +74,21 @@ export class ApprovalHistoryComponent implements OnInit { private readonly translate: TranslateService, private readonly sideSheet: EuiSidesheetService, private readonly projectConfig: ProjectConfigurationService, - auth: AuthenticationService + auth: AuthenticationService, ) { - auth.onSessionResponse.subscribe((session) => (this.currentUser = session.UserUid)); + auth.onSessionResponse.subscribe((session: ISessionState) => (this.currentUser = session.UserUid || '')); } public async ngOnInit(): Promise { this.entitySchema = this.approvalHistoryservice.PortalItshopRequestsSchema; this.filters = await this.buildFilter(); - let overlay: OverlayRef; - - setTimeout(() => { - overlay = this.busy.show(); - }); + if (this.busy.overlayRefs.length === 0) { + this.busy.show(); + } try { this.itShopConfig = (await this.projectConfig.getConfig()).ITShopConfig; } finally { - setTimeout(() => { - this.busy.hide(overlay); - }); + this.busy.hide(); } return this.navigate(); } @@ -113,26 +110,27 @@ export class ApprovalHistoryComponent implements OnInit { return this.navigate(); } - public isApproved(pwo: ItshopRequest): { caption: string; color: string;} { - const approvalStep = pwo.pwoData.WorkflowHistory.Entities.filter( - (entity) => entity.Columns.UID_PersonHead.Value === this.currentUser && entity.Columns.DecisionType.Value !== 'Order' - ).sort((a, b) => new Date(b.Columns.DateHead.Value).valueOf() - new Date(a.Columns.DateHead.Value).valueOf()); + public isApproved(pwo: ItshopRequest): { caption: string; color: string } | undefined { + const approvalStep = pwo.pwoData.WorkflowHistory?.Entities?.find( + (entity) => + entity.Columns?.DecisionLevel.Value === this.approval.DecisionLevel.value && + entity.Columns?.UID_PersonHead.Value === this.currentUser && + entity.Columns?.DecisionType.Value !== 'Order', + ); - if (approvalStep?.length === 0) { + if (approvalStep == null) { this.logger.warn(this, 'no approval step found'); - return null; + return undefined; } - const stepForDisplay = approvalStep.find((elem) => elem.Columns.DecisionLevel.Value === this.approval.DecisionLevel.value) ?? approvalStep[0]; - return { - caption: stepForDisplay.Columns.DecisionType.DisplayValue ?? stepForDisplay.Columns.DecisionType.Value, + caption: approvalStep.Columns?.DecisionType.DisplayValue || '', color: - stepForDisplay.Columns.DecisionType.Value === 'Grant' + approvalStep.Columns?.DecisionType.Value === 'Grant' ? 'green' - : stepForDisplay.Columns.DecisionType.Value === 'Dismiss' - ? 'red' - : 'gray', + : approvalStep.Columns?.DecisionType.Value === 'Dissmiss' + ? 'red' + : 'gray', }; } @@ -142,7 +140,7 @@ export class ApprovalHistoryComponent implements OnInit { } public get isSameStepActive(): boolean { - // tslint:disable-next-line: no-bitwise + // eslint-disable-next-line no-bitwise return (this.filtermode & HistoryFilterMode.SameStep) === HistoryFilterMode.SameStep; } @@ -152,7 +150,7 @@ export class ApprovalHistoryComponent implements OnInit { title: await this.translate.get('#LDS#Heading View Request Details').toPromise(), subTitle: pwo.GetEntity().GetDisplay(), padding: '0px', - width: 'max(700px, 60%)', + width: calculateSidesheetWidth(1000), testId: 'imx-approval-history-request-detail', data: { isReadOnly: true, @@ -167,12 +165,11 @@ export class ApprovalHistoryComponent implements OnInit { } private async navigate(): Promise { - let overlay: OverlayRef; let result: any; - setTimeout(() => { - overlay = this.busy.show(); - }); + if (this.busy.overlayRefs.length === 0) { + this.busy.show(); + } try { this.hasFilterApplied = this.filtermode !== HistoryFilterMode.None; if (!this.hasFilterApplied) { @@ -186,26 +183,29 @@ export class ApprovalHistoryComponent implements OnInit { return; } - result = await this.approvalHistoryservice.getRequests( - currentHelperPwo.Columns.UID_PWOHelperPWO.Value, - this.filtermode, - this.currentUser, - this.navigationState - ); + result = currentHelperPwo.Columns?.UID_PWOHelperPWO.Value + ? await this.approvalHistoryservice.getRequests( + currentHelperPwo.Columns?.UID_PWOHelperPWO.Value, + this.filtermode, + this.currentUser, + this.navigationState, + ) + : undefined; } } finally { - setTimeout(() => { - this.busy.hide(overlay); - }); + this.busy.hide(); } - if (result) { + if (result != null) { this.displayedColumns = [ this.entitySchema.Columns.UID_PersonOrdered, this.entitySchema.Columns.OrderDate, this.entitySchema.Columns.UiOrderState, - this.isSameStepActive ? { Type: ValType.String, ColumnName: 'decision', untranslatedDisplay: '#LDS#Approval decision' } : null, - ].filter((elem) => elem != null); + ]; + + if (this.isSameStepActive) { + this.displayedColumns.push({ Type: ValType.String, ColumnName: 'decision', untranslatedDisplay: '#LDS#Approval decision' }); + } this.dstSettings = { dataSource: { @@ -221,19 +221,19 @@ export class ApprovalHistoryComponent implements OnInit { } } - private getCurrentPwoHelperPwo(): EntityData { - const currentStep = this.approval.pwoData.WorkflowSteps.Entities.find( + private getCurrentPwoHelperPwo(): EntityData | undefined { + const currentStep = this.approval.pwoData.WorkflowSteps?.Entities?.find( (elem) => - elem.Columns.UID_QERWorkingMethod.Value === this.approval.UID_QERWorkingMethod.value && - elem.Columns.LevelNumber.Value === this.approval.DecisionLevel.value + elem.Columns?.UID_QERWorkingMethod.Value === this.approval.UID_QERWorkingMethod.value && + elem.Columns?.LevelNumber.Value === this.approval.DecisionLevel.value, ); this.logger.trace(this, 'current step the user has to decide', currentStep); - return this.approval.pwoData.WorkflowData.Entities.find( + return this.approval.pwoData.WorkflowData?.Entities?.find( (elem) => - elem.Columns.RulerLevel.Value === 0 && - elem.Columns.UID_QERWorkingStep.Value === currentStep.Columns.UID_QERWorkingStep.Value && - elem.Columns.UID_PersonHead.Value === this.currentUser + elem.Columns?.RulerLevel.Value === 0 && + elem.Columns?.UID_QERWorkingStep.Value === currentStep?.Columns?.UID_QERWorkingStep.Value && + elem.Columns?.UID_PersonHead.Value === this.currentUser, ); } diff --git a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/approval-history.service.ts b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/approval-history.service.ts index 9c8892053..f504d9efd 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/approval-history.service.ts +++ b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/approval-history.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,8 +25,8 @@ */ import { Injectable } from '@angular/core'; -import { HistoryFilterMode, PwoExtendedData } from 'imx-api-qer'; -import { CollectionLoadParameters, CompareOperator, DataModel, EntitySchema, ExtendedTypedEntityCollection, FilterType } from 'imx-qbm-dbts'; +import { HistoryFilterMode, PwoExtendedData } from '@imx-modules/imx-api-qer'; +import { CollectionLoadParameters, DataModel, EntitySchema, ExtendedTypedEntityCollection } from '@imx-modules/imx-qbm-dbts'; import { ItshopRequestService } from '../../../itshop/itshop-request.service'; import { ItshopRequestData } from '../../../itshop/request-info/itshop-request-data'; import { QerApiService } from '../../../qer-api-client.service'; @@ -36,8 +36,10 @@ import { ItshopRequest } from '../../../request-history/itshop-request'; providedIn: 'root', }) export class ApprovalHistoryService { - constructor(private api: QerApiService, private itshopRequest: ItshopRequestService) {} - + constructor( + private api: QerApiService, + private itshopRequest: ItshopRequestService, + ) {} public async getDataModel(): Promise { return this.api.client.portal_itshop_requests_datamodel_get(); @@ -47,18 +49,19 @@ export class ApprovalHistoryService { uidhelperpwo: string, filtermode: HistoryFilterMode, uidperson: string, - parameter: CollectionLoadParameters + parameter: CollectionLoadParameters, ): Promise> { - const collection = await this.api.typedClient.PortalItshopRequests.Get({ ...parameter, status: '4,1', // closed or active uidhelperpwo, filtermode, - // tslint:disable-next-line: no-bitwise - UID_PersonDecision: (filtermode & HistoryFilterMode.SameStep) === HistoryFilterMode.SameStep ? uidperson : undefined + // eslint-disable-next-line no-bitwise + UID_PersonDecision: (filtermode & HistoryFilterMode.SameStep) === HistoryFilterMode.SameStep ? uidperson : undefined, }); + const data: PwoExtendedData | undefined = collection.extendedData; + return { tableName: collection.tableName, totalCount: collection.totalCount, @@ -66,7 +69,12 @@ export class ApprovalHistoryService { Data: collection.Data.map((element, index) => { const requestData = new ItshopRequestData({ ...collection.extendedData, ...{ index } }); const parameterColumns = this.itshopRequest.createParameterColumns(element.GetEntity(), requestData.parameters); - return new ItshopRequest(element.GetEntity(), requestData.pwoData, parameterColumns, uidperson); + return new ItshopRequest( + element.GetEntity(), + { ...requestData.pwoData, WorkflowSteps: data?.WorkflowSteps }, + parameterColumns, + uidperson, + ); }), }; } diff --git a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/history-filter/history-filter.component.html b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/history-filter/history-filter.component.html index 8b0471437..a0414f839 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/history-filter/history-filter.component.html +++ b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/history-filter/history-filter.component.html @@ -1,7 +1,11 @@ -

    {{ modeFilter?.Description | translate }}

    +

    {{ modeFilter?.Description || '' | translate }}

    - + {{ option.display }}
    diff --git a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/history-filter/history-filter.component.scss b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/history-filter/history-filter.component.scss index ef6505dd6..66866812e 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/history-filter/history-filter.component.scss +++ b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/history-filter/history-filter.component.scss @@ -5,4 +5,4 @@ p[title] { .mat-divider { margin-top: 15px; -} \ No newline at end of file +} diff --git a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/history-filter/history-filter.component.ts b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/history-filter/history-filter.component.ts index c48ae8127..cce7eb036 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/history-filter/history-filter.component.ts +++ b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/approval-history/history-filter/history-filter.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,7 +26,7 @@ import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core'; -import { HistoryFilterMode } from 'imx-api-qer'; +import { HistoryFilterMode } from '@imx-modules/imx-api-qer'; import { DataSourceToolbarComponent, DataSourceToolbarFilter, DataSourceToolbarSelectedFilter } from 'qbm'; @Component({ @@ -35,22 +35,23 @@ import { DataSourceToolbarComponent, DataSourceToolbarFilter, DataSourceToolbarS styleUrls: ['./history-filter.component.scss'], }) export class HistoryFilterComponent implements OnChanges { - @Input() public dst?: DataSourceToolbarComponent; + @Input() public dst: DataSourceToolbarComponent; @Input() public filter: DataSourceToolbarFilter[]; @Input() public filtermode: HistoryFilterMode; @Output() public selectedFiltersChanged = new EventEmitter(); public options: { display: string; value: HistoryFilterMode; checked: boolean; initialValue: string }[] = []; - public modeFilter: DataSourceToolbarFilter; + public modeFilter: DataSourceToolbarFilter | undefined; public ngOnChanges(): void { this.modeFilter = this.filter?.find((elem) => elem.Name === 'filtermode'); - this.options = this.modeFilter?.Options.map((elem) => ({ - display: elem.Display, - value: this.convertToFilter(elem.Value), - initialValue: elem.Value, - checked: this.isChecked(elem.Value), - })); + this.options = + this.modeFilter?.Options?.map((elem) => ({ + display: elem.Display ?? '', + value: this.convertToFilter(elem.Value ?? ''), + initialValue: elem.Value ?? '', + checked: this.isChecked(elem.Value ?? ''), + })) || []; this.updateFilter(false); } @@ -59,7 +60,7 @@ export class HistoryFilterComponent implements OnChanges { if (!sf) { this.options.forEach((elem) => (elem.checked = false)); } else { - const filter = this.options.find((elem) => elem.initialValue === sf.selectedOption.Value); + const filter = this.options.find((elem) => elem.initialValue === sf?.selectedOption?.Value ?? ''); if (filter) { filter.checked = false; } @@ -68,10 +69,9 @@ export class HistoryFilterComponent implements OnChanges { }); } - public updateFilter( emit: boolean = true): void { + public updateFilter(emit: boolean = true): void { const checked = this.options?.filter((elem) => elem.checked); - if (!checked) - { + if (!checked) { return; } @@ -90,7 +90,7 @@ export class HistoryFilterComponent implements OnChanges { let applyedfilter = HistoryFilterMode.None; filtermode.forEach((element) => { - // tslint:disable-next-line: no-bitwise + // eslint-disable-next-line no-bitwise applyedfilter |= element.value; }); this.selectedFiltersChanged.emit(applyedfilter); @@ -106,7 +106,7 @@ export class HistoryFilterComponent implements OnChanges { private isChecked(id: string): boolean { const value = this.convertToFilter(id); - // tslint:disable-next-line: no-bitwise + // eslint-disable-next-line no-bitwise return (this.filtermode & value) === value; } diff --git a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action-edit-wrapper.interface.ts b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action-edit-wrapper.interface.ts index 4396bc517..92a601eb1 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action-edit-wrapper.interface.ts +++ b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action-edit-wrapper.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action-edit.interface.ts b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action-edit.interface.ts index 59023539e..9897c3760 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action-edit.interface.ts +++ b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action-edit.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,9 +24,8 @@ * */ -import { EntitySchema, IClientProperty, IEntity } from 'imx-qbm-dbts'; +import { EntitySchema, IClientProperty, IEntity, TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { ColumnDependentReference } from 'qbm'; -import { Approval } from '../approval'; import { WorkflowActionParameters } from './workflow-action-parameters.interface'; /** @@ -44,7 +43,7 @@ export interface WorkflowActionEdit { /** * The requests to make a decision for. */ - requests: Approval[]; + requests: TypedEntity[]; /** * Whether or not the decision is an approval. @@ -59,18 +58,17 @@ export interface WorkflowActionEdit { /** * Whether or not the decision shows the decision guidance tab */ - withGuidance?:boolean; - + withGuidance?: boolean; /** * Information about the workflow so far. */ workflow?: { - entitySchema: EntitySchema, - display: { primary: IClientProperty, secondary?: IClientProperty }, - data: { [key: string]: IEntity[] }, - title: string, - placeholder: string + entitySchema: EntitySchema; + display: { primary: IClientProperty; secondary?: IClientProperty }; + data: { [key: string]: IEntity[] }; + title: string; + placeholder: string; }; /** @@ -79,8 +77,8 @@ export interface WorkflowActionEdit { * If given and specified these values are meant to be applied to all requests where such a value can be applied. */ showValidDate?: { - validFrom?: { key: string, placeholder: string }; - validUntil?: { key: string, placeholder: string }; + validFrom?: { key: string; placeholder: string }; + validUntil?: { key: string; placeholder: string }; }; /** @@ -88,6 +86,6 @@ export interface WorkflowActionEdit { */ customValidation?: { validate: () => boolean; - message: string + message: string; }; } diff --git a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action-parameters.interface.ts b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action-parameters.interface.ts index ea07a4046..1e51e86d2 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action-parameters.interface.ts +++ b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action-parameters.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action.component.html b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action.component.html index 97630e259..956ac1070 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action.component.html +++ b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action.component.html @@ -7,17 +7,14 @@
    -
    - +
    - - - +
    @@ -29,7 +26,7 @@
    - +
    @@ -39,7 +36,14 @@ - diff --git a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action.component.scss b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action.component.scss index 30c399bdb..06928cfb6 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action.component.scss +++ b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; :host { display: flex; @@ -12,32 +12,9 @@ display: flex; flex-direction: column; overflow: hidden; - - .mat-tab-group { - flex-grow: 1; - overflow: auto; - } - - ::ng-deep .mat-tab-body-wrapper { - height: 100%; - } - - ::ng-deep .mat-tab-body-content { - display: flex; - flex-direction: column; - } - - ::ng-deep .mat-tab-group { - height: 100%; - - .mat-tab-header { - padding: 0 32px; - } - } } .imx-tab-content { - display: flex; flex-direction: column; height: 100%; @@ -55,9 +32,6 @@ :host { background-color: $color-gray-80; color: $color-gray-2; - .eui-sidesheet-actions{ - background-color: $color-gray-70; - } } } @@ -65,8 +39,5 @@ :host { background-color: $color-gray-100; color: $color-gray-0; - .eui-sidesheet-actions{ - background-color: $color-gray-90; - } } } diff --git a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action.component.ts b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action.component.ts index 3f42e5dc4..823364a95 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action.component.ts +++ b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,6 +28,7 @@ import { Component, Inject } from '@angular/core'; import { UntypedFormGroup } from '@angular/forms'; import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; +import { Approval } from '../approval'; import { WorkflowActionEdit } from './workflow-action-edit.interface'; /** @@ -51,7 +52,7 @@ import { WorkflowActionEdit } from './workflow-action-edit.interface'; @Component({ selector: 'imx-workflow-action', templateUrl: './workflow-action.component.html', - styleUrls: ['./workflow-action.component.scss'] + styleUrls: ['./workflow-action.component.scss'], }) export class WorkflowActionComponent { /** @@ -59,6 +60,10 @@ export class WorkflowActionComponent { */ public readonly formGroup = new UntypedFormGroup({}); + public get approval(): Approval { + return this.data.requests[0] as Approval; + } + /** * Creates a new WorkflowActionComponent * @param data The pre-calculated data object. @@ -66,11 +71,10 @@ export class WorkflowActionComponent { */ constructor( @Inject(EUI_SIDESHEET_DATA) public readonly data: WorkflowActionEdit, - public readonly sideSheetRef: EuiSidesheetRef + public readonly sideSheetRef: EuiSidesheetRef, ) { - if (this.data.customValidation) { - this.formGroup.setValidators(_ => this.data.customValidation.validate() ? null : ({ required: true })); + this.formGroup.setValidators((_) => (this.data.customValidation?.validate() ? null : { required: true })); } } } diff --git a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action.service.ts b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action.service.ts index 92b6c8e5e..d4cd1daa4 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action.service.ts +++ b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-action.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,31 +25,50 @@ */ import { Injectable, Type } from '@angular/core'; -import { OverlayRef } from '@angular/cdk/overlay'; import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; import { Subject } from 'rxjs'; -import { CompareOperator, FilterType, EntityData, ValType } from 'imx-qbm-dbts'; -import { BaseCdr, BaseReadonlyCdr, ClassloggerService, EntityService, ExtService, SnackBarService } from 'qbm'; -import { WorkflowActionComponent } from './workflow-action.component'; -import { ProjectConfigurationService } from '../../project-configuration/project-configuration.service'; +import { + CollectionLoadParameters, + CompareOperator, + EntityData, + FilterData, + FilterType, + FkProviderItem, + MetaTableRelationData, + TypedEntity, + ValType, +} from '@imx-modules/imx-qbm-dbts'; +import { + Action, + BaseCdr, + BaseReadonlyCdr, + ClassloggerService, + EntityService, + ExtService, + ProcessingQueueService, + SnackBarService, + calculateSidesheetWidth, +} from 'qbm'; +import { JustificationType } from '../../justification/justification-type.enum'; +import { JustificationService } from '../../justification/justification.service'; import { PersonService } from '../../person/person.service'; +import { ProjectConfigurationService } from '../../project-configuration/project-configuration.service'; +import { QerApiService } from '../../qer-api-client.service'; +import { TermsOfUseAcceptComponent } from '../../terms-of-use/terms-of-use-accept.component'; +import { UserModelService } from '../../user/user-model.service'; import { Approval } from '../approval'; import { ApprovalsService } from '../approvals.service'; -import { QerApiService } from '../../qer-api-client.service'; -import { JustificationService } from '../../justification/justification.service'; -import { JustificationType } from '../../justification/justification-type.enum'; import { WorkflowActionEditWrapper } from './workflow-action-edit-wrapper.interface'; import { WorkflowActionParameters } from './workflow-action-parameters.interface'; -import { TermsOfUseAcceptComponent } from '../../terms-of-use/terms-of-use-accept.component'; -import { UserModelService } from '../../user/user-model.service'; +import { WorkflowActionComponent } from './workflow-action.component'; @Injectable({ providedIn: 'root', }) export class WorkflowActionService { - public readonly applied = new Subject(); + public readonly applied = new Subject(); constructor( private readonly apiService: QerApiService, @@ -63,11 +82,12 @@ export class WorkflowActionService { private readonly projectConfig: ProjectConfigurationService, private readonly approvalsService: ApprovalsService, private readonly justificationService: JustificationService, + private readonly extService: ExtService, private readonly userService: UserModelService, - private readonly extService: ExtService + private readonly queueService: ProcessingQueueService, ) {} - public async directDecisions(requests: Approval[], userUid: string): Promise { + public async directDecisions(requests: (Approval | TypedEntity)[], userUid: string): Promise { const actionParameters = { reason: this.createCdrReason(), }; @@ -81,7 +101,8 @@ export class WorkflowActionService { data: {}, }; - for (const request of requests) { + for (const req of requests) { + const request = req as Approval; const workFlowDataCollection = await this.apiService.typedClient.PortalWorkflow.Get(request.key, { PageSize: 1000 /* TODO: why 1000? */, }); @@ -89,7 +110,7 @@ export class WorkflowActionService { if (workFlowDataCollection && workFlowDataCollection.Data) { const levelNumbers = request.getLevelNumbers(userUid); workflow.data[request.key] = workFlowDataCollection.Data.filter((item) => levelNumbers.includes(item.LevelNumber.value)).map( - (item) => item.GetEntity() + (item) => item.GetEntity(), ); } } @@ -110,7 +131,7 @@ export class WorkflowActionService { }); } - public async addAdditionalApprovers(requests: Approval[]): Promise { + public async addAdditionalApprovers(requests: TypedEntity[]): Promise { const actionParameters = { uidPerson: this.createCdrPerson('#LDS#Additional approver'), reason: this.createCdrReason(), @@ -132,7 +153,7 @@ export class WorkflowActionService { }); } - public async withDrawApprover(requests: Approval[]): Promise { + public async withDrawApprover(requests: TypedEntity[]): Promise { const actionParameters = { reason: this.createCdrReason(), }; @@ -149,7 +170,7 @@ export class WorkflowActionService { }); } - public async delegateDecisions(requests: Approval[]): Promise { + public async delegateDecisions(requests: TypedEntity[]): Promise { const actionParameters = { uidPerson: this.createCdrPerson('#LDS#Delegate to'), reason: this.createCdrReason(), @@ -171,7 +192,7 @@ export class WorkflowActionService { }); } - public async revokeDelegations(requests: Approval[], withdrawAddApprover: boolean = true): Promise { + public async revokeDelegations(requests: TypedEntity[], withdrawAddApprover: boolean = true): Promise { const actionParameters = { reason: this.createCdrReason(), }; @@ -187,7 +208,7 @@ export class WorkflowActionService { }); } - public async denyDecisions(requests: Approval[]): Promise { + public async denyDecisions(requests: TypedEntity[]): Promise { const actionParameters = { reason: this.createCdrReason(), }; @@ -206,7 +227,7 @@ export class WorkflowActionService { }); } - public async escalateDecisions(requests: Approval[]): Promise { + public async escalateDecisions(requests: TypedEntity[]): Promise { const actionParameters = { reason: this.createCdrReason(), }; @@ -224,12 +245,12 @@ export class WorkflowActionService { public async checkMFA(uidPwo: string[]): Promise { this.busyService.show(); - let workflowActionId: string; - let mfaComponent: Type; - let response: boolean; + let workflowActionId: string = ''; + let mfaComponent: Type | undefined; + let response: boolean = false; try { - workflowActionId = await this.getStepupId(uidPwo); - mfaComponent = (await this.extService.getFittingComponent('mfaComponent')).instance; + workflowActionId = (await this.getStepupId(uidPwo)) || ''; + mfaComponent = (await this.extService.getFittingComponent('mfaComponent'))?.instance; } catch (err) { throw Error('The OLG module is not configured correctly'); } finally { @@ -240,7 +261,7 @@ export class WorkflowActionService { title: await this.translate.get('#LDS#Heading Authenticate Using OneLogin').toPromise(), padding: '0px', testId: 'imx-request-approval-mfa', - width: 'max(700px, 60%)', + width: calculateSidesheetWidth(1000), data: { workflowActionId, }, @@ -256,9 +277,9 @@ export class WorkflowActionService { return this.apiService.v2Client.portal_itshop_approve_requests_stepup_post({ UidPwo: uidPwo }); } - public async approve(requests: Approval[]): Promise { - - const term = (await this.checkTermsOfUse(requests)); + public async approve(entities: (Approval | TypedEntity)[]): Promise { + const requests = entities as Approval[]; + const term = await this.checkTermsOfUse(requests); if (!term.isChecked) { this.snackBar.open({ key: '#LDS#You have canceled the action.' }); return; @@ -268,25 +289,24 @@ export class WorkflowActionService { const schema = this.apiService.typedClient.PortalItshopApproveRequests.GetSchema(); const mfaRequests = requests.filter((req) => req.IsApproveRequiresMfa?.value); - if (itShopConfig.StepUpAuthenticationProvider !== 'NoAuth' && mfaRequests.length > 0) { + if (itShopConfig && itShopConfig.StepUpAuthenticationProvider !== 'NoAuth' && mfaRequests.length > 0) { // Check for MFA, don't continue unless true - const isMFA = term.isAuthenticated || await this.checkMFA(mfaRequests.map((request) => request.key)); + const isMFA = term.isAuthenticated || (await this.checkMFA(mfaRequests.map((request) => request.key))); if (!isMFA) { return; } } - let justification: BaseCdr; + let justification: BaseCdr | undefined; - let busyIndicator: OverlayRef; - setTimeout(() => (busyIndicator = this.busyService.show())); + this.showBusyIndicator(); const maxApproveReasonType = Math.max(...requests.map((elem) => elem.ApproveReasonType.value)); try { justification = await this.justificationService.createCdr(JustificationType.approve); } finally { - setTimeout(() => this.busyService.hide(busyIndicator)); + this.busyService.hide(); } const actionParameters: WorkflowActionParameters = { @@ -299,7 +319,7 @@ export class WorkflowActionService { const minDateFrom = new Date(); - if (itShopConfig.VI_ITShop_ApproverCanSetValidFrom && requests.some((item) => item.canSetValidFrom())) { + if (itShopConfig && itShopConfig.VI_ITShop_ApproverCanSetValidFrom && requests.some((item) => item.canSetValidFrom())) { showValidDate.validFrom = { key: 'validFrom', placeholder: '#LDS#immediately' }; const validFromColumn = this.entityService.createLocalEntityColumn(schema.Columns.ValidFrom, undefined, { @@ -309,7 +329,7 @@ export class WorkflowActionService { actionParameters[showValidDate.validFrom.key] = new BaseCdr(validFromColumn); } - if (itShopConfig.VI_ITShop_ApproverCanSetValidUntil && requests.some((item) => item.canSetValidUntil(itShopConfig))) { + if (itShopConfig && itShopConfig.VI_ITShop_ApproverCanSetValidUntil && requests.some((item) => item.canSetValidUntil(itShopConfig))) { showValidDate.validUntil = { key: 'validUntil', placeholder: '#LDS#unlimited' }; const minDateUntil = new Date(); @@ -365,19 +385,18 @@ export class WorkflowActionService { }); } - public async deny(requests: Approval[]): Promise { + public async deny(requests: TypedEntity[]): Promise { const itShopConfig = (await this.projectConfig.getConfig()).ITShopConfig; - let justification: BaseCdr; + let justification: BaseCdr | undefined; - let busyIndicator: OverlayRef; - setTimeout(() => (busyIndicator = this.busyService.show())); + this.showBusyIndicator(); - const maxDenyReasonType = Math.max(...requests.map((elem) => elem.DenyReasonType.value)); + const maxDenyReasonType = Math.max(...requests.map((elem: Approval) => elem.DenyReasonType.value)); try { justification = await this.justificationService.createCdr(JustificationType.deny); } finally { - setTimeout(() => this.busyService.hide(busyIndicator)); + this.busyService.hide(); } const actionParameters: WorkflowActionParameters = { @@ -394,7 +413,7 @@ export class WorkflowActionService { requests, actionParameters, withGuidance: true, - customValidation: itShopConfig.VI_ITShop_ApproverReasonMandatoryOnDeny + customValidation: itShopConfig?.VI_ITShop_ApproverReasonMandatoryOnDeny ? { validate: () => { const reasonValue = actionParameters.reason.column.GetValue(); @@ -421,7 +440,7 @@ export class WorkflowActionService { }); } - public async recallInquiry(requests: Approval[]): Promise { + public async recallInquiry(requests: TypedEntity[]): Promise { const actionParameters = { reason: this.createCdrReason(), }; @@ -437,7 +456,7 @@ export class WorkflowActionService { }); } - public async resetReservation(requests: Approval[]): Promise { + public async resetReservation(requests: TypedEntity[]): Promise { const actionParameters = { reason: this.createCdrReason(), }; @@ -485,22 +504,22 @@ export class WorkflowActionService { this.entityService.createLocalEntityColumn( { Type: ValType.Date, ColumnName: 'DateHead', Display: '#LDS#Inquiry made on' }, undefined, - pwo.Columns.DateHead - ) + pwo?.Columns?.DateHead, + ), ), new BaseReadonlyCdr( this.entityService.createLocalEntityColumn( { Type: ValType.String, ColumnName: 'ReasonHead', Display: '#LDS#Inquiry' }, undefined, - pwo.Columns.ReasonHead - ) + pwo?.Columns?.ReasonHead, + ), ), new BaseReadonlyCdr( this.entityService.createLocalEntityColumn( { Type: ValType.String, ColumnName: 'DisplayPersonHead', Display: '#LDS#Inquiry made by' }, undefined, - pwo.Columns.DisplayPersonHead - ) + pwo?.Columns?.DisplayPersonHead, + ), ), ]; @@ -516,22 +535,24 @@ export class WorkflowActionService { }); } - public getPwoData(pwo: Approval, userUid: string): EntityData { - return pwo.pwoData.WorkflowHistory.Entities.find( + public getPwoData(pwo: Approval, userUid: string): EntityData | undefined { + return pwo.pwoData.WorkflowHistory?.Entities?.find( (entityData) => - entityData.Columns.DecisionType.Value === 'Query' && - entityData.Columns.UID_PersonRelated.Value === userUid && - entityData.Columns.DecisionLevel.Value === pwo.DecisionLevel.value + entityData.Columns?.DecisionType.Value === 'Query' && + entityData.Columns?.UID_PersonRelated.Value === userUid && + entityData.Columns?.DecisionLevel.Value === pwo.DecisionLevel.value, ); } private async editAction(config: WorkflowActionEditWrapper): Promise { + const title = this.translate.instant(config.title); + const result = await this.sideSheet .open(WorkflowActionComponent, { - title: await this.translate.get(config.title).toPromise(), + title, subTitle: config.data.requests.length === 1 ? config.data.requests[0].GetEntity().GetDisplay() : '', padding: '0', - width: config.data.withGuidance && config.data.requests.length === 1 ? 'max(700px, 70%)' : '600px', + width: config.data.withGuidance && config.data.requests.length === 1 ? '700px' : '600px', testId: 'workflow-action', data: config.data, }) @@ -539,29 +560,48 @@ export class WorkflowActionService { .toPromise(); if (result) { - let busyIndicator: OverlayRef; - setTimeout(() => (busyIndicator = this.busyService.show())); - - let success: boolean; - try { + if (config.data.requests.length > this.queueService.actionThreshold) { + const actions: Action[] = []; for (const request of config.data.requests) { - await config.apply(request); + actions.push( + new Action( + request.GetEntity().GetDisplay(), + '', + async () => { + await config.apply(request as Approval); + }, + request.GetEntity().GetKeys().join(','), + ), + ); + } + this.queueService.submitGroupAction(title, actions, async () => { + // Once all actions are complete, reload the data + await this.userService.reloadPendingItems(); + this.applied.next(); + }); + } else { + this.showBusyIndicator(); + let success: boolean; + try { + for (const request of config.data.requests) { + await config.apply(request as Approval); + } + success = true; + } finally { + this.busyService.hide(); } - success = true; - } finally { - setTimeout(() => this.busyService.hide(busyIndicator)); - } - if (success) { - this.snackBar.open({ - key: config.message, - parameters: [ - config.data.requests.length, - config.data.actionParameters.uidPerson ? config.data.actionParameters.uidPerson.column.GetDisplayValue() : '', - ], - }); - await this.userService.reloadPendingItems(); - this.applied.next(); + if (success) { + this.snackBar.open({ + key: config.message, + parameters: [ + config.data.requests.length, + config.data.actionParameters.uidPerson ? config.data.actionParameters.uidPerson.column.GetDisplayValue() : '', + ], + }); + await this.userService.reloadPendingItems(); + this.applied.next(); + } } } else { if (config.discardChangesOnAbort) { @@ -610,7 +650,7 @@ export class WorkflowActionService { FkRelation: fkRelation, MinLen: 1, }, - [this.person.createFkProviderItem(fkRelation)] + [this.createFkProviderItemItShopInstead(fkRelation)], ); return new BaseCdr(column, display); @@ -635,7 +675,7 @@ export class WorkflowActionService { this.person.createFkProviderItem(fkRelation, [ { ColumnName: 'UID_Person', CompareOp: CompareOperator.NotEqual, Type: FilterType.Compare, Value1: uidPerson }, ]), - ] + ], ); return new BaseCdr(column, '#LDS#Recipient of the inquiry'); @@ -644,20 +684,20 @@ export class WorkflowActionService { private async checkTermsOfUse(requests: Approval[]): Promise<{ isChecked: boolean; isAuthenticated: boolean }> { // get all cart items with terms of uses const approvalItemsWithTermsOfUseToAccept = requests.filter( - (item) => item.UID_QERTermsOfUse?.value !== null && item.UID_QERTermsOfUse?.value !== '' + (item) => item.UID_QERTermsOfUse?.value !== null && item.UID_QERTermsOfUse?.value !== '', ); if (approvalItemsWithTermsOfUseToAccept.length > 0) { this.logger.debug( this, - `There are ${approvalItemsWithTermsOfUseToAccept.length} service items with terms of use the user have to accepted.` + `There are ${approvalItemsWithTermsOfUseToAccept.length} service items with terms of use the user have to accepted.`, ); const termsOfUseAccepted = await this.sideSheet .open(TermsOfUseAcceptComponent, { title: await this.translate.get('#LDS#Heading Accept Terms of Use').toPromise(), padding: '0px', - width: 'max(600px, 60%)', + width: calculateSidesheetWidth(), data: { acceptCartItems: false, approvalItems: approvalItemsWithTermsOfUseToAccept, @@ -670,7 +710,25 @@ export class WorkflowActionService { return termsOfUseAccepted; } else { this.logger.debug(this, 'there are no service items with terms of use the user have to accepted.'); - return { isChecked: true, isAuthenticated: false} + return { isChecked: true, isAuthenticated: false }; + } + } + + private createFkProviderItemItShopInstead(fkRelation: MetaTableRelationData, filter?: FilterData[]): FkProviderItem { + return { + columnName: fkRelation.ChildColumnName || '', + fkTableName: fkRelation.ParentTableName || '', + parameterNames: ['OrderBy', 'StartIndex', 'PageSize', 'filter', 'withProperties', 'search', 'foritshopapprover'], + load: async (_, parameters: CollectionLoadParameters = {}) => + this.apiService.v2Client.portal_candidates_Person_get({ ...parameters, filter, foritshopapprover: true }), + getDataModel: async () => ({}), + getFilterTree: async () => ({ Elements: [] }), + }; + } + + private showBusyIndicator(): void { + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); } } } diff --git a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-multi-action/workflow-multi-action.component.html b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-multi-action/workflow-multi-action.component.html index aeb6c483c..fd51662d1 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-multi-action/workflow-multi-action.component.html +++ b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-multi-action/workflow-multi-action.component.html @@ -35,10 +35,11 @@
    - + {{ '#LDS#Heading Individual Settings' | translate }} {{ - '#LDS#Here you can see all products you have selected. You can make individual settings for specific products if applicable.' | translate + '#LDS#Here you can see all products you have selected. You can make individual settings for specific products if applicable.' + | translate }} diff --git a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-multi-action/workflow-multi-action.component.ts b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-multi-action/workflow-multi-action.component.ts index fe8ae6fb0..f45ad7c27 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-multi-action/workflow-multi-action.component.ts +++ b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-multi-action/workflow-multi-action.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,7 +26,8 @@ import { Component, Input, OnInit } from '@angular/core'; import { UntypedFormGroup } from '@angular/forms'; -import { BaseCdr, BaseReadonlyCdr, BulkItem, BulkItemStatus } from 'qbm'; +import { BaseCdr, BulkItem, BulkItemStatus } from 'qbm'; +import { Approval } from '../../approval'; import { DecisionStepSevice } from '../../decision-step.service'; import { WorkflowActionEdit } from '../workflow-action-edit.interface'; @@ -75,40 +76,47 @@ export class WorkflowMultiActionComponent implements OnInit { */ public ngOnInit(): void { this.requests = this.data.requests.map((item) => { + const approval = item as Approval; const bulkItem: BulkItem = { entity: item, - additionalInfo: item.OrderState.Column.GetDisplayValue(), + additionalInfo: item.GetEntity().GetColumn('OrderState')?.GetDisplayValue(), properties: [], status: BulkItemStatus.valid, }; if (this.data.showValidDate) { - if (this.data.showValidDate.validFrom) { - bulkItem.properties.push(new BaseReadonlyCdr(item.ValidFrom.Column)); + if ( + (this.data.showValidDate.validFrom && approval.ValidFrom.Column.GetValue() !== '') || + approval.ValidFrom.GetMetadata().CanEdit() + ) { + bulkItem.properties.push(new BaseCdr(approval.ValidFrom.Column)); } - if (this.data.showValidDate.validUntil) { - bulkItem.properties.push(new BaseReadonlyCdr(item.ValidUntil.Column)); + if ( + (this.data.showValidDate.validUntil && approval.ValidUntil.Column.GetValue() !== '') || + approval.ValidUntil.GetMetadata().CanEdit() + ) { + bulkItem.properties.push(new BaseCdr(approval.ValidUntil.Column)); } } - const step = this.stepService.getCurrentStepCdr(item, item.pwoData, '#LDS#Current approval step'); + const step = this.stepService.getCurrentStepCdr(item, approval.pwoData, '#LDS#Current approval step'); if (step != null) { bulkItem.properties.unshift(step); } - item.parameterColumns.forEach((column) => - bulkItem.properties.push(this.data.approve ? new BaseCdr(column) : new BaseReadonlyCdr(column)) - ); - + approval.parameterColumns.forEach((column) => { + if (this.data.approve && (column.GetValue() !== '' || column.GetMetadata().CanEdit())) + bulkItem.properties.push(new BaseCdr(column)); + }); if (this.data.workflow) { bulkItem.customSelectProperties = [ { title: this.data.workflow.title, placeholder: this.data.workflow.placeholder, - entities: this.data.workflow.data[item.key], + entities: this.data.workflow.data[approval.key], selectionChange: (entity) => { - if (item.updateDirectDecisionTarget) { - item.updateDirectDecisionTarget(entity); + if (approval.updateDirectDecisionTarget) { + approval.updateDirectDecisionTarget(entity); } }, }, diff --git a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-single-action/workflow-single-action.component.html b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-single-action/workflow-single-action.component.html index f6bd00d27..41763088b 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-single-action/workflow-single-action.component.html +++ b/imxweb/projects/qer/src/lib/itshopapprove/workflow-action/workflow-single-action/workflow-single-action.component.html @@ -3,6 +3,7 @@ @@ -27,7 +28,7 @@ > - + - this.requestParameterColumns.push(this.data.approve ? new BaseCdr(pCol) : new BaseReadonlyCdr(pCol)) + this.requestParameterColumns.push(this.data.approve ? new BaseCdr(pCol) : new BaseReadonlyCdr(pCol)), ); } - this.currentStepCdr = this.stepService.getCurrentStepCdr(this.request, this.request.pwoData, '#LDS#Current approval step'); + if (this.request.pwoData) { + this.currentStepCdr = this.stepService.getCurrentStepCdr(this.request, this.request.pwoData, '#LDS#Current approval step'); + } } /** diff --git a/imxweb/projects/qer/src/lib/itshopapprove/workflow-actions/answer-questions.component.html b/imxweb/projects/qer/src/lib/itshopapprove/workflow-actions/answer-questions.component.html index 6ffac2364..fdba19766 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/workflow-actions/answer-questions.component.html +++ b/imxweb/projects/qer/src/lib/itshopapprove/workflow-actions/answer-questions.component.html @@ -1,55 +1,53 @@ -

    {{'#LDS#Heading Request Inquiries' | translate}}

    +

    {{ '#LDS#Heading Request Inquiries' | translate }}

    -

    {{'#LDS#There are no pending approval inquiries.' | translate}}>

    +

    {{ '#LDS#There are no pending approval inquiries.' | translate }}>

    -

    {{'#LDS#You have been asked to answer inquiries about the following requests.' | translate}}

    - - - - - - - -
    - -
    - -
    -
    -
    - -
    +
    +
    +
    +
    - -

    {{'#LDS#Respond to an inquiry' | translate}}

    - +

    {{ '#LDS#Respond to an inquiry' | translate }}

    + +

    {{ '#LDS#Question' | translate }}:

    -

    {{'#LDS#Question' | translate}}:

    - - - - -
    - - - - - -
    \ No newline at end of file + + + + + + +
    diff --git a/imxweb/projects/qer/src/lib/itshopapprove/workflow-actions/answer-questions.component.ts b/imxweb/projects/qer/src/lib/itshopapprove/workflow-actions/answer-questions.component.ts index f788350bc..68fd44595 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/workflow-actions/answer-questions.component.ts +++ b/imxweb/projects/qer/src/lib/itshopapprove/workflow-actions/answer-questions.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,45 +24,48 @@ * */ -import { Component, OnInit, TemplateRef, ViewChild } from "@angular/core"; +import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core'; import { ImxTranslationProviderService, imx_SessionService } from 'qbm'; import { MatDialog } from '@angular/material/dialog'; import { MatSnackBar } from '@angular/material/snack-bar'; @Component({ - templateUrl: "./answer-questions.component.html" + templateUrl: './answer-questions.component.html', }) export class AnswerQuestionsComponent implements OnInit { + constructor( + private translator: ImxTranslationProviderService, + private dialogService: MatDialog, + private session: imx_SessionService, + private snackBar: MatSnackBar, + ) {} - constructor(private translator: ImxTranslationProviderService, private dialogService: MatDialog, - private session : imx_SessionService, private snackBar: MatSnackBar) { } + Notice: string; // TODO caption = translate("#LDS#Your answer") - Notice : string; // TODO caption = translate("#LDS#Your answer") + public ngOnInit(): void { + // TODO load from API + } - public ngOnInit(): void { - // TODO load from API - } + PersonWantsOrg: any[] = []; - PersonWantsOrg : any[] = []; + @ViewChild('Call1') public tplCall1: TemplateRef; - @ViewChild('Call1') public tplCall1: TemplateRef; + public Button3(): void { + this.Notice = ''; + this.dialogService.open(this.tplCall1); + } - public Button3(): void { - this.Notice = ""; - this.dialogService.open(this.tplCall1); - } + public async Button4(): Promise { + // TODO validate - public async Button4(): Promise { - // TODO validate + await this.session.Client.portal_itshop_answerquery_post(uidPwo, { + Reason: this.Notice, + }); + // TODO update open items + ActionOnControl({ ActionType: 'CloseDialog' }); + // TODO reload data + this.snackBar.open(await this.translator.Translate('#LDS#Your answer has been submitted.').toPromise()); + } - await this.session.Client.portal_itshop_answerquery_post(uidPwo, { - Reason: this.Notice - }); - // TODO update open items - ActionOnControl({ "ActionType": "CloseDialog" }); - // TODO reload data - this.snackBar.open(await this.translator.Translate("#LDS#Your answer has been submitted.").toPromise()); - } - - // Parameter: aeweb_uid_pwohelperpwo (FreeText) (optional) + // Parameter: aeweb_uid_pwohelperpwo (FreeText) (optional) } diff --git a/imxweb/projects/qer/src/lib/itshopapprove/workflow-actions/query-person.component.html b/imxweb/projects/qer/src/lib/itshopapprove/workflow-actions/query-person.component.html index 5c9c94281..b9af83a71 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/workflow-actions/query-person.component.html +++ b/imxweb/projects/qer/src/lib/itshopapprove/workflow-actions/query-person.component.html @@ -1,14 +1,13 @@ -

    {{'#LDS#Heading Send Inquiry' | translate}}

    +

    {{ '#LDS#Heading Send Inquiry' | translate }}

    -

    - {{'#LDS#Select the identity to which you want to send an inquiry.' | translate}} -

    - +

    + {{ '#LDS#Select the identity to which you want to send an inquiry.' | translate }} +

    + - +
    - - - \ No newline at end of file + + + diff --git a/imxweb/projects/qer/src/lib/itshopapprove/workflow-actions/query-person.component.ts b/imxweb/projects/qer/src/lib/itshopapprove/workflow-actions/query-person.component.ts index ff6338a1a..2e4965efe 100644 --- a/imxweb/projects/qer/src/lib/itshopapprove/workflow-actions/query-person.component.ts +++ b/imxweb/projects/qer/src/lib/itshopapprove/workflow-actions/query-person.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,45 +27,48 @@ import { Component, Input } from '@angular/core'; import { MatSnackBar } from '@angular/material/snack-bar'; -import { IWriteValue } from 'imx-qbm-dbts'; -import { PortalItshopApproveRequests } from 'imx-api-qer'; +import { PortalItshopApproveRequests } from '@imx-modules/imx-api-qer'; +import { IWriteValue } from '@imx-modules/imx-qbm-dbts'; -import { ImxTranslationProviderService } from 'qbm'; +import { TranslateService } from '@ngx-translate/core'; +import { LdsReplacePipe } from 'qbm'; import { QerApiService } from '../../qer-api-client.service'; @Component({ - templateUrl: './query-person.component.html', - selector: 'imx-itshop-queryperson' + templateUrl: './query-person.component.html', + selector: 'imx-itshop-queryperson', }) export class QueryPersonComponent { + public Notice: IWriteValue; + // TODO minlen=1, caption:#LDS#Your question - public Notice: IWriteValue; - // TODO minlen=1, caption:#LDS#Your question + public UID_Person: IWriteValue; + // TDO configure as FK to person - public UID_Person: IWriteValue; - // TDO configure as FK to person + @Input() public PersonWantsOrg: PortalItshopApproveRequests; - @Input() public PersonWantsOrg: PortalItshopApproveRequests; + constructor( + private readonly ldsReplace: LdsReplacePipe, + private readonly translate: TranslateService, + private readonly snackBar: MatSnackBar, + private readonly apiService: QerApiService, + ) {} - constructor( - private translator: ImxTranslationProviderService, - private snackBar: MatSnackBar, - private apiService: QerApiService) { } + public async Button3(): Promise { + // ActionOnControl({ 'ActionType': 'PerformValidation' }); - public async Button3(): Promise { - // ActionOnControl({ 'ActionType': 'PerformValidation' }); + await this.apiService.client.portal_itshop_query_post(this.PersonWantsOrg.GetEntity().GetKeys()[0], { + Text: this.Notice.value, + UidPerson: this.UID_Person.value, + }); - await this.apiService.client.portal_itshop_query_post(this.PersonWantsOrg.GetEntity().GetKeys()[0], { - Text: this.Notice.value, - UidPerson: this.UID_Person.value - }); - - // TODO reload all data - - this.snackBar.open(await this.translator.Translate({ - key: '#LDS#Your question was submitted to {0}.', - parameters: [this.UID_Person.Column.GetDisplayValue()] - }).toPromise()); - } + // TODO reload all data + this.snackBar.open( + this.ldsReplace.transform( + await this.translate.get('#LDS#Your question was submitted to {0}.').toPromise(), + this.UID_Person.Column.GetDisplayValue(), + ), + ); + } } diff --git a/imxweb/projects/qer/src/lib/justification/decision-reason/decision-reason.component.html b/imxweb/projects/qer/src/lib/justification/decision-reason/decision-reason.component.html index f04687ebc..5739ba843 100644 --- a/imxweb/projects/qer/src/lib/justification/decision-reason/decision-reason.component.html +++ b/imxweb/projects/qer/src/lib/justification/decision-reason/decision-reason.component.html @@ -1,10 +1,15 @@ - + data-imx-identifier="reason-decision-standard" +> - - \ No newline at end of file + data-imx-identifier="reason-decision-freetext" +> + diff --git a/imxweb/projects/qer/src/lib/justification/decision-reason/decision-reason.component.scss b/imxweb/projects/qer/src/lib/justification/decision-reason/decision-reason.component.scss index 60d9e401f..4cef8a0ce 100644 --- a/imxweb/projects/qer/src/lib/justification/decision-reason/decision-reason.component.scss +++ b/imxweb/projects/qer/src/lib/justification/decision-reason/decision-reason.component.scss @@ -1,4 +1,4 @@ :host { display: flex; flex-direction: column; -} \ No newline at end of file +} diff --git a/imxweb/projects/qer/src/lib/justification/decision-reason/decision-reason.component.ts b/imxweb/projects/qer/src/lib/justification/decision-reason/decision-reason.component.ts index aeb2809a7..46ff9682f 100644 --- a/imxweb/projects/qer/src/lib/justification/decision-reason/decision-reason.component.ts +++ b/imxweb/projects/qer/src/lib/justification/decision-reason/decision-reason.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,11 +25,10 @@ */ import { AfterViewInit, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { AbstractControl, FormControl, FormGroup} from '@angular/forms'; -import _ from 'lodash'; +import { AbstractControl, FormControl, FormGroup } from '@angular/forms'; -import { ValueStruct } from 'imx-qbm-dbts'; -import { BaseCdr } from 'qbm'; +import { ValueStruct } from '@imx-modules/imx-qbm-dbts'; +import { BaseCdr, ColumnDependentReference } from 'qbm'; import { JustificationService } from '../justification.service'; interface ReasonFormGroup { @@ -42,19 +41,30 @@ interface ReasonFormGroup { styleUrls: ['./decision-reason.component.scss'], }) export class DecisionReasonComponent implements OnInit, AfterViewInit { - @Input() public reasonStandard: BaseCdr; - @Input() public reasonFreetext: BaseCdr; + @Input() public reasonStandard: ColumnDependentReference; + @Input() public reasonFreetext: ColumnDependentReference; @Input() public maxReasonType: number; @Output() public controlCreated = new EventEmitter(); private readonly formGroup = new FormGroup({}); - constructor(private readonly justificationService: JustificationService, private readonly changeDetector: ChangeDetectorRef) {} + constructor( + private readonly justificationService: JustificationService, + private readonly changeDetector: ChangeDetectorRef, + ) {} public ngOnInit(): void { - this.reasonStandard?.updateMinLength((this.maxReasonType ?? 0) === 1 ? 1 : 0); - this.reasonFreetext?.updateMinLength((this.maxReasonType ?? 0) === 0 ? 0 : 1); + this.reasonStandardCdr?.updateMinLength((this.maxReasonType ?? 0) === 1 ? 1 : 0); + this.reasonFreetextCdr?.updateMinLength((this.maxReasonType ?? 0) === 0 ? 0 : 1); + } + + private get reasonStandardCdr(): BaseCdr { + return this.reasonStandard as BaseCdr; + } + + private get reasonFreetextCdr(): BaseCdr { + return this.reasonFreetext as BaseCdr; } public ngAfterViewInit(): void { @@ -71,15 +81,17 @@ export class DecisionReasonComponent implements OnInit, AfterViewInit { public async updateMinLength() { switch (this.maxReasonType) { - case 0: { // nothing is mandatory, unless the justification requires a free text + case 0: { + // nothing is mandatory, unless the justification requires a free text const justification = this.formGroup.controls.standard?.value?.DataValue ? await this.justificationService.get(this.formGroup.controls.standard?.value?.DataValue) : undefined; - this.reasonFreetext?.updateMinLength(Math.max(justification && justification.RequiresText.value ? 1 : 0)); + this.reasonFreetextCdr?.updateMinLength(Math.max(justification && justification.RequiresText.value ? 1 : 0)); this.changeDetector.detectChanges(); return; } - case 1:{ // at least one reason (standard or free) is mandatory + case 1: { + // at least one reason (standard or free) is mandatory const justificationRequired = this.formGroup.controls.freetext?.value.length === 0; const justification = this.formGroup.controls.standard?.value?.DataValue @@ -88,12 +100,13 @@ export class DecisionReasonComponent implements OnInit, AfterViewInit { const freetextRequired = (justification == null && this.formGroup.controls.freetext?.value.length === 0) || !!justification?.RequiresText?.value; - this.reasonStandard?.updateMinLength(justificationRequired ? 1 : 0); - this.reasonFreetext?.updateMinLength(freetextRequired ? 1 : 0); + this.reasonStandardCdr?.updateMinLength(justificationRequired ? 1 : 0); + this.reasonFreetextCdr?.updateMinLength(freetextRequired ? 1 : 0); this.changeDetector.detectChanges(); - return ; + return; } - default: return; + default: + return; } } } diff --git a/imxweb/projects/qer/src/lib/justification/justification-type.enum.ts b/imxweb/projects/qer/src/lib/justification/justification-type.enum.ts index 9430f8504..619c996c0 100644 --- a/imxweb/projects/qer/src/lib/justification/justification-type.enum.ts +++ b/imxweb/projects/qer/src/lib/justification/justification-type.enum.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -34,5 +34,5 @@ export enum JustificationType { approveRuleViolation = 64, denyRuleViolation = 128, approvePolicyViolation = 256, - denyPolicyViolation = 512 + denyPolicyViolation = 512, } diff --git a/imxweb/projects/qer/src/lib/justification/justification.module.ts b/imxweb/projects/qer/src/lib/justification/justification.module.ts index 326de05b1..294288a6c 100644 --- a/imxweb/projects/qer/src/lib/justification/justification.module.ts +++ b/imxweb/projects/qer/src/lib/justification/justification.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,21 +30,10 @@ import { JustificationService } from './justification.service'; import { DecisionReasonComponent } from './decision-reason/decision-reason.component'; import { CdrModule } from 'qbm'; - - @NgModule({ - declarations: [ - DecisionReasonComponent - ], - imports: [ - CdrModule, - CommonModule - ], - providers: [ - JustificationService - ], - exports: [ - DecisionReasonComponent - ] + declarations: [DecisionReasonComponent], + imports: [CdrModule, CommonModule], + providers: [JustificationService], + exports: [DecisionReasonComponent], }) -export class JustificationModule { } +export class JustificationModule {} diff --git a/imxweb/projects/qer/src/lib/justification/justification.service.ts b/imxweb/projects/qer/src/lib/justification/justification.service.ts index 388480ef6..aee865887 100644 --- a/imxweb/projects/qer/src/lib/justification/justification.service.ts +++ b/imxweb/projects/qer/src/lib/justification/justification.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,7 +26,7 @@ import { Injectable } from '@angular/core'; -import { PortalJustifications } from 'imx-api-qer'; +import { PortalJustifications } from '@imx-modules/imx-api-qer'; import { CollectionLoadParameters, CompareOperator, @@ -36,9 +36,8 @@ import { IClientProperty, MetaTableRelationData, ValType, -} from 'imx-qbm-dbts'; -import { ImxTranslationProviderService } from 'qbm'; -import { BaseCdr, EntityService } from 'qbm'; +} from '@imx-modules/imx-qbm-dbts'; +import { BaseCdr, EntityService, ImxTranslationProviderService } from 'qbm'; import { QerApiService } from '../qer-api-client.service'; import { JustificationType } from './justification-type.enum'; @@ -51,10 +50,10 @@ export class JustificationService { constructor( private readonly apiService: QerApiService, private readonly entityService: EntityService, - private readonly translate: ImxTranslationProviderService + private readonly translate: ImxTranslationProviderService, ) {} - public async get(uid: string): Promise { + public async get(uid: string): Promise { const collection = await this.apiService.typedClient.PortalJustifications.Get({ filter: [ { @@ -68,13 +67,13 @@ export class JustificationService { return collection && collection.Data && collection.Data.length > 0 ? collection.Data[0] : undefined; } - public async createCdr(justificationType: JustificationType): Promise { + public async createCdr(justificationType: JustificationType): Promise { if ((await this.getByType(justificationType))?.TotalCount === 0) { return undefined; } const property = this.createProperty(); - const fkProviderItem = this.createFkProviderItem(property.FkRelation, justificationType); + const fkProviderItem = this.createFkProviderItem(property.FkRelation || {}, justificationType); const column = this.entityService.createLocalEntityColumn(property, [fkProviderItem]); const cdr = new BaseCdr(column, '#LDS#Reason for your decision'); return cdr; @@ -100,8 +99,8 @@ export class JustificationService { private createFkProviderItem(fkRelation: MetaTableRelationData, justificationType: JustificationType): FkProviderItem { return { - columnName: fkRelation.ChildColumnName, - fkTableName: fkRelation.ParentTableName, + columnName: fkRelation.ChildColumnName || '', + fkTableName: fkRelation.ParentTableName || '', parameterNames: ['OrderBy', 'StartIndex', 'PageSize', 'filter', 'search'], load: async (_, parameters = {}) => this.getByType(justificationType, parameters), getDataModel: async () => ({}), @@ -112,11 +111,11 @@ export class JustificationService { private async getByType(justificationType: JustificationType, parameters: CollectionLoadParameters = {}): Promise { const collection = await this.apiService.client.portal_justifications_get(parameters); - // tslint:disable-next-line:no-bitwise - const entities = collection.Entities.filter((entityData) => (entityData.Columns.JustificationType.Value & justificationType) > 0); + // eslint-disable-next-line no-bitwise + const entities = collection.Entities?.filter((entityData) => (entityData.Columns?.JustificationType.Value & justificationType) > 0); return { - TotalCount: entities.length, + TotalCount: entities?.length || 0, IsLimitReached: collection.IsLimitReached, Entities: entities, TableName: collection.TableName, diff --git a/imxweb/projects/qbm/src/lib/doc/doc-chapter.service.ts b/imxweb/projects/qer/src/lib/metadata/portal-metadata.service.ts similarity index 52% rename from imxweb/projects/qbm/src/lib/doc/doc-chapter.service.ts rename to imxweb/projects/qer/src/lib/metadata/portal-metadata.service.ts index d8db83443..377e500f5 100644 --- a/imxweb/projects/qbm/src/lib/doc/doc-chapter.service.ts +++ b/imxweb/projects/qer/src/lib/metadata/portal-metadata.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,29 +24,28 @@ * */ -import { Injectable } from "@angular/core"; +import { Injectable } from '@angular/core'; +import { MetaTableData } from '@imx-modules/imx-qbm-dbts'; + +import { ApiClientService, MetadataService } from 'qbm'; +import { QerApiService } from '../qer-api-client.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) -export class DocChapterService { - - /** Maps the Angular routes to the corresponding documentation chapter. */ - public readonly chapters: { [route: string]: DocChapter } = {}; - -} - -export interface DocChapter { - - /** Returns the document containing this chapter. */ - readonly document: DocDocument; - - /** Returns the chapter UID. */ - readonly chapterUid: string; -} - -export interface DocDocument { - - /** Maps languages to the document paths. */ - readonly paths: { [language: string]: string }; +export class PortalMetadataService extends MetadataService { + constructor( + private readonly apiClient: QerApiService, + private readonly apiProvider: ApiClientService, + ) { + super(); + } + + protected async getTable(tableName: string, options?: any): Promise { + return this.apiProvider.request(() => + this.apiClient.client.portal_metadata_table_get(tableName, options, { + signal: this.abortController.signal, + }), + ); + } } diff --git a/imxweb/projects/qer/src/lib/my-responsibilities-view/my-responsibilities-registry.service.ts b/imxweb/projects/qer/src/lib/my-responsibilities-view/my-responsibilities-registry.service.ts index 105e1620e..6d8da63d1 100644 --- a/imxweb/projects/qer/src/lib/my-responsibilities-view/my-responsibilities-registry.service.ts +++ b/imxweb/projects/qer/src/lib/my-responsibilities-view/my-responsibilities-registry.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { Injectable } from '@angular/core'; -import { ProjectConfig } from 'imx-api-qbm'; +import { ProjectConfig } from '@imx-modules/imx-api-qbm'; import { SideNavigationExtension, SideNavigationFactory } from 'qbm'; @Injectable({ @@ -34,11 +34,11 @@ import { SideNavigationExtension, SideNavigationFactory } from 'qbm'; export class MyResponsibilitiesRegistryService { private items: SideNavigationFactory[] = []; - public getNavItems(preProps: string[], features: string[], projectConfig?: ProjectConfig): SideNavigationExtension[] { + public getNavItems(preProps: string[], features: string[], projectConfig?: ProjectConfig): (SideNavigationExtension | undefined)[] { return this.items .map((factory) => factory(preProps, features, projectConfig)) - .sort((a, b) => a.sortOrder - b.sortOrder) - .filter((item) => item !== undefined); + .filter((item) => item?.name !== '') + .sort((a, b) => (a?.sortOrder ?? 0) - (b?.sortOrder ?? 0)); } public registerFactory(...factories: SideNavigationFactory[]): void { diff --git a/imxweb/projects/qer/src/lib/my-responsibilities-view/my-responsibilities-view.component.html b/imxweb/projects/qer/src/lib/my-responsibilities-view/my-responsibilities-view.component.html index 4fc67b390..a0e4a7220 100644 --- a/imxweb/projects/qer/src/lib/my-responsibilities-view/my-responsibilities-view.component.html +++ b/imxweb/projects/qer/src/lib/my-responsibilities-view/my-responsibilities-view.component.html @@ -3,6 +3,6 @@ [isAdmin]="isAdmin" [componentName]="componentName" [componentTitle]="componentTitle" - [navItems]="navItems" + [navItems]="navItems ?? []" [contextId]="contextId" > diff --git a/imxweb/projects/qer/src/lib/my-responsibilities-view/my-responsibilities-view.component.ts b/imxweb/projects/qer/src/lib/my-responsibilities-view/my-responsibilities-view.component.ts index 6242a0103..e121eba61 100644 --- a/imxweb/projects/qer/src/lib/my-responsibilities-view/my-responsibilities-view.component.ts +++ b/imxweb/projects/qer/src/lib/my-responsibilities-view/my-responsibilities-view.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,12 +25,12 @@ */ import { ChangeDetectorRef, Component, OnInit } from '@angular/core'; -import { SideNavigationExtension, SystemInfoService, HELP_CONTEXTUAL } from 'qbm'; +import { ProjectConfig } from '@imx-modules/imx-api-qbm'; +import { QerProjectConfig } from '@imx-modules/imx-api-qer'; +import { HELP_CONTEXTUAL, SideNavigationExtension, SystemInfoService } from 'qbm'; +import { ProjectConfigurationService } from '../project-configuration/project-configuration.service'; import { UserModelService } from '../user/user-model.service'; import { MyResponsibilitiesRegistryService } from './my-responsibilities-registry.service'; -import { ProjectConfigurationService } from '../project-configuration/project-configuration.service'; -import { ProjectConfig } from 'imx-api-qbm'; -import { QerProjectConfig } from 'imx-api-qer'; @Component({ selector: 'imx-my-responsibilities-view', @@ -43,7 +43,7 @@ export class MyResponsibilitiesViewComponent implements OnInit { public componentName = 'my-responsibilities-view'; public componentTitle = '#LDS#Heading My Responsibilities'; public contextId = HELP_CONTEXTUAL.MyResponsibilities; - public navItems: SideNavigationExtension[] = []; + public navItems: (SideNavigationExtension | undefined)[] = []; constructor( private readonly systemInfoService: SystemInfoService, private readonly myResponsibilitiesRegistryService: MyResponsibilitiesRegistryService, @@ -61,8 +61,13 @@ export class MyResponsibilitiesViewComponent implements OnInit { const userConfig = await this.userModelService.getUserConfig(); const config: QerProjectConfig & ProjectConfig = await this.projectConfig.getConfig(); this.navItems = this.myResponsibilitiesRegistryService - .getNavItems(systemInfo.PreProps, features, config) - .filter((elem) => elem.name === 'identities' || elem.name === 'devices' || userConfig.Ownerships.find(own => own.TableName === elem.name)?.Count > 0); + .getNavItems(systemInfo.PreProps ?? [], features, config) + .filter( + (elem) => + elem?.name === 'identities' || + elem?.name === 'devices' || + !!userConfig.Ownerships?.find((own) => own.TableName === elem?.name)?.Count, + ); this.cdref.detectChanges(); } } diff --git a/imxweb/projects/qer/src/lib/my-responsibilities-view/my-responsibilities-view.module.ts b/imxweb/projects/qer/src/lib/my-responsibilities-view/my-responsibilities-view.module.ts index a883f0786..15623fad3 100644 --- a/imxweb/projects/qer/src/lib/my-responsibilities-view/my-responsibilities-view.module.ts +++ b/imxweb/projects/qer/src/lib/my-responsibilities-view/my-responsibilities-view.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -56,7 +56,11 @@ const routes: Routes = [ providers: [MyResponsibilitiesRegistryService], }) export class MyResponsibilitiesViewModule { - constructor(readonly router: Router, private readonly menuService: MenuService, logger: ClassloggerService) { + constructor( + readonly router: Router, + private readonly menuService: MenuService, + logger: ClassloggerService, + ) { const config = router.config; routes.forEach((route) => { config.splice(config.length - 1, 0, route); diff --git a/imxweb/projects/qbm/src/lib/select/select-content-provider.interface.ts b/imxweb/projects/qer/src/lib/new-request/constants.ts similarity index 82% rename from imxweb/projects/qbm/src/lib/select/select-content-provider.interface.ts rename to imxweb/projects/qer/src/lib/new-request/constants.ts index 32fab8248..e4eefe6dc 100644 --- a/imxweb/projects/qbm/src/lib/select/select-content-provider.interface.ts +++ b/imxweb/projects/qer/src/lib/new-request/constants.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,8 +24,4 @@ * */ -export interface SelectContentProvider { - display: (item: T) => string; - displayLong?: (item: T) => string; - key: (item: T) => string; -} +export const NEW_REQUEST_ROUTE = 'newrequest'; diff --git a/imxweb/projects/qer/src/lib/new-request/current-product-source.ts b/imxweb/projects/qer/src/lib/new-request/current-product-source.ts index a0f2c6534..e599d24a4 100644 --- a/imxweb/projects/qer/src/lib/new-request/current-product-source.ts +++ b/imxweb/projects/qer/src/lib/new-request/current-product-source.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,8 +24,8 @@ * */ -import { DataSourceToolbarComponent } from "qbm"; -import { SelectedProductSource } from "./new-request-selected-products/selected-product-item.interface"; +import { DataSourceToolbarComponent } from 'qbm'; +import { SelectedProductSource } from './new-request-selected-products/selected-product-item.interface'; export interface CurrentProductSource { dst: DataSourceToolbarComponent; diff --git a/imxweb/projects/qer/src/lib/new-request/element-visibility.directive.ts b/imxweb/projects/qer/src/lib/new-request/element-visibility.directive.ts index d872734fb..67b313c2d 100644 --- a/imxweb/projects/qer/src/lib/new-request/element-visibility.directive.ts +++ b/imxweb/projects/qer/src/lib/new-request/element-visibility.directive.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -34,7 +34,10 @@ export class ElementVisibilityDirective implements OnInit, OnDestroy { @Output() public elementVisibile = new EventEmitter(); - constructor(private element: ElementRef, private zone: NgZone) {} + constructor( + private element: ElementRef, + private zone: NgZone, + ) {} public ngOnInit(): void { this.zone.runOutsideAngular(() => { diff --git a/imxweb/projects/qer/src/lib/new-request/new-request-add-to-cart.service.ts b/imxweb/projects/qer/src/lib/new-request/new-request-add-to-cart.service.ts index 7f9f4c199..f3e84491d 100644 --- a/imxweb/projects/qer/src/lib/new-request/new-request-add-to-cart.service.ts +++ b/imxweb/projects/qer/src/lib/new-request/new-request-add-to-cart.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,38 +26,37 @@ import { Injectable } from '@angular/core'; -import { MultiValue, ValueStruct } from 'imx-qbm-dbts'; import { PortalItshopPatternRequestable, PortalItshopPeergroupMemberships, PortalShopServiceitems, QerProjectConfig, - RequestableProductForPerson -} from 'imx-api-qer'; + RequestableProductForPerson, +} from '@imx-modules/imx-api-qer'; +import { MultiValue, ValueStruct } from '@imx-modules/imx-qbm-dbts'; -import { SnackBarService } from 'qbm'; -import { ProjectConfigurationService } from '../project-configuration/project-configuration.service'; -import { DependencyService } from '../product-selection/optional-items-sidesheet/dependency.service'; import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; -import { OptionalItemsSidesheetComponent } from '../product-selection/optional-items-sidesheet/optional-items-sidesheet.component'; import { TranslateService } from '@ngx-translate/core'; +import { SnackBarService, calculateSidesheetWidth } from 'qbm'; +import { ShelfService } from '../itshop/shelf.service'; +import { PatternItemService } from '../pattern-item-list/pattern-item.service'; +import { DependencyService } from '../product-selection/optional-items-sidesheet/dependency.service'; +import { OptionalItemsSidesheetComponent } from '../product-selection/optional-items-sidesheet/optional-items-sidesheet.component'; +import { RecipientsWrapper } from '../product-selection/recipients-wrapper'; import { ServiceItemOrder } from '../product-selection/service-item-order.interface'; +import { ProjectConfigurationService } from '../project-configuration/project-configuration.service'; import { ServiceItemsService } from '../service-items/service-items.service'; -import { PatternItemService } from '../pattern-item-list/pattern-item.service'; -import { ShelfService } from '../itshop/shelf.service'; import { CartItemsService } from '../shopping-cart/cart-items.service'; -import { SelectedProductSource } from './new-request-selected-products/selected-product-item.interface'; +import { UserModelService } from '../user/user-model.service'; import { NewRequestOrchestrationService } from './new-request-orchestration.service'; +import { SelectedProductSource } from './new-request-selected-products/selected-product-item.interface'; import { NewRequestSelectionService } from './new-request-selection.service'; -import { RecipientsWrapper } from '../product-selection/recipients-wrapper'; -import { UserModelService } from '../user/user-model.service'; // TODO: not used anymore - can be removed @Injectable({ providedIn: 'root', }) export class NewRequestAddToCartService { - private projectConfig: QerProjectConfig; private savedItems = 0; private possibleItems = 0; @@ -74,12 +73,11 @@ export class NewRequestAddToCartService { private readonly shelfService: ShelfService, private readonly snackbar: SnackBarService, private readonly sidesheetService: EuiSidesheetService, - private readonly translate: TranslateService, private readonly userModelService: UserModelService, + private readonly translate: TranslateService, ) {} public async addItemsToCart(): Promise { - if (this.orchestration.recipients == null) { return; } @@ -99,9 +97,10 @@ export class NewRequestAddToCartService { // show snackbar if (this.savedItems !== this.possibleItems) { this.snackbar.open({ - key: this.savedItems === 0 - ? '#LDS#No product could be added to your shopping cart.' - : '#LDS#{0} of {1} products could not be added to your shopping cart.', + key: + this.savedItems === 0 + ? '#LDS#No product could be added to your shopping cart.' + : '#LDS#{0} of {1} products could not be added to your shopping cart.', parameters: [this.possibleItems - this.savedItems, this.possibleItems], }); } else { @@ -122,84 +121,95 @@ export class NewRequestAddToCartService { return recipientsUids.map((uid, index) => ({ DataValue: uid, DisplayValue: recipientsDisplays[index], - })) + })); } private async addRequestablesToCart(requestableProductForPerson: RequestableProductForPerson[]): Promise { if (requestableProductForPerson && requestableProductForPerson.length > 0) { const hasItems = await this.shelfService.setShops(requestableProductForPerson); if (hasItems) { - setTimeout(() => this.busyIndicator.show()); + if (this.busyIndicator.overlayRefs.length === 0) { + this.busyIndicator.show(); + } try { this.copyShopInfoForDups(requestableProductForPerson); - const items = requestableProductForPerson.filter((item) => item.UidITShopOrg?.length > 0); - this.possibleItems = items.length; - this.savedItems = await this.cartItemsProvider.addItems(items); + const items = requestableProductForPerson.filter((item) => !!item.UidITShopOrg?.length); + const itemResult = await this.cartItemsProvider.addItems(items); + this.possibleItems = itemResult.possibleItems; + this.savedItems = itemResult.savedItems; } finally { - setTimeout(() => this.busyIndicator.hide()); + this.busyIndicator.hide(); } } } } private async addOrgsToCart(): Promise { - const roles = this.selectionService.selectedProducts - .filter(x => x.source === SelectedProductSource.PeerGroupOrgs || x.source === SelectedProductSource.ReferenceUserOrgs); + const roles = this.selectionService.selectedProducts.filter( + (x) => x.source === SelectedProductSource.PeerGroupOrgs || x.source === SelectedProductSource.ReferenceUserOrgs, + ); if (roles && roles.length > 0) { const recipientsWrapper = new RecipientsWrapper(this.orchestration.recipients); - setTimeout(() => this.busyIndicator.show()); + this.showBusyIndicator(); try { - const orgs = roles.map(x => x.item) as PortalItshopPeergroupMemberships[] + const orgs = roles.map((x) => x.item) as PortalItshopPeergroupMemberships[]; await this.cartItemsProvider.addItemsFromRoles( orgs.map((item) => item.XObjectKey.value), - recipientsWrapper?.uids + recipientsWrapper?.uids, ); - this.possibleItems += roles.length; - this.savedItems += roles.length; + this.possibleItems = roles.length; + this.savedItems = roles.length; } finally { - setTimeout(() => this.busyIndicator.hide()); + this.busyIndicator.hide(); } } } private async createRequestableProductsFromServiceItems(recipients: ValueStruct[]): Promise { - const requests = this.selectionService.selectedProducts - .filter(x => x.source === SelectedProductSource.AllProducts - || x.source === SelectedProductSource.PeerGroupProducts - || x.source === SelectedProductSource.ReferenceUserProducts ); + const requests = this.selectionService.selectedProducts.filter( + (x) => + x.source === SelectedProductSource.AllProducts || + x.source === SelectedProductSource.PeerGroupProducts || + x.source === SelectedProductSource.ReferenceUserProducts, + ); let serviceItemsForPersons: RequestableProductForPerson[] = []; let optionalItemsForPersons: RequestableProductForPerson[] = []; if (requests && requests.length > 0) { - const serviceItems = requests.map(x => x.item) as PortalShopServiceitems[]; + const serviceItems = requests.map((x) => x.item) as PortalShopServiceitems[]; // first get all optional service items if (this.projectConfig?.ITShopConfig?.VI_ITShop_AddOptionalProductsOnInsert) { optionalItemsForPersons = await this.openOptionalSideSheet(serviceItems); } - setTimeout(() => this.busyIndicator.show()); + this.showBusyIndicator(); try { serviceItemsForPersons = this.serviceItemsProvider.getServiceItemsForPersons(serviceItems, recipients); } finally { - setTimeout(() => this.busyIndicator.hide()); + this.busyIndicator.hide(); } } return serviceItemsForPersons.concat(...optionalItemsForPersons); } - private async createRequestableProductsFromBundleItems (recipients: ValueStruct[]):Promise { - const requests = this.selectionService.selectedProducts.filter(x => x.source === SelectedProductSource.ProductBundles); + private async createRequestableProductsFromBundleItems(recipients: ValueStruct[]): Promise { + const requests = this.selectionService.selectedProducts.filter((x) => x.source === SelectedProductSource.ProductBundles); let productBundleItems: PortalItshopPatternRequestable[] = []; let productBundleItemsForPersons: RequestableProductForPerson[] = []; if (requests && requests.length > 0) { - setTimeout(() => this.busyIndicator.show()); + this.showBusyIndicator(); try { - productBundleItems = requests.map(x => x.item) as PortalItshopPatternRequestable[]; - productBundleItemsForPersons = await this.patternItemsService.getPatternItemsForPersons(productBundleItems, recipients, null, true); + productBundleItems = requests.map((x) => x.item) as PortalItshopPatternRequestable[]; + productBundleItemsForPersons = await this.patternItemsService.getPatternItemsForPersons( + productBundleItems, + recipients, + undefined, + true, + ); } finally { - setTimeout(() => this.busyIndicator.hide()); + this.busyIndicator.hide(); } } return productBundleItemsForPersons; @@ -207,12 +217,12 @@ export class NewRequestAddToCartService { private async openOptionalSideSheet(serviceItems: PortalShopServiceitems[]): Promise { const serviceItemTree = await this.optionalItemsService.checkForOptionalTree(serviceItems, this.orchestration.recipients); - if (serviceItemTree?.totalOptional > 0) { + if (!!serviceItemTree?.totalOptional) { const selectedOptionalOrder: ServiceItemOrder = await this.sidesheetService .open(OptionalItemsSidesheetComponent, { title: await this.translate.get('#LDS#Heading Request Optional Products').toPromise(), padding: '0px', - width: 'max(700px, 60%)', + width: calculateSidesheetWidth(1000), testId: 'optional-items-sidesheet', disableClose: true, data: { @@ -223,7 +233,7 @@ export class NewRequestAddToCartService { .afterClosed() .toPromise(); if (selectedOptionalOrder) { - return selectedOptionalOrder.requestables; + return selectedOptionalOrder.requestables || []; } } return []; @@ -235,19 +245,26 @@ export class NewRequestAddToCartService { * @returns */ private copyShopInfoForDups(serviceItemsForPersons: RequestableProductForPerson[]): void { - const itemsWithItShop = serviceItemsForPersons.filter((item) => item.UidITShopOrg && item.UidITShopOrg.length > 0); - if (itemsWithItShop.length === serviceItemsForPersons.length ) { + if (itemsWithItShop.length === serviceItemsForPersons.length) { // All items have itshops, we can skip - return + return; } itemsWithItShop.forEach((itemWithItShop) => { // Loop over all items that have an ITShop, get any service items that match its uid and also have no itshop set yet, and set them serviceItemsForPersons - .filter((item) => !item.UidITShopOrg && item.UidAccProduct === itemWithItShop.UidAccProduct && item.UidPerson === itemWithItShop.UidPerson) + .filter( + (item) => + !item.UidITShopOrg && item.UidAccProduct === itemWithItShop.UidAccProduct && item.UidPerson === itemWithItShop.UidPerson, + ) .forEach((item) => (item.UidITShopOrg = itemWithItShop.UidITShopOrg)); }); return; } -} + private showBusyIndicator(): void { + if (this.busyIndicator.overlayRefs.length === 0) { + this.busyIndicator.show(); + } + } +} diff --git a/imxweb/projects/qer/src/lib/new-request/new-request-content/new-request-content.component.html b/imxweb/projects/qer/src/lib/new-request/new-request-content/new-request-content.component.html index 5798d6741..e0daab734 100644 --- a/imxweb/projects/qer/src/lib/new-request/new-request-content/new-request-content.component.html +++ b/imxweb/projects/qer/src/lib/new-request/new-request-content/new-request-content.component.html @@ -1,4 +1,4 @@ -

    -
    - +
    +
    + + + + + + + + + + + + + + + + -
    - - - diff --git a/imxweb/projects/qer/src/lib/view-devices/view-devices-home/view-devices.component.scss b/imxweb/projects/qer/src/lib/view-devices/view-devices-home/view-devices.component.scss index 52f351d3b..4d217384c 100644 --- a/imxweb/projects/qer/src/lib/view-devices/view-devices-home/view-devices.component.scss +++ b/imxweb/projects/qer/src/lib/view-devices/view-devices-home/view-devices.component.scss @@ -1,62 +1,10 @@ -@import '../../../../../../shared/scss/common-table.scss'; -@mixin imx-flex-column-container { - display: flex; - flex-direction: column; -} - -@mixin imx-flex-row-container { - display: flex; - flex-direction: row; -} +@import 'base/mixins'; :host { - @include imx-flex-column-container(); - overflow: hidden; - max-width: 100%; - height: 100%; + @include flex-column-container($overflow: hidden, $height: 100%, $max-width: 100%); } .heading-wrapper { - @include imx-flex-row-container(); + @include flex-row-container(); flex: 0 0 auto; } - -.mat-card { - @include imx-flex-fill-control-hidden-overflow(); - - div.imx-table-container { - @include imx-flex-column-container(); - overflow: hidden; - flex: 1 1 auto; - - .mat-card { - height: inherit; - overflow: hidden; - display: flex; - flex-direction: column; - } - - .imx-pickcategory-table { - flex-grow: 1; - overflow: auto; - } - } - &-actions { - display: flex; - flex-direction: row-reverse; - padding: 16px 0; - gap: 16px; - align-items: center; - } -} - -.imx-button-bar { - @include imx-button-bar(); - - button { - eui-icon { - font-size: 14px; - margin-right: 4px; - } - } -} diff --git a/imxweb/projects/qer/src/lib/view-devices/view-devices-home/view-devices.component.ts b/imxweb/projects/qer/src/lib/view-devices/view-devices-home/view-devices.component.ts index c61da9991..8ce892aa4 100644 --- a/imxweb/projects/qer/src/lib/view-devices/view-devices-home/view-devices.component.ts +++ b/imxweb/projects/qer/src/lib/view-devices/view-devices-home/view-devices.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,53 +25,58 @@ */ import { Component, OnDestroy, OnInit } from '@angular/core'; -import { ViewDevicesService } from '../view-devices.service'; +import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; +import { DeviceConfig, PortalCandidatesHardwaretype, PortalDevices } from '@imx-modules/imx-api-qer'; +import { + CollectionLoadParameters, + DataModel, + DisplayColumns, + EntitySchema, + ExtendedTypedEntityCollection, + IClientProperty, + TypedEntityCollectionData, + ValueStruct, +} from '@imx-modules/imx-qbm-dbts'; +import { TranslateService } from '@ngx-translate/core'; import { AuthenticationService, - BusyService, DataModelWrapper, - DataSourceToolbarSettings, - DataSourceWrapper, + BusyService, + DataViewInitParameters, + DataViewSource, + HELP_CONTEXTUAL, HelpContextualComponent, HelpContextualService, - HELP_CONTEXTUAL, - SideNavigationComponent + ISessionState, + SideNavigationComponent, + calculateSidesheetWidth, } from 'qbm'; -import { DeviceConfig, PortalCandidatesHardwaretype, PortalDevices } from 'imx-api-qer'; -import { CollectionLoadParameters, DisplayColumns, EntitySchema, ValueStruct } from 'imx-qbm-dbts'; -import { OverlayRef } from '@angular/cdk/overlay'; -import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; -import { ViewDevicesSidesheetComponent } from '../view-devices-sidesheet/view-devices-sidesheet.component'; -import { TranslateService } from '@ngx-translate/core'; -import { ProjectConfigurationService } from '../../project-configuration/project-configuration.service'; -import { QerPermissionsService } from '../../admin/qer-permissions.service'; import { Subscription } from 'rxjs'; +import { QerPermissionsService } from '../../admin/qer-permissions.service'; import { IdentitiesService } from '../../identities/identities.service'; +import { ProjectConfigurationService } from '../../project-configuration/project-configuration.service'; import { CreateNewDeviceComponent } from '../create-new-device/create-new-device.component'; +import { ViewDevicesSidesheetComponent } from '../view-devices-sidesheet/view-devices-sidesheet.component'; +import { ViewDevicesService } from '../view-devices.service'; @Component({ selector: 'imx-view-devices-home', templateUrl: './view-devices.component.html', styleUrls: ['./view-devices.component.scss'], + providers: [DataViewSource], }) -export class ViewDevicesComponent implements OnInit, OnDestroy, SideNavigationComponent{ - public dataModelWrapper: DataModelWrapper; - public hardwareTypeDataModelWrapper: DataModelWrapper; - public dstWrapper: DataSourceWrapper; - public dstWrapperHardwareType: DataSourceWrapper; +export class ViewDevicesComponent implements OnInit, OnDestroy, SideNavigationComponent { public entitySchema: EntitySchema; - public entitySchemaHardwareType: EntitySchema; - public dstSettings: DataSourceToolbarSettings; - public dstSettingsHardwareType: DataSourceToolbarSettings; + public dataModel: DataModel; + public displayedColumns: IClientProperty[]; + public hardwareCandidates: ExtendedTypedEntityCollection; public DisplayColumns = DisplayColumns; - public deviceModelValueStruct: ValueStruct[]; - public hardwareBasicTypeList: { type: string, basicType: string, key: string }[]; + public deviceModelValueStruct: ValueStruct[] | undefined; + public hardwareBasicTypeList: { type: string; basicType: string; key: string }[]; public busyService = new BusyService(); public contextId = HELP_CONTEXTUAL.PortalDevices; - - public deviceConfig: DeviceConfig; + public deviceConfig: DeviceConfig | undefined; public managerId: string; public isAdmin = false; - public currentUser: string; public isManagerForPersons: boolean; public isAuditor: boolean; @@ -86,16 +91,13 @@ export class ViewDevicesComponent implements OnInit, OnDestroy, SideNavigationCo private readonly authService: AuthenticationService, private readonly identitiesService: IdentitiesService, private readonly helpContextualService: HelpContextualService, - qerPermissionService: QerPermissionsService, + public qerPermissionService: QerPermissionsService, + public dataSource: DataViewSource, ) { this.entitySchema = this.viewDevicesService.devicesSchema; - - this.entitySchemaHardwareType = this.viewDevicesService.hardwareTypeSchema; - - this.sessionResponse$ = this.authService.onSessionResponse.subscribe(async (session) => { + this.sessionResponse$ = this.authService.onSessionResponse.subscribe(async (session: ISessionState) => { if (session.IsLoggedIn) { - this.currentUser = session.UserUid, - this.isManagerForPersons = await qerPermissionService.isPersonManager(); + (this.currentUser = session.UserUid || ''), (this.isManagerForPersons = await qerPermissionService.isPersonManager()); this.isAuditor = await qerPermissionService.isStructStatistics(); } }); @@ -119,137 +121,117 @@ export class ViewDevicesComponent implements OnInit, OnDestroy, SideNavigationCo if (this.deviceConfig == null) { this.deviceConfig = (await this.projectConfigService.getConfig()).DeviceConfig; } - - this.dataModelWrapper = { - dataModel: await this.viewDevicesService.getDataModel(), - }; - - this.hardwareTypeDataModelWrapper = { - dataModel: await this.viewDevicesService.getHardwareTypeDataModel(), - }; - } finally { - isBusy.endBusy(); - } - - this.dstWrapper = new DataSourceWrapper( - (state) => this.viewDevicesService.get(state), - [ + this.dataModel = await this.viewDevicesService.getDataModel(); + this.displayedColumns = [ this.entitySchema.Columns[DisplayColumns.DISPLAY_PROPERTYNAME], this.entitySchema.Columns.UID_HardwareType, this.entitySchema.Columns.UID_PersonOwner, - ], - this.entitySchema, - this.dataModelWrapper, - 'view-devices' - ); - - this.dstWrapperHardwareType = new DataSourceWrapper( - (state) => this.viewDevicesService.getPortalCandidatesHardwaretype(state), - [ - this.entitySchemaHardwareType.Columns[DisplayColumns.DISPLAY_PROPERTYNAME], - ], - this.entitySchemaHardwareType, - this.hardwareTypeDataModelWrapper, - 'hardware-type' - ); - - this.getData(); - } - - public async getData(newState?: CollectionLoadParameters): Promise { - const isbusy = this.busyService.beginBusy(); - try { - this.dstSettings = await this.dstWrapper.getDstSettings(newState); - this.dstSettingsHardwareType = await this.dstWrapperHardwareType.getDstSettings(newState); - - this.deviceModelValueStruct = this.dstSettingsHardwareType.dataSource.Data.map(d => { + ]; + this.hardwareCandidates = await this.viewDevicesService.getPortalCandidatesHardwaretype({}); + this.deviceModelValueStruct = await this.hardwareCandidates?.Data.map((d) => { return { - DataValue: d.GetEntity().GetKeys()[0], - DisplayValue: d.GetEntity().GetDisplay() + DataValue: d.GetEntity().GetKeys()[0], + DisplayValue: d.GetEntity().GetDisplay(), }; - }); + }); } finally { - isbusy.endBusy(); + isBusy.endBusy(); } + this.getData(); + } + + public async getData(): Promise { + const dataViewInitParameters: DataViewInitParameters = { + execute: async (params: CollectionLoadParameters, signal: AbortSignal): Promise> => + this.viewDevicesService.get(params, signal), + schema: this.entitySchema, + columnsToDisplay: this.displayedColumns, + dataModel: this.dataModel, + highlightEntity: (entity: PortalDevices) => { + this.onHighlightedEntityChanged(entity); + }, + }; + this.dataSource.init(dataViewInitParameters); } public async onHighlightedEntityChanged(portalDevices: PortalDevices): Promise { if (portalDevices) { - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.euiBusyService.show())); + if (this.euiBusyService.overlayRefs.length === 0) { + this.euiBusyService.show(); + } let extendedEntity; - let deviceEntityConfig = this.deviceConfig['VI_Hardware_Fields_Default']; + let deviceEntityConfig = this.deviceConfig?.['VI_Hardware_Fields_Default']; try { const key = portalDevices.GetEntity().GetKeys().join(','); extendedEntity = await this.viewDevicesService.getPortalDeviceEntity(key); const hardwareBasicType = extendedEntity.Data[0].HardwareBasicType.value; - - if (this.deviceConfig[`VI_Hardware_Fields_${hardwareBasicType}`]) { - deviceEntityConfig = this.deviceConfig[`VI_Hardware_Fields_${hardwareBasicType}`]; + if (this.deviceConfig?.[`VI_Hardware_Fields_${hardwareBasicType}`]) { + deviceEntityConfig = this.deviceConfig?.[`VI_Hardware_Fields_${hardwareBasicType}`]; } } finally { - setTimeout(() => this.euiBusyService.hide(overlayRef)); + this.euiBusyService.hide(); } if (!extendedEntity || !deviceEntityConfig) { return; } - this.helpContextualService.setHelpContextId(HELP_CONTEXTUAL.PortalDevicesEdit); - const result = await this.sideSheet - .open(ViewDevicesSidesheetComponent, { - title: await this.translate.get('#LDS#Heading Edit Device').toPromise(), - subTitle: portalDevices.GetEntity().GetDisplay(), - padding: '0', - width: 'max(600px, 60%)', - disableClose: true, - testId: 'devices-sidesheet', - data: { - device: extendedEntity.Data[0], - deviceEntityConfig: deviceEntityConfig, - }, - headerComponent: HelpContextualComponent - }) - .afterClosed() - .toPromise(); + this.helpContextualService.setHelpContextId(HELP_CONTEXTUAL.PortalDevicesEdit); + const result = await this.sideSheet + .open(ViewDevicesSidesheetComponent, { + title: await this.translate.get('#LDS#Heading Edit Device').toPromise(), + subTitle: portalDevices.GetEntity().GetDisplay(), + padding: '0', + width: calculateSidesheetWidth(), + disableClose: true, + testId: 'devices-sidesheet', + data: { + device: extendedEntity.Data[0], + deviceEntityConfig: deviceEntityConfig, + }, + headerComponent: HelpContextualComponent, + }) + .afterClosed() + .toPromise(); if (result) { - this.getData(); + this.dataSource.updateState(); } } } public async createNewDevice(key: string, index: number): Promise { - let deviceEntityConfig = this.deviceConfig['VI_Hardware_Fields_Default']; - const hardwareBasicTypeListElement = this.hardwareBasicTypeList.find(hardwareType => hardwareType.key === key); + let deviceEntityConfig = this.deviceConfig?.['VI_Hardware_Fields_Default']; + const hardwareBasicTypeListElement = this.hardwareBasicTypeList.find((hardwareType) => hardwareType.key === key); if (hardwareBasicTypeListElement) { const hardwareBasicType = this.hardwareBasicTypeList[this.hardwareBasicTypeList.indexOf(hardwareBasicTypeListElement)].basicType; - deviceEntityConfig = this.deviceConfig[`VI_Hardware_Fields_${hardwareBasicType}`]; + deviceEntityConfig = this.deviceConfig ? this.deviceConfig[`VI_Hardware_Fields_${hardwareBasicType}`] : undefined; } - let deviceModelValueStruct = this.deviceModelValueStruct[index]; - - this.helpContextualService.setHelpContextId(HELP_CONTEXTUAL.PortalDevicesCreate); - const result = await this.sideSheet - .open(CreateNewDeviceComponent, { - title: await this.translate.get('#LDS#Heading Create Device').toPromise(), - padding: '0px', - width: 'max(650px, 65%)', - disableClose: false, - testId: 'create-new-device-sidesheet', - data: { - newDevice: await this.viewDevicesService.createNewDevice(), - deviceEntityConfig: deviceEntityConfig, - deviceModelValueStruct: deviceModelValueStruct, - }, - headerComponent: HelpContextualComponent - }) - .afterClosed() - .toPromise(); + let deviceModelValueStruct = this.deviceModelValueStruct ? this.deviceModelValueStruct[index] : undefined; + this.helpContextualService.setHelpContextId(HELP_CONTEXTUAL.PortalDevicesCreate); + const newDeviceEntity = await this.viewDevicesService.createNewDevice(); + await newDeviceEntity.UID_HardwareType.Column.PutValue(key); + const result = await this.sideSheet + .open(CreateNewDeviceComponent, { + title: await this.translate.get('#LDS#Heading Create Device').toPromise(), + padding: '0px', + width: calculateSidesheetWidth(1000), + disableClose: false, + testId: 'create-new-device-sidesheet', + data: { + newDevice: newDeviceEntity, + deviceEntityConfig: deviceEntityConfig, + deviceModelValueStruct: deviceModelValueStruct, + }, + headerComponent: HelpContextualComponent, + }) + .afterClosed() + .toPromise(); if (result) { - this.getData(); + this.dataSource.updateState(); } } diff --git a/imxweb/projects/qer/src/lib/view-devices/view-devices-sidesheet/view-devices-sidesheet.component.html b/imxweb/projects/qer/src/lib/view-devices/view-devices-sidesheet/view-devices-sidesheet.component.html index aa9207a21..9acc17bd3 100644 --- a/imxweb/projects/qer/src/lib/view-devices/view-devices-sidesheet/view-devices-sidesheet.component.html +++ b/imxweb/projects/qer/src/lib/view-devices/view-devices-sidesheet/view-devices-sidesheet.component.html @@ -1,24 +1,24 @@ -
    - +
    + -
    +
    -
    +
    diff --git a/imxweb/projects/qer/src/lib/view-devices/view-devices-sidesheet/view-devices-sidesheet.component.scss b/imxweb/projects/qer/src/lib/view-devices/view-devices-sidesheet/view-devices-sidesheet.component.scss index 2427b4dc0..e69de29bb 100644 --- a/imxweb/projects/qer/src/lib/view-devices/view-devices-sidesheet/view-devices-sidesheet.component.scss +++ b/imxweb/projects/qer/src/lib/view-devices/view-devices-sidesheet/view-devices-sidesheet.component.scss @@ -1,20 +0,0 @@ -:host { - eui-icon { - font-size: 14px; - margin-right: 4px; - } -} - -.eui-sidesheet-actions { - button:not(:last-of-type) { - margin-right: 10px; - } -} - -.imx-devices-sidesheet { - ul { - li { - list-style-type: disc; - } - } -} diff --git a/imxweb/projects/qer/src/lib/view-devices/view-devices-sidesheet/view-devices-sidesheet.component.ts b/imxweb/projects/qer/src/lib/view-devices/view-devices-sidesheet/view-devices-sidesheet.component.ts index 46feed41a..72787121d 100644 --- a/imxweb/projects/qer/src/lib/view-devices/view-devices-sidesheet/view-devices-sidesheet.component.ts +++ b/imxweb/projects/qer/src/lib/view-devices/view-devices-sidesheet/view-devices-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,20 +24,19 @@ * */ -import { Component, Inject, OnDestroy } from "@angular/core"; -import { UntypedFormGroup } from "@angular/forms"; -import { EUI_SIDESHEET_DATA, EuiLoadingService, EuiSidesheetRef } from "@elemental-ui/core"; -import { PortalDevices } from "imx-api-qer"; -import { IEntity } from "imx-qbm-dbts"; -import { BaseCdr, ColumnDependentReference, ConfirmationService, SnackBarService } from "qbm"; -import { ViewDevicesService } from "../view-devices.service"; -import { Subscription } from "rxjs"; -import { OverlayRef } from "@angular/cdk/overlay"; +import { Component, Inject, OnDestroy } from '@angular/core'; +import { UntypedFormGroup } from '@angular/forms'; +import { EUI_SIDESHEET_DATA, EuiLoadingService, EuiSidesheetRef } from '@elemental-ui/core'; +import { PortalDevices } from '@imx-modules/imx-api-qer'; +import { IEntity } from '@imx-modules/imx-qbm-dbts'; +import { BaseCdr, ColumnDependentReference, ConfirmationService, SnackBarService } from 'qbm'; +import { Subscription } from 'rxjs'; +import { ViewDevicesService } from '../view-devices.service'; @Component({ selector: 'imx-view-devices-sidesheet', templateUrl: './view-devices-sidesheet.component.html', - styleUrls: ['./view-devices-sidesheet.component.scss'] + styleUrls: ['./view-devices-sidesheet.component.scss'], }) export class ViewDevicesSidesheetComponent implements OnDestroy { public readonly formGroup = new UntypedFormGroup({}); @@ -59,15 +58,17 @@ export class ViewDevicesSidesheetComponent implements OnDestroy { this.canEdit = data.device.UID_HardwareType.GetMetadata().CanEdit(); - this.subscriptions.push(this.sidesheetRef.closeClicked().subscribe(async () => { - if (this.formGroup.pristine || await this.confirmation.confirmLeaveWithUnsavedChanges()) { - this.sidesheetRef.close(); - } - })); + this.subscriptions.push( + this.sidesheetRef.closeClicked().subscribe(async () => { + if (this.formGroup.pristine || (await this.confirmation.confirmLeaveWithUnsavedChanges())) { + this.sidesheetRef.close(); + } + }), + ); } private createCdrList(entity: IEntity): BaseCdr[] { - const cdrList = []; + const cdrList: BaseCdr[] = []; const columnNames: string[] = this.data.deviceEntityConfig; columnNames?.forEach((name) => { try { @@ -99,21 +100,24 @@ export class ViewDevicesSidesheetComponent implements OnDestroy { Message: '#LDS#Are you sure you want to delete the device?', }) ) { - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.euiBusyService.show())); + if (this.euiBusyService.overlayRefs.length === 0) { + this.euiBusyService.show(); + } let confirmMessage = '#LDS#The device has been successfully deleted.'; try { - const uid = this.data.device.EntityKeysData.Keys[0]; - await this.viewDevicesService.deleteDevice(uid); + const uid = this.data.device.EntityKeysData.Keys?.[0] ?? ''; + if (uid != '') { + await this.viewDevicesService.deleteDevice(uid); + } this.sidesheetRef.close(true); this.snackbar.open({ key: confirmMessage }); } finally { - setTimeout(() => this.euiBusyService.hide(overlayRef)); + this.euiBusyService.hide(); } } } ngOnDestroy(): void { - this.subscriptions.forEach(s => s.unsubscribe()); + this.subscriptions.forEach((s) => s.unsubscribe()); } } diff --git a/imxweb/projects/qer/src/lib/view-devices/view-devices.module.ts b/imxweb/projects/qer/src/lib/view-devices/view-devices.module.ts index b7530cb3b..eea90dd4f 100644 --- a/imxweb/projects/qer/src/lib/view-devices/view-devices.module.ts +++ b/imxweb/projects/qer/src/lib/view-devices/view-devices.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,25 +24,30 @@ * */ -import { NgModule } from '@angular/core'; -import { ViewDevicesComponent } from './view-devices-home/view-devices.component'; -import { ViewDevicesSidesheetComponent } from './view-devices-sidesheet/view-devices-sidesheet.component' -import { CdrModule, ClassloggerService, DataSourceToolbarModule, DataTableModule, HelpContextualModule, LdsReplaceModule } from 'qbm'; -import { QerProjectConfig } from 'imx-api-qer'; import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { ReactiveFormsModule } from '@angular/forms'; +import { MatTableModule } from '@angular/material/table'; import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; +import { ProjectConfig } from '@imx-modules/imx-api-qbm'; +import { QerProjectConfig } from '@imx-modules/imx-api-qer'; import { TranslateModule } from '@ngx-translate/core'; -import { ReactiveFormsModule } from '@angular/forms'; -import { ProjectConfig } from 'imx-api-qbm'; -import { CreateNewDeviceComponent } from './create-new-device/create-new-device.component'; +import { + CdrModule, + ClassloggerService, + DataSourceToolbarModule, + DataTableModule, + DataViewModule, + HelpContextualModule, + LdsReplaceModule, +} from 'qbm'; import { MyResponsibilitiesRegistryService } from '../my-responsibilities-view/my-responsibilities-registry.service'; +import { CreateNewDeviceComponent } from './create-new-device/create-new-device.component'; +import { ViewDevicesComponent } from './view-devices-home/view-devices.component'; +import { ViewDevicesSidesheetComponent } from './view-devices-sidesheet/view-devices-sidesheet.component'; @NgModule({ - declarations: [ - ViewDevicesComponent, - ViewDevicesSidesheetComponent, - CreateNewDeviceComponent - ], + declarations: [ViewDevicesComponent, ViewDevicesSidesheetComponent, CreateNewDeviceComponent], imports: [ CommonModule, CdrModule, @@ -54,31 +59,33 @@ import { MyResponsibilitiesRegistryService } from '../my-responsibilities-view/m DataSourceToolbarModule, DataTableModule, HelpContextualModule, + MatTableModule, + DataViewModule, ], - exports: [ - ViewDevicesComponent, - ] + exports: [ViewDevicesComponent, ViewDevicesSidesheetComponent], }) export class ViewDevicesModule { - constructor( private readonly myResponsibilitiesRegistryService: MyResponsibilitiesRegistryService, logger: ClassloggerService, - ) { + ) { logger.info(this, '▶️ ViewDevicesModule loaded'); this.setupMyResponsibilitiesView(); } - private setupMyResponsibilitiesView(): void{ - - this.myResponsibilitiesRegistryService.registerFactory((preProps: string[], features: string[], projectConfig: QerProjectConfig & ProjectConfig) => { - if (preProps.includes('MAC') && projectConfig.DeviceConfig.VI_Hardware_Enabled) { - return { - instance: ViewDevicesComponent, - sortOrder: 13, - name: 'devices', - caption: '#LDS#Heading Devices', + private setupMyResponsibilitiesView(): void { + this.myResponsibilitiesRegistryService.registerFactory( + (preProps: string[], features: string[], projectConfig: QerProjectConfig & ProjectConfig) => { + if (preProps.includes('MAC') && projectConfig.DeviceConfig && projectConfig.DeviceConfig.VI_Hardware_Enabled) { + return { + instance: ViewDevicesComponent, + sortOrder: 13, + name: 'devices', + caption: '#LDS#Heading Devices', + }; + } else { + return { caption: '', name: '' }; } - } - }); + }, + ); } } diff --git a/imxweb/projects/qer/src/lib/view-devices/view-devices.service.ts b/imxweb/projects/qer/src/lib/view-devices/view-devices.service.ts index d5d930a49..e7a8ae940 100644 --- a/imxweb/projects/qer/src/lib/view-devices/view-devices.service.ts +++ b/imxweb/projects/qer/src/lib/view-devices/view-devices.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,51 +24,46 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; import { Injectable } from '@angular/core'; -import { QerApiService } from '../qer-api-client.service'; import { EuiLoadingService } from '@elemental-ui/core'; -import { CollectionLoadParameters, DataModel, EntityCollectionData, EntitySchema, ExtendedTypedEntityCollection } from 'imx-qbm-dbts'; -import { PortalCandidatesHardwaretype, PortalCandidatesHardwaretypeWrapper, PortalDevices } from 'imx-api-qer'; +import { PortalCandidatesHardwaretype, PortalDevices } from '@imx-modules/imx-api-qer'; +import { CollectionLoadParameters, DataModel, EntitySchema, ExtendedTypedEntityCollection } from '@imx-modules/imx-qbm-dbts'; +import { QerApiService } from '../qer-api-client.service'; @Injectable({ providedIn: 'root', }) export class ViewDevicesService { - private busyIndicator: OverlayRef; - constructor( private readonly qerClient: QerApiService, - private readonly busyService: EuiLoadingService - ) {} + private readonly busyService: EuiLoadingService, + ) {} public get devicesSchema(): EntitySchema { return this.qerClient.typedClient.PortalDevices.GetSchema(); } - public get hardwareTypeSchema(): EntitySchema { - return this.qerClient.typedClient.PortalCandidatesHardwaretype.GetSchema(); - } - public handleOpenLoader(): void { - if (!this.busyIndicator) { - setTimeout(() => (this.busyIndicator = this.busyService.show())); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); } } public handleCloseLoader(): void { - if(this.busyIndicator) { - setTimeout(() => { - this.busyService.hide(this.busyIndicator); - this.busyIndicator = undefined; - }); - } + this.busyService.hide(); } - public async get(parameters: CollectionLoadParameters): Promise> { + public async get( + parameters: CollectionLoadParameters, + signal: AbortSignal, + ): Promise> { return this.qerClient.typedClient.PortalDevices.Get(parameters); } + public get hardwareTypeSchema(): EntitySchema { + return this.qerClient.typedClient.PortalCandidatesHardwaretype.GetSchema(); + } + public async getDataModel(): Promise { return this.qerClient.client.portal_devices_datamodel_get(); } @@ -85,7 +80,9 @@ export class ViewDevicesService { await this.qerClient.client.portal_devices_delete(uid); } - public async getPortalCandidatesHardwaretype(parameters: CollectionLoadParameters): Promise> { + public async getPortalCandidatesHardwaretype( + parameters: CollectionLoadParameters, + ): Promise> { return await this.qerClient.typedClient.PortalCandidatesHardwaretype.Get(parameters); } diff --git a/imxweb/projects/qer/src/lib/wport/businessowner-chartsummary/businessowner-chartsummary.component.html b/imxweb/projects/qer/src/lib/wport/businessowner-chartsummary/businessowner-chartsummary.component.html index 0d0644d78..abcc1b6e6 100644 --- a/imxweb/projects/qer/src/lib/wport/businessowner-chartsummary/businessowner-chartsummary.component.html +++ b/imxweb/projects/qer/src/lib/wport/businessowner-chartsummary/businessowner-chartsummary.component.html @@ -1,15 +1,20 @@ - + [caption]="'#LDS#Heading My Responsibilities' | translate" + [identifier]="'my-responsibilities'" + [size]="'Dashboard'" + [hideActionButton]="true" + [subtitle]="'#LDS#View and manage objects you are responsible for.' | translate" + [contentType]="'Container'" +>
    @@ -17,57 +22,85 @@ - +
    -

    - {{'#LDS#Heading My Direct Reports' | translate}} - ({{reports.length }}) -

    +

    + {{ '#LDS#Heading My Direct Reports' | translate }} + ({{ reports.length }}) +

    - {{'#LDS#View and manage identities you are directly responsible for.' | translate}} + {{ '#LDS#View and manage identities you are directly responsible for.' | translate }}

    -
    - +
    - + -
    {{'#LDS#Heading My Direct Reports' | translate}}
    +
    {{ '#LDS#Heading My Direct Reports' | translate }}
    -
    {{'#LDS#View and manage identities you are directly responsible for.' | translate}}
    +
    {{ '#LDS#View and manage identities you are directly responsible for.' | translate }}
    -
    -
    \ No newline at end of file +
    diff --git a/imxweb/projects/qer/src/lib/wport/businessowner-chartsummary/businessowner-chartsummary.component.scss b/imxweb/projects/qer/src/lib/wport/businessowner-chartsummary/businessowner-chartsummary.component.scss index 2ff365de7..82d3ec7c5 100644 --- a/imxweb/projects/qer/src/lib/wport/businessowner-chartsummary/businessowner-chartsummary.component.scss +++ b/imxweb/projects/qer/src/lib/wport/businessowner-chartsummary/businessowner-chartsummary.component.scss @@ -1,5 +1,5 @@ @import '@elemental-ui/core/src/styles/_eui_palette.scss'; -@import "../../../../../qbm/src/lib/tile/tile-variables.scss"; +@import 'base/variables'; :host { display: flex; @@ -58,7 +58,7 @@ line-height: 40px; } -.imx-generic-tile-caption h1 { +.imx-generic-tile-caption h3 { font-size: 1.15em; font-weight: 600; text-overflow: ellipsis; @@ -67,13 +67,12 @@ flex: 1 1 auto; } -.imx-generic-tile-caption h1 > :last-child { +.imx-generic-tile-caption h3 > :last-child { margin-left: 1ex; } -.mat-button { - text-transform: uppercase; - margin-right: -16px; +.mat-mdc-button { + padding-right: 0; } .imx-generic-tile-caption h4 { @@ -104,25 +103,25 @@ } :host > .imx-create-identity-tile { - ::ng-deep .mat-card { + ::ng-deep .mat-mdc-card { height: 140px; display: flex; - width: $tile-width; - min-width: $tile-width; + width: $IMX_Tile_Width; + min-width: $IMX_Tile_Width; flex-direction: column; cursor: auto; padding: 25px; padding-bottom: 10px; } - .mat-card-title { + .mat-mdc-card-title { font-size: 1.15em; font-weight: 600; display: flex; cursor: auto; } - - .mat-card-subtitle { + + .mat-mdc-card-subtitle { flex: 1; font-size: small; text-align: left; @@ -130,14 +129,14 @@ overflow-x: hidden; margin-bottom: 0; } - - .mat-card-actions { + + .mat-mdc-card-actions { margin-left: 0; margin-right: 0; display: flex; } - .mat-card-actions:last-child { + .mat-mdc-card-actions:last-child { margin-bottom: 0; margin-left: -16px; margin-top: -10px; @@ -155,7 +154,7 @@ background-color: $color-gray-10; } - .imx-generic-tile-caption h1 > :last-child { + .imx-generic-tile-caption h3 > :last-child { color: $color-gray-40; } @@ -163,13 +162,11 @@ color: $color-gray-40; } - .mat-card-subtitle { + .mat-mdc-card-subtitle { color: $color-gray-40; } - } - .eui-dark-theme { :host { .imx-valuelist a { diff --git a/imxweb/projects/qer/src/lib/wport/businessowner-chartsummary/businessowner-chartsummary.component.ts b/imxweb/projects/qer/src/lib/wport/businessowner-chartsummary/businessowner-chartsummary.component.ts index ecb9bbac3..f3a700150 100644 --- a/imxweb/projects/qer/src/lib/wport/businessowner-chartsummary/businessowner-chartsummary.component.ts +++ b/imxweb/projects/qer/src/lib/wport/businessowner-chartsummary/businessowner-chartsummary.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,20 +24,20 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; import { Component, ErrorHandler, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; +import { OwnershipInformation, PortalPersonReports, ProjectConfig } from '@imx-modules/imx-api-qer'; import { TranslateService } from '@ngx-translate/core'; -import { OwnershipInformation, PortalPersonReports, ProjectConfig } from 'imx-api-qer'; +import { calculateSidesheetWidth } from 'qbm'; import { QerPermissionsService } from '../../admin/qer-permissions.service'; +import { CreateNewIdentityComponent } from '../../identities/create-new-identity/create-new-identity.component'; +import { IdentitiesService } from '../../identities/identities.service'; import { IdentitySidesheetComponent } from '../../identities/identity-sidesheet/identity-sidesheet.component'; import { ProjectConfigurationService } from '../../project-configuration/project-configuration.service'; import { QerApiService } from '../../qer-api-client.service'; import { UserModelService } from '../../user/user-model.service'; import { DashboardService } from '../start/dashboard.service'; -import { CreateNewIdentityComponent } from '../../identities/create-new-identity/create-new-identity.component'; -import { IdentitiesService } from '../../identities/identities.service'; @Component({ templateUrl: './businessowner-chartsummary.component.html', @@ -46,12 +46,14 @@ import { IdentitiesService } from '../../identities/identities.service'; }) export class BusinessOwnerChartSummaryComponent implements OnInit { public reports: PortalPersonReports[]; - public ownerships: OwnershipInformation[]; + public ownerships: OwnershipInformation[] | undefined; public get viewReady(): boolean { return !this.dashboardService.isBusy; } public allReportsCount: number; + public isPersonManager: boolean; + private projectConfig: ProjectConfig; constructor( @@ -65,7 +67,7 @@ export class BusinessOwnerChartSummaryComponent implements OnInit { private readonly identitiesService: IdentitiesService, private readonly userModelService: UserModelService, public readonly qerPermissions: QerPermissionsService, - public readonly translate: TranslateService + public readonly translate: TranslateService, ) {} public async ngOnInit(): Promise { @@ -73,6 +75,7 @@ export class BusinessOwnerChartSummaryComponent implements OnInit { try { const userConfig = await this.userModelService.getUserConfig(); this.ownerships = userConfig.Ownerships; + this.isPersonManager = await this.qerPermissions.isPersonManager(); this.projectConfig = await this.configService.getConfig(); @@ -90,13 +93,14 @@ export class BusinessOwnerChartSummaryComponent implements OnInit { const uid = identity.GetEntity().GetKeys()[0]; let selectedIdentity: PortalPersonReports; - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.busyService.show())); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } try { const identityCollection = await this.qerClient.typedClient.PortalPersonReportsInteractive.Get_byid(uid); selectedIdentity = identityCollection?.Data?.[0]; } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } if (!selectedIdentity) { @@ -110,14 +114,14 @@ export class BusinessOwnerChartSummaryComponent implements OnInit { subTitle: selectedIdentity.GetEntity().GetDisplay(), padding: '0px', disableClose: true, - width: 'max(768px, 90%)', + width: calculateSidesheetWidth(1100, 0.7), icon: 'contactinfo', testId: 'businessowner-identity-sidesheet', data: { isAdmin: false, projectConfig: this.projectConfig, selectedIdentity, - canEdit: true + canEdit: true, }, }) .afterClosed() @@ -127,19 +131,22 @@ export class BusinessOwnerChartSummaryComponent implements OnInit { } public async openCreateNewIdentitySidesheet(): Promise { - const identityCreated = await this.sideSheet.open(CreateNewIdentityComponent, { - title: await this.translate.get('#LDS#Heading Create Identity').toPromise(), - headerColour: 'iris-blue', - padding: '0px', - width: 'max(650px, 65%)', - disableClose: true, - testId: 'create-new-identity-sidesheet', - icon: 'contactinfo', - data: { - selectedIdentity: await this.identitiesService.createEmptyEntity(), - projectConfig: this.projectConfig - } - }).afterClosed().toPromise(); + const identityCreated = await this.sideSheet + .open(CreateNewIdentityComponent, { + title: await this.translate.get('#LDS#Heading Create Identity').toPromise(), + headerColour: 'iris-blue', + padding: '0px', + width: calculateSidesheetWidth(1000), + disableClose: true, + testId: 'create-new-identity-sidesheet', + icon: 'contactinfo', + data: { + selectedIdentity: await this.identitiesService.createEmptyEntity(), + projectConfig: this.projectConfig, + }, + }) + .afterClosed() + .toPromise(); if (identityCreated) { const busy = this.dashboardService.beginBusy(); @@ -148,7 +155,8 @@ export class BusinessOwnerChartSummaryComponent implements OnInit { } finally { busy.endBusy(); } - } } + } + } public openOwnership(ownerShip: OwnershipInformation): void { this.router.navigate(['myresponsibilities', ownerShip.TableName]); @@ -156,7 +164,7 @@ export class BusinessOwnerChartSummaryComponent implements OnInit { private async getData(): Promise { await this.loadIndirectOrDirectReports(); - if (this.allReportsCount > 0 ) { + if (this.allReportsCount > 0) { await this.loadDirectReports(); } } @@ -167,7 +175,7 @@ export class BusinessOwnerChartSummaryComponent implements OnInit { await this.qerClient.typedClient.PortalPersonReports.Get({ OnlyDirect: true, // direct reports only PageSize: 10000, - isinactive: '0' + isinactive: '0', }) ).Data; } @@ -175,9 +183,11 @@ export class BusinessOwnerChartSummaryComponent implements OnInit { private async loadIndirectOrDirectReports(): Promise { if (await this.qerPermissions.isPersonManager()) { - this.allReportsCount = (await this.qerClient.typedClient.PortalPersonReports.Get({ - PageSize: -1 - })).totalCount; + this.allReportsCount = ( + await this.qerClient.typedClient.PortalPersonReports.Get({ + PageSize: -1, + }) + ).totalCount; } } } diff --git a/imxweb/projects/qer/src/lib/wport/start/dashboard.service.ts b/imxweb/projects/qer/src/lib/wport/start/dashboard.service.ts index 6bd4a5946..a0f582134 100644 --- a/imxweb/projects/qer/src/lib/wport/start/dashboard.service.ts +++ b/imxweb/projects/qer/src/lib/wport/start/dashboard.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,10 +24,8 @@ * */ -import { Injectable } from "@angular/core"; -import { BusyService } from "qbm"; +import { Injectable } from '@angular/core'; +import { BusyService } from 'qbm'; @Injectable({ providedIn: 'root' }) -export class DashboardService extends BusyService { - -} \ No newline at end of file +export class DashboardService extends BusyService {} diff --git a/imxweb/projects/qer/src/lib/wport/start/start.component.html b/imxweb/projects/qer/src/lib/wport/start/start.component.html index 1a0c75cea..0dc625df4 100644 --- a/imxweb/projects/qer/src/lib/wport/start/start.component.html +++ b/imxweb/projects/qer/src/lib/wport/start/start.component.html @@ -1,14 +1,14 @@ -

    {{ '#LDS#Welcome' | translate }}

    +

    {{ '#LDS#Welcome' | translate }}

    -
    +
    @@ -19,7 +19,7 @@

    {{ '#LDS#Welcome' | translate }}

    [caption]="'#LDS#Heading Pending Requests' | translate" [tooltip]="'#LDS#Shows the pending requests you can approve or deny.' | translate" *ngIf="userConfig?.IsITShopEnabled && GetCountPendingRequests() > 0" - [value]="GetCountPendingRequests()" + [value]="GetCountPendingRequests().toString()" [identifier]="'it-shop-approvals'" (actionClick)="GoToItshopApprovals()" > @@ -30,9 +30,9 @@

    {{ '#LDS#Welcome' | translate }}

    [caption]="'#LDS#Heading My Pending Requests' | translate" [tooltip]="'#LDS#Shows your pending requests others can approve or deny.' | translate" *ngIf="userConfig?.IsITShopEnabled && GetCountInRequestHistory() > 0" - [value]="GetCountInRequestHistory()" + [value]="GetCountInRequestHistory().toString()" [identifier]="'request-history'" - (actionClick)="router.navigate(['requesthistory'], { queryParams: { ShowMyPending: 1 } })" + (actionClick)="router.navigate(['requesthistory'], { queryParams: { showMyPendings: true } })" >
    @@ -40,7 +40,7 @@

    {{ '#LDS#Welcome' | translate }}

    data-imx-identifier="start-tile-it-shop-approval-inquiries" [caption]="'#LDS#Heading Request Inquiries' | translate" *ngIf="userConfig?.IsITShopEnabled && GetCountRequestInquiries() > 0" - [value]="GetCountRequestInquiries()" + [value]="GetCountRequestInquiries().toString()" [identifier]="'it-shop-approval-inquiries'" (actionClick)="GoToItShopApprovalInquiries()" > @@ -50,7 +50,7 @@

    {{ '#LDS#Welcome' | translate }}

    data-imx-identifier="start-tile-my-processes" [caption]="'#LDS#Heading My Processes' | translate" *ngIf="GetCountNewProcesses() > 0" - [value]="GetCountNewProcesses()" + [value]="GetCountNewProcesses().toString()" [identifier]="'my-processes'" (actionClick)="GoToMyProcesses()" > @@ -58,15 +58,15 @@

    {{ '#LDS#Welcome' | translate }}

    -
    +
    {{ '#LDS#Welcome' | translate }} [identifier]="'no-password-query-and-answer-set'" > - - + + {{ '#LDS#Welcome' | translate }} [image]="'key'" > - @@ -114,9 +130,15 @@

    {{ '#LDS#Welcome' | translate }}

    [image]="'key'" > - @@ -130,9 +152,15 @@

    {{ '#LDS#Welcome' | translate }}

    *ngIf="viewReady && ShowNewRequestLink()" > - diff --git a/imxweb/projects/qer/src/lib/wport/start/start.component.scss b/imxweb/projects/qer/src/lib/wport/start/start.component.scss index 5b0245796..8075e0f2d 100644 --- a/imxweb/projects/qer/src/lib/wport/start/start.component.scss +++ b/imxweb/projects/qer/src/lib/wport/start/start.component.scss @@ -1,4 +1,4 @@ -@import '../../../../../qbm/src/lib/tile/tile-variables.scss'; +@import 'base/variables'; :host { display: flex; @@ -7,14 +7,13 @@ height: 100%; } - .imx-flexible-tile-row { display: flex; - flex-wrap: wrap ; + flex-wrap: wrap; } -.mat-card { - width: $tile-width; +.mat-mdc-card { + width: $IMX_Tile_Width; height: 108px; overflow: auto; padding: 0; @@ -31,14 +30,6 @@ display: flex; } -.mat-button{ - text-transform: uppercase; -} - -.mat-button ::ng-deep eui-icon{ - margin-left: 15px; -} - .hidden { display: none; } diff --git a/imxweb/projects/qer/src/lib/wport/start/start.component.ts b/imxweb/projects/qer/src/lib/wport/start/start.component.ts index c2a8401b6..3bfabe20a 100644 --- a/imxweb/projects/qer/src/lib/wport/start/start.component.ts +++ b/imxweb/projects/qer/src/lib/wport/start/start.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,12 +27,12 @@ import { ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; -import { UserConfig, ProjectConfig, QerProjectConfig } from 'imx-api-qer'; -import { UserModelService } from '../../user/user-model.service'; -import { PendingItemsType } from '../../user/pending-items-type.interface'; -import { ProjectConfigurationService } from '../../project-configuration/project-configuration.service'; +import { SystemInfo } from '@imx-modules/imx-api-qbm'; +import { ProjectConfig, QerProjectConfig, UserConfig } from '@imx-modules/imx-api-qer'; import { imx_SessionService, SystemInfoService } from 'qbm'; -import { SystemInfo } from 'imx-api-qbm'; +import { ProjectConfigurationService } from '../../project-configuration/project-configuration.service'; +import { PendingItemsType } from '../../user/pending-items-type.interface'; +import { UserModelService } from '../../user/user-model.service'; import { DashboardService } from './dashboard.service'; @Component({ @@ -55,11 +55,11 @@ export class StartComponent implements OnInit { private readonly systemInfoService: SystemInfoService, private readonly sessionService: imx_SessionService, private readonly detectRef: ChangeDetectorRef, - private readonly projectConfigurationService: ProjectConfigurationService + private readonly projectConfigurationService: ProjectConfigurationService, ) {} public async ngOnInit(): Promise { - this.dashboardService.busyStateChanged.subscribe(busy => { + this.dashboardService.busyStateChanged.subscribe((busy) => { this.viewReady = !busy; this.detectRef.detectChanges(); }); @@ -69,7 +69,7 @@ export class StartComponent implements OnInit { this.pendingItems = await this.userModelSvc.getPendingItems(); this.projectConfig = await this.projectConfigurationService.getConfig(); this.systemInfo = await this.systemInfoService.get(); - this.userUid = (await this.sessionService.getSessionState()).UserUid; + this.userUid = (await this.sessionService.getSessionState()).UserUid || ''; } finally { busy.endBusy(); } @@ -80,7 +80,9 @@ export class StartComponent implements OnInit { } public ShowPasswordMgmtTile(): boolean { - return this.projectConfig?.PasswordConfig.VI_MyData_MyPassword_Visibility && !!this.projectConfig?.PasswordConfig.PasswordMgmtUrl; + return ( + (this.projectConfig.PasswordConfig?.VI_MyData_MyPassword_Visibility && !!this.projectConfig.PasswordConfig?.PasswordMgmtUrl) ?? false + ); } public GoToMyPassword(): void { @@ -88,7 +90,7 @@ export class StartComponent implements OnInit { } public GoToPasswordMgmtWeb(): void { - this.router.navigate(['/externalRedirect', { externalUrl: this.projectConfig?.PasswordConfig.PasswordMgmtUrl }]); + this.router.navigate(['/externalRedirect', { externalUrl: this.projectConfig.PasswordConfig?.PasswordMgmtUrl }]); } public GoToShoppingCart(): void { @@ -104,7 +106,7 @@ export class StartComponent implements OnInit { } public GoToItShopApprovalInquiries(): void { - this.router.navigate(['itshop', 'approvals'], {queryParams: {inquiries:true}}); + this.router.navigate(['itshop', 'approvals'], { queryParams: { inquiries: true } }); } public GoToMyProcesses(): void { @@ -112,7 +114,7 @@ export class StartComponent implements OnInit { } public ShowQpmIntegration(): boolean { - return !!this.projectConfig?.PasswordConfig.QpmBaseUrl; + return !!this.projectConfig.PasswordConfig?.QpmBaseUrl; } public GoToQpm(): void { @@ -159,6 +161,6 @@ export class StartComponent implements OnInit { public ShowNewRequestLink(): boolean { // Starting a new request is only allowed when the session has an identity and the ITShop(Requests) feature is enabled - return this.userConfig?.IsITShopEnabled && this.userUid && this.systemInfo.PreProps.includes('ITSHOP'); + return (this.userConfig?.IsITShopEnabled && !!this.userUid?.length && this.systemInfo.PreProps?.includes('ITSHOP')) || false; } } diff --git a/imxweb/projects/qer/src/public_api.ts b/imxweb/projects/qer/src/public_api.ts index e66b665af..3b8e43281 100644 --- a/imxweb/projects/qer/src/public_api.ts +++ b/imxweb/projects/qer/src/public_api.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,8 +30,11 @@ export { AddressbookComponent } from './lib/addressbook/addressbook.component'; export { AddressbookModule } from './lib/addressbook/addressbook.module'; +export { AuthenticationFactors } from './lib/admin/authentication-factors.interface'; export { FeatureConfigService } from './lib/admin/feature-config.service'; +export { isAuditor, isRoleAdmin, isRoleStatistics } from './lib/admin/qer-permissions-helper'; export { QerPermissionsService } from './lib/admin/qer-permissions.service'; +export { ApprovalWorkFlowModule } from './lib/approval-workflows/approval-workflows.module'; export { ArchivedRequestsComponent } from './lib/archived-requests/archived-requests.component'; export { ArchivedRequestsModule } from './lib/archived-requests/archived-requests.module'; export { BusinessOwnerAddOnTileComponent } from './lib/businessowner-addon-tile/businessowner-addon-tile.component'; @@ -50,11 +53,6 @@ export { HelperAlertContent } from './lib/helper-alert-content/helper-alert-cont export { IdentitiesReportsService } from './lib/identities/identities-reports.service'; export { DataExplorerIdentitiesComponent } from './lib/identities/identities.component'; export { IdentitiesModule } from './lib/identities/identities.module'; -export { NotificationRegistryService } from './lib/notifications/notification-registry.service'; -export { NotificationStreamService } from './lib/notifications/notification-stream.service'; -export { NotificationTileComponent } from './lib/tiles/notification-tile/notification-tile.component'; -export { BadgeTileComponent } from './lib/tiles/badge-tile/badge-tile.component'; -export { IconTileComponent } from './lib/tiles/icon-tile/icon-tile.component'; export { IdentitiesService } from './lib/identities/identities.service'; export { IdentityRoleMembershipsParameter, @@ -83,18 +81,24 @@ export { ServiceItemDetailComponent } from './lib/itshop/request-info/service-it export { ShelfService } from './lib/itshop/shelf.service'; export { WorkflowDataWrapper } from './lib/itshop/workflow-data-wrapper'; export { ApprovalsModule } from './lib/itshopapprove/approvals.module'; -export { ApprovalWorkFlowModule } from './lib/approval-workflows/approval-workflows.module'; export { DecisionStepSevice } from './lib/itshopapprove/decision-step.service'; export { RecommendationSidesheetComponent } from './lib/itshopapprove/recommendation-sidesheet/recommendation-sidesheet.component'; +export { WorkflowActionComponent } from './lib/itshopapprove/workflow-action/workflow-action.component'; export { DecisionReasonComponent } from './lib/justification/decision-reason/decision-reason.component'; export { JustificationType } from './lib/justification/justification-type.enum'; export { JustificationModule } from './lib/justification/justification.module'; export { JustificationService } from './lib/justification/justification.service'; export { MyResponsibilitiesRegistryService } from './lib/my-responsibilities-view/my-responsibilities-registry.service'; export { MyResponsibilitiesViewModule } from './lib/my-responsibilities-view/my-responsibilities-view.module'; +export { NewRequestSelectionService } from './lib/new-request/new-request-selection.service'; export * from './lib/new-request/new-request.component'; export * from './lib/new-request/new-request.module'; +export { NotificationRegistryService } from './lib/notifications/notification-registry.service'; +export { NotificationStreamService } from './lib/notifications/notification-stream.service'; export { ObjectAttestationStatistics } from './lib/object-attestation/object-attestation-statistics.interface'; +export { ObjectHyperviewComponent } from './lib/object-hyperview/object-hyperview.component'; +export { ObjectHyperviewModule } from './lib/object-hyperview/object-hyperview.module'; +export { ObjectHyperviewService } from './lib/object-hyperview/object-hyperview.service'; export { ObjectOverviewContainer } from './lib/ops/objectOverviewContainer'; export { ObjectOverviewPersonComponent } from './lib/ops/objectOverviewPerson.component'; export { OpsModule } from './lib/ops/ops.module'; @@ -102,21 +106,24 @@ export { PasscodeService } from './lib/ops/passcode.service'; export { OpSupportUserService } from './lib/ops/user.service'; export { OwnerControlComponent } from './lib/owner-control/owner-control.component'; export { OwnerControlModule } from './lib/owner-control/owner-control.module'; +export { ExtendedEntityWrapper } from './lib/parameter-data/extended-entity-wrapper.interface'; export { ParameterContainer } from './lib/parameter-data/parameter-container'; export { ParameterDataContainer } from './lib/parameter-data/parameter-data-container'; export { ParameterDataLoadParameters } from './lib/parameter-data/parameter-data-load-parameters.interface'; export { ParameterDataService } from './lib/parameter-data/parameter-data.service'; +export { PasswordQuestionsComponent } from './lib/password-questions/password-questions.component'; +export { PasswordQuestionsModule } from './lib/password-questions/password-questions.module'; export { PasswordDashboardComponent } from './lib/password/dashboard.component'; export { PasscodeLoginModule } from './lib/password/passcode-login/passcode-login.module'; export { PasswordQueryComponent } from './lib/password/password-query.component'; export { PasswordResetComponent } from './lib/password/password-reset.component'; export { PasswordModule } from './lib/password/password.module'; +export { PasswordService } from './lib/password/password.service'; export { QaLoginModule } from './lib/password/qa-login/qa-login.module'; export { PersonService } from './lib/person/person.service'; export { ProductSelectionModule } from './lib/product-selection/product-selection.module'; export { ProfileComponent } from './lib/profile/profile.component'; export { ProfileModule } from './lib/profile/profile.module'; -export { PasswordQuestionsComponent } from './lib/profile/password-questions/password-questions.component'; export { ProjectConfigurationModule } from './lib/project-configuration/project-configuration.module'; export { ProjectConfigurationService } from './lib/project-configuration/project-configuration.service'; export { QerApiService } from './lib/qer-api-client.service'; @@ -135,15 +142,15 @@ export { RiskConfigModule } from './lib/risk-config/risk-config.module'; export { RiskModule } from './lib/risk/risk.module'; export { RiskAnalysisSidesheetComponent } from './lib/risk/riskanalysis-sidesheet.component'; export { RiskAnalysisComponent } from './lib/risk/riskanalysis.component'; +export { DataManagementService } from './lib/role-management/data-management.service'; export { BaseTreeRoleRestoreHandler } from './lib/role-management/restore/restore-handler'; export { IRoleDataModel } from './lib/role-management/role-data-model.interface'; -export { BaseTreeEntitlement, IRoleEntitlements } from './lib/role-management/role-entitlements/entitlement-handlers'; export { RoleDetailComponent } from './lib/role-management/role-detail/role-detail.component'; -export { RoleRecommendationsComponent } from './lib/role-management/role-entitlements/role-recommendations/role-recommendations.component'; +export { BaseTreeEntitlement, IRoleEntitlements } from './lib/role-management/role-entitlements/entitlement-handlers'; +export { RoleEntitlementActionService } from './lib/role-management/role-entitlements/role-entitlement-action.service'; export { RoleRecommendationResultItem } from './lib/role-management/role-entitlements/role-recommendations/role-recommendation-result-item'; -export { RoleEntitlementActionService } from './lib/role-management/role-entitlements/role-entitlement-action.service' +export { RoleRecommendationsComponent } from './lib/role-management/role-entitlements/role-recommendations/role-recommendations.component'; export { RoleManangementModule } from './lib/role-management/role-manangement.module'; -export { DataManagementService } from './lib/role-management/data-management.service'; export { BaseMembership, IRoleMembershipType } from './lib/role-management/role-memberships/membership-handlers'; export { RoleMembershipsComponent } from './lib/role-management/role-memberships/role-memberships.component'; export { RoleMembershipsModule } from './lib/role-management/role-memberships/role-memberships.module'; @@ -155,52 +162,51 @@ export { ServiceItemTagsComponent } from './lib/service-item-tags/service-item-t export { ServiceItemTagsModule } from './lib/service-item-tags/service-item-tags.module'; export { ServiceItemTagsService } from './lib/service-item-tags/service-item-tags.service'; export { - additionalColumnsForServiceItemsKey, ServiceItemsEditFormComponent, + additionalColumnsForServiceItemsKey, } from './lib/service-items-edit/service-items-edit-form/service-items-edit-form.component'; export { ServiceItemsEditFormModule } from './lib/service-items-edit/service-items-edit-form/service-items-edit-form.module'; export { ServiceItemsEditModule } from './lib/service-items-edit/service-items-edit.module'; -export { StartComponent } from './lib/wport/start/start.component'; +export { SettingsComponent } from './lib/settings/settings.component'; export { BaseViewerComponent } from './lib/shopping-cart-validation-detail/base-viewer/base-viewer.component'; export { DetailViewerComponent } from './lib/shopping-cart-validation-detail/detail-viewer/detail-viewer.component'; export { DetailsView } from './lib/shopping-cart-validation-detail/details-view.interface'; -export { isRoleAdmin, isRoleStatistics, isAuditor} from './lib/admin/qer-permissions-helper'; -export { SettingsComponent } from './lib/settings/settings.component'; -export * from './lib/new-request/new-request.module'; -export * from './lib/new-request/new-request.component'; -export { ViewConfigService } from './lib/view-config/view-config.service'; - -export { StatisticsForObjectsComponent } from './lib/statistics/statistics-for-objects/statistics-for-objects.component'; -export { ChartInfoTyped } from './lib/statistics/statistics-home-page/chart-info-typed'; - -export { ObjectHyperviewModule } from './lib/object-hyperview/object-hyperview.module'; -export { ObjectHyperviewComponent } from './lib/object-hyperview/object-hyperview.component'; -export { ObjectHyperviewService } from './lib/object-hyperview/object-hyperview.service'; export { DuplicateCheckComponent } from './lib/shopping-cart-validation-detail/duplicate-check/duplicate-check.component'; export { ExclusionCheckComponent } from './lib/shopping-cart-validation-detail/exclusion-check/exclusion-check.component'; export { ShoppingCartValidationDetailModule } from './lib/shopping-cart-validation-detail/shopping-cart-validation-detail.module'; export { ShoppingCartValidationDetailService } from './lib/shopping-cart-validation-detail/shopping-cart-validation-detail.service'; +export { CartItemsExtensionService } from './lib/shopping-cart/cart-items-extension.service'; +export { ICartItemsExtensionService } from './lib/shopping-cart/cart-items.model'; +export { RequestableProduct } from './lib/shopping-cart/requestable-product.interface'; export { ShoppingCartModule } from './lib/shopping-cart/shopping-cart.module'; +export { QueueStatusComponent } from './lib/queue/queue-status/queue-status.component'; export { SourceDetectiveSidesheetComponent, SourceDetectiveSidesheetData } from './lib/sourcedetective/sourcedetective-sidesheet.component'; export { SourceDetectiveType } from './lib/sourcedetective/sourcedetective-type.enum'; export { SourceDetectiveComponent } from './lib/sourcedetective/sourcedetective.component'; export { SourceDetectiveModule } from './lib/sourcedetective/sourcedetective.module'; +export { ChartTileComponent } from './lib/statistics/charts/chart-tile/chart-tile.component'; +export { PointStatVisualService } from './lib/statistics/charts/chart-tile/point-stat-visual/point-stat-visual.service'; +export { StatisticsChartHandlerService } from './lib/statistics/charts/statistics-chart-handler.service'; +export { StatisticsForObjectsComponent } from './lib/statistics/statistics-for-objects/statistics-for-objects.component'; +export { ChartInfoTyped } from './lib/statistics/statistics-home-page/chart-info-typed'; export { StatisticsModule } from './lib/statistics/statistics.module'; export { TeamResponsibilitiesModule } from './lib/team-responsibilities/team-responsibilities.module'; export { TermsOfUseItem } from './lib/terms-of-use/terms-of-use-item'; export { TermsOfUseViewerComponent } from './lib/terms-of-use/terms-of-use-viewer/terms-of-use-viewer.component'; export { TermsOfUseModule } from './lib/terms-of-use/terms-of-use.module'; export { TermsOfUseService } from './lib/terms-of-use/terms-of-use.service'; +export { BadgeTileComponent } from './lib/tiles/badge-tile/badge-tile.component'; +export { IconTileComponent } from './lib/tiles/icon-tile/icon-tile.component'; +export { NotificationTileComponent } from './lib/tiles/notification-tile/notification-tile.component'; export { TilesModule } from './lib/tiles/tiles.module'; +export { UserProcessComponent } from './lib/user-process/user-process.component'; +export { UserProcessModule } from './lib/user-process/user-process.module'; export { PendingItemsType } from './lib/user/pending-items-type.interface'; export { UserModelService } from './lib/user/user-model.service'; export { UserModule } from './lib/user/user.module'; -export { UserProcessComponent } from './lib/user-process/user-process.component'; -export { UserProcessModule } from './lib/user-process/user-process.module'; -export { DashboardService } from './lib/wport/start/dashboard.service'; -export { NewRequestSelectionService } from './lib/new-request/new-request-selection.service'; -export { ViewDevicesModule } from './lib/view-devices/view-devices.module'; +export { ViewConfigService } from './lib/view-config/view-config.service'; export { ViewDevicesComponent } from './lib/view-devices/view-devices-home/view-devices.component'; export { ViewDevicesSidesheetComponent } from './lib/view-devices/view-devices-sidesheet/view-devices-sidesheet.component'; -export { AuthenticationFactors } from './lib/admin/authentication-factors.interface'; - +export { ViewDevicesModule } from './lib/view-devices/view-devices.module'; +export { DashboardService } from './lib/wport/start/dashboard.service'; +export { StartComponent } from './lib/wport/start/start.component'; diff --git a/imxweb/projects/qer/src/test.ts b/imxweb/projects/qer/src/test.ts index 1f09d8a87..9794e7c90 100644 --- a/imxweb/projects/qer/src/test.ts +++ b/imxweb/projects/qer/src/test.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,23 +26,17 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'core-js/es7/reflect'; -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +// Zone must be imported first. Be careful with prettier import organizing +import 'zone.js'; +import 'zone.js/testing'; + import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; +import 'core-js/es/reflect'; import { TestHelperModule } from 'qbm'; import { QerDefaultMocks } from './default-mocks.spec'; - -declare const require: any; - // First, initialize the Angular testing environment. getTestBed().initTestEnvironment([BrowserDynamicTestingModule, TestHelperModule], platformBrowserDynamicTesting()); -// Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); -// And load the modules. -context.keys().map(context); - QerDefaultMocks.registerDefaultMocks(); diff --git a/imxweb/projects/qer/tsconfig.lib.json b/imxweb/projects/qer/tsconfig.lib.json index 6bc419f84..f76eeaeb4 100644 --- a/imxweb/projects/qer/tsconfig.lib.json +++ b/imxweb/projects/qer/tsconfig.lib.json @@ -7,10 +7,11 @@ "declarationMap": true, "sourceMap": true, "inlineSources": true, - "types": [] + "types": [], + "strictNullChecks": true }, - "exclude": [ - "src/test.ts", - "**/*.spec.ts" - ] + "exclude": ["src/test.ts", "**/*.spec.ts"], + "angularCompilerOptions": { + "strictTemplates": true + } } diff --git a/imxweb/projects/qer/tsconfig.spec.json b/imxweb/projects/qer/tsconfig.spec.json index 16da33db0..6bd74e1f7 100644 --- a/imxweb/projects/qer/tsconfig.spec.json +++ b/imxweb/projects/qer/tsconfig.spec.json @@ -1,17 +1,10 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "strictNullChecks": false, "outDir": "../../out-tsc/spec", - "types": [ - "jasmine", - "node" - ] + "types": ["jasmine", "node"] }, - "files": [ - "src/test.ts" - ], - "include": [ - "**/*.spec.ts", - "**/*.d.ts" - ] + "files": ["src/test.ts"], + "include": ["**/*.spec.ts", "**/*.d.ts"] } diff --git a/imxweb/projects/qer/tslint.json b/imxweb/projects/qer/tslint.json deleted file mode 100644 index 8bab15f53..000000000 --- a/imxweb/projects/qer/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "imx", - "camelCase" - ], - "component-selector": [ - true, - "element", - "imx", - "kebab-case" - ] - } -} diff --git a/imxweb/projects/rmb/.compodocrc.json b/imxweb/projects/rmb/.compodocrc.json index 865166461..b6670d351 100644 --- a/imxweb/projects/rmb/.compodocrc.json +++ b/imxweb/projects/rmb/.compodocrc.json @@ -1,6 +1,6 @@ { "name": "IMX Web - RMB Library", - "output": "../../documentation/v92/rmb", + "output": "../../documentation/v93/rmb", "theme": "material", "assetsFolder": "../../compodoc/assets", "customLogo": "../../compodoc/assets/images/oneidentity-logo.png", diff --git a/imxweb/projects/rmb/.eslintrc.json b/imxweb/projects/rmb/.eslintrc.json new file mode 100644 index 000000000..b1f055e0f --- /dev/null +++ b/imxweb/projects/rmb/.eslintrc.json @@ -0,0 +1,35 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*", "**/*.spec.ts"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": ["imxweb/projects/rmb/tsconfig.lib.json", "imxweb/projects/rmb/tsconfig.spec.json"], + "createDefaultProgram": true + }, + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "imx", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "imx", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "rules": {} + } + ] +} diff --git a/imxweb/projects/rmb/imx-plugin-config.json b/imxweb/projects/rmb/imx-plugin-config.json index d8f895bdb..b14aa58c8 100644 --- a/imxweb/projects/rmb/imx-plugin-config.json +++ b/imxweb/projects/rmb/imx-plugin-config.json @@ -5,4 +5,4 @@ "Name": "RmbConfigModule" } ] -} \ No newline at end of file +} diff --git a/imxweb/projects/rmb/karma.conf.js b/imxweb/projects/rmb/karma.conf.js index 211193c9d..3e53a3ac8 100644 --- a/imxweb/projects/rmb/karma.conf.js +++ b/imxweb/projects/rmb/karma.conf.js @@ -15,7 +15,7 @@ module.exports = function (config) { require('karma-junit-reporter'), ], client: { - clearContext: false // leave Jasmine Spec Runner output visible in browser + clearContext: false, // leave Jasmine Spec Runner output visible in browser }, coverageIstanbulReporter: { dir: require('path').join(__dirname, 'results'), @@ -23,7 +23,7 @@ module.exports = function (config) { fixWebpackSourcePaths: true, 'report-config': { html: { - subdir: 'coverage-html' + subdir: 'coverage-html', }, }, thresholds: { @@ -40,13 +40,14 @@ module.exports = function (config) { port: 9876, captureTimeout: 210000, browserDisconnectTolerance: 3, - browserDisconnectTimeout : 210000, - browserNoActivityTimeout : 210000, + browserDisconnectTimeout: 210000, + browserNoActivityTimeout: 210000, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], singleRun: false, - restartOnFileChange: true + failOnEmptyTestSuite: false, + restartOnFileChange: true, }); }; diff --git a/imxweb/projects/rmb/ng-package.dynamic.json b/imxweb/projects/rmb/ng-package.dynamic.json index c7ee1a178..72d34a165 100644 --- a/imxweb/projects/rmb/ng-package.dynamic.json +++ b/imxweb/projects/rmb/ng-package.dynamic.json @@ -16,12 +16,11 @@ "@elemental-ui/core", "@ngx-translate/core", "@ngx-translate/http-loader", - "applicationinsights-js", "billboard.js" ], - "dest": "../../html/rmb", + "dest": "../../html/qer-app-portal/rmb", "lib": { "entryFile": "src/public_api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] } } diff --git a/imxweb/projects/rmb/ng-package.json b/imxweb/projects/rmb/ng-package.json index 8c47e5aa7..7645b7ed0 100644 --- a/imxweb/projects/rmb/ng-package.json +++ b/imxweb/projects/rmb/ng-package.json @@ -3,6 +3,6 @@ "dest": "../../dist/rmb", "lib": { "entryFile": "src/public_api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] } } diff --git a/imxweb/projects/rmb/package.json b/imxweb/projects/rmb/package.json index 57b92335c..bcb509eeb 100644 --- a/imxweb/projects/rmb/package.json +++ b/imxweb/projects/rmb/package.json @@ -1,6 +1,6 @@ { "name": "rmb", - "version": "9.2.1", + "version": "9.3.0", "private": true, "bundledDependencies": [ "imx-api-rmb" diff --git a/imxweb/projects/rmb/project.json b/imxweb/projects/rmb/project.json new file mode 100644 index 000000000..89a52c932 --- /dev/null +++ b/imxweb/projects/rmb/project.json @@ -0,0 +1,64 @@ +{ + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "name": "rmb", + "sourceRoot": "projects/rmb/src", + "implicitDependencies": ["qer"], + "projectType": "library", + "prefix": "imx", + "generators": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:ng-packagr", + "options": { + "tsConfig": "projects/rmb/tsconfig.lib.json", + "project": "projects/rmb/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "projects/rmb/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "projects/rmb/tsconfig.lib.json" + }, + "dynamic": { + "project": "projects/rmb/ng-package.dynamic.json" + }, + "remote-dev": { + "tsConfig": "projects/rmb/tsconfig.lib.json" + }, + "remote-qs": { + "tsConfig": "projects/rmb/tsconfig.lib.json" + } + }, + "outputs": ["{workspaceRoot}/dist/rmb"] + }, + "test": { + "executor": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/rmb/src/test.ts", + "tsConfig": "projects/rmb/tsconfig.spec.json", + "karmaConfig": "projects/rmb/karma.conf.js", + "stylePreprocessorOptions": { + "includePaths": [ + "./shared/assets", + "./shared/scss", + "./node_modules", + "./node_modules/@elemental-ui/cadence-icon", + "./node_modules/@elemental-ui/core" + ] + } + } + }, + "lint": { + "executor": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": ["projects/rmb/tsconfig.lib.json", "projects/rmb/tsconfig.spec.json"], + "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + } + } + } +} diff --git a/imxweb/projects/rmb/src/lib/init.service.ts b/imxweb/projects/rmb/src/lib/init.service.ts index 149e85864..4649be668 100644 --- a/imxweb/projects/rmb/src/lib/init.service.ts +++ b/imxweb/projects/rmb/src/lib/init.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,9 +26,20 @@ import { Injectable } from '@angular/core'; import { Route, Router } from '@angular/router'; +import { ProjectConfig } from '@imx-modules/imx-api-qbm'; +import { RoleExtendedDataWrite } from '@imx-modules/imx-api-qer'; -import { DynamicMethodService, ImxTranslationProviderService, imx_SessionService, MenuService, ExtService, HELP_CONTEXTUAL } from 'qbm'; -import { PortalAdminRoleOrg, PortalPersonRolemembershipsOrg, PortalRespOrg, V2ApiClientMethodFactory } from 'imx-api-rmb'; +import { PortalAdminRoleOrg, PortalPersonRolemembershipsOrg, PortalRespOrg, V2ApiClientMethodFactory } from '@imx-modules/imx-api-rmb'; +import { + CollectionLoadParameters, + EntityCollectionData, + EntitySchema, + ExtendedTypedEntityCollection, + MethodDefinition, + MethodDescriptor, + WriteExtTypedEntity, +} from '@imx-modules/imx-qbm-dbts'; +import { DynamicMethodService, ExtService, HELP_CONTEXTUAL, ImxTranslationProviderService, MenuService, imx_SessionService } from 'qbm'; import { BaseTreeEntitlement, BaseTreeRoleRestoreHandler, @@ -46,10 +57,7 @@ import { import { OrgDataModel } from './org-data-model'; import { OrgMembership } from './org-membership'; import { RmbApiService } from './rmb-api-client.service'; -import { EntitySchema, ExtendedTypedEntityCollection, WriteExtTypedEntity, CollectionLoadParameters, EntityCollectionData, MethodDescriptor, MethodDefinition } from 'imx-qbm-dbts'; -import { RoleExtendedDataWrite } from 'imx-api-qer'; import { TeamRoleComponent } from './team-role/team-role.component'; -import { ProjectConfig } from 'imx-api-qbm'; @Injectable({ providedIn: 'root' }) export class InitService { @@ -68,7 +76,7 @@ export class InitService { private readonly identityRoleMembershipService: IdentityRoleMembershipsService, private readonly myResponsibilitiesRegistryService: MyResponsibilitiesRegistryService, private readonly extService: ExtService, - private readonly qerPermissionsService: QerPermissionsService + private readonly qerPermissionsService: QerPermissionsService, ) {} public onInit(routes: Route[]): void { @@ -83,7 +91,7 @@ export class InitService { }, private getByIdApi: { Get_byid(id: string): Promise, unknown>>; - } + }, ) {} Get(): Promise, unknown>> { @@ -105,7 +113,7 @@ export class InitService { (uid) => this.api.client.portal_roles_Org_restore_byid_get(uid), (uid) => this.api.client.portal_resp_Org_restore_byid_get(uid), (uidRole, actions) => this.api.client.portal_roles_Org_restore_byid_post(uidRole, actions), - (uidRole, actions) => this.api.client.portal_resp_Org_restore_byid_post(uidRole, actions) + (uidRole, actions) => this.api.client.portal_resp_Org_restore_byid_post(uidRole, actions), ); this.roleService.targetMap.set(this.orgTag, { @@ -124,12 +132,16 @@ export class InitService { interactiveResp: new ApiWrapper(this.api.typedClient.PortalRespOrgInteractive, this.api.typedClient.PortalRespOrgInteractive), interactiveAdmin: new ApiWrapper( this.api.typedClient.PortalAdminRoleOrgInteractive, - this.api.typedClient.PortalAdminRoleOrgInteractive + this.api.typedClient.PortalAdminRoleOrgInteractive, ), - adminCanCreate: true, - respCanCreate: true, + adminCanCreate: async () => { + return (await this.api.client.portal_roles_config_businessroles_get()).EnableNewOrg; + }, + respCanCreate: async () => { + return (await this.api.client.portal_roles_config_businessroles_get()).EnableNewOrg; + }, entitlements: new BaseTreeEntitlement(this.qerApi, this.session, this.dynamicMethodService, this.translator, this.orgTag, (e) => - e.GetColumn('UID_OrgRoot').GetValue() + e.GetColumn('UID_OrgRoot').GetValue(), ), membership: new OrgMembership(this.api, this.session, this.translator), canUseRecommendations: true, @@ -176,22 +188,24 @@ export class InitService { this.setupMenu(); - this.dataExplorerRegistryService.registerFactory((preProps: string[], features: string[], projectConfig: ProjectConfig, groups: string[]) => { - if (!isRoleAdmin(features) && !isRoleStatistics(features) && !isAuditor(groups)) { - return; - } - return { - instance: RolesOverviewComponent, - data: { - TableName: this.orgTag, - Count: 0, - }, - contextId: HELP_CONTEXTUAL.DataExplorerBusinessRoles, - sortOrder: 7, - name: 'businessroles', - caption: '#LDS#Menu Entry Business roles', - }; - }); + this.dataExplorerRegistryService.registerFactory( + (preProps: string[], features: string[], projectConfig: ProjectConfig, groups: string[]) => { + if (!isRoleAdmin(features) && !isRoleStatistics(features) && !isAuditor(groups)) { + return undefined; + } + return { + instance: RolesOverviewComponent, + data: { + TableName: this.orgTag, + Count: 0, + }, + contextId: HELP_CONTEXTUAL.DataExplorerBusinessRoles, + sortOrder: 7, + name: 'businessroles', + caption: '#LDS#Menu Entry Business roles', + }; + }, + ); this.myResponsibilitiesRegistryService.registerFactory((preProps: string[], features: string[]) => ({ instance: RolesOverviewComponent, @@ -202,7 +216,7 @@ export class InitService { TableName: this.orgTag, Count: 0, }, - contextId: HELP_CONTEXTUAL.MyResponsibilitiesBusinessRoles + contextId: HELP_CONTEXTUAL.MyResponsibilitiesBusinessRoles, })); this.extService.register('Dashboard-MediumTiles', { instance: TeamRoleComponent }); } @@ -210,7 +224,7 @@ export class InitService { private setupMenu(): void { this.menuService.addMenuFactories((preProps: string[], features: string[], projectConfig: ProjectConfig, groups: string[]) => { if (!isRoleAdmin(features) && !isRoleStatistics(features) && !isAuditor(groups)) { - return null; + return undefined; } const menu = { id: 'ROOT_Data', diff --git a/imxweb/projects/rmb/src/lib/org-data-model.ts b/imxweb/projects/rmb/src/lib/org-data-model.ts index 7d12d43a3..0fedb3c09 100644 --- a/imxweb/projects/rmb/src/lib/org-data-model.ts +++ b/imxweb/projects/rmb/src/lib/org-data-model.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,18 +24,15 @@ * */ -import { FilterData, DataModel } from 'imx-qbm-dbts'; +import { FilterData, DataModel } from '@imx-modules/imx-qbm-dbts'; import { IRoleDataModel } from 'qer'; import { RmbApiService } from './rmb-api-client.service'; export class OrgDataModel implements IRoleDataModel { - - constructor( - private readonly api: RmbApiService - ) { } + constructor(private readonly api: RmbApiService) {} public async getModel(filter: FilterData[], isAdmin: boolean): Promise { - return isAdmin ? - this.api.client.portal_admin_role_org_datamodel_get({ filter: filter }) + return isAdmin + ? this.api.client.portal_admin_role_org_datamodel_get({ filter: filter }) : this.api.client.portal_resp_org_datamodel_get({ filter: filter }); } } diff --git a/imxweb/projects/rmb/src/lib/org-membership.ts b/imxweb/projects/rmb/src/lib/org-membership.ts index 6c870af88..28d72150a 100644 --- a/imxweb/projects/rmb/src/lib/org-membership.ts +++ b/imxweb/projects/rmb/src/lib/org-membership.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,77 +24,27 @@ * */ -import { CollectionLoadParameters, ExtendedTypedEntityCollection, TypedEntity, EntityCollectionData, EntitySchema, IEntity, DataModel } from 'imx-qbm-dbts'; -import { IRoleMembershipType } from 'qer'; -import { DynamicMethod, ImxTranslationProviderService, imx_SessionService } from 'qbm'; +import { + CollectionLoadParameters, + EntityCollectionData, + EntitySchema, + ExtendedTypedEntityCollection, + TypedEntity, +} from '@imx-modules/imx-qbm-dbts'; +import { ImxTranslationProviderService, imx_SessionService } from 'qbm'; +import { BaseMembership } from 'qer'; import { RmbApiService } from './rmb-api-client.service'; // do not inherit from BaseMembership because classes cannot inherit across modules :-( -export class OrgMembership implements IRoleMembershipType { - - public supportsDynamicMemberships = true; - private readonly schemaPaths: Map = new Map(); - - private readonly basePath = 'portal/roles/config/membership/Org'; - +export class OrgMembership extends BaseMembership { constructor( - private readonly api: RmbApiService, - private readonly session: imx_SessionService, - private readonly translator: ImxTranslationProviderService + private api: RmbApiService, + private session: imx_SessionService, + private translator: ImxTranslationProviderService, ) { - this.schemaPaths.set('get', `${this.basePath}/{UID_Org}`); - this.schemaPaths.set('candidates', `${this.basePath}/{UID_Org}/UID_Person/candidates`); - } - - public async get(id: string, navigationState?: CollectionLoadParameters): Promise> { - const api = new DynamicMethod( - this.schemaPaths.get('get'), - `/${this.basePath}/${id}`, - this.api.apiClient, - this.session, - this.translator - ); - - return api.Get(navigationState); - } - - public getSchema(key: string): EntitySchema { - return this.session.Client.getSchema(this.schemaPaths.get(key)); - } - - public GetUidPerson(entity: IEntity) { - return entity.GetColumn('UID_Person').GetValue(); - } - - public GetUidRole(entity: IEntity): string { - return entity.GetColumn("UID_Org").GetValue(); - } - - public async getCandidates( - id: string, - navigationState?: CollectionLoadParameters - ): Promise> { - const api = new DynamicMethod( - this.schemaPaths.get('candidates'), - `/${this.basePath}/${id}/UID_Person/candidates`, - this.api.apiClient, - this.session, - this.translator - ); - - return api.Get(navigationState); - } - - public async getCandidatesDataModel(id: string): Promise { - const dynamicMethod = new DynamicMethod( - this.schemaPaths.get('candidates'), - `/${this.basePath}/${id}/UID_Person/candidates`, - this.api.apiClient, - this.session, - this.translator - ); - return dynamicMethod.getDataModei(); + super(api, session, translator); + this.setRoleName('Org', 'UID_Org'); } public async delete(role: string, identity: string): Promise { @@ -107,7 +57,7 @@ export class OrgMembership implements IRoleMembershipType { public getPrimaryMembers( uid: string, - navigationstate: CollectionLoadParameters + navigationstate: CollectionLoadParameters, ): Promise> { return this.api.typedClient.PortalRolesConfigOrgPrimarymembers.Get(uid, navigationstate); } diff --git a/imxweb/projects/rmb/src/lib/rmb-api-client.service.ts b/imxweb/projects/rmb/src/lib/rmb-api-client.service.ts index 0c780cafd..be05db22f 100644 --- a/imxweb/projects/rmb/src/lib/rmb-api-client.service.ts +++ b/imxweb/projects/rmb/src/lib/rmb-api-client.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,12 +25,12 @@ */ import { Injectable } from '@angular/core'; -import { V2Client, TypedClient } from 'imx-api-rmb'; -import { ApiClient } from 'imx-qbm-dbts'; +import { V2Client, TypedClient } from '@imx-modules/imx-api-rmb'; +import { ApiClient } from '@imx-modules/imx-qbm-dbts'; import { AppConfigService, ClassloggerService, ImxTranslationProviderService } from 'qbm'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class RmbApiService { private tc: TypedClient; @@ -50,7 +50,8 @@ export class RmbApiService { constructor( private readonly config: AppConfigService, private readonly logger: ClassloggerService, - private readonly translationProvider: ImxTranslationProviderService) { + private readonly translationProvider: ImxTranslationProviderService, + ) { try { this.logger.debug(this, 'Initializing RMB API service'); diff --git a/imxweb/projects/rmb/src/lib/rmb-config.module.ts b/imxweb/projects/rmb/src/lib/rmb-config.module.ts index b63c30081..f7f45b707 100644 --- a/imxweb/projects/rmb/src/lib/rmb-config.module.ts +++ b/imxweb/projects/rmb/src/lib/rmb-config.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,14 +30,10 @@ import { Routes, RouterModule } from '@angular/router'; import { InitService } from './init.service'; import { TeamRoleModule } from './team-role/team-role.module'; -const routes: Routes = [ -]; +const routes: Routes = []; @NgModule({ - imports: [ - RouterModule.forChild(routes), - TeamRoleModule - ], + imports: [RouterModule.forChild(routes), TeamRoleModule], }) export class RmbConfigModule { constructor(private readonly initializer: InitService) { diff --git a/imxweb/projects/rmb/src/lib/team-role/team-role.component.html b/imxweb/projects/rmb/src/lib/team-role/team-role.component.html index bc66ee537..5656acc31 100644 --- a/imxweb/projects/rmb/src/lib/team-role/team-role.component.html +++ b/imxweb/projects/rmb/src/lib/team-role/team-role.component.html @@ -2,7 +2,9 @@ data-imx-identifier="start-tile-team-role" [caption]="'#LDS#Heading Team Role' | translate" [subtitle]=" - existTeamRole ? ('#LDS#Manage your team role, its memberships and entitlements.' | translate) : ('#LDS#Manage the entitlements required for your team using a team role.' | translate) + existTeamRole + ? ('#LDS#Manage your team role, its memberships and entitlements.' | translate) + : ('#LDS#Manage the entitlements required for your team using a team role.' | translate) " [identifier]="'team-role'" [loadingState]="loadingState" @@ -10,12 +12,12 @@ > - - diff --git a/imxweb/projects/rmb/src/lib/team-role/team-role.component.scss b/imxweb/projects/rmb/src/lib/team-role/team-role.component.scss deleted file mode 100644 index 4acc7d302..000000000 --- a/imxweb/projects/rmb/src/lib/team-role/team-role.component.scss +++ /dev/null @@ -1,3 +0,0 @@ -.mat-button { - text-transform: uppercase; -} diff --git a/imxweb/projects/rmb/src/lib/team-role/team-role.component.ts b/imxweb/projects/rmb/src/lib/team-role/team-role.component.ts index c4a453dcb..fea9043a0 100644 --- a/imxweb/projects/rmb/src/lib/team-role/team-role.component.ts +++ b/imxweb/projects/rmb/src/lib/team-role/team-role.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,11 +25,12 @@ */ import { Component, OnInit } from '@angular/core'; -import { PortalAdminRoleOrg, PortalRespOrg, TeamRoleData } from 'imx-api-rmb'; import { ActivatedRoute } from '@angular/router'; -import { IEntity, ReadOnlyEntity } from 'imx-qbm-dbts'; import { EuiSidesheetService } from '@elemental-ui/core'; +import { PortalAdminRoleOrg, TeamRoleData } from '@imx-modules/imx-api-rmb'; +import { IEntity, ReadOnlyEntity } from '@imx-modules/imx-qbm-dbts'; import { TranslateService } from '@ngx-translate/core'; +import { calculateSidesheetWidth, SnackBarService } from 'qbm'; import { DataManagementService, QerPermissionsService, @@ -40,12 +41,10 @@ import { RoleService, } from 'qer'; import { RmbApiService } from '../rmb-api-client.service'; -import { MetadataService } from 'qbm'; @Component({ selector: 'imx-team-role', templateUrl: './team-role.component.html', - styleUrls: ['./team-role.component.scss'], }) export class TeamRoleComponent implements OnInit { public teamRole: TeamRoleData; @@ -61,12 +60,14 @@ export class TeamRoleComponent implements OnInit { private readonly route: ActivatedRoute, private readonly permissionService: QerPermissionsService, private readonly roleEntitlementActionService: RoleEntitlementActionService, + private readonly snackbar: SnackBarService, ) {} async ngOnInit(): Promise { - if(await this.permissionService.isPersonManager()){ - this.showTeamRole = true; + const canCreate = (await this.rmbApiService.client.portal_roles_config_businessroles_get()).EnableNewTeamRole; + if (await this.permissionService.isPersonManager()) { await this.getTeamRole(); + this.showTeamRole = canCreate || this.existTeamRole; } } @@ -74,22 +75,32 @@ export class TeamRoleComponent implements OnInit { * Load team role data and open details sidesheet */ public async onManageTeamRole(): Promise { + if (!this.teamRole.Uid) { + return; + } try { this.loadingState = true; - const roleItemCollection = await this.roleService.targetMap.get(this.orgTag).interactiveResp.Get_byid(this.teamRole.Uid); - const roleItem = roleItemCollection.Data[0].GetEntity(); + const roleItem = ( + await this.roleService.targetMap.get(this.orgTag)?.interactiveResp?.Get_byid(this.teamRole.Uid) + )?.Data[0].GetEntity(); + if (!roleItem) { + throw new Error('There was an error getting details about this role'); + } await this.setRoleData(roleItem); await this.dataManagementService.setInteractive(); - this.sidesheetService.open(RoleDetailComponent, { - title: this.translateService.instant('#LDS#Heading Edit Team Role'), - subTitle: roleItem.GetDisplay(), - padding: '0px', - width: 'max(768px, 80%)', - disableClose: true, - testId: `${this.orgTag}-role-detail-sidesheet`, - }).afterClosed().subscribe(() =>{ - this.getTeamRole(); - }) + this.sidesheetService + .open(RoleDetailComponent, { + title: this.translateService.instant('#LDS#Heading Edit Team Role'), + subTitle: roleItem.GetDisplay(), + padding: '0px', + width: calculateSidesheetWidth(1250, 0.7), + disableClose: true, + testId: `${this.orgTag}-role-detail-sidesheet`, + }) + .afterClosed() + .subscribe(() => { + this.getTeamRole(); + }); } finally { this.loadingState = false; } @@ -105,23 +116,26 @@ export class TeamRoleComponent implements OnInit { .open(RoleRecommendationsComponent, { title: this.translateService.instant('#LDS#Heading Create Team Role'), padding: '0px', - width: 'max(650px, 65%)', + width: calculateSidesheetWidth(1000), testId: 'role-recommendation-sidesheet', data: { recommendation: teamRoleRecommendOptions.Items || [], canEdit: true, infoText: - '#LDS#Here you can create a team role. To do this, select the entitlements that are currently assigned individually to the members of your team. This team role will then be automatically assigned to all members of your team (along with the previously mentioned entitlements).', + '#LDS#Here you can create a team role. To do this, select the entitlements that are currently assigned individually to the members of your team. After you have created the team role, the entitlements are added to your shopping cart and you must submit the request. This team role will then be automatically assigned to all members of your team (along with the aforementioned entitlements).', selectionTitle: '#LDS#Selected entitlements', submitButtonTitle: '#LDS#Create team role', actionColumnTitle: '#LDS#Distribution among the team', - hideActionConfirmation: true + hideActionConfirmation: true, + applyWithoutSelection: true, + noDataText: + '#LDS#Currently, no entitlements can be recommended. You can still create the team role and assign entitlements later.', }, }) .afterClosed() - .subscribe((result: RoleRecommendationResultItem[]) => { + .subscribe((result: { items: RoleRecommendationResultItem[] }) => { if (!!result) { - this.saveRecommendations(result); + this.saveRecommendations(result.items); } }); } finally { @@ -155,11 +169,19 @@ export class TeamRoleComponent implements OnInit { try { // It can take a moment for the team role to become visible over the ownerships API because of DBQUeue calculations. this.loadingState = true; - const collectionData = await this.rmbApiService.client.portal_resp_team_teamrole_post({ObjectKeys: resultItem.map(item => item.ObjectKey.value)}); + const collectionData = await this.rmbApiService.client.portal_resp_team_teamrole_post({ + ObjectKeys: resultItem.map((item) => item.ObjectKey.value), + }); // This entity represents the new team role. + if (!collectionData.Entities) { + throw new Error('There was an error posting this role recommendation'); + } const entity = new ReadOnlyEntity(PortalAdminRoleOrg.GetEntitySchema(), collectionData.Entities[0]); await this.setRoleData(entity); - await this.roleEntitlementActionService.addRecommendation(entity, resultItem); + const count = await this.roleEntitlementActionService.addRecommendation(entity, resultItem); + if (count > 0) { + this.snackbar.open({ key: '#LDS#The entitlement assignments have been successfully added to your shopping cart.' }, '#LDS#Close'); + } this.getTeamRole(); } finally { this.loadingState = false; @@ -170,7 +192,7 @@ export class TeamRoleComponent implements OnInit { * Set role service sidesheet data with the required params. * @param IEntity */ - private async setRoleData(entity: IEntity): Promise{ + private async setRoleData(entity: IEntity): Promise { const isAdmin = this.route.snapshot?.url[0]?.path === 'admin'; const isStructureAdmin = await this.permissionService.isStructAdmin(); this.roleService.setSidesheetData({ diff --git a/imxweb/projects/rmb/src/lib/team-role/team-role.module.ts b/imxweb/projects/rmb/src/lib/team-role/team-role.module.ts index 11179f705..8505a4d03 100644 --- a/imxweb/projects/rmb/src/lib/team-role/team-role.module.ts +++ b/imxweb/projects/rmb/src/lib/team-role/team-role.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,26 +24,16 @@ * */ -import { CommonModule } from "@angular/common"; -import { NgModule } from "@angular/core"; -import { EuiCoreModule, EuiMaterialModule } from "@elemental-ui/core"; -import { TranslateModule } from "@ngx-translate/core"; +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; +import { TranslateModule } from '@ngx-translate/core'; import { TeamRoleComponent } from './team-role.component'; import { RoleManangementModule, TilesModule } from 'qer'; -import { FormsModule, ReactiveFormsModule } from "@angular/forms"; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; @NgModule({ - declarations: [ - TeamRoleComponent - ], - imports: [ - CommonModule, - EuiCoreModule, - EuiMaterialModule, - TranslateModule, - TilesModule, - FormsModule, - ReactiveFormsModule, - ] + declarations: [TeamRoleComponent], + imports: [CommonModule, EuiCoreModule, EuiMaterialModule, TranslateModule, TilesModule, FormsModule, ReactiveFormsModule], }) export class TeamRoleModule {} diff --git a/imxweb/projects/rmb/src/public_api.ts b/imxweb/projects/rmb/src/public_api.ts index 14c1b71fb..4c1c7e0de 100644 --- a/imxweb/projects/rmb/src/public_api.ts +++ b/imxweb/projects/rmb/src/public_api.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/rmb/src/test.ts b/imxweb/projects/rmb/src/test.ts index f7ac7d548..159c87d7d 100644 --- a/imxweb/projects/rmb/src/test.ts +++ b/imxweb/projects/rmb/src/test.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,24 +26,14 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'core-js/es7/reflect'; -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +// Zone must be imported first. Be careful with prettier import organizing +import 'zone.js'; +import 'zone.js/testing'; + import { getTestBed } from '@angular/core/testing'; -import { - BrowserDynamicTestingModule, - platformBrowserDynamicTesting -} from '@angular/platform-browser-dynamic/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; +import 'core-js/es/reflect'; import { TestHelperModule } from 'qbm'; -declare const require: any; - // First, initialize the Angular testing environment. -getTestBed().initTestEnvironment( - [BrowserDynamicTestingModule, TestHelperModule], - platformBrowserDynamicTesting() -); -// Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); -// And load the modules. -context.keys().map(context); +getTestBed().initTestEnvironment([BrowserDynamicTestingModule, TestHelperModule], platformBrowserDynamicTesting()); diff --git a/imxweb/projects/rmb/tsconfig.lib.json b/imxweb/projects/rmb/tsconfig.lib.json index 6bc419f84..534c734a0 100644 --- a/imxweb/projects/rmb/tsconfig.lib.json +++ b/imxweb/projects/rmb/tsconfig.lib.json @@ -1,16 +1,17 @@ /* To learn more about this file see: https://angular.io/config/tsconfig. */ { "extends": "../../tsconfig.json", + "angularCompilerOptions": { + "strictTemplates": true + }, "compilerOptions": { "outDir": "../../out-tsc/lib", + "strictNullChecks": true, "declaration": true, "declarationMap": true, "sourceMap": true, "inlineSources": true, "types": [] }, - "exclude": [ - "src/test.ts", - "**/*.spec.ts" - ] + "exclude": ["src/test.ts", "**/*.spec.ts"] } diff --git a/imxweb/projects/rmb/tsconfig.lib.prod.json b/imxweb/projects/rmb/tsconfig.lib.prod.json index 61c7592e7..fb40dbbb0 100644 --- a/imxweb/projects/rmb/tsconfig.lib.prod.json +++ b/imxweb/projects/rmb/tsconfig.lib.prod.json @@ -3,8 +3,5 @@ "extends": "./tsconfig.lib.json", "compilerOptions": { "declarationMap": false - }, - "angularCompilerOptions": { - "enableIvy": true } } diff --git a/imxweb/projects/rmb/tsconfig.spec.json b/imxweb/projects/rmb/tsconfig.spec.json index 16da33db0..6bd74e1f7 100644 --- a/imxweb/projects/rmb/tsconfig.spec.json +++ b/imxweb/projects/rmb/tsconfig.spec.json @@ -1,17 +1,10 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "strictNullChecks": false, "outDir": "../../out-tsc/spec", - "types": [ - "jasmine", - "node" - ] + "types": ["jasmine", "node"] }, - "files": [ - "src/test.ts" - ], - "include": [ - "**/*.spec.ts", - "**/*.d.ts" - ] + "files": ["src/test.ts"], + "include": ["**/*.spec.ts", "**/*.d.ts"] } diff --git a/imxweb/projects/rmb/tslint.json b/imxweb/projects/rmb/tslint.json deleted file mode 100644 index 8bab15f53..000000000 --- a/imxweb/projects/rmb/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "imx", - "camelCase" - ], - "component-selector": [ - true, - "element", - "imx", - "kebab-case" - ] - } -} diff --git a/imxweb/projects/rms/.compodocrc.json b/imxweb/projects/rms/.compodocrc.json index 4c5abfeda..1201ca1f1 100644 --- a/imxweb/projects/rms/.compodocrc.json +++ b/imxweb/projects/rms/.compodocrc.json @@ -1,6 +1,6 @@ { "name": "IMX Web - RMS Library", - "output": "../../documentation/v92/rms", + "output": "../../documentation/v93/rms", "theme": "material", "assetsFolder": "../../compodoc/assets", "customLogo": "../../compodoc/assets/images/oneidentity-logo.png", diff --git a/imxweb/projects/rms/.eslintrc.json b/imxweb/projects/rms/.eslintrc.json new file mode 100644 index 000000000..b2cbe8700 --- /dev/null +++ b/imxweb/projects/rms/.eslintrc.json @@ -0,0 +1,35 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*", "**/*.spec.ts"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": ["imxweb/projects/rms/tsconfig.lib.json", "imxweb/projects/rms/tsconfig.spec.json"], + "createDefaultProgram": true + }, + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "imx", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "imx", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "rules": {} + } + ] +} diff --git a/imxweb/projects/rms/imx-plugin-config.json b/imxweb/projects/rms/imx-plugin-config.json index 7868825da..6499fc339 100644 --- a/imxweb/projects/rms/imx-plugin-config.json +++ b/imxweb/projects/rms/imx-plugin-config.json @@ -5,4 +5,4 @@ "Name": "RmsConfigModule" } ] -} \ No newline at end of file +} diff --git a/imxweb/projects/rms/karma.conf.js b/imxweb/projects/rms/karma.conf.js index 211193c9d..3e53a3ac8 100644 --- a/imxweb/projects/rms/karma.conf.js +++ b/imxweb/projects/rms/karma.conf.js @@ -15,7 +15,7 @@ module.exports = function (config) { require('karma-junit-reporter'), ], client: { - clearContext: false // leave Jasmine Spec Runner output visible in browser + clearContext: false, // leave Jasmine Spec Runner output visible in browser }, coverageIstanbulReporter: { dir: require('path').join(__dirname, 'results'), @@ -23,7 +23,7 @@ module.exports = function (config) { fixWebpackSourcePaths: true, 'report-config': { html: { - subdir: 'coverage-html' + subdir: 'coverage-html', }, }, thresholds: { @@ -40,13 +40,14 @@ module.exports = function (config) { port: 9876, captureTimeout: 210000, browserDisconnectTolerance: 3, - browserDisconnectTimeout : 210000, - browserNoActivityTimeout : 210000, + browserDisconnectTimeout: 210000, + browserNoActivityTimeout: 210000, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], singleRun: false, - restartOnFileChange: true + failOnEmptyTestSuite: false, + restartOnFileChange: true, }); }; diff --git a/imxweb/projects/rms/ng-package.dynamic.json b/imxweb/projects/rms/ng-package.dynamic.json index 5b3ce79a3..59353c540 100644 --- a/imxweb/projects/rms/ng-package.dynamic.json +++ b/imxweb/projects/rms/ng-package.dynamic.json @@ -16,12 +16,11 @@ "@elemental-ui/core", "@ngx-translate/core", "@ngx-translate/http-loader", - "applicationinsights-js", "billboard.js" ], - "dest": "../../html/rms", + "dest": "../../html/qer-app-portal/rms", "lib": { "entryFile": "src/public_api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] } } diff --git a/imxweb/projects/rms/ng-package.json b/imxweb/projects/rms/ng-package.json index 83d381811..2aecf77cf 100644 --- a/imxweb/projects/rms/ng-package.json +++ b/imxweb/projects/rms/ng-package.json @@ -3,6 +3,6 @@ "dest": "../../dist/rms", "lib": { "entryFile": "src/public_api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] } } diff --git a/imxweb/projects/rms/package.json b/imxweb/projects/rms/package.json index 5fd9bf564..8e0d653df 100644 --- a/imxweb/projects/rms/package.json +++ b/imxweb/projects/rms/package.json @@ -1,6 +1,6 @@ { "name": "rms", - "version": "9.2.1", + "version": "9.3.0", "private": true, "bundledDependencies": [ "imx-api-rms" diff --git a/imxweb/projects/rms/project.json b/imxweb/projects/rms/project.json new file mode 100644 index 000000000..493ba4126 --- /dev/null +++ b/imxweb/projects/rms/project.json @@ -0,0 +1,64 @@ +{ + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "name": "rms", + "sourceRoot": "projects/rms/src", + "implicitDependencies": ["qer"], + "projectType": "library", + "prefix": "imx", + "generators": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:ng-packagr", + "options": { + "tsConfig": "projects/rms/tsconfig.lib.json", + "project": "projects/rms/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "projects/rms/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "projects/rms/tsconfig.lib.json" + }, + "dynamic": { + "project": "projects/rms/ng-package.dynamic.json" + }, + "remote-dev": { + "tsConfig": "projects/rms/tsconfig.lib.json" + }, + "remote-qs": { + "tsConfig": "projects/rms/tsconfig.lib.json" + } + }, + "outputs": ["{workspaceRoot}/dist/rms"] + }, + "test": { + "executor": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/rms/src/test.ts", + "tsConfig": "projects/rms/tsconfig.spec.json", + "karmaConfig": "projects/rms/karma.conf.js", + "stylePreprocessorOptions": { + "includePaths": [ + "./shared/assets", + "./shared/scss", + "./node_modules", + "./node_modules/@elemental-ui/cadence-icon", + "./node_modules/@elemental-ui/core" + ] + } + } + }, + "lint": { + "executor": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": ["projects/rms/tsconfig.lib.json", "projects/rms/tsconfig.spec.json"], + "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + } + } + } +} diff --git a/imxweb/projects/rms/src/lib/eset-data-model.ts b/imxweb/projects/rms/src/lib/eset-data-model.ts index 64e2c121a..1decea492 100644 --- a/imxweb/projects/rms/src/lib/eset-data-model.ts +++ b/imxweb/projects/rms/src/lib/eset-data-model.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,18 +24,15 @@ * */ -import { FilterData, DataModel } from 'imx-qbm-dbts'; +import { FilterData, DataModel } from '@imx-modules/imx-qbm-dbts'; import { IRoleDataModel } from 'qer'; import { RmsApiService } from './rms-api-client.service'; export class EsetDataModel implements IRoleDataModel { - - constructor( - private readonly api: RmsApiService - ) { } + constructor(private readonly api: RmsApiService) {} public async getModel(filter: FilterData[], isAdmin: boolean): Promise { - return isAdmin ? - this.api.client.portal_admin_role_eset_datamodel_get({ filter: filter }) + return isAdmin + ? this.api.client.portal_admin_role_eset_datamodel_get({ filter: filter }) : this.api.client.portal_resp_eset_datamodel_get({ filter: filter }); } } diff --git a/imxweb/projects/rms/src/lib/eset-entitlements.ts b/imxweb/projects/rms/src/lib/eset-entitlements.ts index 34c666ea2..5972c09ac 100644 --- a/imxweb/projects/rms/src/lib/eset-entitlements.ts +++ b/imxweb/projects/rms/src/lib/eset-entitlements.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,34 +24,44 @@ * */ -import { RoleAssignmentData } from "imx-api-qer"; -import { CollectionLoadParameters, CompareOperator, ExtendedTypedEntityCollection, FilterType, IEntity, TypedEntity } from "imx-qbm-dbts"; -import { DynamicMethodService, GenericTypedEntity, ImxTranslationProviderService } from "qbm"; -import { IRoleEntitlements } from "qer"; -import { RmsApiService } from "./rms-api-client.service"; +import { RoleAssignmentData } from '@imx-modules/imx-api-qer'; +import { + CollectionLoadParameters, + CompareOperator, + ExtendedTypedEntityCollection, + FilterType, + IEntity, + TypedEntity, +} from '@imx-modules/imx-qbm-dbts'; +import { ImxTranslationProviderService } from 'qbm'; +import { IRoleEntitlements } from 'qer'; +import { RmsApiService } from './rms-api-client.service'; export class EsetEntitlements implements IRoleEntitlements { + public entitlementFkName = 'Entitlement'; // column name in ESetHasEntitlement + constructor( private readonly api: RmsApiService, - private readonly dynamicMethodSvc: DynamicMethodService, - protected readonly translator: ImxTranslationProviderService + protected readonly translator: ImxTranslationProviderService, ) {} public async getCollection( id: string, navigationState?: CollectionLoadParameters, - objectKeyForFiltering?: string + objectKeyForFiltering?: string, ): Promise> { - return await this.api.typedClient.PortalRolesConfigEntitlementsEset.Get(id, { + return this.api.typedClient.PortalRolesConfigEntitlementsEset.Get(id, { ...navigationState, - filter: objectKeyForFiltering ? [ - { - ColumnName: 'Entitlement', - CompareOp: CompareOperator.Equal, - Type: FilterType.Compare, - Value1: objectKeyForFiltering, - }, - ] : undefined, + filter: objectKeyForFiltering + ? [ + { + ColumnName: 'Entitlement', + CompareOp: CompareOperator.Equal, + Type: FilterType.Compare, + Value1: objectKeyForFiltering, + }, + ] + : undefined, }); } @@ -59,26 +69,13 @@ export class EsetEntitlements implements IRoleEntitlements { return this.api.client.portal_roles_config_classes_ESet_get(); } - public getEntitlementFkName() { - return 'Entitlement'; // column name in ESetHasEntitlement - } - async delete(roleId: string, entity: IEntity): Promise { const esethasentl = entity.GetKeys()[0]; await this.api.client.portal_roles_config_entitlements_ESet_delete(roleId, esethasentl); } - public createEntitlementAssignmentEntity(role: IEntity, entlType: RoleAssignmentData): IEntity { + public createEntitlementAssignmentEntity(role: IEntity): IEntity { const uidESet = role.GetKeys()[0]; - const entityColl = this.dynamicMethodSvc.createEntity( - this.api.apiClient, - { - path: '/portal/roles/config/entitlements/ESet/' + uidESet, - type: GenericTypedEntity, - schemaPath: 'portal/roles/config/entitlements/ESet/{' + entlType.RoleFk + '}', - }, - { Columns: { UID_ESet: { Value: uidESet } } } - ); - return entityColl.Data[0].GetEntity(); + return this.api.typedClient.PortalRolesConfigEntitlementsEset.createEntity({ Columns: { UID_ESet: { Value: uidESet } } }).GetEntity(); } -} \ No newline at end of file +} diff --git a/imxweb/projects/rms/src/lib/eset-membership.ts b/imxweb/projects/rms/src/lib/eset-membership.ts index 3525af436..df14725d9 100644 --- a/imxweb/projects/rms/src/lib/eset-membership.ts +++ b/imxweb/projects/rms/src/lib/eset-membership.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,63 +24,27 @@ * */ -import { CollectionLoadParameters, ExtendedTypedEntityCollection, TypedEntity, EntityCollectionData, EntitySchema, IEntity, DataModel } from 'imx-qbm-dbts'; -import { IRoleMembershipType } from 'qer'; -import { DynamicMethod, ImxTranslationProviderService, imx_SessionService } from 'qbm'; +import { + CollectionLoadParameters, + EntityCollectionData, + EntitySchema, + ExtendedTypedEntityCollection, + TypedEntity, +} from '@imx-modules/imx-qbm-dbts'; +import { ImxTranslationProviderService, imx_SessionService } from 'qbm'; +import { BaseMembership } from 'qer'; import { RmsApiService } from './rms-api-client.service'; -export class EsetMembership implements IRoleMembershipType { - +export class EsetMembership extends BaseMembership { public supportsDynamicMemberships = false; - private readonly schemaPaths: Map = new Map(); - - private readonly basePath = 'portal/roles/config/membership/ESet'; constructor( private readonly api: RmsApiService, private readonly session: imx_SessionService, - private readonly translator: ImxTranslationProviderService + private readonly translator: ImxTranslationProviderService, ) { - this.schemaPaths.set('get', `${this.basePath}/{UID_ESet}`); - this.schemaPaths.set('candidates', `${this.basePath}/{UID_ESet}/UID_Person/candidates`); - } - - public async get(id: string, navigationState?: CollectionLoadParameters): Promise> { - return this.api.typedClient.PortalRolesConfigMembershipEset.Get(id, navigationState); - } - - public getSchema(key: string): EntitySchema { - return this.session.Client.getSchema(this.schemaPaths.get(key)); - } - - public GetUidPerson(entity: IEntity) { - return entity.GetColumn('UID_Person').GetValue(); - } - - public async getCandidates( - id: string, - navigationState?: CollectionLoadParameters - ): Promise> { - const api = new DynamicMethod( - this.schemaPaths.get('candidates'), - `/${this.basePath}/${id}/UID_Person/candidates`, - this.api.apiClient, - this.session, - this.translator - ); - - return api.Get(navigationState); - } - - public async getCandidatesDataModel(id: string): Promise { - const dynamicMethod = new DynamicMethod( - this.schemaPaths.get('candidates'), - `/${this.basePath}/${id}/UID_Person/candidates`, - this.api.apiClient, - this.session, - this.translator - ); - return dynamicMethod.getDataModei(); + super(api, session, translator); + this.setRoleName('ESet', 'UID_ESet'); } public async delete(role: string, identity: string): Promise { @@ -91,13 +55,9 @@ export class EsetMembership implements IRoleMembershipType { return false; } - public GetUidRole(entity: IEntity): string { - return entity.GetColumn("UID_ESet").GetValue(); - } - public getPrimaryMembers( uid: string, - navigationstate: CollectionLoadParameters + navigationstate: CollectionLoadParameters, ): Promise> { throw new Error('System roles do not allow primary memberships.'); } diff --git a/imxweb/projects/rms/src/lib/init.service.ts b/imxweb/projects/rms/src/lib/init.service.ts index 27872d642..b2d6634e3 100644 --- a/imxweb/projects/rms/src/lib/init.service.ts +++ b/imxweb/projects/rms/src/lib/init.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,20 +26,21 @@ import { Injectable } from '@angular/core'; import { Route, Router } from '@angular/router'; -import { RoleExtendedDataWrite } from 'imx-api-qer'; +import { RoleExtendedDataWrite } from '@imx-modules/imx-api-qer'; -import { PortalAdminRoleEset, PortalPersonRolemembershipsEset, PortalRespEset, V2ApiClientMethodFactory } from 'imx-api-rms'; +import { ProjectConfig } from '@imx-modules/imx-api-qbm'; +import { PortalAdminRoleEset, PortalPersonRolemembershipsEset, PortalRespEset, V2ApiClientMethodFactory } from '@imx-modules/imx-api-rms'; import { + CollectionLoadParameters, + EntityCollectionData, EntitySchema, ExtendedTypedEntityCollection, + MethodDefinition, + MethodDescriptor, TypedEntity, WriteExtTypedEntity, - CollectionLoadParameters, - MethodDescriptor, - EntityCollectionData, - MethodDefinition, -} from 'imx-qbm-dbts'; -import { DynamicMethodService, ImxTranslationProviderService, imx_SessionService, MenuService, HELP_CONTEXTUAL } from 'qbm'; +} from '@imx-modules/imx-qbm-dbts'; +import { HELP_CONTEXTUAL, ImxTranslationProviderService, MenuService, imx_SessionService } from 'qbm'; import { DataExplorerRegistryService, IdentityRoleMembershipsService, @@ -54,13 +55,6 @@ import { EsetDataModel } from './eset-data-model'; import { EsetEntitlements } from './eset-entitlements'; import { EsetMembership } from './eset-membership'; import { RmsApiService } from './rms-api-client.service'; -import { ProjectConfig } from 'imx-api-qbm'; - -export interface test { - GetSchema(): EntitySchema; - Get_byid(id: string): Promise>; - Get(): Promise>; -} @Injectable({ providedIn: 'root' }) export class InitService { @@ -71,26 +65,25 @@ export class InitService { private readonly api: RmsApiService, private readonly session: imx_SessionService, private readonly translator: ImxTranslationProviderService, - private readonly dynamicMethodSvc: DynamicMethodService, private readonly dataExplorerRegistryService: DataExplorerRegistryService, private readonly menuService: MenuService, private readonly roleService: RoleService, private readonly identityRoleMembershipService: IdentityRoleMembershipsService, - private readonly myResponsibilitiesRegistryService: MyResponsibilitiesRegistryService + private readonly myResponsibilitiesRegistryService: MyResponsibilitiesRegistryService, ) {} public onInit(routes: Route[]): void { this.addRoutes(routes); // wrapper class for interactive methods - // tslint:disable-next-line: max-classes-per-file + // eslint-disable-next-line max-classes-per-file class ApiWrapper { constructor( private getByIdApi: { GetSchema(): EntitySchema; Get_byid(id: string): Promise>; Get?(): Promise>; - } + }, ) {} public async Get(): Promise, unknown>> { @@ -136,15 +129,20 @@ export class InitService { search: parameter.search, risk: parameter.risk, esettype: parameter.esettype, + withProperties: parameter.withProperties, }), }, adminSchema: this.api.typedClient.PortalAdminRoleEset.GetSchema(), dataModel: new EsetDataModel(this.api), - respCanCreate: false, - adminCanCreate: true, + adminCanCreate: async () => { + return (await this.api.client.portal_roles_config_systemroles_get()).EnableNewESet; + }, + respCanCreate: async () => { + return (await this.api.client.portal_roles_config_systemroles_get()).EnableNewESet; + }, interactiveResp: new ApiWrapper(this.api.typedClient.PortalRespEsetInteractive), interactiveAdmin: new ApiWrapper(this.api.typedClient.PortalAdminRoleEsetInteractive), - entitlements: new EsetEntitlements(this.api, this.dynamicMethodSvc, this.translator), + entitlements: new EsetEntitlements(this.api, this.translator), membership: new EsetMembership(this.api, this.session, this.translator), canUseRecommendations: true, exportMethod: (navigationState: CollectionLoadParameters, isAdmin: boolean) => { @@ -190,8 +188,8 @@ export class InitService { this.dataExplorerRegistryService.registerFactory( (preProps: string[], features: string[], projectConfig: ProjectConfig, groups: string[]) => { - if (!isRoleAdmin(features) && !isRoleStatistics(features) && !isAuditor(groups)) { - return; + if (isRoleAdmin(features) && !isRoleStatistics(features) && !isAuditor(groups)) { + return undefined; } return { instance: RolesOverviewComponent, @@ -204,7 +202,7 @@ export class InitService { name: 'systemroles', caption: '#LDS#Menu Entry System roles', }; - } + }, ); this.myResponsibilitiesRegistryService.registerFactory((preProps: string[], features: string[]) => ({ @@ -223,7 +221,7 @@ export class InitService { private setupMenu(): void { this.menuService.addMenuFactories((preProps: string[], features: string[], projectConfig: ProjectConfig, groups: string[]) => { if (!isRoleAdmin(features) && !isRoleStatistics(features) && !isAuditor(groups)) { - return null; + return undefined; } return { id: 'ROOT_Data', diff --git a/imxweb/projects/rms/src/lib/requestable-systemrole-type.ts b/imxweb/projects/rms/src/lib/requestable-systemrole-type.ts index 6760760c3..6fbc69f56 100644 --- a/imxweb/projects/rms/src/lib/requestable-systemrole-type.ts +++ b/imxweb/projects/rms/src/lib/requestable-systemrole-type.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,25 +24,25 @@ * */ -import { EntitySchema, TypedEntity } from "imx-qbm-dbts"; -import { IRequestableEntitlementType } from "qer"; -import { DynamicMethodService, GenericTypedEntity } from "qbm"; -import { RmsApiService } from "./rms-api-client.service"; +import { EntitySchema, TypedEntity } from '@imx-modules/imx-qbm-dbts'; +import { DynamicMethodService, GenericTypedEntity } from 'qbm'; +import { IRequestableEntitlementType } from 'qer'; +import { RmsApiService } from './rms-api-client.service'; export class RequestableSystemRoleType implements IRequestableEntitlementType { - - constructor(private readonly dynamicMethodService: DynamicMethodService, - private readonly rmsApi: RmsApiService + constructor( + private readonly dynamicMethodService: DynamicMethodService, + private readonly rmsApi: RmsApiService, ) { this.schema = this.createAssignmentEntity('dummy').GetEntity().GetSchema(); } getTableName() { - return "ESet"; + return 'ESet'; } getFkColumnName() { - return "UID_ESet"; + return 'UID_ESet'; } private schema: EntitySchema; @@ -52,27 +52,35 @@ export class RequestableSystemRoleType implements IRequestableEntitlementType { } public addEntitlementSelections(shelfId: string, values: string[]): Promise { - const promises = []; - values.forEach(value => { + const promises: Promise[] = []; + values.forEach((value) => { const e = this.createAssignmentEntity(shelfId).GetEntity(); - promises.push(e.GetColumn(this.getFkColumnName()).PutValue(value) - .then(() => e.Commit())); + promises.push( + e + .GetColumn(this.getFkColumnName()) + .PutValue(value) + .then(() => e.Commit()), + ); }); return Promise.all(promises); } public createAssignmentEntity(shelfId: string): TypedEntity { - const entityColl = this.dynamicMethodService.createEntity(this.rmsApi.apiClient, { - path: '/portal/shop/config/entitlements/' + shelfId + '/' + this.getTableName(), - type: GenericTypedEntity, - schemaPath: 'portal/shop/config/entitlements/{UID_ITShopOrg}/' + this.getTableName(), - }, { - Columns: { - "UID_ITShopOrg": { - Value: shelfId - } - } - }); + const entityColl = this.dynamicMethodService.createEntity( + this.rmsApi.apiClient, + { + path: '/portal/shop/config/entitlements/' + shelfId + '/' + this.getTableName(), + type: GenericTypedEntity, + schemaPath: 'portal/shop/config/entitlements/{UID_ITShopOrg}/' + this.getTableName(), + }, + { + Columns: { + UID_ITShopOrg: { + Value: shelfId, + }, + }, + }, + ); return entityColl.Data[0]; } -} \ No newline at end of file +} diff --git a/imxweb/projects/rms/src/lib/rms-api-client.service.ts b/imxweb/projects/rms/src/lib/rms-api-client.service.ts index 5a722334e..ca3a21db5 100644 --- a/imxweb/projects/rms/src/lib/rms-api-client.service.ts +++ b/imxweb/projects/rms/src/lib/rms-api-client.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,14 +25,14 @@ */ import { Injectable } from '@angular/core'; -import { V2Client, TypedClient } from 'imx-api-rms'; -import { ApiClient } from 'imx-qbm-dbts'; +import { V2Client, TypedClient } from '@imx-modules/imx-api-rms'; +import { ApiClient } from '@imx-modules/imx-qbm-dbts'; import { AppConfigService, ClassloggerService, DynamicMethodService, ImxTranslationProviderService } from 'qbm'; import { IRequestableEntitlementType, RequestableEntitlementTypeService } from 'qer'; import { RequestableSystemRoleType } from './requestable-systemrole-type'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class RmsApiService { private tc: TypedClient; @@ -54,7 +54,8 @@ export class RmsApiService { private readonly logger: ClassloggerService, private readonly entlTypeService: RequestableEntitlementTypeService, private readonly dynamicMethodService: DynamicMethodService, - private readonly translationProvider: ImxTranslationProviderService) { + private readonly translationProvider: ImxTranslationProviderService, + ) { try { this.logger.debug(this, 'Initializing RMS API service'); @@ -63,7 +64,7 @@ export class RmsApiService { this.c = new V2Client(this.config.apiClient, schemaProvider); this.tc = new TypedClient(this.c, this.translationProvider); - this.entlTypeService.Register(() => this.GetSystemRoleType()) + this.entlTypeService.Register(() => this.GetSystemRoleType()); } catch (e) { this.logger.error(this, e); } diff --git a/imxweb/projects/rms/src/lib/rms-config.module.ts b/imxweb/projects/rms/src/lib/rms-config.module.ts index 05cd20540..fc09a8c6f 100644 --- a/imxweb/projects/rms/src/lib/rms-config.module.ts +++ b/imxweb/projects/rms/src/lib/rms-config.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -29,13 +29,10 @@ import { Routes, RouterModule } from '@angular/router'; import { InitService } from './init.service'; -const routes: Routes = [ -]; +const routes: Routes = []; @NgModule({ - imports: [ - RouterModule.forChild(routes) - ] + imports: [RouterModule.forChild(routes)], }) export class RmsConfigModule { constructor(private readonly initializer: InitService) { diff --git a/imxweb/projects/rms/src/public_api.ts b/imxweb/projects/rms/src/public_api.ts index 8e00308c2..01293fbd3 100644 --- a/imxweb/projects/rms/src/public_api.ts +++ b/imxweb/projects/rms/src/public_api.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/rms/src/test.ts b/imxweb/projects/rms/src/test.ts index f7ac7d548..159c87d7d 100644 --- a/imxweb/projects/rms/src/test.ts +++ b/imxweb/projects/rms/src/test.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,24 +26,14 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'core-js/es7/reflect'; -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +// Zone must be imported first. Be careful with prettier import organizing +import 'zone.js'; +import 'zone.js/testing'; + import { getTestBed } from '@angular/core/testing'; -import { - BrowserDynamicTestingModule, - platformBrowserDynamicTesting -} from '@angular/platform-browser-dynamic/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; +import 'core-js/es/reflect'; import { TestHelperModule } from 'qbm'; -declare const require: any; - // First, initialize the Angular testing environment. -getTestBed().initTestEnvironment( - [BrowserDynamicTestingModule, TestHelperModule], - platformBrowserDynamicTesting() -); -// Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); -// And load the modules. -context.keys().map(context); +getTestBed().initTestEnvironment([BrowserDynamicTestingModule, TestHelperModule], platformBrowserDynamicTesting()); diff --git a/imxweb/projects/rms/tsconfig.lib.json b/imxweb/projects/rms/tsconfig.lib.json index 6bc419f84..534c734a0 100644 --- a/imxweb/projects/rms/tsconfig.lib.json +++ b/imxweb/projects/rms/tsconfig.lib.json @@ -1,16 +1,17 @@ /* To learn more about this file see: https://angular.io/config/tsconfig. */ { "extends": "../../tsconfig.json", + "angularCompilerOptions": { + "strictTemplates": true + }, "compilerOptions": { "outDir": "../../out-tsc/lib", + "strictNullChecks": true, "declaration": true, "declarationMap": true, "sourceMap": true, "inlineSources": true, "types": [] }, - "exclude": [ - "src/test.ts", - "**/*.spec.ts" - ] + "exclude": ["src/test.ts", "**/*.spec.ts"] } diff --git a/imxweb/projects/rms/tsconfig.lib.prod.json b/imxweb/projects/rms/tsconfig.lib.prod.json index 61c7592e7..fb40dbbb0 100644 --- a/imxweb/projects/rms/tsconfig.lib.prod.json +++ b/imxweb/projects/rms/tsconfig.lib.prod.json @@ -3,8 +3,5 @@ "extends": "./tsconfig.lib.json", "compilerOptions": { "declarationMap": false - }, - "angularCompilerOptions": { - "enableIvy": true } } diff --git a/imxweb/projects/rms/tsconfig.spec.json b/imxweb/projects/rms/tsconfig.spec.json index 16da33db0..6bd74e1f7 100644 --- a/imxweb/projects/rms/tsconfig.spec.json +++ b/imxweb/projects/rms/tsconfig.spec.json @@ -1,17 +1,10 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "strictNullChecks": false, "outDir": "../../out-tsc/spec", - "types": [ - "jasmine", - "node" - ] + "types": ["jasmine", "node"] }, - "files": [ - "src/test.ts" - ], - "include": [ - "**/*.spec.ts", - "**/*.d.ts" - ] + "files": ["src/test.ts"], + "include": ["**/*.spec.ts", "**/*.d.ts"] } diff --git a/imxweb/projects/rms/tslint.json b/imxweb/projects/rms/tslint.json deleted file mode 100644 index 8bab15f53..000000000 --- a/imxweb/projects/rms/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "imx", - "camelCase" - ], - "component-selector": [ - true, - "element", - "imx", - "kebab-case" - ] - } -} diff --git a/imxweb/projects/rps/.compodocrc.json b/imxweb/projects/rps/.compodocrc.json index 3515829fd..30423db42 100644 --- a/imxweb/projects/rps/.compodocrc.json +++ b/imxweb/projects/rps/.compodocrc.json @@ -1,6 +1,6 @@ { "name": "IMX Web - RPS Library", - "output": "../../documentation/v92/rps", + "output": "../../documentation/v93/rps", "theme": "material", "assetsFolder": "../../compodoc/assets", "customLogo": "../../compodoc/assets/images/oneidentity-logo.png", diff --git a/imxweb/projects/rps/.eslintrc.json b/imxweb/projects/rps/.eslintrc.json new file mode 100644 index 000000000..4bddb1138 --- /dev/null +++ b/imxweb/projects/rps/.eslintrc.json @@ -0,0 +1,35 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*", "**/*.spec.ts"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": ["imxweb/projects/rps/tsconfig.lib.json", "imxweb/projects/rps/tsconfig.spec.json"], + "createDefaultProgram": true + }, + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "imx", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "imx", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "rules": {} + } + ] +} diff --git a/imxweb/projects/rps/imx-plugin-config.json b/imxweb/projects/rps/imx-plugin-config.json index 520f0e43c..c2b844e89 100644 --- a/imxweb/projects/rps/imx-plugin-config.json +++ b/imxweb/projects/rps/imx-plugin-config.json @@ -1,8 +1,8 @@ { "qer-app-portal": [ - { - "Container": "rps", - "Name": "RpsConfigModule" - } + { + "Container": "rps", + "Name": "RpsConfigModule" + } ] } diff --git a/imxweb/projects/rps/karma.conf.js b/imxweb/projects/rps/karma.conf.js index 211193c9d..3e53a3ac8 100644 --- a/imxweb/projects/rps/karma.conf.js +++ b/imxweb/projects/rps/karma.conf.js @@ -15,7 +15,7 @@ module.exports = function (config) { require('karma-junit-reporter'), ], client: { - clearContext: false // leave Jasmine Spec Runner output visible in browser + clearContext: false, // leave Jasmine Spec Runner output visible in browser }, coverageIstanbulReporter: { dir: require('path').join(__dirname, 'results'), @@ -23,7 +23,7 @@ module.exports = function (config) { fixWebpackSourcePaths: true, 'report-config': { html: { - subdir: 'coverage-html' + subdir: 'coverage-html', }, }, thresholds: { @@ -40,13 +40,14 @@ module.exports = function (config) { port: 9876, captureTimeout: 210000, browserDisconnectTolerance: 3, - browserDisconnectTimeout : 210000, - browserNoActivityTimeout : 210000, + browserDisconnectTimeout: 210000, + browserNoActivityTimeout: 210000, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], singleRun: false, - restartOnFileChange: true + failOnEmptyTestSuite: false, + restartOnFileChange: true, }); }; diff --git a/imxweb/projects/rps/ng-package.dynamic.json b/imxweb/projects/rps/ng-package.dynamic.json index 999b58e82..eec3ee75f 100644 --- a/imxweb/projects/rps/ng-package.dynamic.json +++ b/imxweb/projects/rps/ng-package.dynamic.json @@ -16,12 +16,11 @@ "@elemental-ui/core", "@ngx-translate/core", "@ngx-translate/http-loader", - "applicationinsights-js", "billboard.js" ], - "dest": "../../html/rps", + "dest": "../../html/qer-app-portal/rps", "lib": { "entryFile": "src/public_api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] } } diff --git a/imxweb/projects/rps/ng-package.json b/imxweb/projects/rps/ng-package.json index e3eda4426..ca8402cfe 100644 --- a/imxweb/projects/rps/ng-package.json +++ b/imxweb/projects/rps/ng-package.json @@ -3,6 +3,6 @@ "dest": "../../dist/rps", "lib": { "entryFile": "src/public_api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] } } diff --git a/imxweb/projects/rps/package.json b/imxweb/projects/rps/package.json index aef856677..7e051f677 100644 --- a/imxweb/projects/rps/package.json +++ b/imxweb/projects/rps/package.json @@ -1,6 +1,6 @@ { "name": "rps", - "version": "9.2.1", + "version": "9.3.0", "private": true, "bundledDependencies": [ "imx-api-rps" diff --git a/imxweb/projects/rps/project.json b/imxweb/projects/rps/project.json new file mode 100644 index 000000000..21c5ed937 --- /dev/null +++ b/imxweb/projects/rps/project.json @@ -0,0 +1,64 @@ +{ + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "name": "rps", + "sourceRoot": "projects/rps/src", + "implicitDependencies": ["qer"], + "projectType": "library", + "generators": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "prefix": "imx", + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:ng-packagr", + "options": { + "tsConfig": "projects/rps/tsconfig.lib.json", + "project": "projects/rps/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "projects/rps/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "projects/rps/tsconfig.lib.json" + }, + "dynamic": { + "project": "projects/rps/ng-package.dynamic.json" + }, + "remote-dev": { + "tsConfig": "projects/rps/tsconfig.lib.json" + }, + "remote-qs": { + "tsConfig": "projects/rps/tsconfig.lib.json" + } + }, + "outputs": ["{workspaceRoot}/dist/rps"] + }, + "test": { + "executor": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/rps/src/test.ts", + "tsConfig": "projects/rps/tsconfig.spec.json", + "karmaConfig": "projects/rps/karma.conf.js", + "stylePreprocessorOptions": { + "includePaths": [ + "./shared/assets", + "./shared/scss", + "./node_modules", + "./node_modules/@elemental-ui/cadence-icon", + "./node_modules/@elemental-ui/core" + ] + } + } + }, + "lint": { + "executor": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": ["projects/rps/tsconfig.lib.json", "projects/rps/tsconfig.spec.json"], + "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + } + } + } +} diff --git a/imxweb/projects/rps/src/lib/admin/permissions-helper.ts b/imxweb/projects/rps/src/lib/admin/permissions-helper.ts index 054f9fc5e..3e8ac6e4a 100644 --- a/imxweb/projects/rps/src/lib/admin/permissions-helper.ts +++ b/imxweb/projects/rps/src/lib/admin/permissions-helper.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,6 +24,6 @@ * */ -export function isRpsAdmin(groups: string[]): boolean { - return groups.find(item => item === 'vi_4_RPSADMIN_ADMIN') != null; +export function isRpsAdmin(groups: (string | undefined)[]): boolean { + return groups.find((item) => item === 'vi_4_RPSADMIN_ADMIN') != null; } diff --git a/imxweb/projects/rps/src/lib/admin/rps-permissions.service.ts b/imxweb/projects/rps/src/lib/admin/rps-permissions.service.ts index e4ff5e57a..13708ab0c 100644 --- a/imxweb/projects/rps/src/lib/admin/rps-permissions.service.ts +++ b/imxweb/projects/rps/src/lib/admin/rps-permissions.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,13 +30,12 @@ import { UserModelService } from 'qer'; import { isRpsAdmin } from './permissions-helper'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class RpsPermissionsService { - constructor(private readonly userService: UserModelService) { } + constructor(private readonly userService: UserModelService) {} public async isRpsAdmin(): Promise { - return isRpsAdmin((await this.userService.getGroups()).map(group => group.Name)); + return isRpsAdmin((await this.userService.getGroups()).map((group) => group.Name)); } - } diff --git a/imxweb/projects/rps/src/lib/init.service.ts b/imxweb/projects/rps/src/lib/init.service.ts index d34b28ad0..5120940d4 100644 --- a/imxweb/projects/rps/src/lib/init.service.ts +++ b/imxweb/projects/rps/src/lib/init.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,13 +25,14 @@ */ import { Injectable } from '@angular/core'; -import { Router, Route } from '@angular/router'; +import { Route, Router } from '@angular/router'; import { DynamicMethodService, ExtService, TabItem } from 'qbm'; import { RequestableEntitlementType, RequestableEntitlementTypeService } from 'qer'; import { ReportButtonComponent } from './report-button/report-button.component'; +import { ReportButtonMailComponent } from './report-button/report-button-mail.component'; import { RpsApiService } from './rps-api-client.service'; -import { SubscriptionsComponent } from './subscriptions/subscriptions.component'; import { StatisticReportButtonComponent } from './statistic-report-button/statistic-report-button.component'; +import { SubscriptionsComponent } from './subscriptions/subscriptions.component'; @Injectable({ providedIn: 'root' }) export class InitService { @@ -40,7 +41,7 @@ export class InitService { private readonly entlTypeService: RequestableEntitlementTypeService, private readonly apiService: RpsApiService, private readonly dynamicMethodService: DynamicMethodService, - private readonly extService: ExtService + private readonly extService: ExtService, ) {} public onInit(routes: Route[]): void { @@ -59,7 +60,7 @@ export class InitService { instance: ReportButtonComponent, inputData: { uidReport: 'CPL-77d3c04ac2084a968433ef7daf7e56ff', - caption: '#LDS#Download report on rule violations by identities you are directly responsible for', + caption: '#LDS#View rule violations by identities you are directly responsible for', preprop: ['COMPLIANCE'], features: ['Portal_UI_PersonManager'] } @@ -99,6 +100,11 @@ export class InitService { } }); + this.extService.register('presetReportButton', { + instance: ReportButtonMailComponent, + }); + + this.entlTypeService.Register(async () => [ new RequestableEntitlementType('RPSReport', this.apiService.apiClient, 'UID_RPSReport', this.dynamicMethodService), ]); diff --git a/imxweb/projects/rps/src/lib/list-report-viewer/list-report-data-provider.interface.ts b/imxweb/projects/rps/src/lib/list-report-viewer/list-report-data-provider.interface.ts index 5adc06a15..5f87a9ff0 100644 --- a/imxweb/projects/rps/src/lib/list-report-viewer/list-report-data-provider.interface.ts +++ b/imxweb/projects/rps/src/lib/list-report-viewer/list-report-data-provider.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,13 +24,13 @@ * */ -import { CollectionLoadParameters, DataModel, EntitySchema, ExtendedTypedEntityCollection, GroupInfoData } from 'imx-qbm-dbts'; -import { ListReportContentData, PortalReportData } from 'imx-api-rps'; +import { CollectionLoadParameters, DataModel, EntitySchema, ExtendedTypedEntityCollection, GroupInfoData } from '@imx-modules/imx-qbm-dbts'; +import { ListReportContentData, PortalReportData } from '@imx-modules/imx-api-rps'; /** - * Provides methods for API interaction + * Provides methods for API interaction * Is used for navigating though the data of a list report - * + * */ export interface ListReportDataProvider { /** diff --git a/imxweb/projects/rps/src/lib/list-report-viewer/list-report-viewer.component.html b/imxweb/projects/rps/src/lib/list-report-viewer/list-report-viewer.component.html index 6739b70fe..6a71fea84 100644 --- a/imxweb/projects/rps/src/lib/list-report-viewer/list-report-viewer.component.html +++ b/imxweb/projects/rps/src/lib/list-report-viewer/list-report-viewer.component.html @@ -13,10 +13,10 @@ diff --git a/imxweb/projects/rps/src/lib/list-report-viewer/list-report-viewer.component.scss b/imxweb/projects/rps/src/lib/list-report-viewer/list-report-viewer.component.scss index 61459b1c2..69cc32493 100644 --- a/imxweb/projects/rps/src/lib/list-report-viewer/list-report-viewer.component.scss +++ b/imxweb/projects/rps/src/lib/list-report-viewer/list-report-viewer.component.scss @@ -1,21 +1,6 @@ -:host { - flex: 1 1 auto; - display: flex; - flex-direction: column; - overflow: hidden; +@import 'base/mixins'; - .mat-card { - flex: 1 1 auto; - display: flex; - flex-direction: column; - overflow: hidden; - margin: 3px; - } +:host { + @include flex-column-container-fill(); - .imx-table-container { - display: flex; - flex-direction: column; - overflow: hidden; - flex: 1 1 auto; - } } diff --git a/imxweb/projects/rps/src/lib/list-report-viewer/list-report-viewer.component.ts b/imxweb/projects/rps/src/lib/list-report-viewer/list-report-viewer.component.ts index cf29e4d86..50f86bec3 100644 --- a/imxweb/projects/rps/src/lib/list-report-viewer/list-report-viewer.component.ts +++ b/imxweb/projects/rps/src/lib/list-report-viewer/list-report-viewer.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,8 +27,15 @@ import { Component, Input, OnInit } from '@angular/core'; import _ from 'lodash'; -import { BusyService, ClassloggerService, DataSourceToolbarGroupData, DataSourceToolbarSettings, createGroupData } from 'qbm'; -import { CollectionLoadParameters, DataModel, DisplayColumns, EntitySchema } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, DataModel, DisplayColumns, EntitySchema } from '@imx-modules/imx-qbm-dbts'; +import { + BusyService, + ClassloggerService, + DataSourceToolbarGroupData, + DataSourceToolbarSettings, + DataTableGroupedData, + createGroupData, +} from 'qbm'; import { ListReportDataProvider } from './list-report-data-provider.interface'; /** @@ -60,9 +67,10 @@ export class ListReportViewerComponent implements OnInit { public busyService = new BusyService(); public dstSettings: DataSourceToolbarSettings; public entitySchema: EntitySchema; - public groupData: DataSourceToolbarGroupData; + public groupedData: { [key: string]: DataTableGroupedData } = {}; private dataModel: DataModel; + private groupData: DataSourceToolbarGroupData | undefined; private reportColumns: string[]; private navigationState: CollectionLoadParameters = {}; @@ -105,16 +113,18 @@ export class ListReportViewerComponent implements OnInit { const isBusy = this.busyService.beginBusy(); try { - const groupedData = this.groupData[groupKey]; - const navigationState = { ...groupedData.navigationState }; - groupedData.data = await this.dataService.get(navigationState); - groupedData.settings = { - displayedColumns: this.dstSettings.displayedColumns, - dataModel: this.dstSettings.dataModel, - dataSource: groupedData.data, - entitySchema: this.dstSettings.entitySchema, - navigationState, - }; + if (this.groupData?.[groupKey]) { + const groupedData = this.groupData[groupKey]; + const navigationState = { ...groupedData.navigationState }; + groupedData.data = await this.dataService.get(navigationState); + groupedData.settings = { + displayedColumns: this.dstSettings.displayedColumns, + dataModel: this.dstSettings.dataModel, + dataSource: groupedData.data, + entitySchema: this.dstSettings.entitySchema, + navigationState, + }; + } } finally { isBusy.endBusy(); } @@ -158,13 +168,15 @@ export class ListReportViewerComponent implements OnInit { this.dataModel = await this.dataService.getDataModel(); const data = await this.dataService.get({ PageSize: -1 }); - this.reportColumns = data.extendedData.Columns; + if (data.extendedData?.Columns) { + this.reportColumns = data.extendedData.Columns; + } // create a copy of listReportSchema and add additional columns to it (because the schema only provides the display at this point) this.entitySchema = _.cloneDeep(this.dataService.entitySchema) as any; for (const column of this.reportColumns) { - (this.entitySchema.Columns[column] as any) = data.extendedData.AdditionalProperties.find((elem) => elem.ColumnName === column); + (this.entitySchema.Columns[column] as any) = data.extendedData?.AdditionalProperties?.find((elem) => elem.ColumnName === column); } this.navigationState = { ...this.navigationState }; @@ -183,7 +195,7 @@ export class ListReportViewerComponent implements OnInit { }, ...parameters, }), - [] + [], ); } } diff --git a/imxweb/projects/rps/src/lib/list-report-viewer/list-report-viewer.module.ts b/imxweb/projects/rps/src/lib/list-report-viewer/list-report-viewer.module.ts index d1154bc14..8a0d3e941 100644 --- a/imxweb/projects/rps/src/lib/list-report-viewer/list-report-viewer.module.ts +++ b/imxweb/projects/rps/src/lib/list-report-viewer/list-report-viewer.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,11 +30,9 @@ import { ListReportViewerComponent } from './list-report-viewer.component'; import { DataSourceToolbarModule, DataTableModule } from 'qbm'; import { MatCardModule } from '@angular/material/card'; - - @NgModule({ declarations: [ListReportViewerComponent], - imports: [CommonModule,MatCardModule, DataSourceToolbarModule,DataTableModule], + imports: [CommonModule, MatCardModule, DataSourceToolbarModule, DataTableModule], exports: [ListReportViewerComponent], }) export class ListReportViewerModule {} diff --git a/imxweb/projects/rps/src/lib/list-report-viewer/list-report-viewer.service.ts b/imxweb/projects/rps/src/lib/list-report-viewer/list-report-viewer.service.ts index 9fcd0dcc7..690a60d82 100644 --- a/imxweb/projects/rps/src/lib/list-report-viewer/list-report-viewer.service.ts +++ b/imxweb/projects/rps/src/lib/list-report-viewer/list-report-viewer.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,8 +26,8 @@ import { Injectable } from '@angular/core'; -import { CollectionLoadParameters, DataModel, EntitySchema, ExtendedTypedEntityCollection, GroupInfoData } from 'imx-qbm-dbts'; -import { ListReportContentData, PortalReportData } from 'imx-api-rps'; +import { CollectionLoadParameters, DataModel, EntitySchema, ExtendedTypedEntityCollection, GroupInfoData } from '@imx-modules/imx-qbm-dbts'; +import { ListReportContentData, PortalReportData } from '@imx-modules/imx-api-rps'; import { RpsApiService } from '../rps-api-client.service'; import { ListReportDataProvider } from './list-report-data-provider.interface'; diff --git a/imxweb/projects/rps/src/lib/report-button/parameter-sidesheet/parameter-sidesheet.component.html b/imxweb/projects/rps/src/lib/report-button/parameter-sidesheet/parameter-sidesheet.component.html index b6ef09098..adc8fa03e 100644 --- a/imxweb/projects/rps/src/lib/report-button/parameter-sidesheet/parameter-sidesheet.component.html +++ b/imxweb/projects/rps/src/lib/report-button/parameter-sidesheet/parameter-sidesheet.component.html @@ -1,12 +1,20 @@
    - + -
    +
    - - -
    \ No newline at end of file + + +
    diff --git a/imxweb/projects/rps/src/lib/report-button/parameter-sidesheet/parameter-sidesheet.component.scss b/imxweb/projects/rps/src/lib/report-button/parameter-sidesheet/parameter-sidesheet.component.scss deleted file mode 100644 index 735131a3c..000000000 --- a/imxweb/projects/rps/src/lib/report-button/parameter-sidesheet/parameter-sidesheet.component.scss +++ /dev/null @@ -1,4 +0,0 @@ -.mat-spinner{ - margin-right: 10px; - align-self: center; -} \ No newline at end of file diff --git a/imxweb/projects/rps/src/lib/report-button/parameter-sidesheet/parameter-sidesheet.component.ts b/imxweb/projects/rps/src/lib/report-button/parameter-sidesheet/parameter-sidesheet.component.ts index 7b1a76f26..364acf897 100644 --- a/imxweb/projects/rps/src/lib/report-button/parameter-sidesheet/parameter-sidesheet.component.ts +++ b/imxweb/projects/rps/src/lib/report-button/parameter-sidesheet/parameter-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,18 +24,17 @@ * */ -import { ChangeDetectorRef, Component, Inject } from '@angular/core'; -import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; -import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { ChangeDetectorRef, Component, Inject, OnInit } from '@angular/core'; +import { AbstractControl, UntypedFormGroup } from '@angular/forms'; +import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; import { ClassloggerService, ColumnDependentReference } from 'qbm'; import { ReportSubscription } from '../../subscriptions/report-subscription/report-subscription'; @Component({ selector: 'imx-parameter-sidesheet', templateUrl: './parameter-sidesheet.component.html', - styleUrls: ['./parameter-sidesheet.component.scss'] }) -export class ParameterSidesheetComponent { +export class ParameterSidesheetComponent implements OnInit { public readonly reportFormGroup = new UntypedFormGroup({}); public cdrs: ColumnDependentReference[]; @@ -43,10 +42,9 @@ export class ParameterSidesheetComponent { constructor( private readonly sidesheetRef: EuiSidesheetRef, private readonly changeDetectorRef: ChangeDetectorRef, - @Inject(EUI_SIDESHEET_DATA) public readonly data: { subscription: ReportSubscription }, - logger: ClassloggerService + @Inject(EUI_SIDESHEET_DATA) public readonly data: { subscription: ReportSubscription; presetParameter: { [key: string]: string } }, + logger: ClassloggerService, ) { - this.cdrs = data.subscription.getParameterCdr(); data.subscription.reportEntityWrapper.startWriteData.subscribe(() => { this.writeOperators = this.writeOperators + 1; logger.debug(this, 'number of write operations:', this.writeOperators); @@ -59,12 +57,21 @@ export class ParameterSidesheetComponent { }); } - public addFormControl(name: string, control: UntypedFormControl) { + public async ngOnInit(): Promise { + if (this.data.presetParameter) { + await this.data.subscription.fillColumnsWithPreset(this.data.presetParameter); + this.cdrs = this.data.subscription.getParameterCdr(Object.entries(this.data.presetParameter).map((elem) => elem[0])); + } else { + this.cdrs = this.data.subscription.getParameterCdr(); + } + } + + public addFormControl(name: string, control: AbstractControl) { this.reportFormGroup.addControl(name, control); this.changeDetectorRef.detectChanges(); } - public viewReport() { + public async viewReport(): Promise { this.sidesheetRef.close(true); } } diff --git a/imxweb/projects/rps/src/lib/report-button/report-button-mail.component.html b/imxweb/projects/rps/src/lib/report-button/report-button-mail.component.html new file mode 100644 index 000000000..0d4a2fb0f --- /dev/null +++ b/imxweb/projects/rps/src/lib/report-button/report-button-mail.component.html @@ -0,0 +1,3 @@ + diff --git a/imxweb/projects/rps/src/lib/report-button/report-button-mail.component.scss b/imxweb/projects/rps/src/lib/report-button/report-button-mail.component.scss new file mode 100644 index 000000000..248a268c5 --- /dev/null +++ b/imxweb/projects/rps/src/lib/report-button/report-button-mail.component.scss @@ -0,0 +1 @@ +// add styles if necessary diff --git a/imxweb/projects/rps/src/lib/report-button/report-button-mail.component.ts b/imxweb/projects/rps/src/lib/report-button/report-button-mail.component.ts new file mode 100644 index 000000000..a938a60ad --- /dev/null +++ b/imxweb/projects/rps/src/lib/report-button/report-button-mail.component.ts @@ -0,0 +1,119 @@ +/* + * ONE IDENTITY LLC. PROPRIETARY INFORMATION + * + * This software is confidential. One Identity, LLC. or one of its affiliates or + * subsidiaries, has supplied this software to you under terms of a + * license agreement, nondisclosure agreement or both. + * + * You may not copy, disclose, or use this software except in accordance with + * those terms. + * + * + * Copyright 2024 One Identity LLC. + * ALL RIGHTS RESERVED. + * + * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR + * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE + * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING + * THIS SOFTWARE OR ITS DERIVATIVES. + * + */ + +import { Component, OnDestroy } from '@angular/core'; +import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; +import { TranslateService } from '@ngx-translate/core'; + +import { calculateSidesheetWidth, SnackBarService } from 'qbm'; +import { ReportSubscription } from '../subscriptions/report-subscription/report-subscription'; +import { ReportSubscriptionService } from '../subscriptions/report-subscription/report-subscription.service'; +import { ParameterSidesheetComponent } from './parameter-sidesheet/parameter-sidesheet.component'; + +@Component({ + templateUrl: './report-button-mail.component.html', + styleUrls: ['./report-button-mail.component.scss'], +}) +export class ReportButtonMailComponent implements OnDestroy { + public referrer: { uid: string; presetParameters: { [key: string]: string } }; + + private subscription: ReportSubscription | undefined; + + constructor( + private readonly reportSubscriptionService: ReportSubscriptionService, + private readonly busy: EuiLoadingService, + private readonly sideSheet: EuiSidesheetService, + private readonly translator: TranslateService, + private readonly snackbarService: SnackBarService, + ) {} + + public ngOnDestroy(): void { + this.subscription?.unsubscribeEvents; + } + + public async sendReport(): Promise { + let over = this.busy.show(); + + try { + if (this.subscription != null) { + this.subscription.unsubscribeEvents(); + this.subscription = undefined; + } + this.subscription = await this.reportSubscriptionService.createNewSubscription(this.referrer.uid, Date.now().toFixed()); + } finally { + this.busy.hide(over); + } + if (!this.subscription) { + return; + } + + this.subscription.subscription.ExportFormat.value = 'PDF'; + + if (this.hasParametersToCompleteByUser()) { + const result = await this.sideSheet + .open(ParameterSidesheetComponent, { + title: await this.translator.get('#LDS#Heading Specify Parameters').toPromise(), + padding: '0px', + width: calculateSidesheetWidth(), + testId: 'report-button-view-parameter-sidesheet', + data: { subscription: this.subscription, presetParameter: this.referrer?.presetParameters }, + }) + .afterClosed() + .toPromise(); + + if (!result) { + return; + } + } + + let errors = false; + over = this.busy.show(); + try { + await this.subscription.submit(); + await this.reportSubscriptionService.sendViaMail(this.subscription.subscription.GetEntity().GetKeys()[0]); + } catch { + errors = true; + } finally { + this.busy.hide(over); + if (!errors) { + this.snackbarService.open({ + key: '#LDS#The report "{0}" will be sent to you.', + parameters: [this.subscription.subscription.UID_RPSReport.Column.GetDisplayValue()], + }); + } + } + } + + private hasParametersToCompleteByUser(): boolean { + if (!this.referrer?.presetParameters) { + return this.subscription?.hasParameter || false; + } + + const presetParameter = Object.entries(this.referrer.presetParameters).map((elem) => elem[0]); + + return !!this.subscription?.parameterNames.filter((elem) => presetParameter.indexOf(elem) === -1); + } +} diff --git a/imxweb/projects/rps/src/lib/report-button/report-button-parameter.ts b/imxweb/projects/rps/src/lib/report-button/report-button-parameter.ts index 1af5dbcc2..dd11f8f66 100644 --- a/imxweb/projects/rps/src/lib/report-button/report-button-parameter.ts +++ b/imxweb/projects/rps/src/lib/report-button/report-button-parameter.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/rps/src/lib/report-button/report-button.component.html b/imxweb/projects/rps/src/lib/report-button/report-button.component.html index f30621741..606602bfa 100644 --- a/imxweb/projects/rps/src/lib/report-button/report-button.component.html +++ b/imxweb/projects/rps/src/lib/report-button/report-button.component.html @@ -1,3 +1,3 @@ \ No newline at end of file + {{ inputData.caption | translate }} + diff --git a/imxweb/projects/rps/src/lib/report-button/report-button.component.scss b/imxweb/projects/rps/src/lib/report-button/report-button.component.scss index e110d11b2..248a268c5 100644 --- a/imxweb/projects/rps/src/lib/report-button/report-button.component.scss +++ b/imxweb/projects/rps/src/lib/report-button/report-button.component.scss @@ -1 +1 @@ -// add styles if necessary \ No newline at end of file +// add styles if necessary diff --git a/imxweb/projects/rps/src/lib/report-button/report-button.component.ts b/imxweb/projects/rps/src/lib/report-button/report-button.component.ts index 1ee69ff73..7919bbd38 100644 --- a/imxweb/projects/rps/src/lib/report-button/report-button.component.ts +++ b/imxweb/projects/rps/src/lib/report-button/report-button.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,18 +26,18 @@ import { Overlay } from '@angular/cdk/overlay'; import { HttpClient } from '@angular/common/http'; -import { Component, Injector, OnDestroy, OnInit } from '@angular/core'; -import { EuiDownloadDirective, EuiDownloadOptions, EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; +import { Component, ElementRef, Injector, OnDestroy, OnInit } from '@angular/core'; +import { EuiDownloadDirective, EuiDownloadOptions, EuiDownloadService, EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { V2ApiClientMethodFactory } from 'imx-api-rps'; -import { MethodDefinition } from 'imx-qbm-dbts'; -import { AppConfigService, ElementalUiConfigService, SystemInfoService } from 'qbm'; +import { V2ApiClientMethodFactory } from '@imx-modules/imx-api-rps'; +import { MethodDefinition } from '@imx-modules/imx-qbm-dbts'; +import { AppConfigService, ElementalUiConfigService, SystemInfoService, calculateSidesheetWidth } from 'qbm'; import { UserModelService } from 'qer'; import { ReportSubscription } from '../subscriptions/report-subscription/report-subscription'; import { ReportSubscriptionService } from '../subscriptions/report-subscription/report-subscription.service'; -import { ReportButtonParameter } from './report-button-parameter'; import { ParameterSidesheetComponent } from './parameter-sidesheet/parameter-sidesheet.component'; +import { ReportButtonParameter } from './report-button-parameter'; @Component({ selector: 'imx-report-button', @@ -51,7 +51,7 @@ export class ReportButtonComponent implements OnInit, OnDestroy { public isButtonRendered = true; public referrer: any; - private subscription: ReportSubscription; + private subscription: ReportSubscription | undefined; private readonly apiMethodFactory: V2ApiClientMethodFactory = new V2ApiClientMethodFactory(); @@ -66,7 +66,8 @@ export class ReportButtonComponent implements OnInit, OnDestroy { private readonly system: SystemInfoService, private readonly sideSheet: EuiSidesheetService, private readonly translator: TranslateService, - private readonly userModelService: UserModelService + private readonly userModelService: UserModelService, + private readonly downloadService: EuiDownloadService, ) {} public ngOnDestroy(): void { @@ -74,25 +75,22 @@ export class ReportButtonComponent implements OnInit, OnDestroy { } public async ngOnInit(): Promise { - if (this.inputData.groups == null && this.inputData.preprop == null) { + if (this.inputData.groups && this.inputData.preprop) { this.isButtonRendered = true; return; } const over = this.busy.show(); try { - const info = await this.system.get(); - const user = (await this.userModelService.getGroups()).map((elem) => elem.Name); - const userFeatures = (await this.userModelService.getFeatures()).Features; + const preprops = (await this.system.get())?.PreProps?.map((elem) => elem?.toUpperCase() ?? '') ?? []; + const user = (await this.userModelService.getGroups()).map((elem) => elem?.Name?.toUpperCase() ?? ''); + const userFeatures = (await this.userModelService.getFeatures()).Features ?? []; - const pre = - this.inputData.preprop == null || - this.inputData.preprop.some((elem) => info.PreProps.find((item) => item.toUpperCase() === elem.toUpperCase()) != null); + const pre = !this.inputData.preprop || this.inputData.preprop.some((elem) => preprops.find((item) => item === elem.toUpperCase())); const groups = - this.inputData.groups == null || - this.inputData.groups.some((elem) => user.find((item) => item.toUpperCase() === elem.toUpperCase()) != null); - const features = this.inputData?.features.some(feature => userFeatures.find(userFeature => feature === userFeature) != null); + !this.inputData.groups || this.inputData.groups.some((elem) => user.find((item) => item.toUpperCase() === elem.toUpperCase())); + const features = this.inputData?.features?.some((feature) => userFeatures.find((userFeature) => feature === userFeature)); - this.isButtonRendered = pre && (groups || features); + this.isButtonRendered = pre && (groups || !!features); } finally { this.busy.hide(over); } @@ -100,19 +98,16 @@ export class ReportButtonComponent implements OnInit, OnDestroy { public async viewReport(): Promise { const over = this.busy.show(); + if (this.subscription) { + this.subscription.unsubscribeEvents(); + this.subscription = undefined; + } try { - if (this.subscription != null) { - this.subscription.unsubscribeEvents(); - this.subscription = null; - } this.subscription = await this.reportSubscriptionService.createNewSubscription(this.inputData.uidReport); } finally { this.busy.hide(over); } - if (!this.subscription) { - return; - } this.subscription.subscription.ExportFormat.value = 'PDF'; @@ -122,7 +117,7 @@ export class ReportButtonComponent implements OnInit, OnDestroy { title: await this.translator.get('#LDS#Heading Specify Parameters').toPromise(), subTitle: await this.translator.get(this.inputData.caption).toPromise(), padding: '0px', - width: 'max(600px,60%)', + width: calculateSidesheetWidth(), testId: 'report-button-view-parameter-sidesheet', data: { subscription: this.subscription }, }) @@ -138,7 +133,13 @@ export class ReportButtonComponent implements OnInit, OnDestroy { const def = new MethodDefinition(this.apiMethodFactory.portal_subscription_interactive_report_get(parameters.entityid, parameters)); // not pretty, but the download directive does not support dynamic URLs - const directive = new EuiDownloadDirective(null /* no element */, this.http, this.overlay, this.injector); + const directive = new EuiDownloadDirective( + new ElementRef('') /* no element */, + this.http, + this.overlay, + this.injector, + this.downloadService, + ); directive.downloadOptions = { ...this.elementalUiConfigService.Config.downloadOptions, fileMimeType: '', // override elementalUiConfigService; get mime type from server diff --git a/imxweb/projects/rps/src/lib/report-button/report-button.module.ts b/imxweb/projects/rps/src/lib/report-button/report-button.module.ts index 5ed0be136..f8066c055 100644 --- a/imxweb/projects/rps/src/lib/report-button/report-button.module.ts +++ b/imxweb/projects/rps/src/lib/report-button/report-button.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,16 +31,17 @@ import { EuiCoreModule } from '@elemental-ui/core'; import { TranslateModule } from '@ngx-translate/core'; import { ReportButtonComponent } from './report-button.component'; +import { ReportButtonMailComponent } from './report-button-mail.component'; import { ParameterSidesheetComponent } from './parameter-sidesheet/parameter-sidesheet.component'; import { ReactiveFormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; -import { CdrModule} from 'qbm'; +import { CdrModule } from 'qbm'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; @NgModule({ - declarations: [ReportButtonComponent, ParameterSidesheetComponent], + declarations: [ReportButtonComponent, ReportButtonMailComponent, ParameterSidesheetComponent], imports: [ CommonModule, CdrModule, @@ -50,8 +51,8 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; MatCardModule, EuiCoreModule, TranslateModule, - ReactiveFormsModule + ReactiveFormsModule, ], - exports: [ReportButtonComponent] + exports: [ReportButtonComponent], }) -export class ReportButtonModule { } +export class ReportButtonModule {} diff --git a/imxweb/projects/rps/src/lib/reports/edit-report-sidesheet/edit-report-sidesheet.component.html b/imxweb/projects/rps/src/lib/reports/edit-report-sidesheet/edit-report-sidesheet.component.html index 3ef509ae3..7d650c45b 100644 --- a/imxweb/projects/rps/src/lib/reports/edit-report-sidesheet/edit-report-sidesheet.component.html +++ b/imxweb/projects/rps/src/lib/reports/edit-report-sidesheet/edit-report-sidesheet.component.html @@ -17,23 +17,35 @@ - + {{ ldsUnsupportedExpression | translate }} {{ '#LDS#Conditions' | translate }}* - + {{ ldsAllRowsInfoText | translate }} - +
    -
    diff --git a/imxweb/projects/rps/src/lib/reports/edit-report-sidesheet/edit-report-sidesheet.component.scss b/imxweb/projects/rps/src/lib/reports/edit-report-sidesheet/edit-report-sidesheet.component.scss index 44349dc24..e69de29bb 100644 --- a/imxweb/projects/rps/src/lib/reports/edit-report-sidesheet/edit-report-sidesheet.component.scss +++ b/imxweb/projects/rps/src/lib/reports/edit-report-sidesheet/edit-report-sidesheet.component.scss @@ -1,25 +0,0 @@ -.eui-sidesheet-content { - display: flex; - flex-direction: column; -} - -:host { - .eui-sidesheet-actions { - .justify-start { - margin-right: auto; - } - - button:not(:last-of-type):not(.justify-start) { - margin-right: 10px; - } - } -} - -eui-alert { - display: block; - margin-bottom: 1em; -} - -mat-card { - margin-bottom: 1em; -} \ No newline at end of file diff --git a/imxweb/projects/rps/src/lib/reports/edit-report-sidesheet/edit-report-sidesheet.component.ts b/imxweb/projects/rps/src/lib/reports/edit-report-sidesheet/edit-report-sidesheet.component.ts index 38c0409fc..83f3c9c8a 100644 --- a/imxweb/projects/rps/src/lib/reports/edit-report-sidesheet/edit-report-sidesheet.component.ts +++ b/imxweb/projects/rps/src/lib/reports/edit-report-sidesheet/edit-report-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,28 +26,28 @@ import { ChangeDetectorRef, Component, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { AbstractControl, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { EuiLoadingService, EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { EUI_SIDESHEET_DATA, EuiLoadingService, EuiSidesheetRef } from '@elemental-ui/core'; import _ from 'lodash'; -import { ListReportDefinitionDto, ListReportDefinitionRead, PortalReportsEdit } from 'imx-api-rps'; +import { ListReportDefinitionDto, ListReportDefinitionRead, PortalReportsEdit } from '@imx-modules/imx-api-rps'; import { ExtendedTypedEntityCollection, - SqlWizardExpression, - isExpressionInvalid, - ValType, FkProviderItem, SqlExpression, -} from 'imx-qbm-dbts'; + SqlWizardExpression, + ValType, + isExpressionInvalid, +} from '@imx-modules/imx-qbm-dbts'; import { BaseCdr, + BaseReadonlyCdr, + CdrFactoryService, ColumnDependentReference, ConfirmationService, EntityService, SnackBarService, SqlWizardComponent, - CdrFactoryService, - BaseReadonlyCdr, } from 'qbm'; import { ProjectConfigurationService } from 'qer'; import { Subscription } from 'rxjs'; @@ -65,9 +65,6 @@ export class EditReportSidesheetComponent implements OnInit, OnDestroy { public cdrList: ColumnDependentReference[] = []; public readonly detailsFormGroup: UntypedFormGroup; - public get sqlExpression(): SqlWizardExpression { - return this.definition?.Data; - } public definition: ListReportDefinitionDto; public report: PortalReportsEdit; @@ -83,9 +80,14 @@ export class EditReportSidesheetComponent implements OnInit, OnDestroy { ); } + public get isCondition(): boolean { + return !!this.sqlExpression?.Expression?.Expressions && this.sqlExpression?.Expression?.Expressions?.length < 1; + } + private closeSubscription: Subscription; - public lastSavedExpression: SqlExpression; + public sqlExpression: SqlWizardExpression; + public lastSavedExpression: SqlExpression | undefined; public exprHasntChanged = true; public checkChanges(): void { @@ -111,7 +113,7 @@ export class EditReportSidesheetComponent implements OnInit, OnDestroy { private readonly cdref: ChangeDetectorRef, private readonly config: ProjectConfigurationService, private readonly cdrFactoryService: CdrFactoryService, - confirmation: ConfirmationService + confirmation: ConfirmationService, ) { this.detailsFormGroup = new UntypedFormGroup({ formArray: formBuilder.array([]) }); @@ -125,7 +127,12 @@ export class EditReportSidesheetComponent implements OnInit, OnDestroy { // Save a local copy of the definition object. The entity will change with every update // of the interactive entity that comes back from the server - this.definition = this.report.extendedDataRead?.Definition[0]; + if (!!this.report.extendedDataRead?.Definition) { + this.definition = this.report.extendedDataRead.Definition[0]; + } + if (this.definition?.Data) { + this.sqlExpression = this.definition.Data; + } this.lastSavedExpression = _.cloneDeep(this.sqlExpression?.Expression); } @@ -135,15 +142,17 @@ export class EditReportSidesheetComponent implements OnInit, OnDestroy { this.cdrList = this.cdrFactoryService.buildCdrFromColumnList( this.report.GetEntity(), - c.OwnershipConfig.EditableFields[this.report.GetEntity().TypeName], - this.data.isReadonly + c?.OwnershipConfig?.EditableFields?.[this.report.GetEntity().TypeName] ?? [], + this.data.isReadonly, ); if (this.definition) { // is it a list report? this.cdrList.push(await this.buildTableCdr()); } - this.cdrList.push(this.data.isReadonly ? new BaseReadonlyCdr(this.report.AvailableTo.Column): new BaseCdr(this.report.AvailableTo.Column)); + this.cdrList.push( + this.data.isReadonly ? new BaseReadonlyCdr(this.report.AvailableTo.Column) : new BaseCdr(this.report.AvailableTo.Column), + ); } public addCdr(control: AbstractControl): void { @@ -170,7 +179,7 @@ export class EditReportSidesheetComponent implements OnInit, OnDestroy { { SelectedColumns: this.definition.SelectedColumns, Data: { - Filters: [this.definition.Data.Expression], + Filters: this.definition.Data?.Expression ? [this.definition.Data?.Expression] : [], }, }, ], @@ -191,7 +200,7 @@ export class EditReportSidesheetComponent implements OnInit, OnDestroy { return false; } // not a list report? - if (this.definition.SelectedColumns.filter((elem) => elem != null && elem !== '').length < 1) { + if (!this.definition.SelectedColumns || this.definition.SelectedColumns?.filter((elem) => !!elem).length === 0) { return true; } // must select at least one column @@ -204,11 +213,14 @@ export class EditReportSidesheetComponent implements OnInit, OnDestroy { return isExpressionInvalid(this.sqlExpression) || !this.hasValuesSet(this.sqlExpression.Expression); } - private hasValuesSet(sqlExpression: SqlExpression, checkCurrent: boolean = false): boolean { + private hasValuesSet(sqlExpression: SqlExpression | undefined, checkCurrent: boolean = false): boolean { + if (!sqlExpression) { + return false; + } const current = !checkCurrent || sqlExpression.Value != null; - if (sqlExpression.Expressions?.length > 0) { - return current && sqlExpression.Expressions.every((elem) => this.hasValuesSet(elem, true)); + if (sqlExpression.Expressions) { + return current && sqlExpression.Expressions?.every((elem) => this.hasValuesSet(elem, true)); } return current; @@ -239,7 +251,7 @@ export class EditReportSidesheetComponent implements OnInit, OnDestroy { ValidReferencedTables: [{ TableName: fkProviderItem.fkTableName }], MinLen: 1, }, - [fkProviderItem] + [fkProviderItem], ); await tableCol.PutValueStruct({ DataValue: this.definition.TableName, @@ -254,7 +266,12 @@ export class EditReportSidesheetComponent implements OnInit, OnDestroy { }, ], }); - this.definition = this.report.extendedDataRead?.Definition[0]; + if (!!this.report.extendedDataRead?.Definition) { + this.definition = this.report.extendedDataRead.Definition[0]; + } + if (this.definition.Data) { + this.sqlExpression = this.definition.Data; + } this.cdref.detectChanges(); }); diff --git a/imxweb/projects/rps/src/lib/reports/edit-report.component.html b/imxweb/projects/rps/src/lib/reports/edit-report.component.html index fda46275a..df8487d80 100644 --- a/imxweb/projects/rps/src/lib/reports/edit-report.component.html +++ b/imxweb/projects/rps/src/lib/reports/edit-report.component.html @@ -1,55 +1,33 @@
    -
    -

    +
    +

    {{ '#LDS#Heading Reports' | translate }} -

    +

    +
    - -
    - - - - - -
    {{ item.GetEntity().GetDisplay() }}
    -
    {{ item.Description.Column.GetDisplayValue() }}
    -
    -
    -
    - -
    + + + +
    + + + + -
    - -
    +
    + - diff --git a/imxweb/projects/rps/src/lib/reports/edit-report.component.scss b/imxweb/projects/rps/src/lib/reports/edit-report.component.scss index 966a40141..f643adde8 100644 --- a/imxweb/projects/rps/src/lib/reports/edit-report.component.scss +++ b/imxweb/projects/rps/src/lib/reports/edit-report.component.scss @@ -1,80 +1,24 @@ -@import '../../../../../shared/scss/common-table.scss'; -@mixin imx-flex-column-container { - display: flex; - flex-direction: column; -} - -@mixin imx-flex-row-container { - display: flex; - flex-direction: row; -} - -.eui-sidesheet-content { - @include imx-flex-column-container(); -} +@import 'base/mixins'; .heading-wrapper { - @include imx-flex-row-container(); + @include flex-row-container(); flex: 0 0 auto; - - .helper-alert { - display: flex; - margin-bottom: 15px; - } - - .alert-wrapper { - margin: 0 0 0 auto; - align-self: flex-end; - width: 50%; - } } :host { - @include imx-flex-column-container(); - overflow: hidden; - height: 100%; - max-width: 100%; -} - -.imx-content-card { - @include imx-flex-fill-control-hidden-overflow(); - margin: 3px; + @include flex-column-container($overflow: hidden, $height: 100%, $max-width: 100%); } .imx-reports-page { + @include flex-column-container($overflow: hidden); background-color: inherit; - @include imx-flex-column-container(); - overflow: hidden; - display: flex; - flex-direction: column; flex: 1 1 auto; - - div.imx-table-container { - @include imx-flex-column-container(); - overflow: hidden; - height: inherit; - flex: 1 1 auto; - - .imx-reports-table { - flex-grow: 1; - overflow: auto; - } - } - - .imx-button-bar { - @include imx-button-bar(); - } } @media screen and (max-width: 768px) { .imx-reports-page { .heading-wrapper { display: block; - - .alert-wrapper { - margin: 0 0 20px 0; - width: 100%; - } } } } diff --git a/imxweb/projects/rps/src/lib/reports/edit-report.component.ts b/imxweb/projects/rps/src/lib/reports/edit-report.component.ts index b6dce5d9d..85923ca2c 100644 --- a/imxweb/projects/rps/src/lib/reports/edit-report.component.ts +++ b/imxweb/projects/rps/src/lib/reports/edit-report.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,45 +24,47 @@ * */ -import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { ListReportDefinitionRead, PortalReports, PortalReportsEdit } from 'imx-api-rps'; -import { CollectionLoadParameters, DisplayColumns, ExtendedTypedEntityCollection } from 'imx-qbm-dbts'; +import { ListReportDefinitionRead, PortalReports, PortalReportsEdit } from '@imx-modules/imx-api-rps'; +import { + CollectionLoadParameters, + DisplayColumns, + ExtendedTypedEntityCollection, + IClientProperty, + StaticSchema, + TypedEntityCollectionData, +} from '@imx-modules/imx-qbm-dbts'; import { BusyService, + calculateSidesheetWidth, ConfirmationService, - DataSourceToolbarSettings, - DataSourceWrapper, - DataTableComponent, - SnackBarService, + DataViewInitParameters, + DataViewSource, + HELP_CONTEXTUAL, HelpContextualComponent, HelpContextualService, - HELP_CONTEXTUAL + SnackBarService, } from 'qbm'; -import { Subscription } from 'rxjs'; +import { RpsPermissionsService } from '../admin/rps-permissions.service'; import { EditReportSidesheetComponent } from './edit-report-sidesheet/edit-report-sidesheet.component'; import { EditReportService } from './edit-report.service'; -import { QerPermissionsService } from 'qer'; -import { RpsPermissionsService } from '../admin/rps-permissions.service'; @Component({ templateUrl: './edit-report.component.html', styleUrls: ['./edit-report.component.scss'], + providers: [DataViewSource], }) -export class EditReportComponent implements OnInit, OnDestroy { +export class EditReportComponent implements OnInit { public readonly DisplayColumns = DisplayColumns; - public dstWrapper: DataSourceWrapper; - @ViewChild('dataTable') private reportsTable: DataTableComponent; - public dstSettings: DataSourceToolbarSettings; public selectedReports: PortalReports[] = []; - + public displayedColumns: IClientProperty[]; public busyService = new BusyService(); - public entitySchema = this.reportService.reportSchema; - - private readonly subscriptions: Subscription[] = []; + public entitySchema: StaticSchema; + private isRpsAdmin: boolean; constructor( private readonly reportService: EditReportService, @@ -72,34 +74,30 @@ export class EditReportComponent implements OnInit, OnDestroy { private readonly confirmationService: ConfirmationService, private readonly rpsPermissionService: RpsPermissionsService, private readonly snackBarService: SnackBarService, - private readonly helpContextualService: HelpContextualService - ) {} + private readonly helpContextualService: HelpContextualService, + public dataSource: DataViewSource, + ) { + this.entitySchema = this.reportService.reportSchema; + } public async ngOnInit(): Promise { - const isRpsAdmin = await this.rpsPermissionService.isRpsAdmin(); - this.dstWrapper = new DataSourceWrapper( - (state) => (isRpsAdmin ? this.reportService.getAllReports(state) : this.reportService.getReportsOwnedByUser(state)), - [this.entitySchema.Columns[DisplayColumns.DISPLAY_PROPERTYNAME]], - this.entitySchema - ); - - await this.getData(); + this.isRpsAdmin = await this.rpsPermissionService.isRpsAdmin(); + this.displayedColumns = [this.entitySchema.Columns[DisplayColumns.DISPLAY_PROPERTYNAME]]; + this.getData(); } - public ngOnDestroy(): void { - this.subscriptions.forEach((s) => s.unsubscribe()); - } - - public async getData(parameter?: CollectionLoadParameters): Promise { - const isBusy = this.busyService.beginBusy(); - try { - const parameters = { - ...parameter, - }; - this.dstSettings = await this.dstWrapper.getDstSettings(parameters); - } finally { - isBusy.endBusy(); - } + public getData(): void { + const dataViewInitParameters: DataViewInitParameters = { + execute: (params: CollectionLoadParameters, signal: AbortSignal): Promise> => + this.isRpsAdmin ? this.reportService.getAllReports(params, signal) : this.reportService.getReportsOwnedByUser(params, signal), + schema: this.entitySchema, + columnsToDisplay: this.displayedColumns, + highlightEntity: (entity: PortalReports) => { + this.viewDetails(entity); + }, + selectionChange: (selection: PortalReports[]) => this.onSelectionChanged(selection), + }; + this.dataSource.init(dataViewInitParameters); } public onSelectionChanged(items: PortalReports[]): void { @@ -116,29 +114,25 @@ export class EditReportComponent implements OnInit, OnDestroy { } if (report) { - await this.openSidesheet(report, true,false); + await this.openSidesheet(report, true, false); } } public async viewDetails(selectedReport: PortalReports): Promise { const overlay = this.busy.show(); - let report; - try { - report = await this.reportService.getReport(selectedReport.GetEntity().GetKeys()[0]); - } finally { - this.busy.hide(overlay); - } - + const entity = selectedReport.GetEntity(); + const report = await this.reportService.getReport(entity.GetKeys()[0]); + this.busy.hide(overlay); if (report) { - await this.openSidesheet(report, false,selectedReport.IsOob.value); + await this.openSidesheet(report, false, entity.GetColumn('IsOob').GetValue()); } } private async openSidesheet( report: ExtendedTypedEntityCollection, isNew: boolean, - isReadonly: boolean + isReadonly: boolean, ): Promise { this.helpContextualService.setHelpContextId(isNew ? HELP_CONTEXTUAL.ReportsCreate : HELP_CONTEXTUAL.ReportsEdit); const result = await this.sidesheet @@ -148,20 +142,20 @@ export class EditReportComponent implements OnInit, OnDestroy { panelClass: 'imx-sidesheet', disableClose: true, padding: '0', - width: 'max(768px, 80%)', + width: calculateSidesheetWidth(1100, 0.7), testId: isNew ? 'report-create-sidesheet' : 'report-details-sidesheet', data: { report, isNew, - isReadonly + isReadonly, }, - headerComponent: HelpContextualComponent + headerComponent: HelpContextualComponent, }) .afterClosed() .toPromise(); if (result) { - this.getData(); + this.dataSource.updateState(); } } @@ -184,7 +178,8 @@ export class EditReportComponent implements OnInit, OnDestroy { } this.snackBarService.open({ key: '#LDS#The reports have been successfully deleted.' }, '#LDS#Close'); - this.reportsTable.clearSelection(); + this.dataSource.selection.clear(); + this.dataSource.updateState(); await this.getData(); } finally { this.busy.hide(overlay); diff --git a/imxweb/projects/rps/src/lib/reports/edit-report.module.ts b/imxweb/projects/rps/src/lib/reports/edit-report.module.ts index d25f788a8..aae730e69 100644 --- a/imxweb/projects/rps/src/lib/reports/edit-report.module.ts +++ b/imxweb/projects/rps/src/lib/reports/edit-report.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,40 +27,39 @@ import { DragDropModule } from '@angular/cdk/drag-drop'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; -import { MatCardModule } from '@angular/material/card'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatCardModule } from '@angular/material/card'; import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; import { TranslateModule } from '@ngx-translate/core'; +import { MatTableModule } from '@angular/material/table'; import { CdrModule, DataSourceToolbarModule, DataTableModule, + DataViewModule, HelpContextualModule, MenuItem, MenuService, OrderedListModule, SelectedElementsModule, SqlWizardApiService, - SqlWizardModule + SqlWizardModule, } from 'qbm'; -import { EditReportComponent } from './edit-report.component'; +import { RpsPermissionsService } from '../admin/rps-permissions.service'; import { EditReportSidesheetComponent } from './edit-report-sidesheet/edit-report-sidesheet.component'; +import { EditReportComponent } from './edit-report.component'; import { EditReportSqlWizardService } from './editreport-sqlwizard.service'; -import { RpsPermissionsService } from '../admin/rps-permissions.service'; @NgModule({ - declarations: [ - EditReportComponent, - EditReportSidesheetComponent - ], + declarations: [EditReportComponent, EditReportSidesheetComponent], providers: [ { // This does not work for some reason! provide: SqlWizardApiService, - useClass: EditReportSqlWizardService + useClass: EditReportSqlWizardService, }, - RpsPermissionsService + RpsPermissionsService, ], imports: [ CdrModule, @@ -78,48 +77,39 @@ import { RpsPermissionsService } from '../admin/rps-permissions.service'; SqlWizardModule, SelectedElementsModule, HelpContextualModule, - ] + MatTableModule, + DataViewModule, + ], }) export class EditReportModule { - - constructor( - private readonly menuService: MenuService, - ) { + constructor(private readonly menuService: MenuService) { this.setupMenu(); } private setupMenu(): void { - this.menuService.addMenuFactories( - (preProps: string[], features: string[]) => { - - const items: MenuItem[] = []; + this.menuService.addMenuFactories((preProps: string[], features: string[]) => { + const items: MenuItem[] = []; - if (preProps.includes('REPORT_SUBSCRIPTION')) { - items.push( - { - id: 'RPS_Reports', - navigationCommands: { - commands: ['reports'] - }, - title: '#LDS#Menu Entry Reports', - sorting: '60-70', - }, - ); - } + if (preProps.includes('REPORT_SUBSCRIPTION')) { + items.push({ + id: 'RPS_Reports', + navigationCommands: { + commands: ['reports'], + }, + title: '#LDS#Menu Entry Reports', + sorting: '60-70', + }); + } - if (items.length === 0) { - return null; - } - return { - id: 'ROOT_Setup', - title: '#LDS#Setup', - sorting: '60', - items - }; - }, - ); + if (items.length === 0) { + return undefined; + } + return { + id: 'ROOT_Setup', + title: '#LDS#Setup', + sorting: '60', + items, + }; + }); } - - - } diff --git a/imxweb/projects/rps/src/lib/reports/edit-report.service.ts b/imxweb/projects/rps/src/lib/reports/edit-report.service.ts index e0b130205..c72dc523e 100644 --- a/imxweb/projects/rps/src/lib/reports/edit-report.service.ts +++ b/imxweb/projects/rps/src/lib/reports/edit-report.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,21 +26,18 @@ import { Injectable } from '@angular/core'; -import { ListReportDefinitionRead, PortalReports, PortalReportsEditInteractive } from 'imx-api-rps'; -import { CollectionLoadParameters, ExtendedTypedEntityCollection } from 'imx-qbm-dbts'; +import { ListReportDefinitionRead, PortalReports, PortalReportsEdit, PortalReportsEditInteractive } from '@imx-modules/imx-api-rps'; +import { CollectionLoadParameters, ExtendedTypedEntityCollection } from '@imx-modules/imx-qbm-dbts'; import { RpsApiService } from '../rps-api-client.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class EditReportService { + constructor(private readonly api: RpsApiService) {} - constructor( - private readonly api: RpsApiService, - ) { } - - public reportSchema = PortalReportsEditInteractive.GetEntitySchema(); + public reportSchema = PortalReportsEdit.GetEntitySchema(); public async getReport(id: string): Promise> { return await this.api.typedClient.PortalReportsEditInteractive.Get_byid(id); @@ -50,14 +47,18 @@ export class EditReportService { return this.api.typedClient.PortalReportsEditInteractive.Get(); } - public async getReportsOwnedByUser(navigationState?: CollectionLoadParameters): - Promise> { - return this.api.typedClient.PortalReports.Get({ owned: true, ...navigationState }); + public async getReportsOwnedByUser( + navigationState?: CollectionLoadParameters, + signal?: AbortSignal, + ): Promise> { + return this.api.typedClient.PortalReports.Get({ owned: true, ...navigationState }, { signal }); } - public async getAllReports(navigationState?: CollectionLoadParameters): - Promise> { - return this.api.typedClient.PortalReports.Get(navigationState); + public async getAllReports( + navigationState?: CollectionLoadParameters, + signal?: AbortSignal, + ): Promise> { + return this.api.typedClient.PortalReports.Get(navigationState, { signal }); } public async deleteReport(report: PortalReports) { diff --git a/imxweb/projects/rps/src/lib/reports/editreport-sqlwizard.service.ts b/imxweb/projects/rps/src/lib/reports/editreport-sqlwizard.service.ts index 95b327525..31433d2e7 100644 --- a/imxweb/projects/rps/src/lib/reports/editreport-sqlwizard.service.ts +++ b/imxweb/projects/rps/src/lib/reports/editreport-sqlwizard.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,20 +24,20 @@ * */ -import { Injectable } from "@angular/core"; -import { CollectionLoadParameters, EntityCollectionData, FilterProperty } from "imx-qbm-dbts"; -import { SqlWizardApiService } from "qbm"; -import { RpsApiService } from "../rps-api-client.service"; +import { Injectable } from '@angular/core'; +import { CollectionLoadParameters, EntityCollectionData, FilterProperty } from '@imx-modules/imx-qbm-dbts'; +import { SqlWizardApiService } from 'qbm'; +import { RpsApiService } from '../rps-api-client.service'; @Injectable({ providedIn: 'root' }) export class EditReportSqlWizardService implements SqlWizardApiService { - constructor(private readonly api: RpsApiService) { } + constructor(private readonly api: RpsApiService) {} public async getFilterProperties(table: string): Promise { - return (await this.api.client.portal_reports_sqlwizard_tables_columns_get(table)).Properties; + return (await this.api.client.portal_reports_sqlwizard_tables_columns_get(table)).Properties ?? []; } public async getCandidates(parentTable: string, options?: CollectionLoadParameters): Promise { return await this.api.client.portal_reports_sqlwizard_candidates_get(parentTable, options); } -} \ No newline at end of file +} diff --git a/imxweb/projects/rps/src/lib/rps-api-client.service.ts b/imxweb/projects/rps/src/lib/rps-api-client.service.ts index 95f2ac6e9..c03eaaff0 100644 --- a/imxweb/projects/rps/src/lib/rps-api-client.service.ts +++ b/imxweb/projects/rps/src/lib/rps-api-client.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,12 +25,12 @@ */ import { Injectable } from '@angular/core'; -import { V2Client, TypedClient } from 'imx-api-rps'; -import { ApiClient } from 'imx-qbm-dbts'; +import { V2Client, TypedClient } from '@imx-modules/imx-api-rps'; +import { ApiClient } from '@imx-modules/imx-qbm-dbts'; import { AppConfigService, ClassloggerService, ImxTranslationProviderService } from 'qbm'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class RpsApiService { private tc: TypedClient; @@ -50,7 +50,8 @@ export class RpsApiService { constructor( private readonly config: AppConfigService, private readonly logger: ClassloggerService, - private readonly translationProvider: ImxTranslationProviderService) { + private readonly translationProvider: ImxTranslationProviderService, + ) { try { this.logger.debug(this, 'Initializing RPS API service'); diff --git a/imxweb/projects/rps/src/lib/rps-config.module.ts b/imxweb/projects/rps/src/lib/rps-config.module.ts index 36c9c18d9..9180c8f4e 100644 --- a/imxweb/projects/rps/src/lib/rps-config.module.ts +++ b/imxweb/projects/rps/src/lib/rps-config.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -33,7 +33,7 @@ import { SubscriptionsModule } from './subscriptions/subscriptions.module'; import { EditReportComponent } from './reports/edit-report.component'; import { EditReportModule } from './reports/edit-report.module'; import { ReportButtonModule } from './report-button/report-button.module'; -import {StatisticReportButtonModule} from './statistic-report-button/statistic-report-button.module'; +import { StatisticReportButtonModule } from './statistic-report-button/statistic-report-button.module'; const routes: Routes = [ { @@ -41,20 +41,15 @@ const routes: Routes = [ component: EditReportComponent, canActivate: [RouteGuardService], resolve: [RouteGuardService], - data:{ - contextId: HELP_CONTEXTUAL.Reports - } - } + data: { + contextId: HELP_CONTEXTUAL.Reports, + }, + }, ]; @NgModule({ - imports: [ - EditReportModule, - SubscriptionsModule, - ReportButtonModule, - StatisticReportButtonModule, - RouterModule.forChild(routes) - ] + imports: [EditReportModule, SubscriptionsModule, ReportButtonModule, StatisticReportButtonModule, RouterModule.forChild(routes)], + declarations: [], }) export class RpsConfigModule { constructor(private readonly initializer: InitService) { diff --git a/imxweb/projects/rps/src/lib/statistic-report-button/statistic-report-button.component.html b/imxweb/projects/rps/src/lib/statistic-report-button/statistic-report-button.component.html index 33c854d0f..ac74e248b 100644 --- a/imxweb/projects/rps/src/lib/statistic-report-button/statistic-report-button.component.html +++ b/imxweb/projects/rps/src/lib/statistic-report-button/statistic-report-button.component.html @@ -1,4 +1,4 @@ - \ No newline at end of file + #LDS#View source data + diff --git a/imxweb/projects/rps/src/lib/statistic-report-button/statistic-report-button.component.scss b/imxweb/projects/rps/src/lib/statistic-report-button/statistic-report-button.component.scss index 84f1418af..2a8491b97 100644 --- a/imxweb/projects/rps/src/lib/statistic-report-button/statistic-report-button.component.scss +++ b/imxweb/projects/rps/src/lib/statistic-report-button/statistic-report-button.component.scss @@ -1,20 +1,12 @@ - -@mixin Ease-Transition($duration: .4s) { - transition: all $duration ease; -} - -@mixin EUI-Elevation-1 { - box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.14), 0px 2px 1px rgba(0, 0, 0, 0.12), 0px 1px 3px rgba(0, 0, 0, 0.2); -} +@import 'base/mixins'; :host { display: flex; flex-direction: row; - + button { display: flex; - @include EUI-Elevation-1; - @include Ease-Transition; + @include ease-transition; eui-icon { margin-right: 4px; } diff --git a/imxweb/projects/rps/src/lib/statistic-report-button/statistic-report-button.component.ts b/imxweb/projects/rps/src/lib/statistic-report-button/statistic-report-button.component.ts index dca2a4ac9..d3a4702e2 100644 --- a/imxweb/projects/rps/src/lib/statistic-report-button/statistic-report-button.component.ts +++ b/imxweb/projects/rps/src/lib/statistic-report-button/statistic-report-button.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,9 +28,10 @@ import { Component } from '@angular/core'; import { EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { StatisticReportButtonService } from './statistic-report-button.service'; -import { ListReportViewerSidesheetComponent } from '../subscriptions/list-report-viewer-sidesheet/list-report-viewer-sidesheet.component'; +import { calculateSidesheetWidth } from 'qbm'; import { ChartInfoTyped } from 'qer'; +import { ListReportViewerSidesheetComponent } from '../subscriptions/list-report-viewer-sidesheet/list-report-viewer-sidesheet.component'; +import { StatisticReportButtonService } from './statistic-report-button.service'; @Component({ selector: 'imx-statistic-report-button', @@ -43,7 +44,7 @@ export class StatisticReportButtonComponent { constructor( private readonly statisticService: StatisticReportButtonService, private readonly sideSheet: EuiSidesheetService, - private readonly translate: TranslateService + private readonly translate: TranslateService, ) {} /** @@ -51,12 +52,12 @@ export class StatisticReportButtonComponent { */ public viewReport(): void { this.statisticService.setIdStatistic(this.referrer.Id.value); - const data = { dataService: this.statisticService}; + const data = { dataService: this.statisticService }; this.sideSheet.open(ListReportViewerSidesheetComponent, { - title: this.translate.instant('#LDS#Heading View Report'), + title: this.translate.instant('#LDS#Heading View Source Data'), subTitle: this.referrer.GetDisplay(), padding: '0', - width: 'max(550px,55%)', + width: calculateSidesheetWidth(), testId: 'statistic-report-button-report-viewer', data, }); diff --git a/imxweb/projects/rps/src/lib/statistic-report-button/statistic-report-button.module.ts b/imxweb/projects/rps/src/lib/statistic-report-button/statistic-report-button.module.ts index 5546fdc57..e5215fe9c 100644 --- a/imxweb/projects/rps/src/lib/statistic-report-button/statistic-report-button.module.ts +++ b/imxweb/projects/rps/src/lib/statistic-report-button/statistic-report-button.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,13 +27,12 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import {StatisticReportButtonComponent} from './statistic-report-button.component'; +import { StatisticReportButtonComponent } from './statistic-report-button.component'; import { MatButtonModule } from '@angular/material/button'; import { TranslateModule } from '@ngx-translate/core'; import { EuiCoreModule } from '@elemental-ui/core'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; - @NgModule({ declarations: [StatisticReportButtonComponent], imports: [CommonModule, MatButtonModule, TranslateModule, EuiCoreModule, MatProgressSpinnerModule], diff --git a/imxweb/projects/rps/src/lib/statistic-report-button/statistic-report-button.service.ts b/imxweb/projects/rps/src/lib/statistic-report-button/statistic-report-button.service.ts index c49927778..2f5b01d0b 100644 --- a/imxweb/projects/rps/src/lib/statistic-report-button/statistic-report-button.service.ts +++ b/imxweb/projects/rps/src/lib/statistic-report-button/statistic-report-button.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,8 +26,8 @@ import { Injectable } from '@angular/core'; -import { CollectionLoadParameters, DataModel, EntitySchema, ExtendedTypedEntityCollection, GroupInfoData } from 'imx-qbm-dbts'; -import { ListReportContentData, PortalReportData } from 'imx-api-rps'; +import { CollectionLoadParameters, DataModel, EntitySchema, ExtendedTypedEntityCollection, GroupInfoData } from '@imx-modules/imx-qbm-dbts'; +import { ListReportContentData, PortalReportData } from '@imx-modules/imx-api-rps'; import { RpsApiService } from '../rps-api-client.service'; import { ListReportDataProvider } from '../list-report-viewer/list-report-data-provider.interface'; diff --git a/imxweb/projects/rps/src/lib/subscriptions/list-report-viewer-sidesheet/list-report-viewer-sidesheet.component.html b/imxweb/projects/rps/src/lib/subscriptions/list-report-viewer-sidesheet/list-report-viewer-sidesheet.component.html index c2c23038f..01171b347 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/list-report-viewer-sidesheet/list-report-viewer-sidesheet.component.html +++ b/imxweb/projects/rps/src/lib/subscriptions/list-report-viewer-sidesheet/list-report-viewer-sidesheet.component.html @@ -1,8 +1,8 @@
    - +
    - -
    \ No newline at end of file +
    diff --git a/imxweb/projects/rps/src/lib/subscriptions/list-report-viewer-sidesheet/list-report-viewer-sidesheet.component.scss b/imxweb/projects/rps/src/lib/subscriptions/list-report-viewer-sidesheet/list-report-viewer-sidesheet.component.scss index 1d7a151a2..005b37b1d 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/list-report-viewer-sidesheet/list-report-viewer-sidesheet.component.scss +++ b/imxweb/projects/rps/src/lib/subscriptions/list-report-viewer-sidesheet/list-report-viewer-sidesheet.component.scss @@ -1,4 +1,4 @@ -:host{ +:host { flex: 1 1 auto; display: flex; flex-direction: column; @@ -8,4 +8,4 @@ display: flex; flex-direction: column; } -} \ No newline at end of file +} diff --git a/imxweb/projects/rps/src/lib/subscriptions/list-report-viewer-sidesheet/list-report-viewer-sidesheet.component.ts b/imxweb/projects/rps/src/lib/subscriptions/list-report-viewer-sidesheet/list-report-viewer-sidesheet.component.ts index d4a6722f7..e81a7b4f4 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/list-report-viewer-sidesheet/list-report-viewer-sidesheet.component.ts +++ b/imxweb/projects/rps/src/lib/subscriptions/list-report-viewer-sidesheet/list-report-viewer-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,9 +26,9 @@ import { Component, Inject } from '@angular/core'; import { EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { ListReportDataProvider } from '../../list-report-viewer/list-report-data-provider.interface'; import { ReportSubscription } from '../report-subscription/report-subscription'; import { ReportSubscriptionService } from '../report-subscription/report-subscription.service'; -import { ListReportDataProvider } from '../../list-report-viewer/list-report-data-provider.interface'; /** * Represents the content of a side sheet, that shows the data of a list report @@ -50,7 +50,7 @@ export class ListReportViewerSidesheetComponent { constructor( @Inject(EUI_SIDESHEET_DATA) public readonly data: { dataService: ListReportDataProvider; subscription?: ReportSubscription }, - private readonly reportSubscriptionService: ReportSubscriptionService + private readonly reportSubscriptionService: ReportSubscriptionService, ) { if (data.subscription) { this.reportParameter = data.subscription.getParameterDictionary(); @@ -58,9 +58,11 @@ export class ListReportViewerSidesheetComponent { } /** - * Downloads the subscription + * Downloads the subscription */ public async downloadReport(): Promise { - this.reportSubscriptionService.downloadSubsciption(this.data.subscription); + if (this.data.subscription) { + this.reportSubscriptionService.downloadSubsciption(this.data.subscription); + } } } diff --git a/imxweb/projects/rps/src/lib/subscriptions/report-subscription/report-parameter-wrapper.ts b/imxweb/projects/rps/src/lib/subscriptions/report-subscription/report-parameter-wrapper.ts index 68a9dc1c9..8403c9ed8 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/report-subscription/report-parameter-wrapper.ts +++ b/imxweb/projects/rps/src/lib/subscriptions/report-subscription/report-parameter-wrapper.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,10 +24,16 @@ * */ -import { EntityWriteDataColumn, IFkCandidateProvider, IEntityColumn, ParameterData, ReadWriteExtTypedEntity } from 'imx-qbm-dbts'; -import { ParameterContainer } from 'qer'; -import { ClassloggerService, ImxTranslationProviderService } from 'qbm'; import { EventEmitter } from '@angular/core'; +import { + EntityWriteDataColumn, + IEntityColumn, + IFkCandidateProvider, + ParameterData, + ReadWriteExtTypedEntity, +} from '@imx-modules/imx-qbm-dbts'; +import { ClassloggerService, ImxTranslationProviderService } from 'qbm'; +import { ParameterContainer } from 'qer'; export class ReportParameterWrapper { public startWriteData = new EventEmitter(); @@ -40,7 +46,7 @@ export class ReportParameterWrapper { private readonly logger: ClassloggerService, private parameters: ParameterData[], private getFkProviderItems: (parameter: ParameterData) => IFkCandidateProvider, - private typedEntity: ReadWriteExtTypedEntity + private typedEntity: ReadWriteExtTypedEntity, ) { this.container = new ParameterContainer(this.translationService, this.getFkProviderItems, this.logger, this.typedEntity); this.container.updateExtendedDataTriggered.subscribe((columnName) => { @@ -65,7 +71,9 @@ export class ReportParameterWrapper { const newParameters: ParameterData[] = this.typedEntity.extendedDataRead[0]; newParameters.forEach((parameter) => { - this.container.update(parameter.Property.ColumnName, parameter); + if (parameter.Property?.ColumnName) { + this.container.update(parameter.Property.ColumnName, parameter); + } // TODO: remove parameters not returned by the server }); @@ -76,16 +84,18 @@ export class ReportParameterWrapper { } this.parameters?.forEach((parameter) => { - const extendedDataGenerator = (newValue) => [ - [ - { - Name: parameter.Property.ColumnName, - Value: newValue, - }, - ], - ]; - const column = this.container.add(parameter.Property.ColumnName, parameter, extendedDataGenerator); - columns.push(column); + if (parameter.Property?.ColumnName) { + const extendedDataGenerator = (newValue) => [ + [ + { + Name: parameter?.Property?.ColumnName, + Value: newValue, + }, + ], + ]; + const column = this.container.add(parameter.Property.ColumnName, parameter, extendedDataGenerator); + if (column) columns.push(column); + } }); return columns; diff --git a/imxweb/projects/rps/src/lib/subscriptions/report-subscription/report-subscription.service.ts b/imxweb/projects/rps/src/lib/subscriptions/report-subscription/report-subscription.service.ts index b6e3963ee..bbe22f2f9 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/report-subscription/report-subscription.service.ts +++ b/imxweb/projects/rps/src/lib/subscriptions/report-subscription/report-subscription.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,26 +24,26 @@ * */ -import { Injectable, Injector } from '@angular/core'; +import { ElementRef, Injectable, Injector } from '@angular/core'; -import { PortalReports, PortalSubscription, V2ApiClientMethodFactory } from 'imx-api-rps'; +import { Overlay } from '@angular/cdk/overlay'; +import { HttpClient } from '@angular/common/http'; +import { EuiDownloadDirective, EuiDownloadService } from '@elemental-ui/core'; +import { PortalReports, PortalSubscription, V2ApiClientMethodFactory } from '@imx-modules/imx-api-rps'; import { CollectionLoadParameters, EntitySchema, + ExtendedTypedEntityCollection, FkProviderItem, - ParameterData, IFkCandidateProvider, - TypedEntityBuilder, - ExtendedTypedEntityCollection, MethodDefinition, -} from 'imx-qbm-dbts'; + ParameterData, + TypedEntityBuilder, +} from '@imx-modules/imx-qbm-dbts'; +import { AppConfigService, ElementalUiConfigService, ImxTranslationProviderService } from 'qbm'; import { ParameterDataService } from 'qer'; import { RpsApiService } from '../../rps-api-client.service'; import { ReportSubscription } from './report-subscription'; -import { EuiDownloadDirective } from '@elemental-ui/core'; -import { AppConfigService, ElementalUiConfigService, ImxTranslationProviderService } from 'qbm'; -import { HttpClient } from '@angular/common/http'; -import { Overlay } from '@angular/cdk/overlay'; @Injectable({ providedIn: 'root', @@ -59,7 +59,8 @@ export class ReportSubscriptionService { private readonly overlay: Overlay, private readonly injector: Injector, private readonly translator: ImxTranslationProviderService, - private readonly parameterDataService: ParameterDataService + private readonly parameterDataService: ParameterDataService, + private readonly downloadService: EuiDownloadService, ) {} public get PortalSubscriptionInteractiveSchema(): EntitySchema { @@ -73,13 +74,19 @@ export class ReportSubscriptionService { } public buildRpsSubscription(subscription: PortalSubscription): ReportSubscription { - return new ReportSubscription(subscription, this.translator, (entity, data) => this.getFkProvider(entity, data), this.parameterDataService); + return new ReportSubscription( + subscription, + this.translator, + (entity, data) => this.getFkProvider(entity, data), + this.parameterDataService, + ); } - public async createNewSubscription(uidReport: string): Promise { + public async createNewSubscription(uidReport: string, suffix?: string): Promise { const subscription = await this.api.typedClient.PortalSubscriptionInteractive.Get(); await subscription.Data[0].UID_RPSReport.Column.PutValue(uidReport); - await subscription.Data[0].Ident_RPSSubscription.Column.PutValue(subscription.Data[0].UID_RPSReport.Column.GetDisplayValue()); + await subscription.Data[0].Ident_RPSSubscription.Column.PutValue(subscription.Data[0].UID_RPSReport.Column.GetDisplayValue() + (suffix ?? ''), + ); const allowedFormats = subscription.Data[0].ExportFormat.Column.GetMetadata().GetLimitedValues(); if (allowedFormats && allowedFormats.filter((f) => f.Value == 'PDF').length > 0) { await subscription.Data[0].ExportFormat.Column.PutValue('PDF'); @@ -93,7 +100,13 @@ export class ReportSubscriptionService { const def = new MethodDefinition(this.apiMethodFactory.portal_subscription_interactive_report_get(parameters.entityid, parameters)); // not pretty, but the download directive does not support dynamic URLs - const directive = new EuiDownloadDirective(null /* no element */, this.http, this.overlay, this.injector); + const directive = new EuiDownloadDirective( + new ElementRef('') /* no element */, + this.http, + this.overlay, + this.injector, + this.downloadService, + ); directive.downloadOptions = { ...this.elementalUiConfigService.Config.downloadOptions, fileMimeType: '', // override elementalUiConfigService; get mime type from server @@ -106,34 +119,43 @@ export class ReportSubscriptionService { private getFkProvider(subscription: PortalSubscription, parameterData: ParameterData): IFkCandidateProvider { var api = this.api; - return new class implements IFkCandidateProvider { - getProviderItem(_columnName, fkTableName) { - if (parameterData.Property.FkRelation) { - return this.getFkProviderItem(subscription, parameterData.Property.ColumnName, parameterData.Property.FkRelation.ParentTableName); + return new (class implements IFkCandidateProvider { + getProviderItem(_columnName, fkTableName): FkProviderItem { + if (parameterData?.Property?.FkRelation) { + return this.getFkProviderItem( + subscription, + _columnName, + parameterData?.Property?.FkRelation?.ParentTableName, + parameterData?.Property?.FkRelation?.ParentColumnName, + ); } - if (parameterData.Property.ValidReferencedTables) { + if (parameterData?.Property?.ValidReferencedTables) { const t = parameterData.Property.ValidReferencedTables.map((parentTableRef) => - this.getFkProviderItem(subscription, parameterData.Property.ColumnName, parentTableRef.TableName) - ).filter(t => t.fkTableName == fkTableName); - if (t.length == 1) - return t[0]; - return null; + this.getFkProviderItem(subscription, parameterData?.Property?.ColumnName, parentTableRef?.TableName, 'XObjectKey'), + ).filter((t) => t.fkTableName == fkTableName); + if (t.length == 1) return t[0]; } + return this.getFkProviderItem(subscription); } - - private getFkProviderItem(subscription: PortalSubscription, columnName: string, fkTableName: string): FkProviderItem { + private getFkProviderItem( + subscription: PortalSubscription, + columnName: string | undefined = '', + fkTableName: string | undefined = '', + fkColumnName: string | undefined = '', + ): FkProviderItem { return { columnName, fkTableName, + fkColumnName, parameterNames: ['OrderBy', 'StartIndex', 'PageSize', 'filter', 'withProperties', 'search'], load: async (__entity, parameters?) => { return api.client.portal_subscription_interactive_parameter_candidates_post( columnName, fkTableName, subscription.InteractiveEntityWriteData, - parameters + parameters, ); }, getDataModel: async () => ({}), @@ -142,11 +164,15 @@ export class ReportSubscriptionService { columnName, fkTableName, subscription.InteractiveEntityWriteData, - { parentkey } + { parentkey }, ); }, }; } - } + })(); + } + + public async sendViaMail(uid: string): Promise{ + return this.api.client.portal_subscription_sendmail_post(uid); } } diff --git a/imxweb/projects/rps/src/lib/subscriptions/report-subscription/report-subscription.ts b/imxweb/projects/rps/src/lib/subscriptions/report-subscription/report-subscription.ts index 5b16c13b5..0003a40d5 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/report-subscription/report-subscription.ts +++ b/imxweb/projects/rps/src/lib/subscriptions/report-subscription/report-subscription.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,8 +26,8 @@ import { EventEmitter } from '@angular/core'; -import { PortalSubscription } from 'imx-api-rps'; -import { IFkCandidateProvider, IClientProperty, IEntityColumn, ParameterData } from 'imx-qbm-dbts'; +import { PortalSubscription } from '@imx-modules/imx-api-rps'; +import { IClientProperty, IEntityColumn, IFkCandidateProvider, ParameterData } from '@imx-modules/imx-qbm-dbts'; import { BaseCdr, ColumnDependentReference, ImxTranslationProviderService } from 'qbm'; import { ParameterDataService } from 'qer'; @@ -40,6 +40,8 @@ export class ReportSubscription { private parameterColumns: IEntityColumn[] = []; + public parameterNames: string[]; + public startWriteData = new EventEmitter(); public endWriteData = new EventEmitter(); @@ -47,18 +49,19 @@ export class ReportSubscription { public subscription: PortalSubscription, translationService: ImxTranslationProviderService, private getFkProviderItem: (cartItem: PortalSubscription, parameter: ParameterData) => IFkCandidateProvider, - parameterDataService: ParameterDataService + parameterDataService: ParameterDataService, ) { this.reportEntityWrapper = new ReportParameterWrapper( translationService, parameterDataService.logger, - this.subscription?.extendedDataRead?.length ? this.subscription.extendedDataRead[0] : null, + this.subscription?.extendedDataRead?.length ? this.subscription.extendedDataRead[0] : [], (parameterData) => this.getFkProviderItem(this.subscription, parameterData), - this.subscription + this.subscription, ); this.hasParameter = this.subscription?.extendedDataRead?.[0]?.length > 0; this.parameterColumns = this.hasParameter ? this.reportEntityWrapper.columns : []; + this.parameterNames = this.parameterColumns.map((elem) => elem.ColumnName); this.reportEntityWrapper.startWriteData.subscribe((elem) => this.startWriteData.emit(elem)); this.reportEntityWrapper.endWriteData.subscribe(() => this.endWriteData.emit()); @@ -78,13 +81,21 @@ export class ReportSubscription { this.subscription.ExportFormat.Column, this.subscription.AddtlSubscribers.Column, ] - : properties.map((prop) => this.subscription.GetEntity().GetColumn(prop.ColumnName)); + : properties.map((prop) => this.subscription.GetEntity().GetColumn(prop.ColumnName ?? '')); return columns.map((col) => new BaseCdr(col)); } - public getParameterCdr(): ColumnDependentReference[] { - return this.parameterColumns.map((col) => new BaseCdr(col)); + public getParameterCdr(hiddenColumns: string[] = []): ColumnDependentReference[] { + return this.parameterColumns.filter(elem=> hiddenColumns.indexOf(elem.ColumnName) === -1).map((col) => new BaseCdr(col)); + } + + public async fillColumnsWithPreset(presetParameter: { [key: string]: string }): Promise { + Object.entries(presetParameter).forEach(async ([key, value]) => { + const test = this.parameterColumns.find((elem) => elem.ColumnName === key); + await test?.PutValue(value); + + }); } public getParameterDictionary(): { [key: string]: any } { @@ -104,7 +115,7 @@ export class ReportSubscription { ].concat(this.parameterColumns); } - public async submit(reload:boolean = false): Promise { + public async submit(reload: boolean = false): Promise { this.subscription.extendedData = [this.parameterColumns.map((col) => ({ Name: col.ColumnName, Value: col.GetValue() }))]; return this.subscription.GetEntity().Commit(reload); diff --git a/imxweb/projects/rps/src/lib/subscriptions/report-view-config/report-view-config.component.html b/imxweb/projects/rps/src/lib/subscriptions/report-view-config/report-view-config.component.html index ff638ff7d..d26ea5ec6 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/report-view-config/report-view-config.component.html +++ b/imxweb/projects/rps/src/lib/subscriptions/report-view-config/report-view-config.component.html @@ -22,7 +22,7 @@
    @@ -37,7 +37,13 @@
    - diff --git a/imxweb/projects/rps/src/lib/subscriptions/report-view-config/report-view-config.component.scss b/imxweb/projects/rps/src/lib/subscriptions/report-view-config/report-view-config.component.scss index fcfb1d417..4c582e9ec 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/report-view-config/report-view-config.component.scss +++ b/imxweb/projects/rps/src/lib/subscriptions/report-view-config/report-view-config.component.scss @@ -18,7 +18,7 @@ overflow: hidden; } - .imx-export-type{ + .imx-export-type { display: flex; flex-direction: row; @@ -27,7 +27,7 @@ } } - .mat-card { + .mat-mdc-card { margin: 3px; } @@ -35,14 +35,6 @@ margin-top: 20px; } - .mat-radio-group { - margin-bottom: 10px; - - > .mat-radio-button { - margin-right: 10px; - } - } - .imx-report-form { flex: 1 1 auto; display: flex; @@ -69,10 +61,6 @@ display: none; } - .imx-report-alert { - margin-top: 20px; - } - .imx-no-results { text-align: center; display: flex; @@ -82,10 +70,6 @@ align-items: center; justify-content: center; - .eui-icon { - font-size: 100px; - } - p { margin: 0; font-size: 18px; @@ -97,10 +81,6 @@ :host { .imx-no-results { - .eui-icon { - color: $color-gray-10; - } - p { color: $color-gray-40; } @@ -110,10 +90,6 @@ .eui-dark-theme { :host { .imx-no-results { - .eui-icon { - color: $color-gray-20; - } - p { color: $color-gray-10; } @@ -124,10 +100,6 @@ .eui-contrast-theme { :host { .imx-no-results { - .eui-icon { - color: $color-gray-10; - } - p { color: $color-gray-0; } diff --git a/imxweb/projects/rps/src/lib/subscriptions/report-view-config/report-view-config.component.ts b/imxweb/projects/rps/src/lib/subscriptions/report-view-config/report-view-config.component.ts index 70f726472..09a53ec4c 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/report-view-config/report-view-config.component.ts +++ b/imxweb/projects/rps/src/lib/subscriptions/report-view-config/report-view-config.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,21 +25,21 @@ */ import { ChangeDetectorRef, Component, OnDestroy } from '@angular/core'; -import { FormArray, FormControl, FormGroup, UntypedFormControl, Validators } from '@angular/forms'; +import { AbstractControl, FormArray, FormControl, FormGroup, Validators } from '@angular/forms'; import { EuiSidesheetRef, EuiSidesheetService } from '@elemental-ui/core'; -import { Subscription } from 'rxjs'; import { TranslateService } from '@ngx-translate/core'; +import { Subscription } from 'rxjs'; -import { BaseCdr, BusyService, ColumnDependentReference, ErrorService } from 'qbm'; +import { BaseCdr, BusyService, calculateSidesheetWidth, ColumnDependentReference, ErrorService } from 'qbm'; +import { ListReportViewerService } from '../../list-report-viewer/list-report-viewer.service'; +import { ListReportViewerSidesheetComponent } from '../list-report-viewer-sidesheet/list-report-viewer-sidesheet.component'; import { ReportSubscription } from '../report-subscription/report-subscription'; import { ReportSubscriptionService } from '../report-subscription/report-subscription.service'; -import { ListReportViewerSidesheetComponent } from '../list-report-viewer-sidesheet/list-report-viewer-sidesheet.component'; -import { ListReportViewerService } from '../../list-report-viewer/list-report-viewer.service'; interface reportForm { viewType: FormControl<'view' | 'export'>; - reportTable: FormControl; - exportType: FormControl + reportTable: FormControl; + exportType: FormControl; parameters: FormArray; } @Component({ @@ -49,12 +49,12 @@ interface reportForm { }) export class ReportViewConfigComponent implements OnDestroy { public readonly reportFormGroup = new FormGroup({ - viewType: new FormControl('view'), - reportTable: new FormControl(undefined, Validators.required), - exportType: new FormControl(undefined), - parameters: new FormArray([]), + viewType: new FormControl('view', { nonNullable: true }), + reportTable: new FormControl(undefined, { nonNullable: true, validators: Validators.required }), + exportType: new FormControl(undefined, { nonNullable: true }), + parameters: new FormArray([]), }); - public newSubscription: ReportSubscription; + public newSubscription: ReportSubscription | undefined; public parameterCdrList: ColumnDependentReference[] = []; public formatCdr: ColumnDependentReference; public busyService = new BusyService(); @@ -84,26 +84,26 @@ export class ReportViewConfigComponent implements OnDestroy { private readonly cdref: ChangeDetectorRef, private readonly translate: TranslateService, private readonly viewReportService: ListReportViewerService, - errorService: ErrorService + errorService: ErrorService, ) { this.subscriptions.push( this.sidesheetRef.closeClicked().subscribe(async () => { this.sidesheetRef.close(false); - }) + }), ); this.subscriptions.push( this.busyService.busyStateChanged.subscribe((elem) => { this.isLoading = elem; this.cdref.detectChanges(); - }) + }), ); this.subscriptions.push( this.reportFormGroup.controls.reportTable.valueChanges.subscribe(async (val) => { const isBusy = this.busyService.beginBusy(); try { - this.uidReport = val; + this.uidReport = val ?? ''; this.formControls.clear(); // Standard caption is "Format (e-mail attachment)" -> change @@ -122,18 +122,17 @@ export class ReportViewConfigComponent implements OnDestroy { this.formatCdr = new BaseCdr(this.newSubscription.subscription.ExportFormat.Column, format); this.parameterCdrList = [...this.newSubscription.getParameterCdr()]; this.cdref.detectChanges(); - } else this.newSubscription = null; + } else this.newSubscription = undefined; } finally { isBusy.endBusy(); - this.reportIsLoaded = this.newSubscription != null; + this.reportIsLoaded = !!this.newSubscription; } - }) + }), ); this.disposable = errorService.setTarget('sidesheet'); } - public ngOnDestroy(): void { // sometimes there are problems with usage of disposed components setTimeout(() => { @@ -147,23 +146,29 @@ export class ReportViewConfigComponent implements OnDestroy { if (this.reportFormGroup.controls.viewType.value === 'view' && this.isListReport) { await this.viewReportInTable(); } else { - this.reportSubscriptionService.downloadSubsciption(this.newSubscription); + if (this.newSubscription) { + this.reportSubscriptionService.downloadSubsciption(this.newSubscription); + } } } - public addFormControl(control: UntypedFormControl): void { + public addFormControl(control: AbstractControl): void { this.formControls.push(control); this.cdref.detectChanges(); } + public addExportControl(control: AbstractControl): void { + this.reportFormGroup.controls.exportType = control.value; + } + private async viewReportInTable(): Promise { this.viewReportService.setUidReport(this.uidReport); const data = { dataService: this.viewReportService, subscription: this.newSubscription }; this.sideSheet.open(ListReportViewerSidesheetComponent, { title: await this.translate.get('#LDS#Heading View Report').toPromise(), - subTitle: this.newSubscription.subscription.GetEntity().GetDisplay(), + subTitle: this.newSubscription?.subscription.GetEntity().GetDisplay(), padding: '0', - width: 'max(550px,55%)', + width: calculateSidesheetWidth(), testId: 'report-view-config-report-viewer', data, }); diff --git a/imxweb/projects/rps/src/lib/subscriptions/subscription-details/subscription-details.component.html b/imxweb/projects/rps/src/lib/subscriptions/subscription-details/subscription-details.component.html index 6a5ac50b4..abdc75da7 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/subscription-details/subscription-details.component.html +++ b/imxweb/projects/rps/src/lib/subscriptions/subscription-details/subscription-details.component.html @@ -7,7 +7,13 @@
    -
    diff --git a/imxweb/projects/rps/src/lib/subscriptions/subscription-details/subscription-details.component.scss b/imxweb/projects/rps/src/lib/subscriptions/subscription-details/subscription-details.component.scss index e07bc8be0..aac8fbdeb 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/subscription-details/subscription-details.component.scss +++ b/imxweb/projects/rps/src/lib/subscriptions/subscription-details/subscription-details.component.scss @@ -3,16 +3,3 @@ flex-direction: column; max-height: 100%; } - - -.button-row { - border-top-style: solid; - border-top-width: 0px; - display: flex; - flex-direction: row; - justify-content: flex-end; - padding: 16px 8px 16px 24px; -} -.button-row > * { - margin-left: 10px; -} \ No newline at end of file diff --git a/imxweb/projects/rps/src/lib/subscriptions/subscription-details/subscription-details.component.ts b/imxweb/projects/rps/src/lib/subscriptions/subscription-details/subscription-details.component.ts index e77f874f4..ee7c07b14 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/subscription-details/subscription-details.component.ts +++ b/imxweb/projects/rps/src/lib/subscriptions/subscription-details/subscription-details.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,19 +26,17 @@ import { Component, Inject, OnDestroy } from '@angular/core'; import { UntypedFormGroup } from '@angular/forms'; -import { EuiLoadingService, EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { EUI_SIDESHEET_DATA, EuiLoadingService, EuiSidesheetRef } from '@elemental-ui/core'; import { Subscription } from 'rxjs'; -import { OverlayRef } from '@angular/cdk/overlay'; import { ColumnDependentReference, ConfirmationService } from 'qbm'; import { ReportSubscription } from '../report-subscription/report-subscription'; @Component({ templateUrl: './subscription-details.component.html', - styleUrls: ['./subscription-details.component.scss'] + styleUrls: ['./subscription-details.component.scss'], }) export class SubscriptionDetailsComponent implements OnDestroy { - public readonly formGroup = new UntypedFormGroup({}); public readonly cdrList: ColumnDependentReference[]; public closeClickSubscription: Subscription; @@ -48,11 +46,10 @@ export class SubscriptionDetailsComponent implements OnDestroy { @Inject(EUI_SIDESHEET_DATA) public readonly subscription: ReportSubscription, public readonly sidesheetRef: EuiSidesheetRef, private readonly busyService: EuiLoadingService, - private readonly confirmation: ConfirmationService + private readonly confirmation: ConfirmationService, ) { this.closeClickSubscription = this.sidesheetRef.closeClicked().subscribe(async () => { - if (!this.formGroup.dirty - || await this.confirmation.confirmLeaveWithUnsavedChanges()) { + if (!this.formGroup.dirty || (await this.confirmation.confirmLeaveWithUnsavedChanges())) { this.sidesheetRef.close(this.reload); } }); @@ -63,12 +60,13 @@ export class SubscriptionDetailsComponent implements OnDestroy { } public async submit(): Promise { - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } try { await this.subscription.submit(true); } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); this.reload = true; this.formGroup.markAsPristine(); } diff --git a/imxweb/projects/rps/src/lib/subscriptions/subscription-properties/subscription-properties.component.html b/imxweb/projects/rps/src/lib/subscriptions/subscription-properties/subscription-properties.component.html index d067d362a..bcd63b47b 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/subscription-properties/subscription-properties.component.html +++ b/imxweb/projects/rps/src/lib/subscriptions/subscription-properties/subscription-properties.component.html @@ -1,35 +1,49 @@ -
    +

    - {{'#LDS#Heading Subscription Details' | translate}} + {{ '#LDS#Heading Subscription Details' | translate }}

    - +

    - {{'#LDS#Heading Parameters' | translate}} + {{ '#LDS#Heading Parameters' | translate }}

    - +
    - {{'#LDS#There are no parameters for this subscription.' | translate}} + {{ '#LDS#There are no parameters for this subscription.' | translate }}
    - + - +
    -
    \ No newline at end of file + diff --git a/imxweb/projects/rps/src/lib/subscriptions/subscription-properties/subscription-properties.component.scss b/imxweb/projects/rps/src/lib/subscriptions/subscription-properties/subscription-properties.component.scss index 32f1f29d8..134cd679b 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/subscription-properties/subscription-properties.component.scss +++ b/imxweb/projects/rps/src/lib/subscriptions/subscription-properties/subscription-properties.component.scss @@ -1,4 +1,3 @@ - .imx-parameter-grid { display: grid; grid-template-columns: 1fr 1fr; @@ -11,9 +10,9 @@ h4 { margin-bottom: 40px; } -@media only screen and (max-width: 1024px) { +@media only screen and (max-width: 1024px) { .imx-parameter-grid > div { grid-column-start: 1; grid-column-end: 3; - } -} \ No newline at end of file + } +} diff --git a/imxweb/projects/rps/src/lib/subscriptions/subscription-properties/subscription-properties.component.ts b/imxweb/projects/rps/src/lib/subscriptions/subscription-properties/subscription-properties.component.ts index 251e9d437..c9d6a56fe 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/subscription-properties/subscription-properties.component.ts +++ b/imxweb/projects/rps/src/lib/subscriptions/subscription-properties/subscription-properties.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,19 +25,18 @@ */ import { Component, Input, OnChanges, OnInit } from '@angular/core'; -import { UntypedFormArray, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; +import { AbstractControl, UntypedFormArray, UntypedFormGroup } from '@angular/forms'; -import { IClientProperty } from 'imx-qbm-dbts'; +import { IClientProperty } from '@imx-modules/imx-qbm-dbts'; import { ColumnDependentReference } from 'qbm'; import { ReportSubscription } from '../report-subscription/report-subscription'; @Component({ selector: 'imx-subscription-properties', templateUrl: './subscription-properties.component.html', - styleUrls: ['./subscription-properties.component.scss'] + styleUrls: ['./subscription-properties.component.scss'], }) export class SubscriptionPropertiesComponent implements OnInit, OnChanges { - public cdrList: ColumnDependentReference[] = []; public parameterCdrList: ColumnDependentReference[] = []; public readonly subscriptionPropertiesFormArray = new UntypedFormArray([]); @@ -58,7 +57,6 @@ export class SubscriptionPropertiesComponent implements OnInit, OnChanges { } public ngOnChanges(): void { - this.subscriptionPropertiesFormArray.clear(); this.cdrList = this.subscription == null ? [] : this.subscription.getCdrs(this.displayedColumns); @@ -75,7 +73,7 @@ export class SubscriptionPropertiesComponent implements OnInit, OnChanges { } } - public addFormControl(array: UntypedFormArray, control: UntypedFormControl): void { + public addFormControl(array: UntypedFormArray, control: AbstractControl): void { setTimeout(() => { array.push(control); }); diff --git a/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/report-selector/report-selector.component.html b/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/report-selector/report-selector.component.html index 1eb105405..c03f8a199 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/report-selector/report-selector.component.html +++ b/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/report-selector/report-selector.component.html @@ -1,17 +1,33 @@
    - + -
    - +
    +
    {{ '#LDS#Report' | translate }} - - - + + +
    {{ candidate.GetEntity().GetDisplay() }}
    {{ candidate.Description.Column.GetDisplayValue() }}
    diff --git a/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/report-selector/report-selector.component.scss b/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/report-selector/report-selector.component.scss index 0c13d3358..46fd915fe 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/report-selector/report-selector.component.scss +++ b/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/report-selector/report-selector.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; :host { flex: 1; @@ -8,16 +8,6 @@ .report-selector-eui-search { display: flex; - - .eui-search { - width: 100%; - } - - ::ng-deep .eui-search.mat-form-field.mat-form-field-appearance-outline { - min-width: 280px; - flex: 1; - margin-bottom: 10px; - } } .imx-candidate { @@ -53,15 +43,9 @@ } } - .imx-report-listing { flex: 1; display: grid; grid-template-columns: 1fr 21px; margin-right: -21px; } - -.mat-spinner { - margin-top: 15px; - margin-left: 5px; -} diff --git a/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/report-selector/report-selector.component.ts b/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/report-selector/report-selector.component.ts index aea127694..a86dae8bf 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/report-selector/report-selector.component.ts +++ b/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/report-selector/report-selector.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,18 +24,16 @@ * */ -import { ListRange } from '@angular/cdk/collections'; -import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; -import { AfterViewInit, ChangeDetectorRef, Component, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; -import { ControlValueAccessor, UntypedFormControl, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { AfterViewInit, ChangeDetectorRef, Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms'; import { MatSelectionListChange } from '@angular/material/list'; import { Subscription } from 'rxjs'; import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; -import { PortalReports } from 'imx-api-rps'; -import { CollectionLoadParameters } from 'imx-qbm-dbts'; -import { ReportSubscriptionService } from '../../report-subscription/report-subscription.service'; +import { PortalReports } from '@imx-modules/imx-api-rps'; +import { CollectionLoadParameters } from '@imx-modules/imx-qbm-dbts'; import { SettingsService } from 'qbm'; +import { ReportSubscriptionService } from '../../report-subscription/report-subscription.service'; @Component({ selector: 'imx-report-selector', @@ -68,7 +66,7 @@ export class ReportSelectorComponent implements ControlValueAccessor, OnInit, On constructor( private readonly changeDetectorRef: ChangeDetectorRef, private readonly settings: SettingsService, - private readonly reportSubscriptionService: ReportSubscriptionService + private readonly reportSubscriptionService: ReportSubscriptionService, ) {} public async ngOnInit(): Promise { @@ -107,8 +105,10 @@ export class ReportSelectorComponent implements ControlValueAccessor, OnInit, On public updateSelected(elem: MatSelectionListChange): void { // Only one can be selected const chosenElem = elem.options.find((ele) => ele.selected); - this.writeValue(chosenElem.value.GetEntity().GetKeys()[0]); - this.onChange(chosenElem.value.GetEntity().GetKeys()[0]); + if (chosenElem) { + this.writeValue(chosenElem.value.GetEntity().GetKeys()[0]); + this.onChange(chosenElem.value.GetEntity().GetKeys()[0]); + } } private async loadReports(newState?: CollectionLoadParameters): Promise { @@ -128,7 +128,7 @@ export class ReportSelectorComponent implements ControlValueAccessor, OnInit, On this.searchControl.valueChanges.pipe(distinctUntilChanged(), debounceTime(300)).subscribe(async (value) => { await this.loadReports({ StartIndex: 0, PageSize: this.settings.PageSizeForAllElements, search: value }); this.changeDetectorRef.detectChanges(); - }) + }), ); } } diff --git a/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/subscription-overview/subscription-overview.component.html b/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/subscription-overview/subscription-overview.component.html index f58a45c78..c4d01482b 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/subscription-overview/subscription-overview.component.html +++ b/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/subscription-overview/subscription-overview.component.html @@ -2,27 +2,35 @@
    -
    - +
    + - {{'#LDS#You cannot receive the report. You have not specified an email address. Please add an email address to your user account.' | - translate}} + {{ + '#LDS#You cannot receive the report. You have not specified an email address. Please add an email address to your user account.' + | translate + }}
    -
    - - - {{'#LDS#The following subscribers cannot receive the report. No email addresses have been specified for the user accounts of these subscribers.' |translate}} - - - {{additionalRecipientWithoutEmail.join('; ')}} - +
    + + + {{ + '#LDS#The following subscribers cannot receive the report. No email addresses have been specified for the user accounts of these subscribers.' + | translate + }} + + + {{ additionalRecipientWithoutEmail.join('; ') }} +
    - {{'#LDS#The overview is calculated. Please wait.' | translate}} -
    \ No newline at end of file + {{ '#LDS#The overview is calculated. Please wait.' | translate }} +
    diff --git a/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/subscription-overview/subscription-overview.component.scss b/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/subscription-overview/subscription-overview.component.scss index 24b9d07b6..129269625 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/subscription-overview/subscription-overview.component.scss +++ b/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/subscription-overview/subscription-overview.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; .imx-report-table { display: flex; @@ -14,10 +14,6 @@ imx-property-viewer { gap: 10px 20px; } -.imx-overview-alert { - margin: 10px 0; -} - .imx-wait-for-overview-load { margin: 10px 0; } diff --git a/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/subscription-overview/subscription-overview.component.ts b/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/subscription-overview/subscription-overview.component.ts index a4d0d48bb..6efd8da64 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/subscription-overview/subscription-overview.component.ts +++ b/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/subscription-overview/subscription-overview.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,22 +25,22 @@ */ import { Component, Input, OnDestroy, OnInit } from '@angular/core'; -import { IEntityColumn } from 'imx-qbm-dbts'; -import { BehaviorSubject } from 'rxjs'; import { EuiLoadingService } from '@elemental-ui/core'; -import { OverlayRef } from '@angular/cdk/overlay'; +import { IEntityColumn } from '@imx-modules/imx-qbm-dbts'; +import { BehaviorSubject } from 'rxjs'; -import { MultiValueService, AuthenticationService } from 'qbm'; +import { AuthenticationService, MultiValueService } from 'qbm'; import { PersonService } from 'qer'; import { ReportSubscription } from '../../report-subscription/report-subscription'; +type NameAddress = { name: string; address: string }; + @Component({ selector: 'imx-subscription-overview', templateUrl: './subscription-overview.component.html', - styleUrls: ['./subscription-overview.component.scss'] + styleUrls: ['./subscription-overview.component.scss'], }) export class SubscriptionOverviewComponent implements OnInit, OnDestroy { - public userIsMissingEMail = false; public additionalRecipientWithoutEmail: string[] = []; public setProperties: IEntityColumn[] = []; @@ -55,14 +55,15 @@ export class SubscriptionOverviewComponent implements OnInit, OnDestroy { private readonly multiValue: MultiValueService, private readonly busyService: EuiLoadingService, private readonly personService: PersonService, - authentication: AuthenticationService) { - authentication.onSessionResponse.subscribe(state => this.currentUserId = state.UserUid); + authentication: AuthenticationService, + ) { + authentication.onSessionResponse.subscribe((state) => (this.currentUserId = state.UserUid ?? '')); } public ngOnInit(): void { if (this.subscribersChanged) { this.subscribersChanged.subscribe(async () => { - if (this.subscription != null) { + if (this.subscription) { this.setProperties = this.subscription.getDisplayableColums(); } await this.checkEmailAddresses(); @@ -77,35 +78,37 @@ export class SubscriptionOverviewComponent implements OnInit, OnDestroy { } private async checkEmailAddresses(): Promise { - if (this.subscription == null) { + if (!this.subscription) { return; } - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } try { - this.userIsMissingEMail = (await this.getPersonNameAndAddress(this.currentUserId)).address === ''; - const subscribers = this.multiValue.getValues(this.subscription.subscription.AddtlSubscribers.value); - const subscriberMails = []; + const subscribers = this.multiValue.getValues(this.subscription.subscription.AddtlSubscribers.value) ?? []; + const subscriberMails: NameAddress[] = []; for (const value of subscribers) { const mail = await this.getPersonNameAndAddress(value); subscriberMails.push(mail); } this.additionalRecipientWithoutEmail = subscriberMails - .filter(elem => elem.address == null || elem.address === '').map(elem => elem.name); + .filter((elem) => elem.address == null || elem.address === '') + .map((elem) => elem.name); } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } } - private async getPersonNameAndAddress(uid: string): Promise<{ name: string, address: string }> { + private async getPersonNameAndAddress(uid: string): Promise { const user = await this.personService.get(uid); - return user.Data.length > 0 ? { - name: user.Data[0].GetEntity().GetDisplay(), - address: user.Data[0].DefaultEmailAddress.value - } : null; + return user.Data.length > 0 + ? { + name: user.Data[0].GetEntity().GetDisplay(), + address: user.Data[0].DefaultEmailAddress.value, + } + : { name: '', address: '' }; } } - diff --git a/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/subscription-wizard.component.html b/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/subscription-wizard.component.html index 907927116..bb39922bd 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/subscription-wizard.component.html +++ b/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/subscription-wizard.component.html @@ -5,8 +5,14 @@
    -
    -
    @@ -23,11 +29,17 @@ [subscription]="newSubscription" >
    -
    +
    -
    @@ -46,7 +58,7 @@ formControlName="additionalSubscribers" > -
    +
    @@ -58,12 +70,18 @@ {{ '#LDS#Check and create subscription' | translate }} - -
    + +
    - +
    diff --git a/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/subscription-wizard.component.scss b/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/subscription-wizard.component.scss index 97991f3be..1c8b2f24d 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/subscription-wizard.component.scss +++ b/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/subscription-wizard.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; .imx-report-table { display: flex; margin-top: 10px; @@ -18,12 +18,6 @@ imx-property-viewer { margin: 30px 0; } -.imx-step-button { - > :first-child { - margin-right: 10px; - } -} - @media only screen and (max-width: 1024px) { .imx-wizard-info { margin: 0; diff --git a/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/subscription-wizard.component.ts b/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/subscription-wizard.component.ts index 8e7901481..c550ddbc5 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/subscription-wizard.component.ts +++ b/imxweb/projects/rps/src/lib/subscriptions/subscription-wizard/subscription-wizard.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,14 +24,13 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; import { StepperSelectionEvent } from '@angular/cdk/stepper'; import { Component, OnDestroy, ViewChild } from '@angular/core'; import { UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; import { EuiLoadingService, EuiSidesheetRef } from '@elemental-ui/core'; import { BehaviorSubject, Subscription } from 'rxjs'; -import { EntitySchema, IClientProperty } from 'imx-qbm-dbts'; +import { EntitySchema, IClientProperty } from '@imx-modules/imx-qbm-dbts'; import { ConfirmationService, MultiSelectFormcontrolComponent } from 'qbm'; import { ReportSubscription } from '../report-subscription/report-subscription'; import { ReportSubscriptionService } from '../report-subscription/report-subscription.service'; @@ -39,16 +38,15 @@ import { ReportSubscriptionService } from '../report-subscription/report-subscri @Component({ selector: 'imx-subscription-wizard', templateUrl: './subscription-wizard.component.html', - styleUrls: ['./subscription-wizard.component.scss'] + styleUrls: ['./subscription-wizard.component.scss'], }) export class SubscriptionWizardComponent implements OnDestroy { - public readonly reportFormGroup = new UntypedFormGroup({ - reportTable: new UntypedFormControl(undefined, Validators.required) + reportTable: new UntypedFormControl(undefined, Validators.required), }); public readonly reportParameterFormGroup = new UntypedFormGroup({}); public readonly additionalSubscribersFormGroup = new UntypedFormGroup({ - additionalSubscribers: new UntypedFormControl(undefined) + additionalSubscribers: new UntypedFormControl(undefined), }); public isLoadingOverview = false; public newSubscription: ReportSubscription; @@ -73,8 +71,10 @@ export class SubscriptionWizardComponent implements OnDestroy { this.entitySchema.Columns.ExportFormat, ]; this.closeClickSubscription = this.sidesheetRef.closeClicked().subscribe(async () => { - if ((!this.reportFormGroup.dirty && !this.reportParameterFormGroup.dirty && !this.additionalSubscribersFormGroup.dirty) - || await this.confirmation.confirmLeaveWithUnsavedChanges()) { + if ( + (!this.reportFormGroup.dirty && !this.reportParameterFormGroup.dirty && !this.additionalSubscribersFormGroup.dirty) || + (await this.confirmation.confirmLeaveWithUnsavedChanges()) + ) { this.sidesheetRef.close(false); } }); @@ -86,16 +86,16 @@ export class SubscriptionWizardComponent implements OnDestroy { public async selectedStepChanged(event: StepperSelectionEvent): Promise { if (event.selectedIndex === 1 && event.previouslySelectedIndex === 0) { - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } try { - this.newSubscription = - await this.reportSubscriptionService.createNewSubscription(this.reportFormGroup.get('reportTable').value); - this.additionalSubscribersFormGroup.get('additionalSubscribers') - .setValue(this.newSubscription.subscription.AddtlSubscribers.Column); - + this.newSubscription = await this.reportSubscriptionService.createNewSubscription(this.reportFormGroup.get('reportTable')?.value); + this.additionalSubscribersFormGroup + .get('additionalSubscribers') + ?.setValue(this.newSubscription.subscription.AddtlSubscribers.Column); } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } } @@ -108,14 +108,15 @@ export class SubscriptionWizardComponent implements OnDestroy { } public async submit(): Promise { - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } try { await this.newSubscription.submit(); this.newSubscription.unsubscribeEvents(); this.sidesheetRef.close(true); } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } } } diff --git a/imxweb/projects/rps/src/lib/subscriptions/subscriptions.component.html b/imxweb/projects/rps/src/lib/subscriptions/subscriptions.component.html index 7c3abe49c..53d662e52 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/subscriptions.component.html +++ b/imxweb/projects/rps/src/lib/subscriptions/subscriptions.component.html @@ -1,4 +1,4 @@ - +
    - - + + @@ -32,10 +41,21 @@ - - - diff --git a/imxweb/projects/rps/src/lib/subscriptions/subscriptions.component.scss b/imxweb/projects/rps/src/lib/subscriptions/subscriptions.component.scss index 46ace64b8..a5ae8d46d 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/subscriptions.component.scss +++ b/imxweb/projects/rps/src/lib/subscriptions/subscriptions.component.scss @@ -1,11 +1,8 @@ @import '@elemental-ui/core/src/styles/_palette.scss'; -@import '../../../../../shared/scss/common-table.scss'; +@import 'base/mixins'; :host { - display: flex; - flex-direction: column; - overflow: hidden; - height: inherit; + @include flex-column-container-fill(); } imx-data-table { @@ -17,50 +14,18 @@ imx-data-table { margin-right: 10px; } -:host ::ng-deep imx-data-table .mat-header-row .mat-header-cell { - font-weight: 600; - font-size: 14px; - padding: 0 10px; -} - -:host ::ng-deep imx-data-table .mat-row .mat-cell { - padding: 0 10px; -} - .imx-separate-menu-item { border-top: 1px solid $black-c; } -.imx-table-container { - @include imx-flex-fill-control-hidden-overflow(); -} - -:host ::ng-deep eui-sidesheet .eui-sidesheet__content { - display: flex; - flex-direction: column; - max-height: 100%; - box-sizing: content-box; -} - -.mat-card { - display: flex; - flex-direction: column; - height: inherit; - overflow: hidden; - - .mat-card-content { +.mat-mdc-card { + .mat-mdc-card-content { + @include flex-column-container-fill(); overflow-y: auto; overflow-x: hidden; - flex: 1 1 auto; - display: flex; - flex-direction: column; } } -.mat-stroked-button { - @include imx-icon-for-image-button(); -} - -.imx-button-bar { - @include imx-button-bar(); +.mat-mdc-outlined-button { + @include image-button-icon(); } diff --git a/imxweb/projects/rps/src/lib/subscriptions/subscriptions.component.ts b/imxweb/projects/rps/src/lib/subscriptions/subscriptions.component.ts index c476d87c2..bee66bb3f 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/subscriptions.component.ts +++ b/imxweb/projects/rps/src/lib/subscriptions/subscriptions.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,22 +24,28 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; import { Component, OnInit } from '@angular/core'; import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { PortalSubscription } from 'imx-api-rps'; -import { CollectionLoadParameters, DisplayColumns, EntitySchema, ValType } from 'imx-qbm-dbts'; -import { ConfirmationService, DataSourceToolbarSettings, ClientPropertyForTableColumns, SnackBarService, BusyService } from 'qbm'; +import { PortalSubscription } from '@imx-modules/imx-api-rps'; +import { CollectionLoadParameters, DisplayColumns, EntitySchema, TypedEntity, ValType } from '@imx-modules/imx-qbm-dbts'; +import { + BusyService, + ClientPropertyForTableColumns, + ConfirmationService, + DataSourceToolbarSettings, + SnackBarService, + calculateSidesheetWidth, +} from 'qbm'; +import { ListReportViewerService } from '../list-report-viewer/list-report-viewer.service'; +import { ListReportViewerSidesheetComponent } from './list-report-viewer-sidesheet/list-report-viewer-sidesheet.component'; import { ReportSubscription } from './report-subscription/report-subscription'; import { ReportSubscriptionService } from './report-subscription/report-subscription.service'; import { ReportViewConfigComponent } from './report-view-config/report-view-config.component'; import { SubscriptionDetailsComponent } from './subscription-details/subscription-details.component'; import { SubscriptionWizardComponent } from './subscription-wizard/subscription-wizard.component'; import { SubscriptionsService } from './subscriptions.service'; -import { ListReportViewerSidesheetComponent } from './list-report-viewer-sidesheet/list-report-viewer-sidesheet.component'; -import { ListReportViewerService } from '../list-report-viewer/list-report-viewer.service'; @Component({ selector: 'imx-subscriptions', @@ -66,7 +72,7 @@ export class SubscriptionsComponent implements OnInit { private readonly snackbar: SnackBarService, private readonly translate: TranslateService, private readonly busyServiceElemental: EuiLoadingService, - private readonly listReportViewerService: ListReportViewerService + private readonly listReportViewerService: ListReportViewerService, ) { this.entitySchema = subscriptionService.PortalSubscriptionSchema; this.displayedColumns = [ @@ -130,7 +136,7 @@ export class SubscriptionsComponent implements OnInit { title: await this.translate.get('#LDS#Heading View Report').toPromise(), subTitle: subscription.GetEntity().GetDisplay(), padding: '0', - width: 'max(550px,55%)', + width: calculateSidesheetWidth(), testId: 'subscription-report-viewer', data, }); @@ -144,12 +150,13 @@ export class SubscriptionsComponent implements OnInit { identifier: 'subscriptions-confirm-unsubscribe-report', }) ) { - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.busyServiceElemental.show())); + if (this.busyServiceElemental.overlayRefs.length === 0) { + this.busyServiceElemental.show(); + } try { await this.subscriptionService.deleteSubscription(subscription.GetEntity().GetKeys()[0]); } finally { - setTimeout(() => this.busyServiceElemental.hide(overlayRef)); + this.busyServiceElemental.hide(); } const message = { key: '#LDS#You have successfully unsubscribed from the report "{0}".', @@ -161,17 +168,18 @@ export class SubscriptionsComponent implements OnInit { } } - public async editSubscription(subscription: PortalSubscription): Promise { - let rpsSubscription: ReportSubscription; - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.busyServiceElemental.show())); + public async editSubscription(subscription: TypedEntity): Promise { + let rpsSubscription: ReportSubscription | undefined = undefined; + if (this.busyServiceElemental.overlayRefs.length === 0) { + this.busyServiceElemental.show(); + } try { const sub = await this.subscriptionService.getSubscriptionInteractive(subscription.GetEntity().GetKeys()[0]); if (sub.Data.length > 0) { rpsSubscription = this.rpsReportService.buildRpsSubscription(sub.Data[0]); } } finally { - setTimeout(() => this.busyServiceElemental.hide(overlayRef)); + this.busyServiceElemental.hide(); } if (rpsSubscription) { @@ -180,7 +188,7 @@ export class SubscriptionsComponent implements OnInit { subTitle: subscription.GetEntity().GetDisplay(), testId: 'edit-subscription-sidesheet', padding: '0px', - width: 'max(700px,70%)', + width: calculateSidesheetWidth(1000), disableClose: true, data: rpsSubscription, }); @@ -195,7 +203,7 @@ export class SubscriptionsComponent implements OnInit { const sidesheetRef = this.sideSheet.open(SubscriptionWizardComponent, { title: await this.translate.get('#LDS#Heading Add Report Subscription').toPromise(), padding: '0px', - width: '70%', + width: calculateSidesheetWidth(1000), disableClose: true, testId: 'subscriptions-create', }); @@ -211,7 +219,7 @@ export class SubscriptionsComponent implements OnInit { const sidesheetRef = this.sideSheet.open(ReportViewConfigComponent, { title: await this.translate.get('#LDS#Heading View a Report').toPromise(), padding: '0px', - width: '70%', + width: calculateSidesheetWidth(1000), testId: 'subscriptions-view-config', }); diff --git a/imxweb/projects/rps/src/lib/subscriptions/subscriptions.module.ts b/imxweb/projects/rps/src/lib/subscriptions/subscriptions.module.ts index 10002c9fd..5eb75acb2 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/subscriptions.module.ts +++ b/imxweb/projects/rps/src/lib/subscriptions/subscriptions.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -41,7 +41,7 @@ import { MultiSelectFormcontrolModule, UserMessageModule, RouteGuardService, - BusyIndicatorModule + BusyIndicatorModule, } from 'qbm'; import { ReportSelectorComponent } from './subscription-wizard/report-selector/report-selector.component'; import { ReportSubscriptionService } from './report-subscription/report-subscription.service'; @@ -53,52 +53,44 @@ import { SubscriptionsService } from './subscriptions.service'; import { SubscriptionWizardComponent } from './subscription-wizard/subscription-wizard.component'; import { ReportViewConfigComponent } from './report-view-config/report-view-config.component'; import { ListReportViewerModule } from '../list-report-viewer/list-report-viewer.module'; -import { HttpClientModule } from '@angular/common/http'; +import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; import { OverlayModule } from '@angular/cdk/overlay'; import { ListReportViewerSidesheetComponent } from './list-report-viewer-sidesheet/list-report-viewer-sidesheet.component'; - const routes: Routes = [ { path: 'reportsubscriptions', component: SubscriptionsComponent, canActivate: [RouteGuardService], - resolve: [RouteGuardService] - } + resolve: [RouteGuardService], + }, ]; -@NgModule({ - declarations: [ - ReportSelectorComponent, - ReportViewConfigComponent, - SubscriptionDetailsComponent, - SubscriptionPropertiesComponent, - SubscriptionOverviewComponent, - SubscriptionWizardComponent, - SubscriptionsComponent, - ListReportViewerSidesheetComponent, - ], - imports: [ - CdrModule, - CommonModule, - ConfirmationModule, - DataSourceToolbarModule, - DataTableModule, - EuiCoreModule, - EuiMaterialModule, - FormsModule, - HttpClientModule, - LdsReplaceModule, - MultiSelectFormcontrolModule, - OverlayModule, - ReactiveFormsModule, - RouterModule.forChild(routes), - ScrollingModule, - TranslateModule, - UserMessageModule, - BusyIndicatorModule, - ListReportViewerModule, - ], - providers: [ReportSubscriptionService, SubscriptionsService], -}) +@NgModule({ declarations: [ + ReportSelectorComponent, + ReportViewConfigComponent, + SubscriptionDetailsComponent, + SubscriptionPropertiesComponent, + SubscriptionOverviewComponent, + SubscriptionWizardComponent, + SubscriptionsComponent, + ListReportViewerSidesheetComponent, + ], imports: [CdrModule, + CommonModule, + ConfirmationModule, + DataSourceToolbarModule, + DataTableModule, + EuiCoreModule, + EuiMaterialModule, + FormsModule, + LdsReplaceModule, + MultiSelectFormcontrolModule, + OverlayModule, + ReactiveFormsModule, + RouterModule.forChild(routes), + ScrollingModule, + TranslateModule, + UserMessageModule, + BusyIndicatorModule, + ListReportViewerModule], providers: [ReportSubscriptionService, SubscriptionsService, provideHttpClient(withInterceptorsFromDi())] }) export class SubscriptionsModule {} diff --git a/imxweb/projects/rps/src/lib/subscriptions/subscriptions.service.ts b/imxweb/projects/rps/src/lib/subscriptions/subscriptions.service.ts index 1f84c57ff..08edf1881 100644 --- a/imxweb/projects/rps/src/lib/subscriptions/subscriptions.service.ts +++ b/imxweb/projects/rps/src/lib/subscriptions/subscriptions.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,25 +26,26 @@ import { Injectable } from '@angular/core'; -import { PortalSubscription, PortalSubscriptionInteractive } from 'imx-api-rps'; -import { CollectionLoadParameters, EntitySchema, ExtendedEntityCollectionData, ExtendedTypedEntityCollection } from 'imx-qbm-dbts'; +import { PortalSubscription, PortalSubscriptionInteractive } from '@imx-modules/imx-api-rps'; +import { + CollectionLoadParameters, + EntitySchema, + ExtendedEntityCollectionData, + ExtendedTypedEntityCollection, +} from '@imx-modules/imx-qbm-dbts'; import { RpsApiService } from '../rps-api-client.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class SubscriptionsService { - - constructor(private readonly api: RpsApiService) { - } + constructor(private readonly api: RpsApiService) {} public get PortalSubscriptionSchema(): EntitySchema { return this.api.typedClient.PortalSubscription.GetSchema(); } - public async getSubscriptions(parameters?: CollectionLoadParameters): - Promise> { - + public async getSubscriptions(parameters?: CollectionLoadParameters): Promise> { return this.api.typedClient.PortalSubscription.Get(parameters); } diff --git a/imxweb/projects/rps/src/public_api.ts b/imxweb/projects/rps/src/public_api.ts index 993943c42..6fad357ed 100644 --- a/imxweb/projects/rps/src/public_api.ts +++ b/imxweb/projects/rps/src/public_api.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,5 +31,5 @@ export { RpsConfigModule } from './lib/rps-config.module'; export { SubscriptionsModule } from './lib/subscriptions/subscriptions.module'; export { SubscriptionsComponent } from './lib/subscriptions/subscriptions.component'; -export { ReportButtonModule} from './lib/report-button/report-button.module'; -export { ReportButtonComponent} from './lib/report-button/report-button.component'; +export { ReportButtonModule } from './lib/report-button/report-button.module'; +export { ReportButtonComponent } from './lib/report-button/report-button.component'; diff --git a/imxweb/projects/rps/src/test.ts b/imxweb/projects/rps/src/test.ts index f7ac7d548..159c87d7d 100644 --- a/imxweb/projects/rps/src/test.ts +++ b/imxweb/projects/rps/src/test.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,24 +26,14 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'core-js/es7/reflect'; -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +// Zone must be imported first. Be careful with prettier import organizing +import 'zone.js'; +import 'zone.js/testing'; + import { getTestBed } from '@angular/core/testing'; -import { - BrowserDynamicTestingModule, - platformBrowserDynamicTesting -} from '@angular/platform-browser-dynamic/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; +import 'core-js/es/reflect'; import { TestHelperModule } from 'qbm'; -declare const require: any; - // First, initialize the Angular testing environment. -getTestBed().initTestEnvironment( - [BrowserDynamicTestingModule, TestHelperModule], - platformBrowserDynamicTesting() -); -// Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); -// And load the modules. -context.keys().map(context); +getTestBed().initTestEnvironment([BrowserDynamicTestingModule, TestHelperModule], platformBrowserDynamicTesting()); diff --git a/imxweb/projects/rps/tsconfig.lib.json b/imxweb/projects/rps/tsconfig.lib.json index 6bc419f84..534c734a0 100644 --- a/imxweb/projects/rps/tsconfig.lib.json +++ b/imxweb/projects/rps/tsconfig.lib.json @@ -1,16 +1,17 @@ /* To learn more about this file see: https://angular.io/config/tsconfig. */ { "extends": "../../tsconfig.json", + "angularCompilerOptions": { + "strictTemplates": true + }, "compilerOptions": { "outDir": "../../out-tsc/lib", + "strictNullChecks": true, "declaration": true, "declarationMap": true, "sourceMap": true, "inlineSources": true, "types": [] }, - "exclude": [ - "src/test.ts", - "**/*.spec.ts" - ] + "exclude": ["src/test.ts", "**/*.spec.ts"] } diff --git a/imxweb/projects/rps/tsconfig.lib.prod.json b/imxweb/projects/rps/tsconfig.lib.prod.json index 61c7592e7..fb40dbbb0 100644 --- a/imxweb/projects/rps/tsconfig.lib.prod.json +++ b/imxweb/projects/rps/tsconfig.lib.prod.json @@ -3,8 +3,5 @@ "extends": "./tsconfig.lib.json", "compilerOptions": { "declarationMap": false - }, - "angularCompilerOptions": { - "enableIvy": true } } diff --git a/imxweb/projects/rps/tsconfig.spec.json b/imxweb/projects/rps/tsconfig.spec.json index 16da33db0..6bd74e1f7 100644 --- a/imxweb/projects/rps/tsconfig.spec.json +++ b/imxweb/projects/rps/tsconfig.spec.json @@ -1,17 +1,10 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "strictNullChecks": false, "outDir": "../../out-tsc/spec", - "types": [ - "jasmine", - "node" - ] + "types": ["jasmine", "node"] }, - "files": [ - "src/test.ts" - ], - "include": [ - "**/*.spec.ts", - "**/*.d.ts" - ] + "files": ["src/test.ts"], + "include": ["**/*.spec.ts", "**/*.d.ts"] } diff --git a/imxweb/projects/rps/tslint.json b/imxweb/projects/rps/tslint.json deleted file mode 100644 index 8bab15f53..000000000 --- a/imxweb/projects/rps/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "imx", - "camelCase" - ], - "component-selector": [ - true, - "element", - "imx", - "kebab-case" - ] - } -} diff --git a/imxweb/projects/sac/.compodocrc.json b/imxweb/projects/sac/.compodocrc.json index a6c3a50eb..6c98fce8a 100644 --- a/imxweb/projects/sac/.compodocrc.json +++ b/imxweb/projects/sac/.compodocrc.json @@ -1,6 +1,6 @@ { "name": "IMX Web - SAC Library", - "output": "../../documentation/v92/sac", + "output": "../../documentation/v93/sac", "theme": "material", "assetsFolder": "../../compodoc/assets", "customLogo": "../../compodoc/assets/images/oneidentity-logo.png", diff --git a/imxweb/projects/sac/.eslintrc.json b/imxweb/projects/sac/.eslintrc.json new file mode 100644 index 000000000..e30ddf787 --- /dev/null +++ b/imxweb/projects/sac/.eslintrc.json @@ -0,0 +1,35 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*", "**/*.spec.ts"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": ["imxweb/projects/sac/tsconfig.lib.json", "imxweb/projects/sac/tsconfig.spec.json"], + "createDefaultProgram": true + }, + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "imx", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "imx", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "rules": {} + } + ] +} diff --git a/imxweb/projects/sac/imx-plugin-config.json b/imxweb/projects/sac/imx-plugin-config.json index c0e0963e8..4c90b46d7 100644 --- a/imxweb/projects/sac/imx-plugin-config.json +++ b/imxweb/projects/sac/imx-plugin-config.json @@ -1,8 +1,8 @@ { "qer-app-portal": [ - { - "Container": "sac", - "Name": "SacConfigModule" - } + { + "Container": "sac", + "Name": "SacConfigModule" + } ] } diff --git a/imxweb/projects/sac/karma.conf.js b/imxweb/projects/sac/karma.conf.js index 211193c9d..3e53a3ac8 100644 --- a/imxweb/projects/sac/karma.conf.js +++ b/imxweb/projects/sac/karma.conf.js @@ -15,7 +15,7 @@ module.exports = function (config) { require('karma-junit-reporter'), ], client: { - clearContext: false // leave Jasmine Spec Runner output visible in browser + clearContext: false, // leave Jasmine Spec Runner output visible in browser }, coverageIstanbulReporter: { dir: require('path').join(__dirname, 'results'), @@ -23,7 +23,7 @@ module.exports = function (config) { fixWebpackSourcePaths: true, 'report-config': { html: { - subdir: 'coverage-html' + subdir: 'coverage-html', }, }, thresholds: { @@ -40,13 +40,14 @@ module.exports = function (config) { port: 9876, captureTimeout: 210000, browserDisconnectTolerance: 3, - browserDisconnectTimeout : 210000, - browserNoActivityTimeout : 210000, + browserDisconnectTimeout: 210000, + browserNoActivityTimeout: 210000, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], singleRun: false, - restartOnFileChange: true + failOnEmptyTestSuite: false, + restartOnFileChange: true, }); }; diff --git a/imxweb/projects/sac/ng-package.dynamic.json b/imxweb/projects/sac/ng-package.dynamic.json index cf1da4569..92e2e6b4f 100644 --- a/imxweb/projects/sac/ng-package.dynamic.json +++ b/imxweb/projects/sac/ng-package.dynamic.json @@ -16,12 +16,11 @@ "@elemental-ui/core", "@ngx-translate/core", "@ngx-translate/http-loader", - "applicationinsights-js", "billboard.js" ], - "dest": "../../html/sac", + "dest": "../../html/qer-app-portal/sac", "lib": { "entryFile": "src/public_api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] } } diff --git a/imxweb/projects/sac/ng-package.json b/imxweb/projects/sac/ng-package.json index 78073cb8b..657dc704f 100644 --- a/imxweb/projects/sac/ng-package.json +++ b/imxweb/projects/sac/ng-package.json @@ -16,12 +16,11 @@ "@elemental-ui/core", "@ngx-translate/core", "@ngx-translate/http-loader", - "applicationinsights-js", "billboard.js" ], "dest": "../../dist/sac", "lib": { "entryFile": "src/public_api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] } } diff --git a/imxweb/projects/sac/package.json b/imxweb/projects/sac/package.json index 61cfeef62..1143c92c1 100644 --- a/imxweb/projects/sac/package.json +++ b/imxweb/projects/sac/package.json @@ -1,29 +1,7 @@ { "name": "sac", - "version": "9.2.1", + "version": "9.3.0", "private": true, - "peerDependencies": { - - }, - "dependencies": { - "@angular/animations": "^11.2.14", - "@angular/cdk": "^11.2.13", - "@angular/common": "^11.2.14", - "@angular/compiler": "^11.2.14", - "@angular/core": "^11.2.14", - "@angular/forms": "^11.2.14", - "@angular/material": "^11.2.13", - "@angular/material-moment-adapter": "^11.2.13", - "@angular/platform-browser": "^11.2.14", - "@angular/platform-browser-dynamic": "^11.2.14", - "@angular/router": "^11.2.14", - "@elemental-ui/cadence-icon": "file:imx-modules/elemental-ui_cadence-icon.tgz", - "@elemental-ui/core": "file:imx-modules/elemental-ui_core.tgz", - "@ngx-translate/core": "^11.0.1", - "@ngx-translate/http-loader": "^4.0.0", - "applicationinsights-js": "^1.0.21", - "billboard.js": "^1.8.0" - }, "bundledDependencies": [ "imx-api-sac" ] diff --git a/imxweb/projects/sac/project.json b/imxweb/projects/sac/project.json new file mode 100644 index 000000000..eae60b493 --- /dev/null +++ b/imxweb/projects/sac/project.json @@ -0,0 +1,49 @@ +{ + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "name": "sac", + "sourceRoot": "projects/sac/src", + "implicitDependencies": ["qer"], + "projectType": "library", + "generators": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "prefix": "imx", + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:ng-packagr", + "options": { + "tsConfig": "projects/sac/tsconfig.lib.json", + "project": "projects/sac/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "projects/sac/tsconfig.lib.prod.json" + }, + "dynamic": { + "project": "projects/sac/ng-package.dynamic.json" + } + }, + "outputs": ["{workspaceRoot}/dist/sac"] + }, + "test": { + "executor": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/sac/src/test.ts", + "tsConfig": "projects/sac/tsconfig.spec.json", + "karmaConfig": "projects/sac/karma.conf.js", + "stylePreprocessorOptions": { + "includePaths": ["./shared/assets", "./shared/scss"] + } + } + }, + "lint": { + "executor": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": ["projects/sac/tsconfig.lib.json", "projects/sac/tsconfig.spec.json"], + "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + } + } + } +} diff --git a/imxweb/projects/sac/src/lib/init.service.ts b/imxweb/projects/sac/src/lib/init.service.ts index ee065dc5c..8afb476de 100644 --- a/imxweb/projects/sac/src/lib/init.service.ts +++ b/imxweb/projects/sac/src/lib/init.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,18 +24,18 @@ * */ -import { Injectable } from "@angular/core"; -import { ExtService } from "qbm"; -import { SapComplianceViolationComponent } from "./sap-compliance-violation/sap-compliance-violation.component"; +import { Injectable } from '@angular/core'; +import { ExtService } from 'qbm'; +import { SapComplianceViolationComponent } from './sap-compliance-violation/sap-compliance-violation.component'; @Injectable({ providedIn: 'root' }) export class InitService { - - constructor( - private readonly extService: ExtService - ) { } + constructor(private readonly extService: ExtService) {} onInit() { - this.extService.register('RuleViolationsTab', { instance: SapComplianceViolationComponent, inputData: {label: '#LDS#Heading SAP Functions'}}); + this.extService.register('RuleViolationsTab', { + instance: SapComplianceViolationComponent, + inputData: { label: '#LDS#Heading SAP Functions' }, + }); } } diff --git a/imxweb/projects/sac/src/lib/sac-api-client.service.ts b/imxweb/projects/sac/src/lib/sac-api-client.service.ts index 263f341f8..bcb0688da 100644 --- a/imxweb/projects/sac/src/lib/sac-api-client.service.ts +++ b/imxweb/projects/sac/src/lib/sac-api-client.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,12 +25,12 @@ */ import { Injectable } from '@angular/core'; -import { V2Client, TypedClient } from 'imx-api-sac'; -import { ApiClient } from 'imx-qbm-dbts'; +import { V2Client, TypedClient } from '@imx-modules/imx-api-sac'; +import { ApiClient } from '@imx-modules/imx-qbm-dbts'; import { AppConfigService, ClassloggerService, ImxTranslationProviderService } from 'qbm'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class SapComplianceApiService { private tc: TypedClient; @@ -50,7 +50,8 @@ export class SapComplianceApiService { constructor( private readonly config: AppConfigService, private readonly logger: ClassloggerService, - private readonly translationProvider: ImxTranslationProviderService) { + private readonly translationProvider: ImxTranslationProviderService, + ) { try { // Use schema loaded by QBM client const schemaProvider = config.client; diff --git a/imxweb/projects/sac/src/lib/sac-config.module.ts b/imxweb/projects/sac/src/lib/sac-config.module.ts index 1795bf4d1..f5f442a4d 100644 --- a/imxweb/projects/sac/src/lib/sac-config.module.ts +++ b/imxweb/projects/sac/src/lib/sac-config.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -61,7 +61,10 @@ import { MatSortModule } from '@angular/material/sort'; ], }) export class SacConfigModule { - constructor(private readonly initservice: InitService, private readonly logger: ClassloggerService) { + constructor( + private readonly initservice: InitService, + private readonly logger: ClassloggerService, + ) { this.logger.info(this, '🔥 SAC loaded'); this.initservice.onInit(); this.logger.info(this, '▶️ SAC initialized'); diff --git a/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-ability/sap-compliance-violation-views-by-ability-builder.ts b/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-ability/sap-compliance-violation-views-by-ability-builder.ts index 6ab88b244..60fc66aa7 100644 --- a/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-ability/sap-compliance-violation-views-by-ability-builder.ts +++ b/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-ability/sap-compliance-violation-views-by-ability-builder.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,9 +24,9 @@ * */ -import { DbObjectKey, EntityCollectionData, EntityData, TypedEntityBuilder, TypedEntityCollectionData } from 'imx-qbm-dbts'; +import { DbObjectKey, EntityCollectionData, EntityData, TypedEntityBuilder, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { SapComplianceByAbilityEntity } from './sap-compliance-violation-views-by-ability-entity'; -import { SAPUserFunctionSrcFLD } from 'imx-api-sac'; +import { SAPUserFunctionSrcFLD } from '@imx-modules/imx-api-sac'; export class SapComplianceByAbilityBuilder { public readonly entitySchema = SapComplianceByAbilityEntity.GetEntitySchema(); diff --git a/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-ability/sap-compliance-violation-views-by-ability-entity.ts b/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-ability/sap-compliance-violation-views-by-ability-entity.ts index fbc57bc87..cf78c926a 100644 --- a/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-ability/sap-compliance-violation-views-by-ability-entity.ts +++ b/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-ability/sap-compliance-violation-views-by-ability-entity.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { TypedEntity, EntitySchema, DisplayColumns, ValType, IReadValue } from 'imx-qbm-dbts'; +import { TypedEntity, EntitySchema, DisplayColumns, ValType, IReadValue } from '@imx-modules/imx-qbm-dbts'; export class SapComplianceByAbilityEntity extends TypedEntity { public readonly Ident_SAPProfile: IReadValue = this.GetEntityValue('Ident_SAPProfile'); diff --git a/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-ability/sap-compliance-violation-views-by-ability.component.html b/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-ability/sap-compliance-violation-views-by-ability.component.html index 1671260df..9a2fa46b7 100644 --- a/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-ability/sap-compliance-violation-views-by-ability.component.html +++ b/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-ability/sap-compliance-violation-views-by-ability.component.html @@ -19,16 +19,29 @@ [groupData]="groupData" [mode]="'manual'" data-imx-identifier="role-recommendation-result-data-table" - detailViewVisible="false" + [detailViewVisible]="false" (groupDataChanged)="onGroupingChange($event)" > - - - - - - - + + + + + + + + + + + + + +
    diff --git a/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-ability/sap-compliance-violation-views-by-ability.component.scss b/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-ability/sap-compliance-violation-views-by-ability.component.scss index a11973441..111600262 100644 --- a/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-ability/sap-compliance-violation-views-by-ability.component.scss +++ b/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-ability/sap-compliance-violation-views-by-ability.component.scss @@ -1,12 +1,12 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; -.sap-analysis{ +.sap-analysis { display: flex; flex-direction: column; height: 100%; justify-content: space-between; gap: 16px; - &__table{ + &__table { overflow: hidden auto; flex-grow: 1; } diff --git a/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-ability/sap-compliance-violation-views-by-ability.component.ts b/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-ability/sap-compliance-violation-views-by-ability.component.ts index 10abcdf37..b3eb30f3d 100644 --- a/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-ability/sap-compliance-violation-views-by-ability.component.ts +++ b/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-ability/sap-compliance-violation-views-by-ability.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,9 +24,8 @@ * */ -import { Component, Input, ViewChild } from '@angular/core'; -import { MatSort } from '@angular/material/sort'; -import { ByAbilityResult, SAPUserFunctionSrcFLD } from 'imx-api-sac'; +import { Component, Input } from '@angular/core'; +import { ByAbilityResult, SAPUserFunctionSrcFLD } from '@imx-modules/imx-api-sac'; import { CollectionLoadParameters, EntitySchema, @@ -35,11 +34,11 @@ import { GroupInfoData, TypedEntityCollectionData, ValType, -} from 'imx-qbm-dbts'; +} from '@imx-modules/imx-qbm-dbts'; +import { TranslateService } from '@ngx-translate/core'; import { DataSourceToolBarGroup, DataSourceToolbarGroupData, DataSourceToolbarSettings, DataTableGroupedData } from 'qbm'; import { SapComplianceByAbilityBuilder } from './sap-compliance-violation-views-by-ability-builder'; import { SapComplianceByAbilityEntity } from './sap-compliance-violation-views-by-ability-entity'; -import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'imx-sap-compliance-violation-views-by-ability', templateUrl: './sap-compliance-violation-views-by-ability.component.html', @@ -71,19 +70,26 @@ export class SapComplianceViolationViewsByAbilityComponent { * Called when search action is emitted. * @param searchValue current search param */ - public onSearch(searchValue: string): void { - if (!!searchValue) { + public onSearch(searchValue: string | undefined): void { + if (searchValue) { searchValue = searchValue.toLocaleLowerCase(); - this.dstSettings.dataSource.Data = this.defaultData.filter( + const Data = this.defaultData.filter( (profile) => - profile.Ident_SAPProfile.value.toLocaleLowerCase().includes(searchValue) || - profile.Ident_SAPAuthObject.value.toLocaleLowerCase().includes(searchValue) || - profile.Ident_SAPField.value.toLocaleLowerCase().includes(searchValue) || - profile.LowerLimit.value.toLocaleLowerCase().includes(searchValue) || - profile.UpperLimit.value.toLocaleLowerCase().includes(searchValue) + profile.Ident_SAPProfile.value.toLocaleLowerCase().includes(searchValue!) || + profile.Ident_SAPAuthObject.value.toLocaleLowerCase().includes(searchValue!) || + profile.Ident_SAPField.value.toLocaleLowerCase().includes(searchValue!) || + profile.LowerLimit.value.toLocaleLowerCase().includes(searchValue!) || + profile.UpperLimit.value.toLocaleLowerCase().includes(searchValue!), ); + this.dstSettings.dataSource = { + Data, + totalCount: Data.length, + }; } else { - this.dstSettings.dataSource.Data = this.defaultData; + this.dstSettings.dataSource = { + Data: this.defaultData, + totalCount: this.defaultData.length, + }; } this.dstSettings = { ...this.dstSettings, navigationState: { ...this.dstSettings.navigationState, search: searchValue } }; } @@ -94,8 +100,9 @@ export class SapComplianceViolationViewsByAbilityComponent { */ public onGroupingChange(groupKey: string): void { const groupedData = this.groupData[groupKey]; - let filter = groupedData.navigationState?.filter; - groupedData.data = this.getFilteredData(filter[0]); + if (groupedData.navigationState?.filter) { + groupedData.data = this.getFilteredData(groupedData.navigationState.filter[0]); + } groupedData.settings = { displayedColumns: this.dstSettings.displayedColumns, dataModel: this.dstSettings.dataModel, @@ -104,7 +111,7 @@ export class SapComplianceViolationViewsByAbilityComponent { totalCount: groupedData.data.length, }, entitySchema: this.dstSettings.entitySchema, - navigationState: groupedData.navigationState, + navigationState: groupedData.navigationState ?? {}, }; } @@ -149,8 +156,8 @@ export class SapComplianceViolationViewsByAbilityComponent { Property: { Type: ValType.String, ColumnName: 'DisplaySapFunctionInstance', - Display: this.translateService.instant('#LDS#SAP function instance'), }, + Display: this.translateService.instant('#LDS#SAP function instance'), }, getData: async () => await this.groupingData('DisplaySapFunctionInstance'), }, @@ -159,8 +166,8 @@ export class SapComplianceViolationViewsByAbilityComponent { Property: { Type: ValType.String, ColumnName: 'DisplaySapTransaction', - Display: this.translateService.instant('#LDS#SAP transaction'), }, + Display: this.translateService.instant('#LDS#SAP transaction'), }, getData: async () => await this.groupingData('DisplaySapTransaction'), }, @@ -176,7 +183,7 @@ export class SapComplianceViolationViewsByAbilityComponent { public async groupingData(groupColumn: string): Promise { const Groups: GroupInfo[] = []; this.dataSource.Data?.map((item, index) => { - if (Groups.every((groupItem) => item[groupColumn].value !== groupItem.Display[0].Display)) { + if (Groups.every((groupItem) => item[groupColumn].value !== (groupItem?.Display?.[0]?.Display ?? ''))) { const groupItems = this.dataSource.Data.filter((row) => row[groupColumn].value === item[groupColumn].value); Groups.push({ Display: [{ Display: item[groupColumn].value }], @@ -197,6 +204,6 @@ export class SapComplianceViolationViewsByAbilityComponent { * @returns Filtered data source. */ private getFilteredData(filter: FilterData): SapComplianceByAbilityEntity[] { - return this.dataSource.Data.filter((dataRow) => filter.Values.indexOf(dataRow[filter.ColumnName].value) >= 0); + return this.dataSource.Data.filter((dataRow) => (filter?.Values?.indexOf(dataRow?.[filter.ColumnName ?? '']?.value) ?? -1) >= 0); } } diff --git a/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-role/sap-compliance-violation-views-by-role.component.html b/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-role/sap-compliance-violation-views-by-role.component.html index bd8d0c546..688027a54 100644 --- a/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-role/sap-compliance-violation-views-by-role.component.html +++ b/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-role/sap-compliance-violation-views-by-role.component.html @@ -1,7 +1,7 @@
    - -
    -

    {{'#LDS#Heading Rule Analysis by Role' | translate}}

    + +
    +

    {{ '#LDS#Heading Rule Analysis by Role' | translate }}

    -
    {{ '#LDS#Created on' | translate }} @@ -27,7 +27,7 @@

    > { const isBusy = this.busyService.beginBusy(); - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.euiLoadingService.show()); + if (this.euiLoadingService.overlayRefs.length === 0) { + this.euiLoadingService.show(); + } try { this.userProcesses = await this.userProcessService.get(); - this.displayedColumns = ['xDateInserted', 'display', 'processState' ]; + this.displayedColumns = ['xDateInserted', 'display', 'processState']; } finally { - setTimeout(() => this.euiLoadingService.hide(overlayRef)); + this.euiLoadingService.hide(); isBusy.endBusy(); } } - } diff --git a/imxweb/projects/qer/src/lib/user-process/user-process.module.ts b/imxweb/projects/qer/src/lib/user-process/user-process.module.ts index ab99e3c7d..4b6fc8da4 100644 --- a/imxweb/projects/qer/src/lib/user-process/user-process.module.ts +++ b/imxweb/projects/qer/src/lib/user-process/user-process.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,30 +24,28 @@ * */ -import { NgModule } from "@angular/core"; -import { UserProcessComponent } from "./user-process.component"; -import { CommonModule } from "@angular/common"; -import { DateModule, RouteGuardService } from "qbm"; -import { EuiCoreModule, EuiMaterialModule } from "@elemental-ui/core"; -import { TranslateModule } from "@ngx-translate/core"; -import { FormsModule, ReactiveFormsModule } from "@angular/forms"; -import { RouterModule, Routes } from "@angular/router"; -import { MatCardModule } from "@angular/material/card"; -import { MatTableModule } from "@angular/material/table"; +import { NgModule } from '@angular/core'; +import { UserProcessComponent } from './user-process.component'; +import { CommonModule } from '@angular/common'; +import { DateModule, RouteGuardService } from 'qbm'; +import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; +import { TranslateModule } from '@ngx-translate/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { RouterModule, Routes } from '@angular/router'; +import { MatCardModule } from '@angular/material/card'; +import { MatTableModule } from '@angular/material/table'; const routes: Routes = [ { path: 'userprocess', component: UserProcessComponent, canActivate: [RouteGuardService], - resolve: [RouteGuardService] - } + resolve: [RouteGuardService], + }, ]; @NgModule({ - declarations: [ - UserProcessComponent - ], + declarations: [UserProcessComponent], imports: [ CommonModule, EuiCoreModule, @@ -60,8 +58,6 @@ const routes: Routes = [ EuiMaterialModule, DateModule, ], - exports: [ - UserProcessComponent - ], + exports: [UserProcessComponent], }) export class UserProcessModule {} diff --git a/imxweb/projects/qer/src/lib/user-process/user-processes.service.ts b/imxweb/projects/qer/src/lib/user-process/user-processes.service.ts index 9960e5298..e095c7490 100644 --- a/imxweb/projects/qer/src/lib/user-process/user-processes.service.ts +++ b/imxweb/projects/qer/src/lib/user-process/user-processes.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,20 +26,15 @@ import { Injectable } from '@angular/core'; import { QerApiService } from '../qer-api-client.service'; -import { ProcessChain } from 'imx-api-qer'; +import { ProcessChain } from '@imx-modules/imx-api-qer'; @Injectable({ providedIn: 'root', }) export class UserProcessService { - - constructor( - private readonly qerApiService: QerApiService, - ) {} + constructor(private readonly qerApiService: QerApiService) {} public async get(): Promise { return this.qerApiService.v2Client.portal_userprocess_get(); } - } - diff --git a/imxweb/projects/qer/src/lib/user/pending-items-type.interface.ts b/imxweb/projects/qer/src/lib/user/pending-items-type.interface.ts index b71dbe928..a17b3edd1 100644 --- a/imxweb/projects/qer/src/lib/user/pending-items-type.interface.ts +++ b/imxweb/projects/qer/src/lib/user/pending-items-type.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,5 +25,5 @@ */ export interface PendingItemsType { - [key: string]: number; + [key: string]: number; } diff --git a/imxweb/projects/qer/src/lib/user/user-model.service.ts b/imxweb/projects/qer/src/lib/user/user-model.service.ts index 79a6d8f45..f7bcad3a5 100644 --- a/imxweb/projects/qer/src/lib/user/user-model.service.ts +++ b/imxweb/projects/qer/src/lib/user/user-model.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,11 +27,11 @@ import { Injectable } from '@angular/core'; import { Subject } from 'rxjs'; -import { PortalPersonReports, UserConfig, UserFeatureInfo, UserGroupInfo } from 'imx-api-qer'; -import { PendingItemsType } from './pending-items-type.interface'; -import { QerApiService } from '../qer-api-client.service'; +import { PortalPersonReports, UserConfig, UserFeatureInfo, UserGroupInfo } from '@imx-modules/imx-api-qer'; +import { CachedPromise } from '@imx-modules/imx-qbm-dbts'; import { CacheService, SettingsService } from 'qbm'; -import { CachedPromise } from 'imx-qbm-dbts'; +import { QerApiService } from '../qer-api-client.service'; +import { PendingItemsType } from './pending-items-type.interface'; @Injectable() export class UserModelService { @@ -42,15 +42,21 @@ export class UserModelService { private pendingItemsCache: CachedPromise; private cachedGroups: CachedPromise; private cachedFeatures: CachedPromise; + private cachedUserConfig: CachedPromise; - constructor(private readonly qerClient: QerApiService, private readonly settingsService: SettingsService, cacheService: CacheService) { + constructor( + private readonly qerClient: QerApiService, + private readonly settingsService: SettingsService, + cacheService: CacheService, + ) { this.pendingItemsCache = cacheService.buildCache(() => this.qerClient.client.portal_pendingitems_get()); this.cachedGroups = cacheService.buildCache(() => this.qerClient.client.portal_usergroups_get()); this.cachedFeatures = cacheService.buildCache(() => this.qerClient.client.portal_features_get()); + this.cachedUserConfig = cacheService.buildCache(() => this.qerClient.client.portal_person_config_get()); } public getUserConfig(): Promise { - return this.qerClient.client.portal_person_config_get(); + return this.cachedUserConfig.get(); } public getPendingItems(): Promise { @@ -72,7 +78,7 @@ export class UserModelService { public async getDirectReports(uidperson?: string): Promise { const features = (await this.cachedFeatures.get()).Features; - if (features.find((feature) => feature === 'Portal_UI_PersonManager')) { + if (features?.find((feature) => feature === 'Portal_UI_PersonManager')) { return ( await this.qerClient.typedClient.PortalPersonReports.Get({ OnlyDirect: true, diff --git a/imxweb/projects/qer/src/lib/user/user.module.ts b/imxweb/projects/qer/src/lib/user/user.module.ts index 134b2b84d..c47175845 100644 --- a/imxweb/projects/qer/src/lib/user/user.module.ts +++ b/imxweb/projects/qer/src/lib/user/user.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -31,11 +31,7 @@ import { UserModelService } from './user-model.service'; @NgModule({ declarations: [], - imports: [ - CommonModule - ], - providers: [ - UserModelService - ] + imports: [CommonModule], + providers: [UserModelService], }) -export class UserModule { } +export class UserModule {} diff --git a/imxweb/projects/qer/src/lib/view-config/view-config.service.ts b/imxweb/projects/qer/src/lib/view-config/view-config.service.ts index 76d750baf..5add231db 100644 --- a/imxweb/projects/qer/src/lib/view-config/view-config.service.ts +++ b/imxweb/projects/qer/src/lib/view-config/view-config.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,29 +25,26 @@ */ import { Injectable } from '@angular/core'; -import { ViewConfigData } from 'imx-api-qer'; +import { ViewConfigData } from '@imx-modules/imx-api-qer'; +import { DataModel } from '@imx-modules/imx-qbm-dbts'; import { DataSourceToolbarViewConfig, DSTViewConfig, isConfigDefault, isDefaultId } from 'qbm'; import { QerApiService } from '../qer-api-client.service'; -import { DataModel } from 'imx-qbm-dbts'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ViewConfigService { + private allConfigs: DSTViewConfig[] = []; - private allConfigs: DSTViewConfig[]; - - constructor( - private readonly qerClient: QerApiService, - ) { } + constructor(private readonly qerClient: QerApiService) {} /** * Get all configs associated to the current viewId * @param viewId ViewConfigData.ViewId * @returns viewConfigData[] */ - public async getViewConfig(viewId: string): Promise { - return (await this.qerClient.v2Client.portal_viewconfig_get()).filter(config => config.ViewId === viewId); + public async getViewConfig(viewId: string, signal?: AbortSignal): Promise { + return (await this.qerClient.v2Client.portal_viewconfig_get(undefined, { signal })).filter((config) => config.ViewId === viewId); } /** @@ -72,30 +69,28 @@ export class ViewConfigService { * @param viewId ViewConfigData.ViewId * @returns The list of available configs along with the viewId for the DST */ - public async getInitialDSTExtension(dataModel: DataModel, viewId: string): Promise { + public async getInitialDSTExtension(dataModel: DataModel, viewId: string, signal?: AbortSignal): Promise { // Take configs from the data model, this should be a superset of the user's configs. - const userConfigs = await this.getViewConfig(viewId); - const userDefault = userConfigs.find(config => config.UseAsDefault); - this.allConfigs = dataModel?.Configurations?.map(config => { - // If we have a user default, set that as the default here, otherwise set the default, if there is no default then set as false - const UseAsDefault = userDefault - ? userDefault.Id === config?.Id - : (dataModel?.DefaultConfigId - ? dataModel.DefaultConfigId === config?.Id - : false); + const userConfigs = await this.getViewConfig(viewId, signal); + const userDefault = userConfigs.find((config) => config.UseAsDefault); + this.allConfigs = + dataModel?.Configurations?.map((config) => { + // If we have a user default, set that as the default here, otherwise set the default from the default config id which is stored as the display name on the DataModelConfig, if there is no default then set as false + const UseAsDefault = userDefault + ? userDefault.Id === config?.Id + : dataModel?.DefaultConfigId + ? dataModel.DefaultConfigId === config?.DisplayName + : false; - // Use the user config if it exists, if not this is a readonly config - let IsReadOnly: boolean; - const userConfig = userConfigs.find(userConfig => config?.Id === userConfig?.Id); - userConfig - ? config = userConfig - : IsReadOnly = true; + // Use the user config if it exists, if not this is a readonly config + let IsReadOnly: boolean = false; + const userConfig = userConfigs.find((userConfig) => config?.Id === userConfig?.Id); + userConfig ? (config = userConfig) : (IsReadOnly = true); - return config?.DisplayName - ? {...config, UseAsDefault, IsReadOnly, ViewId: viewId} - : {...config, UseAsDefault, IsReadOnly, DisplayName: config?.Id, ViewId: viewId}; - } - ); + return config?.DisplayName + ? { ...config, UseAsDefault, IsReadOnly, ViewId: viewId } + : { ...config, UseAsDefault, IsReadOnly, DisplayName: config?.Id, ViewId: viewId }; + }) || []; if (!this.allConfigs) { // There were no data model configs, use the user configs as is @@ -103,34 +98,36 @@ export class ViewConfigService { } return { viewConfigs: this.allConfigs, - viewId - } + viewId, + }; } - /** + /** * Now assumes the datamodel is out of sync and will only update the configs based on what isn't present in the user configs * @param viewId ViewConfigData.ViewId * @returns The list of available configs along with the viewId for the DST */ - public async getDSTExtensionChanges(viewId: string): Promise { - const userConfigs = await this.getViewConfig(viewId); - const userConfigIds = userConfigs.map(config => config?.Id); - // Filter out any removed configs that aren't marked as readonly - this.allConfigs.filter(config => config.IsReadOnly || userConfigIds.includes(config.Id)); - // Add / update any new configs - userConfigs.forEach(userConfig => { - const ind = this.allConfigs.findIndex(oldConfig => oldConfig.Id === userConfig.Id) - ind < 0 - ? this.allConfigs.push(userConfig) - : this.allConfigs[ind] = userConfig; - }) - return { - viewConfigs: this.allConfigs, - viewId - } - } + public async getDSTExtensionChanges(viewId: string): Promise { + const userConfigs = await this.getViewConfig(viewId); + const userConfigIds = userConfigs.map((config) => config?.Id); + // Filter out any removed configs that aren't marked as readonly + this.allConfigs = this.allConfigs.filter((config) => config.IsReadOnly || userConfigIds.includes(config.Id)); + // Add / update any new configs + userConfigs.forEach((userConfig) => { + const ind = this.allConfigs.findIndex((oldConfig) => oldConfig.Id === userConfig.Id); + ind < 0 ? this.allConfigs.push(userConfig) : (this.allConfigs[ind] = userConfig); + }); + return { + viewConfigs: this.allConfigs, + viewId, + }; + } + /** + * Check to see if we have a user config set as the default view + * @returns if we are using a user config as default + */ public isDefaultConfigSet(): boolean { - return !!this.allConfigs.find(config => isConfigDefault(config) && !isDefaultId(config)); + return !!this.allConfigs.find((config) => isConfigDefault(config) && !isDefaultId(config)); } } diff --git a/imxweb/projects/qer/src/lib/view-devices/create-new-device/create-new-device.component.html b/imxweb/projects/qer/src/lib/view-devices/create-new-device/create-new-device.component.html index 93b967c6d..dd794c327 100644 --- a/imxweb/projects/qer/src/lib/view-devices/create-new-device/create-new-device.component.html +++ b/imxweb/projects/qer/src/lib/view-devices/create-new-device/create-new-device.component.html @@ -1,12 +1,12 @@
    - + -
    +
    @@ -20,7 +20,7 @@ data-imx-identifier="new-device-sidesheet-button-save" (click)="createDevice()" [disabled]="formGroup.pristine || formGroup.invalid" - > - {{'#LDS#Create'| translate}} + > + {{ '#LDS#Create' | translate }}
    diff --git a/imxweb/projects/qer/src/lib/view-devices/create-new-device/create-new-device.component.scss b/imxweb/projects/qer/src/lib/view-devices/create-new-device/create-new-device.component.scss index 3b1922649..e69de29bb 100644 --- a/imxweb/projects/qer/src/lib/view-devices/create-new-device/create-new-device.component.scss +++ b/imxweb/projects/qer/src/lib/view-devices/create-new-device/create-new-device.component.scss @@ -1,7 +0,0 @@ -.imx-new-device-sidesheet { - ul { - li { - list-style-type: disc; - } - } -} diff --git a/imxweb/projects/qer/src/lib/view-devices/create-new-device/create-new-device.component.ts b/imxweb/projects/qer/src/lib/view-devices/create-new-device/create-new-device.component.ts index 4035c2389..555862fbb 100644 --- a/imxweb/projects/qer/src/lib/view-devices/create-new-device/create-new-device.component.ts +++ b/imxweb/projects/qer/src/lib/view-devices/create-new-device/create-new-device.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,25 +24,26 @@ * */ -import { Component, Inject } from "@angular/core"; -import { UntypedFormGroup } from "@angular/forms"; -import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from "@elemental-ui/core"; -import { PortalDevices } from "imx-api-qer"; -import { IEntity, ValueStruct } from "imx-qbm-dbts"; -import { BaseCdr, ColumnDependentReference, SnackBarService } from "qbm"; -import { ViewDevicesService } from "../view-devices.service"; +import { Component, Inject } from '@angular/core'; +import { UntypedFormGroup } from '@angular/forms'; +import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; +import { PortalDevices } from '@imx-modules/imx-api-qer'; +import { IEntity, ValueStruct } from '@imx-modules/imx-qbm-dbts'; +import { BaseCdr, ColumnDependentReference, SnackBarService } from 'qbm'; +import { ViewDevicesService } from '../view-devices.service'; @Component({ selector: 'imx-create-new-device', templateUrl: './create-new-device.component.html', - styleUrls: ['./create-new-device.component.scss'] + styleUrls: ['./create-new-device.component.scss'], }) export class CreateNewDeviceComponent { public readonly formGroup = new UntypedFormGroup({}); public cdrList: ColumnDependentReference[] = []; constructor( - @Inject(EUI_SIDESHEET_DATA) public data: { newDevice: PortalDevices, deviceEntityConfig: string[], deviceModelValueStruct: ValueStruct }, + @Inject(EUI_SIDESHEET_DATA) + public data: { newDevice: PortalDevices; deviceEntityConfig: string[]; deviceModelValueStruct: ValueStruct }, public viewDevicesService: ViewDevicesService, private readonly sidesheetRef: EuiSidesheetRef, private readonly snackbar: SnackBarService, @@ -52,14 +53,11 @@ export class CreateNewDeviceComponent { } private createCdrList(entity: IEntity): BaseCdr[] { - const cdrList = []; + const cdrList: BaseCdr[] = []; const columnNames: string[] = this.data.deviceEntityConfig; - columnNames?.forEach(async (name) => { + columnNames?.forEach((name) => { try { - if (name === 'UID_HardwareType') { - await entity.GetColumn(name).PutValueStruct(this.data.deviceModelValueStruct); - cdrList.unshift(new BaseCdr(entity.GetColumn(name), null, true)); - } else { + if (name !== 'UID_HardwareType') { cdrList.push(new BaseCdr(entity.GetColumn(name))); } } catch {} diff --git a/imxweb/projects/qer/src/lib/view-devices/view-devices-home/view-devices.component.html b/imxweb/projects/qer/src/lib/view-devices/view-devices-home/view-devices.component.html index de3bc03e9..063ba9807 100644 --- a/imxweb/projects/qer/src/lib/view-devices/view-devices-home/view-devices.component.html +++ b/imxweb/projects/qer/src/lib/view-devices/view-devices-home/view-devices.component.html @@ -1,50 +1,67 @@ - - -
    -
    - #LDS#Heading Devices + +
    +
    +

    #LDS#Heading Devices

    -
    - - - - - - - - - - - -
    + + + +
    + {{ entitySchema?.Columns?.[DisplayColumns.DISPLAY_PROPERTYNAME]?.Display }} + +
    + {{ item?.GetEntity()?.GetDisplay() }} +
    +
    + {{ entitySchema?.Columns?.UID_HardwareType?.Display }} + + + {{ item.UID_HardwareType.Column.GetDisplayValue() }} + + + {{ entitySchema?.Columns?.UID_PersonOwner?.Display }} + + + {{ item.UID_PersonOwner?.Column.GetDisplayValue() }} + + + {{ '#LDS#Display name' | translate }} + +
    {{ item.GetEntity().GetDisplay() }}
    +
    {{ item.Description.Column.GetDisplayValue() }}
    +
    +
    - - + + - + - +
    - {{'#LDS#Group name' | translate}} + {{ '#LDS#Group name' | translate }} - {{'#LDS#Description' | translate}}{{ data.Description }}{{ '#LDS#Description' | translate }} + {{ data.Description }} + {{'#LDS#Group type' | translate}}{{ '#LDS#Group type' | translate }} {{ data.GroupType }}
    - -
    + +
    -

    {{'#LDS#Heading Selected Role' | translate}}: {{selectedRole?.GroupName}}

    +

    + {{ '#LDS#Heading Selected Role' | translate }}: {{ selectedRole?.GroupName }} +

    - - +
    - +
    - + - + - + - + - + @@ -91,7 +99,7 @@

    {{'#LDS#Headi

    {{'#LDS#Authentication' | translate}}{{ '#LDS#Authentication' | translate }} {{ data.Ident_SAPProfile }} {{'#LDS#Objects' | translate}}{{ '#LDS#Objects' | translate }} {{ data.Objects }} {{'#LDS#Field' | translate}}{{ '#LDS#Field' | translate }} {{ data.Field }} {{'#LDS#From' | translate}}{{ '#LDS#From' | translate }} {{ data.LowerLimit }} {{'#LDS#To' | translate}}{{ '#LDS#To' | translate }} {{ data.UpperLimit }}
    -

    {{ '#LDS#No data' | translate}}

    +

    {{ '#LDS#No data' | translate }}

    diff --git a/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-role/sap-compliance-violation-views-by-role.component.scss b/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-role/sap-compliance-violation-views-by-role.component.scss index 48bdc1bc5..f697d3ed8 100644 --- a/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-role/sap-compliance-violation-views-by-role.component.scss +++ b/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-role/sap-compliance-violation-views-by-role.component.scss @@ -1,108 +1,84 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/mixins'; -.sap-analysis{ +.sap-analysis { display: flex; gap: 16px; height: 100%; justify-content: space-between; - &__table{ + &__table { overflow: hidden auto; flex-grow: 1; } - .mat-card{ - width: 100%; + .mat-mdc-card { + @include flex-column-container($width: 100%); transition: width 400ms ease; - display: flex; - flex-direction: column; gap: 20px; - &--decreased{ + + &--decreased { padding: 0; width: 48px; - .mat-card__header{ + + .mat-mdc-card__header { height: 100%; - &__title{ - display:none; + + &__title { + display: none; } - button{ + + button { width: 48px; padding: 20px 0; min-width: unset; height: 100%; - ::ng-deep .mat-button-wrapper{ + ::ng-deep .mdc-button__label { height: 100%; display: flex; flex-direction: column; } - h3{ + + h3 { display: block; margin: auto 0; rotate: -90deg; + text-wrap: nowrap; } - .eui-icon{ + + .eui-icon { rotate: 180deg; } } - } - .sap-analysis__table, eui-search{ + + .sap-analysis__table, + eui-search { display: none; } } - &__header{ - display:flex; + + &__header { + display: flex; align-items: center; justify-content: space-between; - &__title{ - &--wide{ - margin:auto; + + &__title { + &--wide { + margin: auto; } } - button{ + + button { padding: 0; min-width: 32px; - h3{ + + h3 { display: none; } - .eui-icon{ + + .eui-icon { transition: rotate 400ms ease; } } } } - .mat-table{ - box-shadow: none; - .td--no-wrap{ - white-space: nowrap; - } - .sap-analysis-table-row-highlighted:hover{ - background-color: $color-gray-5; - cursor: pointer; - } - } -} -:host ::ng-deep .mat-table{ - tbody{ - .mat-row{ - .mat-cell{ - .imx-table-column-ellipsis{ - width:120px; - display: block; - white-space: nowrap; - overflow-x:hidden; - text-overflow: ellipsis; - } - } - } - } -} -.eui-dark-theme{ - :host{ - .sap-analysis{ - .mat-table{ - .sap-analysis-table-row-highlighted:hover{ - background-color: $color-gray-60; - } - } - } - } } diff --git a/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-role/sap-compliance-violation-views-by-role.component.ts b/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-role/sap-compliance-violation-views-by-role.component.ts index 4cdc5eba8..5f23e69f6 100644 --- a/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-role/sap-compliance-violation-views-by-role.component.ts +++ b/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation-views-by-role/sap-compliance-violation-views-by-role.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,16 +24,14 @@ * */ -import { AfterViewInit, ChangeDetectorRef, Component, Input, OnInit, ViewChild } from '@angular/core'; -import { ByRoleResult, ByRoleResultElement, SAPUserFunctionSrcPROF } from 'imx-api-sac'; -import { SapRoleTreeNodeModel } from './../sap-compliance-violation.model'; import { FlatTreeControl } from '@angular/cdk/tree'; -import { MatTreeFlatDataSource, MatTreeFlattener } from '@angular/material/tree'; -import { MatTableDataSource } from '@angular/material/table'; -import { EntitySchema, IClientProperty, TypedEntityCollectionData, ValType } from 'imx-qbm-dbts'; -import { DataSourceToolbarSettings } from 'qbm'; -import { MatSort } from '@angular/material/sort'; +import { ChangeDetectorRef, Component, Input, OnInit, ViewChild } from '@angular/core'; import { FormControl } from '@angular/forms'; +import { MatSort } from '@angular/material/sort'; +import { MatTableDataSource } from '@angular/material/table'; +import { MatTreeFlatDataSource, MatTreeFlattener } from '@angular/material/tree'; +import { ByRoleResult, ByRoleResultElement, SAPUserFunctionSrcPROF } from '@imx-modules/imx-api-sac'; +import { SapRoleTreeNodeModel } from './../sap-compliance-violation.model'; @Component({ selector: 'imx-sap-compliance-violation-views-by-role', @@ -67,13 +65,13 @@ export class SapComplianceViolationViewsByRoleComponent implements OnInit { }; public treeControl = new FlatTreeControl( (node) => node.level, - (node) => node.expandable + (node) => node.expandable, ); public treeFlattener = new MatTreeFlattener( this.transformer, (node) => node.level, (node) => node.expandable, - (node) => node.ChildElements + (node) => node.ChildElements, ); public dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener); public selectedProfiles: SAPUserFunctionSrcPROF[] = []; @@ -82,7 +80,7 @@ export class SapComplianceViolationViewsByRoleComponent implements OnInit { public extendProfiles = true; public showProfiles = false; public selectedRole: SapRoleTreeNodeModel; - public profileSearchControl = new FormControl(''); + public profileSearchControl = new FormControl('', { nonNullable: true }); private _resultByRole: ByRoleResult; constructor(private cdref: ChangeDetectorRef) {} @@ -125,11 +123,11 @@ export class SapComplianceViolationViewsByRoleComponent implements OnInit { searchValue = searchValue.toLocaleLowerCase(); this.profileDataSource.data = this.selectedProfiles.filter( (profile) => - profile.Ident_SAPProfile.toLocaleLowerCase().includes(searchValue) || - profile.Objects.toLocaleLowerCase().includes(searchValue) || - profile.Field.toLocaleLowerCase().includes(searchValue)|| - profile.LowerLimit.toLocaleLowerCase().includes(searchValue)|| - profile.UpperLimit.toLocaleLowerCase().includes(searchValue) + profile.Ident_SAPProfile?.toLocaleLowerCase().includes(searchValue) || + profile.Objects?.toLocaleLowerCase().includes(searchValue) || + profile.Field?.toLocaleLowerCase().includes(searchValue) || + profile.LowerLimit?.toLocaleLowerCase().includes(searchValue) || + profile.UpperLimit?.toLocaleLowerCase().includes(searchValue), ); } else { this.profileDataSource.data = this.selectedProfiles; diff --git a/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation.component.html b/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation.component.html index 45f3168e9..0aa767490 100644 --- a/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation.component.html +++ b/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation.component.html @@ -1,30 +1,46 @@
    - - {{ '#LDS#SAP user' | translate }} - - + + {{ '#LDS#SAP user account' | translate }} + + {{ account.GetEntity().GetDisplay() }} {{ '#LDS#Rule analysis' | translate }}: - - + + {{ option.label | translate }} - - + +
    -
    +

    {{ '#LDS#No data' | translate }}

    diff --git a/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation.component.scss b/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation.component.scss index 75ffea6d1..ab4b043de 100644 --- a/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation.component.scss +++ b/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation.component.scss @@ -1,38 +1,35 @@ -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; -.eui-sidesheet-content{ +.eui-sidesheet-content { display: flex; flex-direction: column; - imx-sap-compliance-violation-views-by-role, imx-sap-compliance-violation-views-by-ability{ + + imx-sap-compliance-violation-views-by-role, + imx-sap-compliance-violation-views-by-ability { display: block; overflow: hidden; - height: 100%; - } - .mat-progress-spinner { - margin: auto; + flex-grow: 1; } - .mat-chip{ + + .mat-mdc-chip { cursor: pointer; } - .imx-sap-compliance-selection{ + + .imx-sap-compliance-selection { display: flex; - align-items: baseline; + align-items: center; font-size: 14px; gap: 20px; - span{ - padding-bottom: 20px; - } + padding-bottom: 20px; } - ::ng-deep .no-results{ + + .imx-no-results { height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; - .eui-icon{ - font-size: 100px; - color: $color-gray-10; - } + p { margin: 0; font-size: 18px; @@ -43,15 +40,9 @@ .eui-dark-theme { :host { - .eui-sidesheet-content{ - ::ng-deep .no-results { - eui-icon { - color: $color-gray-20; - } - - p { - color: $color-gray-10; - } + .imx-no-results { + p { + color: $color-gray-10; } } } diff --git a/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation.component.ts b/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation.component.ts index 171e9b023..ecd41fe94 100644 --- a/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation.component.ts +++ b/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,8 +25,8 @@ */ import { Component, OnInit } from '@angular/core'; -import { PortalRules, PortalRulesViolations } from 'imx-api-cpl'; -import { ByAbilityResult, ByRoleResult, PortalTargetsystemSapuser } from 'imx-api-sac'; +import { PortalRules, PortalRulesViolations } from '@imx-modules/imx-api-cpl'; +import { ByAbilityResult, ByRoleResult, PortalTargetsystemSapuser } from '@imx-modules/imx-api-sac'; import { SapComplianceApiService } from '../sac-api-client.service'; import { SapSelectionOptions, SapSelectionTypeEnum } from './sap-compliance-violation.model'; @@ -94,11 +94,11 @@ export class SapComplianceViolationComponent implements OnInit { const uidsapuser = this.selectedAccount.GetEntity().GetKeys()[0]; this.resultByAbility = await this.api.client.portal_sap_ruleanalysis_fld_get( uidsapuser, - this.data.complianceRule?.EntityKeysData?.Keys?.[0] + this.data.complianceRule?.EntityKeysData?.Keys?.[0] ?? '', ); this.resultByRole = await this.api.client.portal_sap_ruleanalysis_byrole_get( uidsapuser, - this.data.complianceRule?.EntityKeysData?.Keys?.[0] + this.data.complianceRule?.EntityKeysData?.Keys?.[0] ?? '', ); this.loading = false; } diff --git a/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation.model.ts b/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation.model.ts index 3931573fc..bc798c1d8 100644 --- a/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation.model.ts +++ b/imxweb/projects/sac/src/lib/sap-compliance-violation/sap-compliance-violation.model.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,20 +24,19 @@ * */ +import { ByRoleResultElement } from '@imx-modules/imx-api-sac'; -import { ByRoleResultElement } from 'imx-api-sac'; - -export interface SapRoleTreeNodeModel extends ByRoleResultElement{ +export interface SapRoleTreeNodeModel extends ByRoleResultElement { level: number; expandable: boolean; } export enum SapSelectionTypeEnum { ROLE = 'ROLE', - ABILITY = 'ABILITY' + ABILITY = 'ABILITY', } -export interface SapSelectionOptions{ +export interface SapSelectionOptions { label: string; - value: SapSelectionTypeEnum + value: SapSelectionTypeEnum; } diff --git a/imxweb/projects/sac/src/public_api.ts b/imxweb/projects/sac/src/public_api.ts index a043e4ff9..e5b8c4e4c 100644 --- a/imxweb/projects/sac/src/public_api.ts +++ b/imxweb/projects/sac/src/public_api.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/sac/src/test.ts b/imxweb/projects/sac/src/test.ts index f7ac7d548..159c87d7d 100644 --- a/imxweb/projects/sac/src/test.ts +++ b/imxweb/projects/sac/src/test.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,24 +26,14 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'core-js/es7/reflect'; -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +// Zone must be imported first. Be careful with prettier import organizing +import 'zone.js'; +import 'zone.js/testing'; + import { getTestBed } from '@angular/core/testing'; -import { - BrowserDynamicTestingModule, - platformBrowserDynamicTesting -} from '@angular/platform-browser-dynamic/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; +import 'core-js/es/reflect'; import { TestHelperModule } from 'qbm'; -declare const require: any; - // First, initialize the Angular testing environment. -getTestBed().initTestEnvironment( - [BrowserDynamicTestingModule, TestHelperModule], - platformBrowserDynamicTesting() -); -// Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); -// And load the modules. -context.keys().map(context); +getTestBed().initTestEnvironment([BrowserDynamicTestingModule, TestHelperModule], platformBrowserDynamicTesting()); diff --git a/imxweb/projects/sac/tsconfig.lib.json b/imxweb/projects/sac/tsconfig.lib.json index d2c384016..ba946e3ca 100644 --- a/imxweb/projects/sac/tsconfig.lib.json +++ b/imxweb/projects/sac/tsconfig.lib.json @@ -2,6 +2,7 @@ "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "../../out-tsc/lib", + "strictNullChecks": true, "declarationMap": true, "target": "es2020", "module": "es2015", @@ -13,20 +14,14 @@ "experimentalDecorators": true, "importHelpers": true, "types": [], - "lib": [ - "dom", - "es2018" - ] + "lib": ["dom", "es2018"] }, "angularCompilerOptions": { "skipTemplateCodegen": true, "strictMetadataEmit": true, - "fullTemplateTypeCheck": true, + "strictTemplates": true, "strictInjectionParameters": true, "enableResourceInlining": true }, - "exclude": [ - "src/test.ts", - "**/*.spec.ts" - ] + "exclude": ["src/test.ts", "**/*.spec.ts"] } diff --git a/imxweb/projects/sac/tsconfig.lib.prod.json b/imxweb/projects/sac/tsconfig.lib.prod.json index a05c79305..46634e44f 100644 --- a/imxweb/projects/sac/tsconfig.lib.prod.json +++ b/imxweb/projects/sac/tsconfig.lib.prod.json @@ -2,8 +2,5 @@ "extends": "./tsconfig.lib.json", "compilerOptions": { "declarationMap": false - }, - "angularCompilerOptions": { - "enableIvy": true } } diff --git a/imxweb/projects/sac/tsconfig.spec.json b/imxweb/projects/sac/tsconfig.spec.json index 16da33db0..6bd74e1f7 100644 --- a/imxweb/projects/sac/tsconfig.spec.json +++ b/imxweb/projects/sac/tsconfig.spec.json @@ -1,17 +1,10 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "strictNullChecks": false, "outDir": "../../out-tsc/spec", - "types": [ - "jasmine", - "node" - ] + "types": ["jasmine", "node"] }, - "files": [ - "src/test.ts" - ], - "include": [ - "**/*.spec.ts", - "**/*.d.ts" - ] + "files": ["src/test.ts"], + "include": ["**/*.spec.ts", "**/*.d.ts"] } diff --git a/imxweb/projects/sac/tslint.json b/imxweb/projects/sac/tslint.json deleted file mode 100644 index 8bab15f53..000000000 --- a/imxweb/projects/sac/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "imx", - "camelCase" - ], - "component-selector": [ - true, - "element", - "imx", - "kebab-case" - ] - } -} diff --git a/imxweb/projects/tsb/.compodocrc.json b/imxweb/projects/tsb/.compodocrc.json index 103e340ed..8a4482ad7 100644 --- a/imxweb/projects/tsb/.compodocrc.json +++ b/imxweb/projects/tsb/.compodocrc.json @@ -1,6 +1,6 @@ { "name": "IMX Web - TSB Library", - "output": "../../documentation/v92/tsb", + "output": "../../documentation/v93/tsb", "theme": "material", "assetsFolder": "../../compodoc/assets", "customLogo": "../../compodoc/assets/images/oneidentity-logo.png", diff --git a/imxweb/projects/tsb/.eslintrc.json b/imxweb/projects/tsb/.eslintrc.json new file mode 100644 index 000000000..45f490f30 --- /dev/null +++ b/imxweb/projects/tsb/.eslintrc.json @@ -0,0 +1,35 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*", "**/*.spec.ts"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": ["imxweb/projects/tsb/tsconfig.lib.json", "imxweb/projects/tsb/tsconfig.spec.json"], + "createDefaultProgram": true + }, + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "imx", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "imx", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "rules": {} + } + ] +} diff --git a/imxweb/projects/tsb/imx-plugin-config.json b/imxweb/projects/tsb/imx-plugin-config.json index 777bcb8d1..a3e41fcd5 100644 --- a/imxweb/projects/tsb/imx-plugin-config.json +++ b/imxweb/projects/tsb/imx-plugin-config.json @@ -1,8 +1,8 @@ { "qer-app-portal": [ - { - "Container": "tsb", - "Name": "TsbConfigModule" - } + { + "Container": "tsb", + "Name": "TsbConfigModule" + } ] } diff --git a/imxweb/projects/tsb/karma.conf.js b/imxweb/projects/tsb/karma.conf.js index 211193c9d..3e53a3ac8 100644 --- a/imxweb/projects/tsb/karma.conf.js +++ b/imxweb/projects/tsb/karma.conf.js @@ -15,7 +15,7 @@ module.exports = function (config) { require('karma-junit-reporter'), ], client: { - clearContext: false // leave Jasmine Spec Runner output visible in browser + clearContext: false, // leave Jasmine Spec Runner output visible in browser }, coverageIstanbulReporter: { dir: require('path').join(__dirname, 'results'), @@ -23,7 +23,7 @@ module.exports = function (config) { fixWebpackSourcePaths: true, 'report-config': { html: { - subdir: 'coverage-html' + subdir: 'coverage-html', }, }, thresholds: { @@ -40,13 +40,14 @@ module.exports = function (config) { port: 9876, captureTimeout: 210000, browserDisconnectTolerance: 3, - browserDisconnectTimeout : 210000, - browserNoActivityTimeout : 210000, + browserDisconnectTimeout: 210000, + browserNoActivityTimeout: 210000, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], singleRun: false, - restartOnFileChange: true + failOnEmptyTestSuite: false, + restartOnFileChange: true, }); }; diff --git a/imxweb/projects/tsb/ng-package.dynamic.json b/imxweb/projects/tsb/ng-package.dynamic.json index 64fcab96b..ce71b33fe 100644 --- a/imxweb/projects/tsb/ng-package.dynamic.json +++ b/imxweb/projects/tsb/ng-package.dynamic.json @@ -16,12 +16,11 @@ "@elemental-ui/core", "@ngx-translate/core", "@ngx-translate/http-loader", - "applicationinsights-js", "billboard.js" ], - "dest": "../../html/tsb", + "dest": "../../html/qer-app-portal/tsb", "lib": { "entryFile": "src/public_api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] } } diff --git a/imxweb/projects/tsb/ng-package.json b/imxweb/projects/tsb/ng-package.json index 6b46cbd04..729a15375 100644 --- a/imxweb/projects/tsb/ng-package.json +++ b/imxweb/projects/tsb/ng-package.json @@ -3,6 +3,6 @@ "dest": "../../dist/tsb", "lib": { "entryFile": "src/public_api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] } } diff --git a/imxweb/projects/tsb/package.json b/imxweb/projects/tsb/package.json index cb36ff59f..aa04edb90 100644 --- a/imxweb/projects/tsb/package.json +++ b/imxweb/projects/tsb/package.json @@ -1,6 +1,6 @@ { "name": "tsb", - "version": "9.2.1", + "version": "9.3.0", "private": true, "bundledDependencies": [ "imx-api-tsb" diff --git a/imxweb/projects/tsb/project.json b/imxweb/projects/tsb/project.json new file mode 100644 index 000000000..8d9eb2bc8 --- /dev/null +++ b/imxweb/projects/tsb/project.json @@ -0,0 +1,64 @@ +{ + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "name": "tsb", + "sourceRoot": "projects/tsb/src", + "implicitDependencies": ["qer"], + "projectType": "library", + "prefix": "imx", + "generators": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:ng-packagr", + "options": { + "tsConfig": "projects/tsb/tsconfig.lib.json", + "project": "projects/tsb/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "projects/tsb/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "projects/tsb/tsconfig.lib.json" + }, + "dynamic": { + "project": "projects/tsb/ng-package.dynamic.json" + }, + "remote-dev": { + "tsConfig": "projects/tsb/tsconfig.lib.json" + }, + "remote-qs": { + "tsConfig": "projects/tsb/tsconfig.lib.json" + } + }, + "outputs": ["{workspaceRoot}/dist/tsb"] + }, + "test": { + "executor": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/tsb/src/test.ts", + "tsConfig": "projects/tsb/tsconfig.spec.json", + "karmaConfig": "projects/tsb/karma.conf.js", + "stylePreprocessorOptions": { + "includePaths": [ + "./shared/assets", + "./shared/scss", + "./node_modules", + "./node_modules/@elemental-ui/cadence-icon", + "./node_modules/@elemental-ui/core" + ] + } + } + }, + "lint": { + "executor": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": ["projects/tsb/tsconfig.lib.json", "projects/tsb/tsconfig.spec.json"], + "exclude": ["**/node_modules/**", "**/*.spec.ts", "**/*.json"] + } + } + } +} diff --git a/imxweb/projects/tsb/src/lib/accounts/account-ext/account-ext.service.ts b/imxweb/projects/tsb/src/lib/accounts/account-ext/account-ext.service.ts index eb94ff575..9e5bd51d5 100644 --- a/imxweb/projects/tsb/src/lib/accounts/account-ext/account-ext.service.ts +++ b/imxweb/projects/tsb/src/lib/accounts/account-ext/account-ext.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,14 +26,13 @@ import { Injectable } from '@angular/core'; -import { PortalPersonAccounts, portal_person_accounts_get_args } from 'imx-api-tsb'; -import { EntitySchema, ExtendedTypedEntityCollection } from 'imx-qbm-dbts'; +import { PortalPersonAccounts, portal_person_accounts_get_args } from '@imx-modules/imx-api-tsb'; +import { EntitySchema, ExtendedTypedEntityCollection } from '@imx-modules/imx-qbm-dbts'; import { TsbApiService } from '../../tsb-api-client.service'; @Injectable({ providedIn: 'root' }) export class AccountsExtService { - - constructor(private readonly apiService: TsbApiService) { } + constructor(private readonly apiService: TsbApiService) {} public get portalPersonAccountsSchema(): EntitySchema { return this.apiService.typedClient.PortalPersonAccounts.GetSchema(); @@ -42,5 +41,4 @@ export class AccountsExtService { public getAccounts(uid: string, parameters?: portal_person_accounts_get_args): Promise> { return this.apiService.typedClient.PortalPersonAccounts.Get(uid, parameters); } - } diff --git a/imxweb/projects/tsb/src/lib/accounts/account-ext/accounts-ext.component.html b/imxweb/projects/tsb/src/lib/accounts/account-ext/accounts-ext.component.html index 6973ae9e7..25928253f 100644 --- a/imxweb/projects/tsb/src/lib/accounts/account-ext/accounts-ext.component.html +++ b/imxweb/projects/tsb/src/lib/accounts/account-ext/accounts-ext.component.html @@ -1,28 +1,33 @@ - - +
    - - + + -
    {{item.GetEntity().GetDisplay()}}
    +
    {{ item.GetEntity().GetDisplay() }}
    - - - + + -
    {{item.UID_UNSRoot.Column.GetDisplayValue()}}
    -
    {{item.UID_DPRNameSpace.Column.GetDisplayValue()}}
    +
    {{ item.UID_UNSRoot.Column.GetDisplayValue() }}
    +
    {{ item.UID_DPRNameSpace.Column.GetDisplayValue() }}
    - +
    {{ item.XMarkedForDeletion.Column.GetDisplayValue() }} @@ -32,4 +37,4 @@
    - \ No newline at end of file + diff --git a/imxweb/projects/tsb/src/lib/accounts/account-ext/accounts-ext.component.scss b/imxweb/projects/tsb/src/lib/accounts/account-ext/accounts-ext.component.scss index 445b669d8..bb0fa0525 100644 --- a/imxweb/projects/tsb/src/lib/accounts/account-ext/accounts-ext.component.scss +++ b/imxweb/projects/tsb/src/lib/accounts/account-ext/accounts-ext.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; :host { overflow: hidden; @@ -7,7 +7,6 @@ height: 100%; } - .imx-memberships { flex: 1 1 auto; display: flex; @@ -22,4 +21,3 @@ overflow: auto; margin-top: 20px; } - diff --git a/imxweb/projects/tsb/src/lib/accounts/account-ext/accounts-ext.component.ts b/imxweb/projects/tsb/src/lib/accounts/account-ext/accounts-ext.component.ts index 13420292d..8f1164a7f 100644 --- a/imxweb/projects/tsb/src/lib/accounts/account-ext/accounts-ext.component.ts +++ b/imxweb/projects/tsb/src/lib/accounts/account-ext/accounts-ext.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,8 +26,8 @@ import { Component, Input, OnInit } from '@angular/core'; -import { CollectionLoadParameters, DisplayColumns, EntitySchema, IClientProperty } from 'imx-qbm-dbts'; -import { DataSourceToolbarSettings, DynamicTabDataProviderDirective, SettingsService, BusyService } from 'qbm'; +import { CollectionLoadParameters, DisplayColumns, EntitySchema, IClientProperty } from '@imx-modules/imx-qbm-dbts'; +import { BusyService, DataSourceToolbarSettings, DynamicTabDataProviderDirective, SettingsService } from 'qbm'; import { AccountsExtService } from './account-ext.service'; @Component({ @@ -37,7 +37,7 @@ import { AccountsExtService } from './account-ext.service'; export class AccountsExtComponent implements OnInit { @Input() public referrer: { objecttable?: string; - objectuid?: string; + objectuid: string; tablename?: string; }; @@ -53,7 +53,7 @@ export class AccountsExtComponent implements OnInit { constructor( private readonly settingService: SettingsService, private readonly accountsService: AccountsExtService, - dataProvider: DynamicTabDataProviderDirective + dataProvider: DynamicTabDataProviderDirective, ) { this.referrer = dataProvider?.data; this.navigationState = { PageSize: this.settingService.DefaultPageSize }; diff --git a/imxweb/projects/tsb/src/lib/accounts/account-sidesheet/account-sidesheet.component.html b/imxweb/projects/tsb/src/lib/accounts/account-sidesheet/account-sidesheet.component.html index f2e2d7442..bf111fccc 100644 --- a/imxweb/projects/tsb/src/lib/accounts/account-sidesheet/account-sidesheet.component.html +++ b/imxweb/projects/tsb/src/lib/accounts/account-sidesheet/account-sidesheet.component.html @@ -1,54 +1,64 @@ - + -
    -
    - -
    - -
    - - #LDS#The manager assigned to this user account differs from the manager assigned to the listed identity. - - - #LDS#There is no manager assigned to this user account. - - - #LDS#Synchronize the user account's manager with the listed identity - -
    -
    -
    -
    -
    - - - -
    +
    + +
    + +
    + + #LDS#The manager assigned to this user account differs from the manager assigned to the listed identity. + + + #LDS#There is no manager assigned to this user account. + + + #LDS#Synchronize the user account's manager with the listed identity + +
    +
    +
    +
    +
    + + +
    -
    -
    - - -
    +
    + +
    -
    +
    diff --git a/imxweb/projects/tsb/src/lib/accounts/account-sidesheet/account-sidesheet.component.scss b/imxweb/projects/tsb/src/lib/accounts/account-sidesheet/account-sidesheet.component.scss index a3c1b00af..a94edcb97 100644 --- a/imxweb/projects/tsb/src/lib/accounts/account-sidesheet/account-sidesheet.component.scss +++ b/imxweb/projects/tsb/src/lib/accounts/account-sidesheet/account-sidesheet.component.scss @@ -1,98 +1,14 @@ - -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; - -.eui-sidesheet-content:not(.imx-hyperview-sidesheet){ - padding: 0; - - ::ng-deep .mat-tab-body-content { - display: flex; - flex-direction: column; - } -} - -.eui-sidesheet-actions { - .justify-start { - margin-right: auto; - } - - button:not(:last-of-type):not(.justify-start) { - margin-right: 10px; - } -} - -.governance-sidesheet__tab-content { - display: flex; - flex-direction: column; - overflow: hidden; - > .governance-sidesheet__tab-content-body { - - .imx-tab-card{ - margin:20px; - } - flex: 1 1 auto; - overflow: auto; - padding: 0px; - - > :first-child { - overflow: auto; - } - - > .imx-ext { - overflow: hidden; - } - } -} - -.imx-entitlements-card{ +.imx-entitlements-card { flex: 1 1 auto; display: flex; flex-direction: column; } -.mat-checkbox { +.mat-mdc-checkbox { display: flex; margin-bottom: 15px; } - -::ng-deep .mat-tab-group { - height: 100%; - flex-grow: 1; - overflow: auto; - - .mat-tab-header { - padding: 0 32px; - background-color: $white; - } - - ::ng-deep .mat-tab-body-wrapper { - height: 100%; - } - - - .imx-edit-fk-open-picker { - display: none; - } -} - -.eui-dark-theme { - :host { - ::ng-deep .mat-tab-group { - .mat-tab-header { - background-color: $color-gray-80; - } - } - } -} - -.eui-contrast-theme { - :host { - ::ng-deep .mat-tab-group { - .mat-tab-header { - background-color: $color-gray-90; - } - } - } -} diff --git a/imxweb/projects/tsb/src/lib/accounts/account-sidesheet/account-sidesheet.component.ts b/imxweb/projects/tsb/src/lib/accounts/account-sidesheet/account-sidesheet.component.ts index 25698d2d4..0c99b7754 100644 --- a/imxweb/projects/tsb/src/lib/accounts/account-sidesheet/account-sidesheet.component.ts +++ b/imxweb/projects/tsb/src/lib/accounts/account-sidesheet/account-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,27 +24,25 @@ * */ -import { Component, OnInit, Inject } from '@angular/core'; -import { UntypedFormBuilder, UntypedFormGroup, UntypedFormArray, UntypedFormControl } from '@angular/forms'; +import { Component, Inject, OnInit } from '@angular/core'; +import { UntypedFormArray, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; +import { MatSlideToggleChange } from '@angular/material/slide-toggle'; +import { EUI_SIDESHEET_DATA, EuiDownloadOptions, EuiLoadingService, EuiSidesheetRef } from '@elemental-ui/core'; +import { DbObjectKey } from '@imx-modules/imx-qbm-dbts'; import { - ColumnDependentReference, - BaseCdr, + CdrFactoryService, ClassloggerService, - SnackBarService, + ColumnDependentReference, ElementalUiConfigService, - TabItem, ExtService, - CdrFactoryService, + SnackBarService, + TabItem, } from 'qbm'; -import { DbObjectKey } from 'imx-qbm-dbts'; -import { EuiLoadingService, EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; -import { AccountSidesheetData } from '../accounts.models'; import { IdentitiesService, ProjectConfigurationService } from 'qer'; -import { MatSlideToggleChange } from '@angular/material/slide-toggle'; -import { AccountsService } from '../accounts.service'; -import { EuiDownloadOptions } from '@elemental-ui/core'; -import { AccountsReportsService } from '../accounts-reports.service'; import { AccountTypedEntity } from '../account-typed-entity'; +import { AccountsReportsService } from '../accounts-reports.service'; +import { AccountSidesheetData } from '../accounts.models'; +import { AccountsService } from '../accounts.service'; @Component({ selector: 'imx-account-sidesheet', @@ -54,12 +52,12 @@ import { AccountTypedEntity } from '../account-typed-entity'; export class AccountSidesheetComponent implements OnInit { public readonly detailsFormGroup: UntypedFormGroup; public cdrList: ColumnDependentReference[] = []; - public linkedIdentitiesManager: DbObjectKey; + public linkedIdentitiesManager: DbObjectKey | undefined; public unsavedSyncChanges = false; public initialAccountManagerValue: string; public reportDownload: EuiDownloadOptions; public neverConnectFormControl = new UntypedFormControl(); - public parameters: { objecttable: string; objectuid: string }; + public parameters: { objecttable: string; objectuid: string; display: string }; public dynamicTabs: TabItem[] = []; @@ -76,13 +74,14 @@ export class AccountSidesheetComponent implements OnInit { private readonly accountsService: AccountsService, private readonly reports: AccountsReportsService, private readonly tabService: ExtService, - private cdrFactory: CdrFactoryService + private cdrFactory: CdrFactoryService, ) { this.detailsFormGroup = new UntypedFormGroup({ formArray: formBuilder.array([]) }); this.parameters = { objecttable: sidesheetData.unsDbObjectKey?.TableName, objectuid: sidesheetData.unsDbObjectKey?.Keys.join(','), + display: this.sidesheetData.selectedAccount.GetEntity().GetDisplay(), }; this.reportDownload = { @@ -135,13 +134,13 @@ export class AccountSidesheetComponent implements OnInit { } get accountManagerIsEditable(): boolean { - return this.selectedAccount.objectKeyManagerColumn?.GetMetadata().CanEdit(); + return this.selectedAccount.objectKeyManagerColumn?.GetMetadata().CanEdit() ?? false; } public async syncToIdentityManager(event: MatSlideToggleChange): Promise { const objectKeyManagerColumn = this.selectedAccount.objectKeyManagerColumn; - if (objectKeyManagerColumn != null && event.checked) { + if (objectKeyManagerColumn && this.linkedIdentitiesManager && event.checked) { await objectKeyManagerColumn.PutValue(this.linkedIdentitiesManager.ToXmlString()); this.detailsFormGroup.markAsDirty(); this.unsavedSyncChanges = true; @@ -149,13 +148,13 @@ export class AccountSidesheetComponent implements OnInit { } private async setup(): Promise { - const cols = (await this.configService.getConfig()).OwnershipConfig.EditableFields[this.parameters.objecttable]; + const cols = (await this.configService.getConfig())?.OwnershipConfig?.EditableFields?.[this.parameters.objecttable] ?? []; this.cdrList = this.cdrFactory.buildCdrFromColumnList(this.selectedAccount.GetEntity(), cols); this.dynamicTabs = ( await this.tabService.getFittingComponents('accountSidesheet', (ext) => ext.inputData.checkVisibility(this.parameters)) - ).sort((tab1: TabItem, tab2: TabItem) => tab1.sortOrder - tab2.sortOrder); + ).sort((tab1: TabItem, tab2: TabItem) => tab1.sortOrder! - tab2.sortOrder!); this.setupIdentityManagerSync(); } @@ -168,7 +167,7 @@ export class AccountSidesheetComponent implements OnInit { } } - private async getLinkedIdentitiesManager(linkedIdentityId: string, tableName: string): Promise { + private async getLinkedIdentitiesManager(linkedIdentityId: string, tableName: string): Promise { const identityData = await this.identitiesService.getPerson(linkedIdentityId); if (identityData?.UID_PersonHead.value) { const managerAccountData = await this.accountsService.getAccount( @@ -176,7 +175,7 @@ export class AccountSidesheetComponent implements OnInit { TableName: tableName, Keys: [identityData.UID_PersonHead.value], }, - 'UID_Person' + 'UID_Person', ); if (managerAccountData) { diff --git a/imxweb/projects/tsb/src/lib/accounts/account-typed-entity.ts b/imxweb/projects/tsb/src/lib/accounts/account-typed-entity.ts index 12e2bac11..9f9eadf2d 100644 --- a/imxweb/projects/tsb/src/lib/accounts/account-typed-entity.ts +++ b/imxweb/projects/tsb/src/lib/accounts/account-typed-entity.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,7 @@ * */ -import { DisplayColumns, IEntityColumn, TypedEntity } from 'imx-qbm-dbts'; +import { DisplayColumns, IEntityColumn, TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { CdrFactoryService } from 'qbm'; export class AccountTypedEntity extends TypedEntity { diff --git a/imxweb/projects/tsb/src/lib/accounts/accounts-reports.service.ts b/imxweb/projects/tsb/src/lib/accounts/accounts-reports.service.ts index cfb66aec2..217f2f41f 100644 --- a/imxweb/projects/tsb/src/lib/accounts/accounts-reports.service.ts +++ b/imxweb/projects/tsb/src/lib/accounts/accounts-reports.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,18 +25,18 @@ */ import { Injectable } from '@angular/core'; -import { CollectionLoadParameters, TypedEntityCollectionData } from 'imx-qbm-dbts'; -import { - PortalTargetsystemUnsAccount -} from 'imx-api-tsb'; +import { CollectionLoadParameters, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; +import { PortalTargetsystemUnsAccount } from '@imx-modules/imx-api-tsb'; import { AppConfigService, SettingsService } from 'qbm'; import { TsbApiService } from '../tsb-api-client.service'; @Injectable({ providedIn: 'root' }) export class AccountsReportsService { - constructor(private tsbClient: TsbApiService, + constructor( + private tsbClient: TsbApiService, private readonly settings: SettingsService, - private appConfig: AppConfigService) { } + private appConfig: AppConfigService, + ) {} public accountsReport(historyDays: number, accountId: string, tableName: string): string { const path = `targetsystem/uns/account/${tableName}/${accountId}/report?historydays=${historyDays}`; diff --git a/imxweb/projects/tsb/src/lib/accounts/accounts.component.html b/imxweb/projects/tsb/src/lib/accounts/accounts.component.html index 0fe2fa93c..1e7a7f948 100644 --- a/imxweb/projects/tsb/src/lib/accounts/accounts.component.html +++ b/imxweb/projects/tsb/src/lib/accounts/accounts.component.html @@ -1,77 +1,74 @@ -
    -
    - #LDS#Heading User Accounts +
    +
    +

    #LDS#Heading User Accounts

    - - -
    - -
    -
    - -
    - -
    - - - #LDS#There is currently no data synchronization configured for the selected domain. - - -
    + > +
    - - - + + + + {{ entitySchemaUnsAccount?.Columns?.[this.DisplayColumns.DISPLAY_PROPERTYNAME]?.Display }} + +
    {{ item.GetEntity().GetDisplay() }}
    -
    -
    - - -
    {{ item.UID_Person.Column.GetDisplayValue() }}
    -
    -
    - - - + + + + + {{ entitySchemaUnsAccount?.Columns?.UID_Person?.Display }} + + +
    + {{ item.UID_Person.Column.GetDisplayValue() }} +
    + +
    + + {{ entitySchemaUnsAccount?.Columns?.AccountDisabled?.Display }} + +
    {{ item.AccountDisabled.Column.GetDisplayValue() }}
    + +
    + + {{ entitySchemaUnsAccount?.Columns?.UID_UNSRoot?.Display }} +
    {{ item.UID_UNSRoot.Column.GetDisplayValue() }}
    {{ item.UID_DPRNameSpace.Column.GetDisplayValue() }}
    -
    -
    - - + + + + +
    {{ item.XMarkedForDeletion.Column.GetDisplayValue() }}
    -
    -
    -
    - + + + +
    -
    +
    diff --git a/imxweb/projects/tsb/src/lib/accounts/target-system-report/target-system-report.component.scss b/imxweb/projects/tsb/src/lib/accounts/target-system-report/target-system-report.component.scss index 675781889..d742eb934 100644 --- a/imxweb/projects/tsb/src/lib/accounts/target-system-report/target-system-report.component.scss +++ b/imxweb/projects/tsb/src/lib/accounts/target-system-report/target-system-report.component.scss @@ -3,12 +3,6 @@ display: flex; } -.eui-sidesheet-content { - overflow: hidden; - display: flex; - flex-direction: column; -} - .imx-content-card { margin-top: 10px; flex: 1 1 auto; diff --git a/imxweb/projects/tsb/src/lib/accounts/target-system-report/target-system-report.component.ts b/imxweb/projects/tsb/src/lib/accounts/target-system-report/target-system-report.component.ts index 22ece1f68..c6b4ab885 100644 --- a/imxweb/projects/tsb/src/lib/accounts/target-system-report/target-system-report.component.ts +++ b/imxweb/projects/tsb/src/lib/accounts/target-system-report/target-system-report.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,21 +26,20 @@ import { Overlay } from '@angular/cdk/overlay'; import { HttpClient } from '@angular/common/http'; -import { Component, Injector, OnInit } from '@angular/core'; -import { EuiDownloadDirective, EuiLoadingService } from '@elemental-ui/core'; +import { Component, ElementRef, Injector, OnInit } from '@angular/core'; +import { EuiDownloadDirective, EuiDownloadService, EuiLoadingService } from '@elemental-ui/core'; -import { IEntity } from 'imx-qbm-dbts'; -import { FilterTreeEntityWrapperService, FilterTreeDatabase, ElementalUiConfigService } from 'qbm'; +import { IEntity } from '@imx-modules/imx-qbm-dbts'; +import { ElementalUiConfigService, FilterTreeDatabase, FilterTreeEntityWrapperService } from 'qbm'; import { AccountsReportsService } from '../accounts-reports.service'; import { AccountsService } from '../accounts.service'; @Component({ selector: 'imx-target-system-report', templateUrl: './target-system-report.component.html', - styleUrls: ['./target-system-report.component.scss'] + styleUrls: ['./target-system-report.component.scss'], }) export class TargetSystemReportComponent implements OnInit { - public showHelperAlert = true; public database: FilterTreeDatabase; public selectedEntity: IEntity; @@ -54,19 +53,20 @@ export class TargetSystemReportComponent implements OnInit { private readonly http: HttpClient, private readonly injector: Injector, private readonly overlay: Overlay, - ) { } + private readonly downloadService: EuiDownloadService, + ) {} public async ngOnInit(): Promise { - this.database = new FilterTreeDatabase(this.entityWrapper, + this.database = new FilterTreeDatabase( + this.entityWrapper, async (parentkey) => { return this.accountsService.getFilterTree({ parentkey, - container: undefined, - system: undefined, - filter: undefined + filter: undefined, }); - } - , this.busyService); + }, + this.busyService, + ); } public onHelperDismissed(): void { @@ -74,7 +74,7 @@ export class TargetSystemReportComponent implements OnInit { } public onNodeSelected(entity: IEntity): void { - this.selectedEntity = entity; + this.selectedEntity = entity; } public loadReport(): void { @@ -87,18 +87,25 @@ export class TargetSystemReportComponent implements OnInit { const columnName = val.ColumnName; if (columnName === 'UID_UNSRoot') { url = this.accountReport.accountsByRootReport(30, val.Value1); - } - else if (columnName === 'UID_UNSContainer') { + } else if (columnName === 'UID_UNSContainer') { url = this.accountReport.accountsByContainerReport(30, val.Value1); + } else { + return; } - const directive = new EuiDownloadDirective(null, this.http, this.overlay, this.injector); + const directive = new EuiDownloadDirective( + new ElementRef('') /* no element */, + this.http, + this.overlay, + this.injector, + this.downloadService, + ); if (directive && url !== '') { directive.downloadOptions = { - ... this.elementalUiConfigService.Config.downloadOptions, + ...this.elementalUiConfigService.Config.downloadOptions, url, fileName: `${this.selectedEntity.GetDisplay()}.pdf`, - disableElement: false + disableElement: false, }; directive.onClick(); } diff --git a/imxweb/projects/tsb/src/lib/admin/tsb-permissions-helper.ts b/imxweb/projects/tsb/src/lib/admin/tsb-permissions-helper.ts index dc6a462de..303d57502 100644 --- a/imxweb/projects/tsb/src/lib/admin/tsb-permissions-helper.ts +++ b/imxweb/projects/tsb/src/lib/admin/tsb-permissions-helper.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,6 +24,6 @@ * */ -export function isTsbNameSpaceAdminBase(groups: string[]): boolean { - return groups.find(item => item.toUpperCase() === 'TSB_4_NAMESPACEADMIN_BASE') != null; +export function isTsbNameSpaceAdminBase(groups: (string | undefined)[]): boolean { + return groups.find((item) => item && item.toUpperCase() === 'TSB_4_NAMESPACEADMIN_BASE') != null; } diff --git a/imxweb/projects/tsb/src/lib/admin/tsb-permissions.service.ts b/imxweb/projects/tsb/src/lib/admin/tsb-permissions.service.ts index 95a161d8f..bb9e4e6fe 100644 --- a/imxweb/projects/tsb/src/lib/admin/tsb-permissions.service.ts +++ b/imxweb/projects/tsb/src/lib/admin/tsb-permissions.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -30,12 +30,12 @@ import { UserModelService } from 'qer'; import { isTsbNameSpaceAdminBase } from './tsb-permissions-helper'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class TsbPermissionsService { - constructor(private readonly userService: UserModelService) { } + constructor(private readonly userService: UserModelService) {} public async isTsbNameSpaceAdminBase(): Promise { - return isTsbNameSpaceAdminBase((await this.userService.getGroups()).map(userGroupInfo => userGroupInfo.Name)); + return isTsbNameSpaceAdminBase((await this.userService.getGroups()).map((userGroupInfo) => userGroupInfo.Name)); } } diff --git a/imxweb/projects/tsb/src/lib/claim-group/claim-group.component.html b/imxweb/projects/tsb/src/lib/claim-group/claim-group.component.html index 590c92c5b..8536fed0b 100644 --- a/imxweb/projects/tsb/src/lib/claim-group/claim-group.component.html +++ b/imxweb/projects/tsb/src/lib/claim-group/claim-group.component.html @@ -1,23 +1,41 @@ -

    +

    {{ '#LDS#Heading Assign an Owner for a System Entitlement' | translate }} -

    +
    - +
    - +
    - +
    + +
    - {{ '#LDS#Specify the owner for the system entitlement "{0}".' | translate | ldsReplace : entitlementCdr.column.GetDisplayValue() }} + {{ '#LDS#Specify the owner for the system entitlement "{0}".' | translate | ldsReplace: entitlementCdr.column.GetDisplayValue() }} {{ '#LDS#Specify the owner' | translate }} @@ -31,24 +49,36 @@

    }}

    - + {{ option.title | translate }}
    - +
    - - +
    + + +
    - {{ '#LDS#Assign ownership for the system entitlement "{0}" to "{1}".' | translate | ldsReplace : entitlementCdr.column.GetDisplayValue() : ownerDisplay }} + {{ + '#LDS#Assign ownership for the system entitlement "{0}" to "{1}".' + | translate + | ldsReplace: entitlementCdr.column.GetDisplayValue() : ownerDisplay + }} {{ '#LDS#Assign ownership' | translate }} @@ -59,11 +89,18 @@

    {{ '#LDS#Your changes have been successfully saved. It may take some time for the changes to take effect.' | translate }}

    - +

    -

    {{ '#LDS#You cannot currently assign owners for system entitlements. The system entitlement ownership attestation policy is deactivated.' | translate }}

    +

    + {{ + '#LDS#You cannot currently assign owners for system entitlements. The system entitlement ownership attestation policy is deactivated.' + | translate + }} +

    diff --git a/imxweb/projects/tsb/src/lib/claim-group/claim-group.component.scss b/imxweb/projects/tsb/src/lib/claim-group/claim-group.component.scss index dfdb6cf97..c56cbacea 100644 --- a/imxweb/projects/tsb/src/lib/claim-group/claim-group.component.scss +++ b/imxweb/projects/tsb/src/lib/claim-group/claim-group.component.scss @@ -6,31 +6,22 @@ overflow: hidden; } -mat-radio-group { - display: flex; - flex-direction: column; -} - -.imx-button-previous { - margin-right: 10px; -} - .imx-owner-assigned { display: flex; flex-direction: column; - >*:not(:last-child) { + > *:not(:last-child) { margin-bottom: 10px; } - >button { - align-self: flex-end; + > button { + align-self: flex-end; } } -.imx-system-entitlement-stepper{ +.imx-system-entitlement-stepper { flex: 1 1 auto; max-width: 100%; overflow: auto; display: flex; flex-direction: column; -} \ No newline at end of file +} diff --git a/imxweb/projects/tsb/src/lib/claim-group/claim-group.component.ts b/imxweb/projects/tsb/src/lib/claim-group/claim-group.component.ts index 0e770c08b..d50f700df 100644 --- a/imxweb/projects/tsb/src/lib/claim-group/claim-group.component.ts +++ b/imxweb/projects/tsb/src/lib/claim-group/claim-group.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -27,34 +27,35 @@ import { StepperSelectionEvent } from '@angular/cdk/stepper'; import { Component, ErrorHandler, OnDestroy, OnInit } from '@angular/core'; import { UntypedFormGroup } from '@angular/forms'; -import { OverlayRef } from '@angular/cdk/overlay'; import { MatRadioChange } from '@angular/material/radio'; import { EuiLoadingService } from '@elemental-ui/core'; import { Subscription } from 'rxjs'; import { AuthenticationService, BaseCdr, ISessionState } from 'qbm'; import { PersonService } from 'qer'; -import { ClaimGroupService } from './claim-group.service'; import { TargetSystemService } from '../target-system/target-system.service'; +import { ClaimGroupService } from './claim-group.service'; @Component({ styleUrls: ['./claim-group.component.scss'], - templateUrl: './claim-group.component.html' + templateUrl: './claim-group.component.html', }) export class ClaimGroupComponent implements OnInit, OnDestroy { - public get ownerDisplay(): string { return this.ownerCdr ? this.ownerCdr.column.GetDisplayValue() : this.user.name; } + public get ownerDisplay(): string { + return this.ownerCdr ? this.ownerCdr.column.GetDisplayValue() : this.user.name; + } public entitlementCdr: BaseCdr; public ownerCdr: BaseCdr; - public ownershipOptions: { title: string; createOwnerCdr: () => BaseCdr; }[]; + public ownershipOptions: { title: string; createOwnerCdr: () => BaseCdr | undefined }[] = []; public ownerAssigned = false; public canClaimGroup = false; public readonly entitlementForm = new UntypedFormGroup({}); public readonly ownerForm = new UntypedFormGroup({}); - private readonly entitlementFkValue: { table?: string, key?: string } = { }; - private readonly user: { uid?: string; name?: string; } = { }; + private readonly entitlementFkValue: { table: string; key: string } = { table: '', key: '' }; + private readonly user: { uid: string; name: string } = { uid: '', name: '' }; private readonly subscriptions: Subscription[] = []; constructor( @@ -63,31 +64,32 @@ export class ClaimGroupComponent implements OnInit, OnDestroy { private readonly personService: PersonService, private readonly busyService: EuiLoadingService, private readonly authentication: AuthenticationService, - private readonly errorHandler: ErrorHandler + private readonly errorHandler: ErrorHandler, ) { this.initEntitlementCdr(); - this.subscriptions.push(this.authentication.onSessionResponse.subscribe(async (sessionState: ISessionState) => { - if (sessionState) { - this.user.uid = sessionState.UserUid; - this.user.name = sessionState.Username; - } - })); + this.subscriptions.push( + this.authentication.onSessionResponse.subscribe(async (sessionState: ISessionState) => { + if (sessionState) { + this.user.uid = sessionState.UserUid!; + this.user.name = sessionState.Username!; + } + }), + ); } public async ngOnInit(): Promise { - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + this.showBusyIndicator(); try { // TODO wrap in cache this.canClaimGroup = (await this.targetSystem.getUserConfig()).CanClaimGroup; } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } } public ngOnDestroy(): void { - this.subscriptions.forEach(subscription => subscription.unsubscribe()); + this.subscriptions.forEach((subscription) => subscription.unsubscribe()); } public resetForms(): void { @@ -121,12 +123,11 @@ export class ClaimGroupComponent implements OnInit, OnDestroy { let hasSuggestedOwners = false; - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + this.showBusyIndicator(); try { hasSuggestedOwners = await this.claimGroupService.hasSuggestedOwners(this.entitlementFkValue.table, this.entitlementFkValue.key); } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } const display = '#LDS#Designated owner'; // TODO: globalize; @@ -135,57 +136,61 @@ export class ClaimGroupComponent implements OnInit, OnDestroy { this.ownershipOptions.push({ title: '#LDS#I want to take ownership of this system entitlement', - createOwnerCdr: () => undefined + createOwnerCdr: () => undefined, }); if (hasSuggestedOwners) { this.ownershipOptions.push({ title: '#LDS#Select from the suggested possible owners', - createOwnerCdr: () => new BaseCdr( - this.claimGroupService.createColumnSuggestedOwner(this.entitlementFkValue.table, this.entitlementFkValue.key), - display - ) + createOwnerCdr: () => + new BaseCdr( + this.claimGroupService.createColumnSuggestedOwner(this.entitlementFkValue.table, this.entitlementFkValue.key), + display, + ), }); } this.ownershipOptions.push({ title: '#LDS#Select another owner', - createOwnerCdr: () => new BaseCdr( - this.personService.createColumnCandidatesPerson(), - display - ) + createOwnerCdr: () => new BaseCdr(this.personService.createColumnCandidatesPerson(), display), }); this.initOwnerCdr(this.ownershipOptions[0].createOwnerCdr()); } public async assignOwner(): Promise { - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + this.showBusyIndicator(); try { await this.claimGroupService.assignOwner( this.entitlementFkValue.table, this.entitlementFkValue.key, - this.ownerCdr ? this.ownerCdr.column.GetValue() : this.user.uid + this.ownerCdr ? this.ownerCdr.column.GetValue() : this.user.uid, ); this.ownerAssigned = true; } catch (error) { this.errorHandler.handleError(error); } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } } private initEntitlementCdr(): void { - Object.keys(this.entitlementForm.controls).forEach(name => this.entitlementForm.removeControl(name)); + Object.keys(this.entitlementForm.controls).forEach((name) => this.entitlementForm.removeControl(name)); this.entitlementCdr = this.claimGroupService.createCdrSystemEntitlement(); } - private initOwnerCdr(cdr: BaseCdr): void { - Object.keys(this.ownerForm.controls).forEach(name => this.ownerForm.removeControl(name)); + private initOwnerCdr(cdr: BaseCdr | undefined): void { + Object.keys(this.ownerForm.controls).forEach((name) => this.ownerForm.removeControl(name)); + if (cdr) { + this.ownerCdr = cdr; + } + } - this.ownerCdr = cdr; + private showBusyIndicator(): void { + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } } } diff --git a/imxweb/projects/tsb/src/lib/claim-group/claim-group.module.ts b/imxweb/projects/tsb/src/lib/claim-group/claim-group.module.ts index 9298bafb9..13ab85e20 100644 --- a/imxweb/projects/tsb/src/lib/claim-group/claim-group.module.ts +++ b/imxweb/projects/tsb/src/lib/claim-group/claim-group.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -37,9 +37,7 @@ import { CdrModule, HelpContextualModule, LdsReplaceModule } from 'qbm'; import { ClaimGroupComponent } from './claim-group.component'; @NgModule({ - declarations: [ - ClaimGroupComponent - ], + declarations: [ClaimGroupComponent], imports: [ CdrModule, CommonModule, @@ -52,7 +50,6 @@ import { ClaimGroupComponent } from './claim-group.component'; ReactiveFormsModule, TranslateModule, HelpContextualModule, - ] + ], }) -export class ClaimGroupModule { -} +export class ClaimGroupModule {} diff --git a/imxweb/projects/tsb/src/lib/claim-group/claim-group.service.ts b/imxweb/projects/tsb/src/lib/claim-group/claim-group.service.ts index 4c2bafa41..6ca86e397 100644 --- a/imxweb/projects/tsb/src/lib/claim-group/claim-group.service.ts +++ b/imxweb/projects/tsb/src/lib/claim-group/claim-group.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,15 +26,25 @@ import { Injectable } from '@angular/core'; -import { CollectionLoadParameters, DbObjectKey, EntityCollectionData, IEntityColumn, MetaTableRelationData, ValType } from 'imx-qbm-dbts'; +import { + CollectionLoadParameters, + DbObjectKey, + EntityCollectionData, + IEntityColumn, + MetaTableRelationData, + ValType, +} from '@imx-modules/imx-qbm-dbts'; import { BaseCdr, EntityService } from 'qbm'; import { TsbApiService } from '../tsb-api-client.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ClaimGroupService { - constructor(private readonly apiService: TsbApiService, private readonly entityService: EntityService) { } + constructor( + private readonly apiService: TsbApiService, + private readonly entityService: EntityService, + ) {} public async hasSuggestedOwners(tableName: string, key: string): Promise { const collection = await this.getOwnerCandidates(tableName, key); @@ -45,11 +55,11 @@ export class ClaimGroupService { return this.apiService.client.portal_claimgroup_post(uidOwner, tableName, key); } - public getFkValue(column: IEntityColumn): { table?: string, key?: string; } { + public getFkValue(column: IEntityColumn): { table: string; key: string } { const dbObjectKey = DbObjectKey.FromXml(column.GetValue()); return { table: dbObjectKey.TableName, - key: dbObjectKey.Keys[0] + key: dbObjectKey.Keys[0], }; } @@ -59,9 +69,9 @@ export class ClaimGroupService { ChildColumnName: 'UID_UNSGroup', IsMemberRelation: false, ParentTableName: 'UNSGroup', - ParentColumnName: 'XObjectKey' + ParentColumnName: 'XObjectKey', }, - parameters => this.apiService.client.portal_claimgroup_group_get(parameters) + (parameters) => this.apiService.client.portal_claimgroup_group_get(parameters), ); return new BaseCdr(column, '#LDS#System entitlement' /* TODO: globalize */); @@ -72,58 +82,47 @@ export class ClaimGroupService { ChildColumnName: 'UID_Owner', IsMemberRelation: false, ParentTableName: tableName, - ParentColumnName: 'XObjectKey' + ParentColumnName: 'XObjectKey', }; - return this.createColumn( - fkRelation, - parameters => this.getOwnerCandidates(fkRelation.ParentTableName, key, parameters) - ); + return this.createColumn(fkRelation, (parameters) => this.getOwnerCandidates(fkRelation.ParentTableName, key, parameters)); } private createColumn( fkRelation: MetaTableRelationData, - loadFkCandidates: (parameters: CollectionLoadParameters) => Promise + loadFkCandidates: (parameters: CollectionLoadParameters) => Promise, ): IEntityColumn { return this.entityService.createLocalEntityColumn( { - ColumnName: fkRelation.ChildColumnName, + ColumnName: fkRelation.ChildColumnName!, Type: ValType.String, MinLen: 1, - FkRelation: fkRelation + FkRelation: fkRelation, }, - [{ - columnName: fkRelation.ChildColumnName, - fkTableName: fkRelation.ParentTableName, - parameterNames: [ - 'OrderBy', - 'StartIndex', - 'PageSize', - 'filter', - 'search', - ], - load: async (_, parameters: CollectionLoadParameters = {}) => loadFkCandidates(parameters), - getDataModel: async () => ({}), - getFilterTree: async () => ({ Elements: [] }) - }] + [ + { + columnName: fkRelation.ChildColumnName!, + fkTableName: fkRelation.ParentTableName!, + parameterNames: ['OrderBy', 'StartIndex', 'PageSize', 'filter', 'search'], + load: async (_, parameters: CollectionLoadParameters = {}) => loadFkCandidates(parameters), + getDataModel: async () => ({}), + getFilterTree: async () => ({ Elements: [] }), + }, + ], ); } private async getOwnerCandidates( - tableName: string, uid: string, parameters: CollectionLoadParameters = {} + tableName: string, + uid: string, + parameters: CollectionLoadParameters = {}, ): Promise { - const collection = await this.apiService.client.portal_claimgroup_suggestedowner_get( - tableName, - uid, - parameters); + const collection = await this.apiService.client.portal_claimgroup_suggestedowner_get(tableName, uid, parameters); if (collection.Entities && collection.Entities.length > 0) { return collection; } - return this.apiService.client.portal_claimgroup_suggestedowner2_get( - tableName, - uid, - parameters); + return this.apiService.client.portal_claimgroup_suggestedowner2_get(tableName, uid, parameters); } } diff --git a/imxweb/projects/tsb/src/lib/container-list/container-tree-database-wrapper.ts b/imxweb/projects/tsb/src/lib/container-list/container-tree-database-wrapper.ts index f35c39b5e..258e08a26 100644 --- a/imxweb/projects/tsb/src/lib/container-list/container-tree-database-wrapper.ts +++ b/imxweb/projects/tsb/src/lib/container-list/container-tree-database-wrapper.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,22 +24,18 @@ * */ -import { CollectionLoadParameters, IEntity } from 'imx-qbm-dbts'; +import { CollectionLoadParameters, IEntity } from '@imx-modules/imx-qbm-dbts'; import { BusyService, EntityTreeDatabase } from 'qbm'; import { DeHelperService } from '../de-helper.service'; export class ContainerTreeDatabaseWrapper { - public get targetSystemFilterValue(): string { return this.system; } public set targetSystemFilterValue(value: string) { this.system = value; - this.entityTreeDatabase = new EntityTreeDatabase( - parameters => this.getEntities(parameters, value), - this.busyService - ); + this.entityTreeDatabase = new EntityTreeDatabase((parameters) => this.getEntities(parameters, value), this.busyService); } public selectionEnabled = false; @@ -50,12 +46,9 @@ export class ContainerTreeDatabaseWrapper { constructor( private readonly busyService: BusyService, - private readonly dataHelper: DeHelperService + private readonly dataHelper: DeHelperService, ) { - this.entityTreeDatabase = new EntityTreeDatabase( - parameters => this.getEntities(parameters), - this.busyService - ); + this.entityTreeDatabase = new EntityTreeDatabase((parameters) => this.getEntities(parameters), this.busyService); } private async getEntities(parameters: CollectionLoadParameters = {}, system: string = ''): Promise { @@ -67,6 +60,6 @@ export class ContainerTreeDatabaseWrapper { navigationState.system = system; } const containerData = await this.dataHelper.getContainers(navigationState); - return containerData.Data.map(element => element.GetEntity()); + return containerData.Data.map((element) => element.GetEntity()); } } diff --git a/imxweb/projects/tsb/src/lib/data-filters/data-explorer-filters.component.html b/imxweb/projects/tsb/src/lib/data-filters/data-explorer-filters.component.html index e32ca8b4f..8d720c353 100644 --- a/imxweb/projects/tsb/src/lib/data-filters/data-explorer-filters.component.html +++ b/imxweb/projects/tsb/src/lib/data-filters/data-explorer-filters.component.html @@ -6,22 +6,37 @@ (optionsClear)="clearTargetSystemFilterSelection(false)" [options]="targetSystemOptions" [isPending]="pendingAsyncApiCall" - class="imx-de-targetsystem-filter" - #tsSelect> + class="imx-margin-bottom-15" + #tsSelect +> -
    - - - +
    + class="imx-container-search" + #containerSelect + >
    -
    - - +
    diff --git a/imxweb/projects/tsb/src/lib/data-filters/data-explorer-filters.component.scss b/imxweb/projects/tsb/src/lib/data-filters/data-explorer-filters.component.scss index 57c720621..6ffef42ef 100644 --- a/imxweb/projects/tsb/src/lib/data-filters/data-explorer-filters.component.scss +++ b/imxweb/projects/tsb/src/lib/data-filters/data-explorer-filters.component.scss @@ -1,10 +1,5 @@ -.imx-de-targetsystem-filter { - margin-bottom: 15px; -} - .imx-de-container-tree-filter { - display: flex; align-items: center; margin-bottom: 15px; @@ -20,16 +15,9 @@ overflow: hidden; text-overflow: ellipsis; } - - .container-search { - &--hidden { - display: none; - } - } - } -::ng-deep .mat-menu-panel { +::ng-deep .mat-mdc-menu-panel { &.container-filter-menu { min-width: 250px; max-width: 400px; @@ -37,5 +25,3 @@ padding: 0 10px; } } - - diff --git a/imxweb/projects/tsb/src/lib/data-filters/data-explorer-filters.component.ts b/imxweb/projects/tsb/src/lib/data-filters/data-explorer-filters.component.ts index 2a3061df3..88ec77278 100644 --- a/imxweb/projects/tsb/src/lib/data-filters/data-explorer-filters.component.ts +++ b/imxweb/projects/tsb/src/lib/data-filters/data-explorer-filters.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,15 +26,15 @@ import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; import { EuiSelectComponent, EuiSelectOption } from '@elemental-ui/core'; -import { PortalTargetsystemUnsSystem } from 'imx-api-tsb'; -import { IEntity } from 'imx-qbm-dbts'; +import { PortalTargetsystemUnsSystem } from '@imx-modules/imx-api-tsb'; +import { IEntity } from '@imx-modules/imx-qbm-dbts'; import { DataSourceToolbarComponent, DataSourceToolbarSelectedFilter } from 'qbm'; import { ContainerTreeDatabaseWrapper } from '../container-list/container-tree-database-wrapper'; import { DeHelperService } from '../de-helper.service'; export enum DataExplorerFilterTypes { TargetSystem = 'targetsystem', - Container = 'container' + Container = 'container', } @Component({ @@ -43,7 +43,6 @@ export enum DataExplorerFilterTypes { styleUrls: ['./data-explorer-filters.component.scss'], }) export class DataExplorerFiltersComponent implements OnInit { - public targetSystemOptions: EuiSelectOption[] = []; public selectedTargetSystemUid: string; public dstTargetSystemFilterRef: DataSourceToolbarSelectedFilter; @@ -66,12 +65,12 @@ export class DataExplorerFiltersComponent implements OnInit { private skipSelectionEmitMode = false; - constructor(private readonly dataHelper: DeHelperService) { } + constructor(private readonly dataHelper: DeHelperService) {} public get selectedTreeNodeFilterDisplay(): string { let display = ''; if (this.selectedContainerUid) { - display = `Container: ${this.dstContainerFilterRef.selectedOption.Display}`; + display = `Container: ${this.dstContainerFilterRef.selectedOption?.Display}`; } return display; } @@ -119,7 +118,9 @@ export class DataExplorerFiltersComponent implements OnInit { } } - public targetSystemSelected(selected: EuiSelectOption): void { + public targetSystemSelected(chosen: EuiSelectOption | EuiSelectOption[]): void { + const selected = Array.isArray(chosen) ? chosen[0] : chosen; + this.selectedTargetSystemUid = selected.value; this.updateDataSyncStateForTs(selected); if (this.treeDbWrapper) { @@ -134,7 +135,7 @@ export class DataExplorerFiltersComponent implements OnInit { this.dstTargetSystemFilterRef = { selectedOption: { Value: selected.value, Display: selected.display }, filter: { Name: DataExplorerFilterTypes.TargetSystem }, - isCustom: true + isCustom: true, }; this.dst.selectedFilters.push(this.dstTargetSystemFilterRef); } @@ -205,10 +206,12 @@ export class DataExplorerFiltersComponent implements OnInit { const selectedValue: string = entity.GetKeys()[0]; // Set the selected value on the search control as well this.containerSelect.writeValue(selectedValue); - this.setSelectedContainer({value: selectedValue, display: entity.GetDisplay()}); + this.setSelectedContainer({ value: selectedValue, display: entity.GetDisplay() }); } - public setSelectedContainer(selected: EuiSelectOption): void { + public setSelectedContainer(chosen: EuiSelectOption | EuiSelectOption[]): void { + const selected = Array.isArray(chosen) ? chosen[0] : chosen; + this.selectedContainerUid = selected.value; if (this.dst) { @@ -216,9 +219,9 @@ export class DataExplorerFiltersComponent implements OnInit { this.clearDstSelectedFilter(this.dstContainerFilterRef); if (selected.value && selected.value.length > 0) { this.dstContainerFilterRef = { - selectedOption: { Value: selected.value, Display: selected.display}, + selectedOption: { Value: selected.value, Display: selected.display }, filter: { Name: DataExplorerFilterTypes.Container }, - isCustom: true + isCustom: true, }; this.dst.selectedFilters.push(this.dstContainerFilterRef); } @@ -236,8 +239,8 @@ export class DataExplorerFiltersComponent implements OnInit { this.containerSearchMode = !this.containerSearchMode; if (this.containerSearchMode) { if (this.treeDbWrapper?.entityTreeDatabase?.topLevelEntities) { - this.containerSelectOptions = this.treeDbWrapper.entityTreeDatabase.topLevelEntities.map(d => - this.convertEntityToEuiSelectOption(d) + this.containerSelectOptions = this.treeDbWrapper.entityTreeDatabase.topLevelEntities.map((d) => + this.convertEntityToEuiSelectOption(d), ); } else { this.getContainers(); @@ -257,15 +260,15 @@ export class DataExplorerFiltersComponent implements OnInit { if (!sf) { // If no singular filter is supplied, then all have been cleared this.clearAllSelectedFilters(); - } else if (sf.filter.Name === DataExplorerFilterTypes.TargetSystem) { + } else if (sf.filter?.Name === DataExplorerFilterTypes.TargetSystem) { this.clearTargetSystemFilterSelection(true); - } else if (sf.filter.Name === DataExplorerFilterTypes.Container) { + } else if (sf.filter?.Name === DataExplorerFilterTypes.Container) { this.clearContainerFilterSelection(true); } }); } - private updateDataSyncStateForTs(selectedOption?: EuiSelectOption): void { + private updateDataSyncStateForTs(selectedOption: EuiSelectOption): void { this.selectedTsOption = selectedOption; this.tsIssueMode = ''; if (this.showTsSyncStatus) { @@ -278,22 +281,19 @@ export class DataExplorerFiltersComponent implements OnInit { } private clearDstSelectedFilter(selectedFilterRef: DataSourceToolbarSelectedFilter): void { - if (this.dst && selectedFilterRef) { + if (this.dst && selectedFilterRef && selectedFilterRef.selectedOption?.Value) { // Remove the 'isCustom' property to avoid an event being triggered in dst code selectedFilterRef.isCustom = undefined; // Then make call to remove selected filter - this.dst.removeSelectedFilter( - selectedFilterRef.filter, - false, - selectedFilterRef.selectedOption.Value, - selectedFilterRef - ); + this.dst.removeSelectedFilter(selectedFilterRef.filter, false, selectedFilterRef.selectedOption.Value, selectedFilterRef); } } private async getTargetSystemOptions(search?: string): Promise { const data = await this.dataHelper.getAuthorityData(search, true); - this.setupTsSelectOptions(data.authorities); + if (data.authorities) { + this.setupTsSelectOptions(data.authorities); + } } private async getContainers(search?: string): Promise { @@ -301,11 +301,11 @@ export class DataExplorerFiltersComponent implements OnInit { navState.search = search ? search : undefined; navState.system = this.selectedTargetSystemUid ? this.selectedTargetSystemUid : undefined; const data = await this.dataHelper.getContainers(navState); - this.containerSelectOptions = data?.Data.map(d => this.convertEntityToEuiSelectOption(d.GetEntity())); + this.containerSelectOptions = data?.Data.map((d) => this.convertEntityToEuiSelectOption(d.GetEntity())); } private setupTsSelectOptions(targetSystems: PortalTargetsystemUnsSystem[]): void { - this.targetSystemOptions = targetSystems.map(d => this.convertTsToEuiSelectOption(d)); + this.targetSystemOptions = targetSystems.map((d) => this.convertTsToEuiSelectOption(d)); } private convertTsToEuiSelectOption(tEntity: PortalTargetsystemUnsSystem): EuiSelectOption { @@ -318,5 +318,4 @@ export class DataExplorerFiltersComponent implements OnInit { private convertEntityToEuiSelectOption(entity: IEntity): EuiSelectOption { return { display: entity.GetDisplay(), value: entity.GetKeys()[0] }; } - } diff --git a/imxweb/projects/tsb/src/lib/data-filters/data-filters.module.ts b/imxweb/projects/tsb/src/lib/data-filters/data-filters.module.ts index 832e333d6..35f3aa17b 100644 --- a/imxweb/projects/tsb/src/lib/data-filters/data-filters.module.ts +++ b/imxweb/projects/tsb/src/lib/data-filters/data-filters.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -36,12 +36,10 @@ import { TranslateModule } from '@ngx-translate/core'; import { DataSourceToolbarModule, DataTableModule, CdrModule, LdsReplaceModule, DataTreeModule } from 'qbm'; -import { DataExplorerFiltersComponent} from './data-explorer-filters.component'; +import { DataExplorerFiltersComponent } from './data-explorer-filters.component'; @NgModule({ - declarations: [ - DataExplorerFiltersComponent - ], + declarations: [DataExplorerFiltersComponent], imports: [ CommonModule, FormsModule, @@ -57,12 +55,9 @@ import { DataExplorerFiltersComponent} from './data-explorer-filters.component'; DataSourceToolbarModule, DataTableModule, LdsReplaceModule, - DataTreeModule - ], - providers: [ + DataTreeModule, ], - exports: [ - DataExplorerFiltersComponent - ] + providers: [], + exports: [DataExplorerFiltersComponent], }) export class DataFiltersModule {} diff --git a/imxweb/projects/tsb/src/lib/de-helper.service.ts b/imxweb/projects/tsb/src/lib/de-helper.service.ts index 6b74f1dfa..e6609af4d 100644 --- a/imxweb/projects/tsb/src/lib/de-helper.service.ts +++ b/imxweb/projects/tsb/src/lib/de-helper.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,14 +25,10 @@ */ import { Injectable } from '@angular/core'; -import { - ExtendedTypedEntityCollection, - CollectionLoadParameters, - TypedEntityCollectionData, -} from 'imx-qbm-dbts'; -import { PortalTargetsystemUnsContainer, PortalTargetsystemUnsSystem } from 'imx-api-tsb'; -import { Subject } from 'rxjs'; import { EuiLoadingService } from '@elemental-ui/core'; +import { PortalTargetsystemUnsContainer, PortalTargetsystemUnsSystem } from '@imx-modules/imx-api-tsb'; +import { CollectionLoadParameters, ExtendedTypedEntityCollection, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; +import { Subject } from 'rxjs'; import { TsbApiService } from './tsb-api-client.service'; @Injectable({ providedIn: 'root' }) @@ -41,7 +37,7 @@ export class DeHelperService { constructor( private loader: EuiLoadingService, - private tsbClient: TsbApiService + private tsbClient: TsbApiService, ) {} public async getAuthorityData(search?: string, skipLoader?: boolean): Promise { @@ -50,7 +46,7 @@ export class DeHelperService { loadParams.search = search; } const method = this.tsbClient.typedClient.PortalTargetsystemUnsSystem; - let data: ExtendedTypedEntityCollection; + let data: ExtendedTypedEntityCollection; if (skipLoader) { data = await method.Get(loadParams); } else { @@ -63,7 +59,7 @@ export class DeHelperService { } public async getContainers( - navigationState: CollectionLoadParameters + navigationState: CollectionLoadParameters, ): Promise> { return this.tsbClient.typedClient.PortalTargetsystemUnsContainer.Get(navigationState); } diff --git a/imxweb/projects/tsb/src/lib/groups/group-memberships-ext/group-memberships-ext.component.html b/imxweb/projects/tsb/src/lib/groups/group-memberships-ext/group-memberships-ext.component.html index 5353ee53a..4eec80b0a 100644 --- a/imxweb/projects/tsb/src/lib/groups/group-memberships-ext/group-memberships-ext.component.html +++ b/imxweb/projects/tsb/src/lib/groups/group-memberships-ext/group-memberships-ext.component.html @@ -1,28 +1,39 @@ - - {{ '#LDS#Here you can get an overview of the memberships of the identity. Additionally, you can view the assignment analysis for each membership.' | translate }} + + {{ + '#LDS#Here you can get an overview of the memberships of the identity. Additionally, you can view the assignment analysis for each membership.' + | translate + }} - - -
    - - - + + + + + {{ entitySchema?.Columns?.[DisplayColumns.DISPLAY_PROPERTYNAME]?.Display }} + + +
    {{ item.GetEntity().GetDisplay() }}
    -
    + @if (item.XIsInEffect?.value === true) { + {{ '#LDS#Not effective' | translate }} + } +
    + @if (item.GetEntity().GetDisplay() !== item.GetEntity().GetDisplayLong()) { +
    {{ item.GetEntity().GetDisplayLong() }}
    - - - - - -
    + } + +
    + + @for (column of displayColumns; track column.ColumnName) { + + {{ column.Display }} + + {{ item.GetEntity().GetColumn(column.ColumnName)?.GetDisplayValue() }} + + + } +
    + diff --git a/imxweb/projects/tsb/src/lib/groups/group-memberships-ext/group-memberships-ext.component.scss b/imxweb/projects/tsb/src/lib/groups/group-memberships-ext/group-memberships-ext.component.scss index 4ee2d6742..593fe0ef5 100644 --- a/imxweb/projects/tsb/src/lib/groups/group-memberships-ext/group-memberships-ext.component.scss +++ b/imxweb/projects/tsb/src/lib/groups/group-memberships-ext/group-memberships-ext.component.scss @@ -1,4 +1,3 @@ - :host { overflow: hidden; display: flex; @@ -6,7 +5,6 @@ height: 100%; } - .imx-memberships { flex: 1 1 auto; display: flex; @@ -22,6 +20,12 @@ margin-top: 20px; } -.helper-alert { - margin: 20px 2px; -} \ No newline at end of file +.fill-cell { + width: 100%; + display: flex; + flex-direction: row; + + eui-badge { + align-self: flex-end; + } +} diff --git a/imxweb/projects/tsb/src/lib/groups/group-memberships-ext/group-memberships-ext.component.ts b/imxweb/projects/tsb/src/lib/groups/group-memberships-ext/group-memberships-ext.component.ts index e08f627b0..2d4df374d 100644 --- a/imxweb/projects/tsb/src/lib/groups/group-memberships-ext/group-memberships-ext.component.ts +++ b/imxweb/projects/tsb/src/lib/groups/group-memberships-ext/group-memberships-ext.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,20 +28,36 @@ import { Component, Input, OnInit } from '@angular/core'; import { EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { CollectionLoadParameters, DbObjectKey, DisplayColumns, EntitySchema, IClientProperty, TypedEntity } from 'imx-qbm-dbts'; -import { BusyService, DataSourceToolbarSettings, DynamicTabDataProviderDirective, SettingsService } from 'qbm'; -import { GroupMembershipsExtService } from './group-memberships-ext.service'; +import { PortalPersonGroupmemberships } from '@imx-modules/imx-api-tsb'; +import { + CollectionLoadParameters, + DbObjectKey, + DisplayColumns, + EntitySchema, + ExtendedTypedEntityCollection, + IClientProperty, + TypedEntity, +} from '@imx-modules/imx-qbm-dbts'; +import { + BusyService, + calculateSidesheetWidth, + DataSourceToolbarSettings, + DataViewInitParameters, + DataViewSource, + DynamicTabDataProviderDirective, +} from 'qbm'; import { SourceDetectiveSidesheetComponent, SourceDetectiveSidesheetData, SourceDetectiveType } from 'qer'; -import { PortalPersonGroupmemberships } from 'imx-api-tsb'; +import { GroupMembershipsExtService } from './group-memberships-ext.service'; @Component({ templateUrl: './group-memberships-ext.component.html', styleUrls: ['./group-memberships-ext.component.scss'], + providers: [DataViewSource], }) export class GroupMembershipsExtComponent implements OnInit { @Input() public referrer: { - objecttable?: string; - objectuid?: string; + objecttable: string; + objectuid: string; tablename?: string; }; @@ -51,50 +67,36 @@ export class GroupMembershipsExtComponent implements OnInit { public entitySchema: EntitySchema; public displayColumns: IClientProperty[] = []; - private displayedColumnsWithDisplay = []; + private displayedColumnsWithDisplay: IClientProperty[] = []; private navigationState: CollectionLoadParameters; public busyService = new BusyService(); constructor( - private readonly settingService: SettingsService, private readonly groupService: GroupMembershipsExtService, private readonly translate: TranslateService, private readonly sidesheet: EuiSidesheetService, - dataProvider: DynamicTabDataProviderDirective + dataProvider: DynamicTabDataProviderDirective, + public dataSource: DataViewSource, ) { this.referrer = dataProvider?.data; - this.navigationState = { PageSize: this.settingService.DefaultPageSize }; this.entitySchema = groupService.portalPersonGroupmembershipsSchema; this.displayColumns = [ - this.entitySchema.Columns.XOrigin, + this.entitySchema.Columns?.XOrigin, this.entitySchema.Columns.XDateInserted, this.entitySchema.Columns.OrderState, this.entitySchema.Columns.ValidUntil, ]; - this.displayedColumnsWithDisplay = [...[this.entitySchema.Columns[DisplayColumns.DISPLAY_PROPERTYNAME]], ...this.displayColumns]; + this.displayedColumnsWithDisplay = [this.entitySchema.Columns[DisplayColumns.DISPLAY_PROPERTYNAME], ...this.displayColumns]; } public async ngOnInit(): Promise { return this.getData(); } - public async onNavigationStateChanged(newState?: CollectionLoadParameters): Promise { - if (newState) { - this.navigationState = newState; - } - await this.getData(); - } - - public async onSearch(keywords: string): Promise { - this.navigationState.StartIndex = 0; - this.navigationState.search = keywords; - await this.getData(); - } - - public async onShowDetails(entity: PortalPersonGroupmemberships): Promise { - const unsGroupId = DbObjectKey.FromXml(entity.ObjectKeyGroup.value); + public async onShowDetails(entity: TypedEntity): Promise { + const unsGroupId = DbObjectKey.FromXml(entity.GetEntity().GetColumn('ObjectKeyGroup').GetValue()); const data: SourceDetectiveSidesheetData = { UID_Person: this.referrer.objectuid, @@ -107,24 +109,30 @@ export class GroupMembershipsExtComponent implements OnInit { title: await this.translate.get('#LDS#Heading View Assignment Analysis').toPromise(), subTitle: entity.GetEntity().GetDisplay(), padding: '0px', - width: '800px', + width: calculateSidesheetWidth(800, 0.5), disableClose: false, testId: 'group-membership-assingment-analysis', data, }); - } private async getData(): Promise { - const isBusy = this.busyService.beginBusy(); + const isBusy = this.busyService.beginBusy(); try { - const groupsPerIdentity = await this.groupService.getGroupMemberships(this.referrer.objectuid, this.navigationState); - this.dstSettings = { - displayedColumns: this.displayedColumnsWithDisplay, - dataSource: groupsPerIdentity, - entitySchema: this.entitySchema, - navigationState: this.navigationState, + const dataViewInitParameters: DataViewInitParameters = { + execute: ( + params: CollectionLoadParameters, + signal: AbortSignal, + ): Promise> => { + return this.groupService.getGroupMemberships(this.referrer.objectuid, params, signal); + }, + schema: this.entitySchema, + columnsToDisplay: this.displayedColumnsWithDisplay, + highlightEntity: (identity: PortalPersonGroupmemberships) => { + this.onShowDetails(identity); + }, }; + await this.dataSource.init(dataViewInitParameters); } finally { isBusy.endBusy(); } diff --git a/imxweb/projects/tsb/src/lib/groups/group-memberships-ext/group-memberships-ext.service.ts b/imxweb/projects/tsb/src/lib/groups/group-memberships-ext/group-memberships-ext.service.ts index a473291ff..4ada06578 100644 --- a/imxweb/projects/tsb/src/lib/groups/group-memberships-ext/group-memberships-ext.service.ts +++ b/imxweb/projects/tsb/src/lib/groups/group-memberships-ext/group-memberships-ext.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,8 +26,8 @@ import { Injectable } from '@angular/core'; -import { PortalPersonGroupmemberships, portal_person_groupmemberships_get_args } from 'imx-api-tsb'; -import { EntitySchema, ExtendedTypedEntityCollection } from 'imx-qbm-dbts'; +import { PortalPersonGroupmemberships, portal_person_groupmemberships_get_args } from '@imx-modules/imx-api-tsb'; +import { EntitySchema, ExtendedTypedEntityCollection } from '@imx-modules/imx-qbm-dbts'; import { TsbApiService } from '../../tsb-api-client.service'; @Injectable({ providedIn: 'root' }) @@ -38,7 +38,11 @@ export class GroupMembershipsExtService { return this.apiService.typedClient.PortalPersonGroupmemberships.GetSchema(); } - public getGroupMemberships(uid: string, parameters: portal_person_groupmemberships_get_args): Promise> { - return this.apiService.typedClient.PortalPersonGroupmemberships.Get(uid, parameters); + public getGroupMemberships( + uid: string, + parameters: portal_person_groupmemberships_get_args, + signal: AbortSignal, + ): Promise> { + return this.apiService.typedClient.PortalPersonGroupmemberships.Get(uid, parameters, { signal }); } } diff --git a/imxweb/projects/tsb/src/lib/groups/group-sidesheet/child-system-entitlements/child-system-entitlements.component.html b/imxweb/projects/tsb/src/lib/groups/group-sidesheet/child-system-entitlements/child-system-entitlements.component.html index a7f044b5b..c83c01b91 100644 --- a/imxweb/projects/tsb/src/lib/groups/group-sidesheet/child-system-entitlements/child-system-entitlements.component.html +++ b/imxweb/projects/tsb/src/lib/groups/group-sidesheet/child-system-entitlements/child-system-entitlements.component.html @@ -1,8 +1,12 @@ - + - + - \ No newline at end of file + diff --git a/imxweb/projects/tsb/src/lib/groups/group-sidesheet/child-system-entitlements/child-system-entitlements.component.spec.ts b/imxweb/projects/tsb/src/lib/groups/group-sidesheet/child-system-entitlements/child-system-entitlements.component.spec.ts index 18b215081..ddf8da26e 100644 --- a/imxweb/projects/tsb/src/lib/groups/group-sidesheet/child-system-entitlements/child-system-entitlements.component.spec.ts +++ b/imxweb/projects/tsb/src/lib/groups/group-sidesheet/child-system-entitlements/child-system-entitlements.component.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,13 +26,13 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { EuiLoadingService } from '@elemental-ui/core'; -import { TypedEntityCollectionData } from 'imx-qbm-dbts'; +import { TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; import { GroupsService } from '../../groups.service'; import { ChildSystemEntitlementsComponent } from './child-system-entitlements.component'; function mockGetGroups(): TypedEntityCollectionData { - return {totalCount: 100, Data: ['1', '2']}; + return { totalCount: 100, Data: ['1', '2'] }; } describe('ChildSystemEntitlementsComponent', () => { @@ -41,7 +41,8 @@ describe('ChildSystemEntitlementsComponent', () => { const euiLoadingServiceStub = { hide: jasmine.createSpy('hide'), - show: jasmine.createSpy('show') + show: jasmine.createSpy('show'), + overlayRefs: [], }; beforeEach(waitForAsync(() => { @@ -49,26 +50,25 @@ describe('ChildSystemEntitlementsComponent', () => { declarations: [ChildSystemEntitlementsComponent], providers: [ { - provide: EuiLoadingService, - useValue: euiLoadingServiceStub - }, - { - provide: GroupsService, - useValue: { - getDataModel:jasmine.createSpy('getDataModel').and.returnValue(Promise.resolve({})), - getGroupsGroupMembers: jasmine.createSpy('getGroupsGroupMembers').and.returnValue(Promise.resolve(mockGetGroups())), - UnsGroupMembersSchema: { - Columns: { - __Display: { ColumnName: '__Display' }, - UID_UNSGroupChild: { ColumnName: 'UID_UNSGroupChild' }, - XDateInserted: { ColumnName: 'XDateInserted' } - } - } - } - } - ] - }) - .compileComponents(); + provide: EuiLoadingService, + useValue: euiLoadingServiceStub, + }, + { + provide: GroupsService, + useValue: { + getDataModel: jasmine.createSpy('getDataModel').and.returnValue(Promise.resolve({})), + getGroupsGroupMembers: jasmine.createSpy('getGroupsGroupMembers').and.returnValue(Promise.resolve(mockGetGroups())), + UnsGroupMembersSchema: { + Columns: { + __Display: { ColumnName: '__Display' }, + UID_UNSGroupChild: { ColumnName: 'UID_UNSGroupChild' }, + XDateInserted: { ColumnName: 'XDateInserted' }, + }, + }, + }, + }, + ], + }).compileComponents(); })); beforeEach(() => { diff --git a/imxweb/projects/tsb/src/lib/groups/group-sidesheet/child-system-entitlements/child-system-entitlements.component.ts b/imxweb/projects/tsb/src/lib/groups/group-sidesheet/child-system-entitlements/child-system-entitlements.component.ts index e9df0ef57..22511560b 100644 --- a/imxweb/projects/tsb/src/lib/groups/group-sidesheet/child-system-entitlements/child-system-entitlements.component.ts +++ b/imxweb/projects/tsb/src/lib/groups/group-sidesheet/child-system-entitlements/child-system-entitlements.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,18 +24,23 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; import { Component, Input, OnInit } from '@angular/core'; import { EuiLoadingService } from '@elemental-ui/core'; -import { PortalTargetsystemUnsGroupmembers } from 'imx-api-tsb'; -import { CollectionLoadParameters, DataModel, DisplayColumns, EntitySchema, IClientProperty, TypedEntityCollectionData } from 'imx-qbm-dbts'; +import { PortalTargetsystemUnsGroupmembers } from '@imx-modules/imx-api-tsb'; +import { + CollectionLoadParameters, + DisplayColumns, + EntitySchema, + IClientProperty, + TypedEntityCollectionData, +} from '@imx-modules/imx-qbm-dbts'; import { DataSourceToolbarSettings, SettingsService } from 'qbm'; import { GroupsService } from '../../groups.service'; @Component({ selector: 'imx-child-system-entitlements', templateUrl: './child-system-entitlements.component.html', - styleUrls: ['./child-system-entitlements.component.scss'] + styleUrls: ['./child-system-entitlements.component.scss'], }) export class ChildSystemEntitlementsComponent implements OnInit { @Input() public groupId: string; @@ -47,12 +52,11 @@ export class ChildSystemEntitlementsComponent implements OnInit { public readonly entitySchemaUnsGroupMemberships: EntitySchema; private groupDisplayedColumns: IClientProperty[] = []; - private dataModel: DataModel; constructor( private readonly busyService: EuiLoadingService, private readonly settingsService: SettingsService, - private readonly groupsService: GroupsService + private readonly groupsService: GroupsService, ) { this.navigationState = { PageSize: settingsService.DefaultPageSize, StartIndex: 0 }; this.entitySchemaUnsGroupMemberships = groupsService.UnsGroupMembersSchema; @@ -65,14 +69,6 @@ export class ChildSystemEntitlementsComponent implements OnInit { this.entitySchemaUnsGroupMemberships.Columns.XDateInserted, ]; - let overlayRef: OverlayRef; - setTimeout(() => (overlayRef = this.busyService.show())); - - try { - this.dataModel = await this.groupsService.getDataModel(this.isAdmin); - } finally { - setTimeout(() => this.busyService.hide(overlayRef)); - } await this.groupNavigate(); } @@ -81,8 +77,9 @@ export class ChildSystemEntitlementsComponent implements OnInit { } private async groupNavigate(newState?: CollectionLoadParameters): Promise { - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } try { if (newState) { @@ -95,12 +92,9 @@ export class ChildSystemEntitlementsComponent implements OnInit { dataSource: this.groupsGroupMembershipData, entitySchema: this.entitySchemaUnsGroupMemberships, navigationState: this.navigationState, - dataModel: this.dataModel }; } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } - } - } diff --git a/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-members/group-members.component.html b/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-members/group-members.component.html index ac88d2f8c..a08dd96e5 100644 --- a/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-members/group-members.component.html +++ b/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-members/group-members.component.html @@ -1,79 +1,114 @@
    - - {{ LdsDirectlyAssigned - | translate }} - {{ LdsIndirectlyAssigned - | translate }} + + + {{ LdsDirectlyAssigned | translate }} + + + {{ LdsIndirectlyAssigned | translate }} +
    - - - {{ '#LDS#Here you can manage the memberships of the system entitlement. You can request and remove memberships and view the assignment analysis for each membership.' | translate }} - - {{LdsNotUnsubscribableHint | translate}} - - - +@if (membershipView === 'direct') { + + {{ + '#LDS#Here you can manage the memberships of the system entitlement. You can request and remove memberships and view the assignment analysis for each membership.' + | translate + }} + + @if (showUnsubscribeWarning) { + {{ + LdsNotUnsubscribableHint | translate + }} + } + + + + + {{ entitySchemaGroupDirectMemberships.Columns.UID_Person.Display }} + +
    +
    {{ item.UID_Person.Column.GetDisplayValue() }}
    + @if (item.XIsInEffect.value === true) { + {{ '#LDS#Not effective' | translate }} + } +
    + +
    - - - - - - - - - - - - - - - -
    - {{ item.XMarkedForDeletion.Column.GetDisplayValue() }} -
    -
    -
    -
    + @for (column of automaticColumnDirect; track column.ColumnName) { + + {{ column.Display }} + + {{ item.GetEntity().GetColumn(column.ColumnName)?.GetDisplayValue() }} + + + } + + + +
    + @if (item.XMarkedForDeletion.value !== 0) { + {{ item.XMarkedForDeletion.Column.GetDisplayValue() }} + } +
    + +
    +
    + +
    + +} @else if (membershipView === 'nested') { + + {{ + '#LDS#Here you can get an overview of the memberships of the system entitlement. Additionally, you can view the assignment analysis for each membership.' + | translate + }} + - -
    + + + + + {{ entitySchemaGroupNestedMemberships.Columns.UID_Person.Display }} + +
    +
    {{ item.UID_Person.Column.GetDisplayValue() }}
    + @if (item.XIsInEffect.value === true) { + {{ '#LDS#Not effective' | translate }} + } +
    + +
    - - - {{ '#LDS#Here you can get an overview of the memberships of the system entitlement. Additionally, you can view the assignment analysis for each membership.' | translate }} - - - + + {{ entitySchemaGroupNestedMemberships.Columns.UID_UNSGroupChild.Display }} + +
    +
    {{ item.UID_UNSGroupChild.Column.GetDisplayValue() }}
    +
    + +
    - - - - - - - -
    - {{ item.XMarkedForDeletion.Column.GetDisplayValue() }} -
    -
    -
    -
    - -
    + + + +
    + @if (item.XMarkedForDeletion.value !== 0) { + {{ item.XMarkedForDeletion.Column.GetDisplayValue() }} + } +
    + +
    +
    + +
    +} diff --git a/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-members/group-members.component.scss b/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-members/group-members.component.scss index bd5a2a596..257ed61b0 100644 --- a/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-members/group-members.component.scss +++ b/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-members/group-members.component.scss @@ -6,21 +6,17 @@ flex-direction: column; } -.mat-button-toggle-group { - margin-bottom: 10px; - - .mat-button-toggle-checked { - background-color: $color-orange-20; - } +.imx-selection-toggle { + min-height: 40px; + display: flex; } -::ng-deep .mat-checkbox-disabled { - cursor: not-allowed; +.filled-cell { + width: 100% !important; } - -.helper-alert { - margin: 20px 2px; +.mat-button-toggle-group { + margin-bottom: 10px; } @media screen and (max-width: 768px) { @@ -28,13 +24,3 @@ width: 100%; } } - -.eui-dark-theme { - :host { - ::ng-deep.mat-button-toggle-group { - .mat-button-toggle-checked { - background-color: $color-gray-60; - } - } - } -} diff --git a/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-members/group-members.component.ts b/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-members/group-members.component.ts index d38a33033..421a7314a 100644 --- a/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-members/group-members.component.ts +++ b/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-members/group-members.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,22 +24,26 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; -import { Component, Input, OnInit, ViewChild } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; import { UntypedFormControl } from '@angular/forms'; import { MatButtonToggleChange } from '@angular/material/button-toggle'; import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { PortalTargetsystemUnsDirectmembers, PortalTargetsystemUnsGroupServiceitem, PortalTargetsystemUnsNestedmembers } from 'imx-api-tsb'; -import { CollectionLoadParameters, EntitySchema, TypedEntity, TypedEntityCollectionData, ValType } from 'imx-qbm-dbts'; import { + PortalTargetsystemUnsDirectmembers, + PortalTargetsystemUnsGroupServiceitem, + PortalTargetsystemUnsNestedmembers, +} from '@imx-modules/imx-api-tsb'; +import { CollectionLoadParameters, EntitySchema, IClientProperty, TypedEntity, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; +import { + calculateSidesheetWidth, ConfirmationService, - DataSourceToolbarSettings, - DataTableComponent, + DataViewInitParameters, + DataViewSource, + DataViewSourceFactoryService, FkAdvancedPickerComponent, - ClientPropertyForTableColumns, - SettingsService, + isMobile, SnackBarService, } from 'qbm'; import { SourceDetectiveSidesheetComponent, SourceDetectiveSidesheetData, SourceDetectiveType } from 'qer'; @@ -51,25 +55,13 @@ import { NewMembershipService } from './new-membership/new-membership.service'; selector: 'imx-group-members', templateUrl: './group-members.component.html', styleUrls: ['./group-members.component.scss'], + providers: [DataViewSource], }) export class GroupMembersComponent implements OnInit { @Input() public groupDirectMembershipData: TypedEntityCollectionData; @Input() public groupNestedMembershipData: TypedEntityCollectionData; @Input() public unsGroupDbObjectKey: DbObjectKeyBase; - @ViewChild('membersTable') public membersTable: DataTableComponent; - - /** - * Settings needed by the DataSourceToolbarComponent - */ - public dstSettings: DataSourceToolbarSettings; - public dstNestedGroupSettings: DataSourceToolbarSettings; - - /** - * Page size, start index, search and filtering options etc. - */ - public navigationState: CollectionLoadParameters; - public nestedNavigationState: CollectionLoadParameters; public viewDirectMemberships = new UntypedFormControl(true); public showUnsubscribeWarning = false; @@ -86,13 +78,19 @@ export class GroupMembersComponent implements OnInit { }, }; - private displayedColumns: ClientPropertyForTableColumns[] = []; - private nestedDisplayColumns: ClientPropertyForTableColumns[] = []; - private selectedItems: TypedEntity[] = []; + protected abortController: AbortController; + + public get selectedItemsCount() { + return this.dataSourceDirect.selection.selected.length; + } + private selectedMembershipView: 'direct' | 'nested' = 'direct'; - private busyIndicator: OverlayRef; private groupId: string; + public automaticColumnDirect: IClientProperty[]; + public dataSourceDirect: DataViewSource; + public dataSourceNested: DataViewSource; + constructor( private readonly busyService: EuiLoadingService, private readonly snackBarService: SnackBarService, @@ -101,12 +99,20 @@ export class GroupMembersComponent implements OnInit { private readonly confirmationService: ConfirmationService, private readonly membershipService: NewMembershipService, private readonly translate: TranslateService, - private readonly settingsService: SettingsService + dataSourceFactory: DataViewSourceFactoryService, ) { + this.abortController = new AbortController(); this.entitySchemaGroupDirectMemberships = groupsService.UnsGroupDirectMembersSchema; this.entitySchemaGroupNestedMemberships = groupsService.UnsGroupNestedMembersSchema; - this.navigationState = { PageSize: settingsService.DefaultPageSize, StartIndex: 0 }; - this.nestedNavigationState = { PageSize: settingsService.DefaultPageSize, StartIndex: 0 }; + this.dataSourceDirect = dataSourceFactory.getDataSource(); + this.dataSourceNested = dataSourceFactory.getDataSource(); + this.automaticColumnDirect = [ + this.entitySchemaGroupDirectMemberships.Columns.UID_UNSAccount, + this.entitySchemaGroupDirectMemberships.Columns?.XOrigin, + this.entitySchemaGroupDirectMemberships.Columns.XDateInserted, + this.entitySchemaGroupDirectMemberships.Columns.OrderState, + this.entitySchemaGroupDirectMemberships.Columns.ValidUntil, + ]; } public get membershipView(): 'direct' | 'nested' { @@ -114,80 +120,45 @@ export class GroupMembersComponent implements OnInit { } get isMobile(): boolean { - return document.body.offsetWidth <= 768; + return isMobile(); } public async ngOnInit(): Promise { - this.displayedColumns = [ - this.entitySchemaGroupDirectMemberships.Columns.UID_Person, - this.entitySchemaGroupDirectMemberships.Columns.UID_UNSAccount, - this.entitySchemaGroupDirectMemberships.Columns.XOrigin, - this.entitySchemaGroupDirectMemberships.Columns.XDateInserted, - this.entitySchemaGroupDirectMemberships.Columns.OrderState, - this.entitySchemaGroupDirectMemberships.Columns.ValidUntil, - this.entitySchemaGroupDirectMemberships.Columns.XMarkedForDeletion, - ]; - this.nestedDisplayColumns = [ - this.entitySchemaGroupNestedMemberships.Columns.UID_Person, - this.entitySchemaGroupNestedMemberships.Columns.UID_UNSGroupChild, - this.entitySchemaGroupNestedMemberships.Columns.XMarkedForDeletion, - ]; - this.groupId = this.unsGroupDbObjectKey.Keys[0]; await this.navigateDirect(); } - get selectedItemsCount(): number { - return this.selectedItems.length; - } - - public onSelectionChanged(items: TypedEntity[]): void { - this.selectedItems = items; - } - public canUnsubscribeSelected(): boolean { return ( - this.selectedItems != null && - this.selectedItemsCount > 0 && - this.selectedItems.every( + this.dataSourceDirect.selection.selected != null && + this.dataSourceDirect.selection.selected.length > 0 && + this.dataSourceDirect.selection.selected.every( (elem) => elem.GetEntity().GetColumn('UID_PersonWantsOrg').GetValue() !== '' && - elem.GetEntity().GetColumn('IsRequestCancellable').GetValue() + elem.GetEntity().GetColumn('IsRequestCancellable').GetValue(), ) ); } public canDeleteSelected(): boolean { + this.dataSourceDirect.selection != null; return ( - this.selectedItems != null && - this.selectedItemsCount > 0 && - this.selectedItems.every((elem) => elem.GetEntity().GetColumn('XOrigin').GetValue() === 1) + this.dataSourceDirect.selection.selected != null && + this.dataSourceDirect.selection.selected.length > 0 && + this.dataSourceDirect.selection.selected.every((elem) => elem.GetEntity().GetColumn('XOrigin').GetValue() === 1) ); } public async onToggleChanged(change: MatButtonToggleChange): Promise { this.selectedMembershipView = change.value; - this.selectedItems = []; + this.abortController.abort(); + if (this.selectedMembershipView === 'direct') { - return this.onDirectNavigationStateChanged({ PageSize: this.settingsService.DefaultPageSize, StartIndex: 0 }); + await this.navigateDirect(); } else { - return this.onNestedNavigationStateChanged({ PageSize: this.settingsService.DefaultPageSize, StartIndex: 0 }); - } - } - - public async onDirectNavigationStateChanged(newState?: CollectionLoadParameters): Promise { - if (newState) { - this.navigationState = newState; - } - await this.navigateDirect(); - } - - public async onNestedNavigationStateChanged(newState?: CollectionLoadParameters): Promise { - if (newState) { - this.nestedNavigationState = newState; + await this.navigateNested(); } - await this.navigateNested(); } public async deleteMembers(): Promise { @@ -201,11 +172,12 @@ export class GroupMembersComponent implements OnInit { ) { this.handleOpenLoader(); try { + console.log(this.dataSourceDirect.selection.selected); await this.groupsService.deleteGroupMembers( this.unsGroupDbObjectKey, - this.selectedItems.map((i) => i.GetEntity().GetColumn('UID_UNSAccount').GetValue()) + this.dataSourceDirect.selection.selected.map((i) => i.GetEntity().GetColumn('UID_UNSAccount').GetValue()), ); - this.membersTable.clearSelection(); + this.dataSourceDirect.selection.clear(); this.snackBarService.open({ key: '#LDS#The memberships have been successfully removed.' }, '#LDS#Close'); await this.navigateDirect(); } finally { @@ -216,10 +188,10 @@ export class GroupMembersComponent implements OnInit { public async requestMembership(serviceItem: PortalTargetsystemUnsGroupServiceitem): Promise { const sidesheetRef = this.sidesheet.open(FkAdvancedPickerComponent, { - title: await this.translate.get('#LDS#Heading Request Memberships').toPromise(), + title: await this.translate.instant('#LDS#Heading Request Memberships'), subTitle: serviceItem.GetEntity().GetDisplay(), padding: '0px', - width: 'max(600px, 60%)', + width: calculateSidesheetWidth(), icon: 'usergroup', testId: 'systementitlements-reqeust-memberships', data: { @@ -244,10 +216,12 @@ export class GroupMembersComponent implements OnInit { public async unsubscribeMembership(): Promise { // if there is at least 1 item, that is not unsubscribable, show a warning instead - const notSubscribable = this.selectedItems.some((entity) => entity.GetEntity().GetColumn('IsRequestCancellable').GetValue() === false); + const notSubscribable = this.dataSourceDirect.selection.selected.some( + (entity) => entity.GetEntity().GetColumn('IsRequestCancellable').GetValue() === false, + ); if (notSubscribable) { this.showUnsubscribeWarning = true; - this.membersTable.clearSelection(); + this.dataSourceDirect.selection.clear(); return; } if ( @@ -259,32 +233,30 @@ export class GroupMembersComponent implements OnInit { ) { this.handleOpenLoader(); try { - await Promise.all(this.selectedItems.map((entity) => this.membershipService.unsubscribeMembership(entity))); + await Promise.all(this.dataSourceDirect.selection.selected.map((entity) => this.membershipService.unsubscribeMembership(entity))); this.snackBarService.open({ key: '#LDS#The memberships have been successfully unsubscribed. It may take some time for the changes to take effect. The displayed data may differ from the actual state.', }); } finally { this.handleCloseLoader(); - this.membersTable.clearSelection(); + this.dataSourceDirect.selection.clear(); await this.navigateDirect(); } } } - public async onShowDetails(item: PortalTargetsystemUnsDirectmembers): Promise { - const uidPerson = item.UID_Person.value; - + public async onShowDetails(item: TypedEntity): Promise { const data: SourceDetectiveSidesheetData = { - UID_Person: uidPerson, + UID_Person: item.GetEntity().GetColumn('UID_Person').GetValue(), Type: SourceDetectiveType.MembershipOfSystemEntitlement, UID: this.unsGroupDbObjectKey.Keys[0], TableName: this.unsGroupDbObjectKey.TableName, }; this.sidesheet.open(SourceDetectiveSidesheetComponent, { - title: await this.translate.get('#LDS#Heading View Assignment Analysis').toPromise(), + title: await this.translate.instant('#LDS#Heading View Assignment Analysis'), subTitle: item.GetEntity().GetDisplay(), padding: '0px', - width: '800px', + width: calculateSidesheetWidth(800, 0.5), disableClose: false, testId: 'systementitlements-membership-assingment-analysis', data, @@ -292,49 +264,57 @@ export class GroupMembersComponent implements OnInit { } private async navigateDirect(): Promise { - this.handleOpenLoader(); - try { - this.groupDirectMembershipData = await this.groupsService.getGroupDirectMembers(this.groupId, this.navigationState); - this.dstSettings = { - displayedColumns: this.displayedColumns, - dataSource: this.groupDirectMembershipData, - entitySchema: this.entitySchemaGroupDirectMemberships, - navigationState: this.navigationState, - }; - } finally { - this.handleCloseLoader(); - } + this.dataSourceDirect.itemStatus = this.itemStatus; + const columnsToDisplay = [ + this.entitySchemaGroupDirectMemberships.Columns.UID_Person, + ...this.automaticColumnDirect, + this.entitySchemaGroupDirectMemberships.Columns.XMarkedForDeletion, + ]; + const dataViewInitParameters: DataViewInitParameters = { + execute: ( + params: CollectionLoadParameters, + signal: AbortSignal, + ): Promise> => { + return this.groupsService.getGroupDirectMembers(this.groupId, params, signal); + }, + schema: this.entitySchemaGroupDirectMemberships, + columnsToDisplay, + highlightEntity: (identity: PortalTargetsystemUnsDirectmembers) => { + this.onShowDetails(identity); + }, + }; + await this.dataSourceDirect.init(dataViewInitParameters); } private async navigateNested(): Promise { this.showUnsubscribeWarning = false; - this.handleOpenLoader(); - try { - this.groupNestedMembershipData = await this.groupsService.getGroupNestedMembers(this.groupId, this.nestedNavigationState); - this.dstNestedGroupSettings = { - displayedColumns: this.nestedDisplayColumns, - dataSource: this.groupNestedMembershipData, - entitySchema: this.entitySchemaGroupNestedMemberships, - navigationState: this.nestedNavigationState, - }; - } finally { - this.handleCloseLoader(); - } + const columnsToDisplay = [ + this.entitySchemaGroupNestedMemberships.Columns.UID_Person, + this.entitySchemaGroupNestedMemberships.Columns.UID_UNSGroupChild, + this.entitySchemaGroupNestedMemberships.Columns.XMarkedForDeletion, + ]; + const dataViewInitParameters: DataViewInitParameters = { + execute: ( + params: CollectionLoadParameters, + signal: AbortSignal, + ): Promise> => { + return this.groupsService.getGroupNestedMembers(this.groupId, params, signal); + }, + schema: this.entitySchemaGroupNestedMemberships, + columnsToDisplay, + highlightEntity: (identity: PortalTargetsystemUnsNestedmembers) => { + this.onShowDetails(identity); + }, + }; + await this.dataSourceNested.init(dataViewInitParameters); } private handleOpenLoader(): void { - if (!this.busyIndicator) { - this.busyIndicator = this.busyService.show(); - } + this.busyService.show(); } private handleCloseLoader(): void { - if (this.busyIndicator) { - setTimeout(() => { - this.busyService.hide(this.busyIndicator); - this.busyIndicator = undefined; - }); - } + this.busyService.hide(); } public LdsNotUnsubscribableHint = diff --git a/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-members/new-membership/new-membership.service.ts b/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-members/new-membership/new-membership.service.ts index 085a0f4bf..feded685a 100644 --- a/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-members/new-membership/new-membership.service.ts +++ b/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-members/new-membership/new-membership.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,14 +24,13 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; import { Injectable } from '@angular/core'; import { EuiLoadingService } from '@elemental-ui/core'; -import { RequestableProductForPerson } from 'imx-api-qer'; -import { PortalTargetsystemUnsGroupServiceitem } from 'imx-api-tsb'; -import { IForeignKeyInfo, TypedEntity, ValueStruct } from 'imx-qbm-dbts'; -import { ShelfService, QerApiService, UserModelService } from 'qer'; +import { RequestableProductForPerson } from '@imx-modules/imx-api-qer'; +import { PortalTargetsystemUnsGroupServiceitem } from '@imx-modules/imx-api-tsb'; +import { IForeignKeyInfo, TypedEntity, ValueStruct } from '@imx-modules/imx-qbm-dbts'; +import { QerApiService, ShelfService, UserModelService } from 'qer'; @Injectable({ providedIn: 'root', @@ -41,7 +40,7 @@ export class NewMembershipService { private readonly itShop: ShelfService, private readonly qerClient: QerApiService, private readonly busyService: EuiLoadingService, - private readonly userService: UserModelService + private readonly userService: UserModelService, ) {} public async requestMembership(members: ValueStruct[], product: PortalTargetsystemUnsGroupServiceitem): Promise { @@ -52,23 +51,25 @@ export class NewMembershipService { return false; } - let busyIndicator: OverlayRef; - setTimeout(() => (busyIndicator = this.busyService.show())); + this.busyService.show(); try { - items = items.filter((elem) => elem.UidITShopOrg != null && elem.UidITShopOrg !== ''); + items = items.filter((elem) => elem.UidITShopOrg && elem.UidITShopOrg.length > 0); + + const promises: Promise[] = []; for (const item of items) { - const entity = this.qerClient.typedClient.PortalCartitem.createEntity(); - entity.UID_ITShopOrg.value = item.UidITShopOrg; - entity.UID_PersonOrdered.value = item.UidPerson; - await this.qerClient.typedClient.PortalCartitem.Post(entity); + if (item.UidITShopOrg && item.UidPerson) { + const entity = this.qerClient.typedClient.PortalCartitem.createEntity(); + entity.UID_ITShopOrg.value = item.UidITShopOrg; + entity.UID_PersonOrdered.value = item.UidPerson; + promises.push(this.qerClient.typedClient.PortalCartitem.Post(entity)); + } } + await Promise.all(promises); await this.userService.reloadPendingItems(); } finally { - setTimeout(() => { - this.busyService.hide(busyIndicator); - }); + this.busyService.hide(); } return true; } @@ -83,7 +84,7 @@ export class NewMembershipService { private getServiceItemsForPersons( serviceItem: PortalTargetsystemUnsGroupServiceitem, - recipients: ValueStruct[] + recipients: ValueStruct[], ): RequestableProductForPerson[] { return recipients .map((recipient) => ({ @@ -92,6 +93,6 @@ export class NewMembershipService { Display: serviceItem.GetEntity().GetDisplay(), DisplayRecipient: recipient.DisplayValue, })) - .reduce((a, b) => a.concat(b), []); + .reduce((a: RequestableProductForPerson[], b) => a.concat(b), []); } } diff --git a/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-sidesheet.component.html b/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-sidesheet.component.html index 39669b54f..d812b3dbd 100644 --- a/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-sidesheet.component.html +++ b/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-sidesheet.component.html @@ -1,45 +1,54 @@ - +
    #LDS#Heading Main Data  - +
    -
    -
    - -
    - -
    -
    -
    -
    -
    - - - -
    +
    + +
    + +
    +
    +
    +
    +
    + +
    +
    @@ -48,36 +57,51 @@
    #LDS#Heading Service Item   - +
    -
    -
    - - -
    -
    - -
    +
    + + +
    +
    +
    -
    -
    - -
    -
    - @@ -100,57 +124,46 @@ > #LDS#Unsubscribe -
    -
    - -
    +
    -
    -
    - -
    +
    +
    -
    - +
    + +
    -
    -
    +
    + -
    +
    - + -
    -
    - -
    +
    +
    diff --git a/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-sidesheet.component.scss b/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-sidesheet.component.scss index 609f90919..73df6baf8 100644 --- a/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-sidesheet.component.scss +++ b/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-sidesheet.component.scss @@ -1,5 +1,6 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; -@import "@elemental-ui/core/src/styles/_eui_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/mixins'; .product-owner-datasource-container { margin: 10px 0 25px; @@ -10,23 +11,21 @@ color: $black-9; padding-bottom: 2px; } - mat-radio-button { - margin-right: 10px; - } .hidden { display: none; } - .help-icon { - color: $iris-blue; - font-size: 16px; + .imx-icon-info { margin-left: 5px; } +} - .mat-radio-group { - display: block; - margin-bottom: 10px; +.imx-dynamic-content { + @include flex-column-container-fill(); + :first-child { + height: 100%; + @include flex-column-container-fill(); } } @@ -37,84 +36,14 @@ background: $corbin-orange; } } - - ::ng-deep .mat-tab-group { - height: 100%; - flex-grow: 1; - overflow: auto; - - .mat-tab-header { - padding: 0 32px; - background-color: $white; - } - - ::ng-deep .mat-tab-body-wrapper { - height: 100%; - } - - .imx-edit-fk-open-picker { - display: none; - } - } } @media screen and (max-width: 769px) { - .product-owner-datasource-container .help-icon { + .product-owner-datasource-container .imx-icon-info { display: none; } } -.governance-sidesheet__tab-content { - display: flex; - flex-direction: column; - overflow: hidden; - > .governance-sidesheet__tab-content-body { - flex: 1 1 auto; - overflow: auto; - padding: 20px; - - > :first-child { - overflow: auto; - - } - - > .imx-ext { - overflow: hidden; - } - } - - .imx-flex-child { - ng-container { - flex: 1 1 auto; - display: flex; - } - } - - eui-icon { - font-size: 14px; - margin-right: 4px; - } -} - -.mat-tab-body-content { - display: flex; - flex-direction: column; - overflow: hidden; -} - -.imx-dirty-indicator { - color: $corbin_orange; -} - -.eui-sidesheet-content:not(.imx-hyperview-sidesheet) { - padding: 0; - - ::ng-deep .mat-tab-body-content { - display: flex; - flex-direction: column; - } -} - .eui-sidesheet-actions { .justify-start { margin-right: auto; @@ -148,12 +77,6 @@ color: $color-gray-10; } } - - ::ng-deep .mat-tab-group { - .mat-tab-header { - background-color: $color-gray-80; - } - } } } @@ -164,11 +87,5 @@ color: $color-gray-0; } } - - ::ng-deep .mat-tab-group { - .mat-tab-header { - background-color: $color-gray-90; - } - } } } diff --git a/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-sidesheet.component.ts b/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-sidesheet.component.ts index 5a14f333a..7579cf146 100644 --- a/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-sidesheet.component.ts +++ b/imxweb/projects/tsb/src/lib/groups/group-sidesheet/group-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,33 +24,29 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; -import { Component, OnInit, Inject, ViewChild } from '@angular/core'; -import { UntypedFormGroup, UntypedFormBuilder, UntypedFormArray } from '@angular/forms'; -import { EuiDownloadOptions, EuiLoadingService, EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { Component, Inject, OnInit, ViewChild } from '@angular/core'; +import { UntypedFormArray, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { EUI_SIDESHEET_DATA, EuiDownloadOptions, EuiLoadingService, EuiSidesheetRef } from '@elemental-ui/core'; +import { PortalTargetsystemUnsGroupServiceitem } from '@imx-modules/imx-api-tsb'; +import { TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { + BaseCdr, + ClassloggerService, ColumnDependentReference, - BaseCdr, ClassloggerService, - SystemInfoService, - SnackBarService, - ElementalUiConfigService, ConfirmationService, - TabItem, + ElementalUiConfigService, ExtService, + SnackBarService, + SystemInfoService, + TabItem, } from 'qbm'; -import { - HelperAlertContent, - ProjectConfigurationService, - ServiceItemsEditFormComponent -} from 'qer'; -import { PortalTargetsystemUnsGroupServiceitem } from 'imx-api-tsb'; -import { TypedEntity } from 'imx-qbm-dbts'; -import { GroupsService } from '../groups.service'; -import { GroupSidesheetData } from '../groups.models'; +import { HelperAlertContent, ProjectConfigurationService, ServiceItemsEditFormComponent } from 'qer'; +import { DbObjectKeyBase } from '../../target-system/db-object-key-wrapper.interface'; import { GroupsReportsService } from '../groups-reports.service'; +import { GroupSidesheetData } from '../groups.models'; +import { GroupsService } from '../groups.service'; import { GroupMembersComponent } from './group-members/group-members.component'; -import { DbObjectKeyBase } from '../../target-system/db-object-key-wrapper.interface'; @Component({ selector: 'imx-group-sidesheet', @@ -63,17 +59,17 @@ export class GroupSidesheetComponent implements OnInit { } public get isAdmin(): boolean { - return this.sidesheetData.isAdmin; + return this.sidesheetData?.isAdmin ?? false; } public readonly serviceItemFormGroup: UntypedFormGroup; public readonly detailsFormGroup: UntypedFormGroup; public cdrList: ColumnDependentReference[] = []; public isRequestable: boolean; - public parameters: { objecttable: string; objectuid: string; }; + public parameters: { objecttable: string; objectuid: string; display: string }; public unsGroupDbObjectKey: DbObjectKeyBase; public reportDownload: EuiDownloadOptions; - public buttonBarExtensionReferrer: { type: string, uidGroup: string, defaultDownloadOptions: EuiDownloadOptions }; + public buttonBarExtensionReferrer: { type: string; uidGroup: string; defaultDownloadOptions?: EuiDownloadOptions }; public readonly pendingAttestations: HelperAlertContent = { loading: false }; public canCreateServiceItem = false; @@ -86,7 +82,7 @@ export class GroupSidesheetComponent implements OnInit { constructor( formBuilder: UntypedFormBuilder, public groups: GroupsService, - @Inject(EUI_SIDESHEET_DATA) private readonly sidesheetData: GroupSidesheetData, + @Inject(EUI_SIDESHEET_DATA) public readonly sidesheetData: GroupSidesheetData, private readonly logger: ClassloggerService, private readonly busyService: EuiLoadingService, private readonly snackbar: SnackBarService, @@ -98,7 +94,6 @@ export class GroupSidesheetComponent implements OnInit { private readonly tabService: ExtService, private readonly confirmation: ConfirmationService, ) { - this.sidesheetRef.closeClicked().subscribe(async () => { if (!this.detailsFormGroup.dirty && !this.serviceItemFormGroup.dirty) { this.sidesheetRef.close(); @@ -116,7 +111,7 @@ export class GroupSidesheetComponent implements OnInit { this.isRequestable = sidesheetData.groupServiceItem != null && !sidesheetData.groupServiceItem.IsInActive.value; this.reportDownload = { - ... this.elementalUiConfigService.Config.downloadOptions, + ...this.elementalUiConfigService.Config.downloadOptions, url: this.reports.groupsByGroupReport(30, this.groupId), }; @@ -125,7 +120,8 @@ export class GroupSidesheetComponent implements OnInit { if (this.sidesheetData.unsGroupDbObjectKey) { this.parameters = { objecttable: this.unsGroupDbObjectKey.TableName, - objectuid: this.unsGroupDbObjectKey.Keys[0] + objectuid: this.unsGroupDbObjectKey.Keys[0], + display: this.sidesheetData.group.GetEntity().GetDisplay(), }; } this.canCreateServiceItem = !sidesheetData.group.GetEntity().GetColumn('XReadOnlyMemberships')?.GetValue(); @@ -133,7 +129,7 @@ export class GroupSidesheetComponent implements OnInit { this.buttonBarExtensionReferrer = { type: this.sidesheetData.unsGroupDbObjectKey.TableName, uidGroup: this.sidesheetData.group.GetEntity().GetKeys()[0], - defaultDownloadOptions: this.elementalUiConfigService.Config.downloadOptions + defaultDownloadOptions: this.elementalUiConfigService.Config.downloadOptions, }; } @@ -166,7 +162,7 @@ export class GroupSidesheetComponent implements OnInit { public async createServiceItem(): Promise { this.sidesheetData.group.extendedData = { - CreateServiceItem: true + CreateServiceItem: true, }; await this.saveChanges(this.detailsFormGroup, this.sidesheetData.group, '#LDS#The service item has been successfully created.', true); } @@ -186,7 +182,7 @@ export class GroupSidesheetComponent implements OnInit { }; confirmMessage = '#LDS#The service item has been successfully saved. It may take some time for the changes to take effect.'; } else { - this.groupServiceItem.extendedData = undefined; + this.groupServiceItem.extendedData = { CopyAllMembers: false }; } this.saveChanges(this.serviceItemFormGroup, this.groupServiceItem, confirmMessage); } @@ -216,33 +212,34 @@ export class GroupSidesheetComponent implements OnInit { } private async setup(): Promise { - let overlayRef: OverlayRef; - setTimeout(() => overlayRef = this.busyService.show()); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); + } try { const systemInfo = await this.systemInfoService.get(); const config = (await this.configService.getConfig()).OwnershipConfig; const type = this.parameters?.objecttable; - this.dynamicTabs = (await this.tabService.getFittingComponents('groupSidesheet', - (ext) => ext.inputData.checkVisibility(this.parameters))) - .sort((tab1: TabItem, tab2: TabItem) => tab1.sortOrder - tab2.sortOrder); + this.dynamicTabs = ( + await this.tabService.getFittingComponents('groupSidesheet', (ext) => ext.inputData.checkVisibility(this.parameters)) + ).sort((tab1: TabItem, tab2: TabItem) => tab1.sortOrder! - tab2.sortOrder!); - const cols = this.sidesheetData.group - .getColumns(systemInfo.PreProps.includes('RISKINDEX'), type == null ? [] : config.EditableFields[type]); + const cols = this.sidesheetData.group.getColumns( + systemInfo?.PreProps?.includes('RISKINDEX') ?? false, + config?.EditableFields?.[type] ?? [], + ); - this.cdrList = cols - .map(column => new BaseCdr(column)); + this.cdrList = cols.map((column) => new BaseCdr(column)); } finally { - setTimeout(() => this.busyService.hide(overlayRef)); + this.busyService.hide(); } } - private async saveChanges( formGroup: UntypedFormGroup, objectToSave: TypedEntity, confirmationText: string, - reloadServiceItem: boolean = false + reloadServiceItem: boolean = false, ): Promise { if (formGroup.valid) { this.logger.debug(this, `Saving group changes`); diff --git a/imxweb/projects/tsb/src/lib/groups/group-typed-entity.ts b/imxweb/projects/tsb/src/lib/groups/group-typed-entity.ts index 10e8bc7fd..9a8c95f22 100644 --- a/imxweb/projects/tsb/src/lib/groups/group-typed-entity.ts +++ b/imxweb/projects/tsb/src/lib/groups/group-typed-entity.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,22 +24,20 @@ * */ -import { IEntityColumn, WriteExtTypedEntity } from 'imx-qbm-dbts'; +import { IEntityColumn, WriteExtTypedEntity } from '@imx-modules/imx-qbm-dbts'; import { CdrFactoryService } from 'qbm'; export class GroupTypedEntity extends WriteExtTypedEntity { public getColumns(showRiskIndex: boolean, propertyList: string[]): ReadonlyArray { - // TODO: for each property, determine from dynamic entity schema (282445) - if (propertyList.indexOf('DisplayName') === -1) { + if (!propertyList.includes('DisplayName')) { propertyList.unshift('DisplayName'); } - if (showRiskIndex && propertyList.indexOf('RiskIndex') === -1) { + if (showRiskIndex && !propertyList.includes('RiskIndex')) { propertyList.push('RiskIndex'); } - - return propertyList.map(elem=> CdrFactoryService.tryGetColumn(this.GetEntity(),elem)).filter(elem=>elem != null); + return propertyList.map((elem) => CdrFactoryService.tryGetColumn(this.GetEntity(), elem)).filter(Boolean) as IEntityColumn[]; } } diff --git a/imxweb/projects/tsb/src/lib/groups/groups-reports.service.ts b/imxweb/projects/tsb/src/lib/groups/groups-reports.service.ts index 2e0ce8fa0..3241cb25f 100644 --- a/imxweb/projects/tsb/src/lib/groups/groups-reports.service.ts +++ b/imxweb/projects/tsb/src/lib/groups/groups-reports.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,16 +25,18 @@ */ import { Injectable } from '@angular/core'; -import { CollectionLoadParameters, TypedEntityCollectionData } from 'imx-qbm-dbts'; -import { PortalTargetsystemUnsGroup } from 'imx-api-tsb'; +import { CollectionLoadParameters, TypedEntityCollectionData } from '@imx-modules/imx-qbm-dbts'; +import { PortalTargetsystemUnsGroup } from '@imx-modules/imx-api-tsb'; import { AppConfigService, SettingsService } from 'qbm'; import { TsbApiService } from '../tsb-api-client.service'; @Injectable({ providedIn: 'root' }) export class GroupsReportsService { - constructor(private tsbClient: TsbApiService, + constructor( + private tsbClient: TsbApiService, private readonly settings: SettingsService, - private appConfig: AppConfigService) { } + private appConfig: AppConfigService, + ) {} public groupsByGroupReport(historyDays: number, groupId: string): string { const path = `targetsystem/uns/group/${groupId}/report?historydays=${historyDays}`; diff --git a/imxweb/projects/tsb/src/lib/groups/groups.component.html b/imxweb/projects/tsb/src/lib/groups/groups.component.html index 57d749f5a..3b866710f 100644 --- a/imxweb/projects/tsb/src/lib/groups/groups.component.html +++ b/imxweb/projects/tsb/src/lib/groups/groups.component.html @@ -1,17 +1,17 @@ - -
    -
    - #LDS#Heading System Entitlements + +
    +
    +

    #LDS#Heading System Entitlements

    -
    +
    - +
    @@ -19,56 +19,53 @@
    - - - +
    - - - + + + {{ entitySchemaUnsGroup?.Columns?.[DisplayColumns.DISPLAY_PROPERTYNAME]?.Display }} + +
    {{ item.GetEntity().GetDisplay() }}
    {{ item.Description.Column.GetDisplayValue() }}
    -
    -
    - - + + + + {{ entitySchemaUnsGroup?.Columns?.Requestable?.Display }} + + +
    {{ item.Requestable.Column.GetDisplayValue() }}
    + +
    + - + {{ entitySchemaUnsGroup?.Columns?.XMarkedForDeletion?.Display }} +
    {{ item.XMarkedForDeletion.Column.GetDisplayValue() }}
    -
    -
    - - + + + + {{ entitySchemaUnsGroup?.Columns?.UID_UNSRoot?.Display }} + - - -
    - + + + +
    - -
    - - diff --git a/imxweb/projects/tsb/src/lib/groups/groups.component.scss b/imxweb/projects/tsb/src/lib/groups/groups.component.scss index 0e0119e1a..744080ca8 100644 --- a/imxweb/projects/tsb/src/lib/groups/groups.component.scss +++ b/imxweb/projects/tsb/src/lib/groups/groups.component.scss @@ -1,6 +1,8 @@ -@import '../../../../../shared/scss/common-table.scss'; +@import 'base/mixins'; + :host { overflow: hidden; + height: 100%; } .data-explorer--groups > :nth-child(2) { @@ -22,31 +24,8 @@ } } -.imx-tab-card { - margin: 20px; - flex: 1 1 auto; -} -.eui-sidesheet-actions { - .imx-spacer { - flex: 1 1 auto; - } -} -.data-explorer-bottom-button-row { - display: flex; - flex-direction: row; - align-items: flex-end; - margin-top: 15px; - - .mat-stroked-button{ - @include imx-icon-for-image-button(); - } - - .imx-spacer { - flex: 1 1 auto; - } -} .hidden { display: none; } diff --git a/imxweb/projects/tsb/src/lib/groups/groups.component.ts b/imxweb/projects/tsb/src/lib/groups/groups.component.ts index 57fb0622d..1de2bb2ed 100644 --- a/imxweb/projects/tsb/src/lib/groups/groups.component.ts +++ b/imxweb/projects/tsb/src/lib/groups/groups.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,40 +24,40 @@ * */ -import { OverlayRef } from '@angular/cdk/overlay'; +import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { UntypedFormControl } from '@angular/forms'; import { ActivatedRoute } from '@angular/router'; import { EuiLoadingService, EuiSidesheetService } from '@elemental-ui/core'; -import { TranslateService } from '@ngx-translate/core'; -import { Component, OnInit, Input, OnDestroy, ViewChild } from '@angular/core'; -import { - CollectionLoadParameters, - IClientProperty, - DisplayColumns, - DbObjectKey, - EntitySchema, - DataModel, - FilterData, - ValType, -} from 'imx-qbm-dbts'; -import { ViewConfigData } from 'imx-api-qer'; +import { ViewConfigData } from '@imx-modules/imx-api-qer'; import { EntityWriteDataBulk, + PortalRespUnsgroup, PortalTargetsystemUnsGroup, PortalTargetsystemUnsGroupServiceitem, PortalTargetsystemUnsSystem, -} from 'imx-api-tsb'; +} from '@imx-modules/imx-api-tsb'; +import { + CollectionLoadParameters, + DataModel, + DbObjectKey, + DisplayColumns, + EntitySchema, + IClientProperty, + TypedEntityCollectionData, + ValType, +} from '@imx-modules/imx-qbm-dbts'; +import { TranslateService } from '@ngx-translate/core'; import { BusyService, ClassloggerService, DataSourceToolbarFilter, - DataSourceToolbarSettings, - DataTableComponent, - SettingsService, - SideNavigationComponent, - SnackBarService, DataSourceToolbarViewConfig, + DataViewInitParameters, + DataViewSource, HelpContextualValues, + SideNavigationComponent, + SnackBarService, + calculateSidesheetWidth, } from 'qbm'; import { SourceDetectiveSidesheetComponent, SourceDetectiveSidesheetData, SourceDetectiveType, ViewConfigService } from 'qer'; import { Subscription } from 'rxjs'; @@ -65,7 +65,7 @@ import { Subscription } from 'rxjs'; import { ContainerTreeDatabaseWrapper } from '../container-list/container-tree-database-wrapper'; import { DeHelperService } from '../de-helper.service'; import { GroupSidesheetComponent } from './group-sidesheet/group-sidesheet.component'; -import { GetGroupsOptionalParameters, GroupSidesheetData } from './groups.models'; +import { GroupSidesheetData } from './groups.models'; import { GroupsService } from './groups.service'; import { ProductOwnerSidesheetComponent } from './product-owner-sidesheet/product-owner-sidesheet.component'; @@ -73,6 +73,7 @@ import { ProductOwnerSidesheetComponent } from './product-owner-sidesheet/produc selector: 'imx-data-explorer-groups', templateUrl: './groups.component.html', styleUrls: ['./groups.component.scss'], + providers: [DataViewSource], }) export class DataExplorerGroupsComponent implements OnInit, OnDestroy, SideNavigationComponent { @Input() public unsAccountIdFilter: string; @@ -84,23 +85,12 @@ export class DataExplorerGroupsComponent implements OnInit, OnDestroy, SideNavig @Input() public uidPerson = ''; @Input() public usedInSidesheet = false; @Input() public contextId: HelpContextualValues; - - @ViewChild('dataTable', { static: false }) public dataTable: DataTableComponent; - /** - * Settings needed by the DataSourceToolbarComponent - */ - - public dstSettings: DataSourceToolbarSettings; - /** - * Page size, start index, search and filtering options etc. - */ - public navigationState: CollectionLoadParameters; public filterOptions: DataSourceToolbarFilter[] = []; public treeDbWrapper: ContainerTreeDatabaseWrapper; public requestableBulkUpdateCtrl = new UntypedFormControl(true); public entitySchemaUnsGroup: EntitySchema; public readonly DisplayColumns = DisplayColumns; - public selectedGroupsForUpdate: PortalTargetsystemUnsGroup[] = []; + public selectedGroupsForUpdate: Array = []; public data: any; public busyService = new BusyService(); @@ -128,12 +118,11 @@ export class DataExplorerGroupsComponent implements OnInit, OnDestroy, SideNavig private readonly dataHelper: DeHelperService, private readonly translate: TranslateService, private readonly snackbar: SnackBarService, - settingsService: SettingsService + public dataSource: DataViewSource, ) { this.isAdmin = this.route.snapshot?.url[0]?.path === 'admin'; - this.navigationState = { PageSize: settingsService.DefaultPageSize, StartIndex: 0 }; this.entitySchemaUnsGroup = this.groupsService.unsGroupsSchema(this.isAdmin); - this.authorityDataDeleted$ = this.dataHelper.authorityDataDeleted.subscribe(() => this.navigate()); + this.authorityDataDeleted$ = this.dataHelper.authorityDataDeleted.subscribe(() => this.dataSource.updateState()); this.treeDbWrapper = new ContainerTreeDatabaseWrapper(this.busyService, dataHelper); } @@ -152,38 +141,54 @@ export class DataExplorerGroupsComponent implements OnInit, OnDestroy, SideNavig if (this.unsAccountIdFilter) { this.displayedColumns.push({ ColumnName: 'action', Type: ValType.String }); } - const isBusy = this.busyService.beginBusy(); try { this.filterOptions = await this.groupsService.getFilterOptions(this.isAdmin); + this.dataSource.itemStatus = this.itemStatus; this.dataModel = await this.groupsService.getDataModel(this.isAdmin); this.viewConfigPath = this.isAdmin || this.unsAccountIdFilter ? 'targetsystem/uns/group' : 'resp/unsgroup'; this.viewConfig = await this.viewConfigService.getInitialDSTExtension(this.dataModel, this.viewConfigPath); + const dataViewInitParameters: DataViewInitParameters = { + execute: + this.isAdmin || this.unsAccountIdFilter // Wenn wir filtern, muss auch der Admin-Endpoint genutzt werden + ? (params: CollectionLoadParameters, signal: AbortSignal): Promise> => { + if (this.unsAccountIdFilter) { + params = { ...params, uid_unsaccount: this.unsAccountIdFilter }; + } + return this.groupsService.getGroups(params, signal); + } + : (params: CollectionLoadParameters, signal: AbortSignal): Promise> => + this.groupsService.getGroupsResp(params, signal), + schema: this.entitySchemaUnsGroup, + columnsToDisplay: this.displayedColumns, + dataModel: this.dataModel, + exportFunction: + this.isAdmin || this.unsAccountIdFilter + ? this.groupsService.exportGroups(this.dataSource.state()) + : this.groupsService.exportGroupsResp(this.dataSource.state()), + viewConfig: this.viewConfig, + highlightEntity: (identity: PortalTargetsystemUnsGroup | PortalRespUnsgroup) => { + this.onGroupChanged(identity); + }, + filterTree: { + filterMethode: async (parentkey) => { + return this.groupsService.getFilterTree({ + parentkey, + container: this.dataSource.state().container, + system: this.dataSource.state().system, + uid_unsaccount: this.unsAccountIdFilter, + }); + }, + multiSelect: false, + }, + selectionChange: (selection: Array) => this.onGroupSelected(selection), + }; + this.dataSource.init(dataViewInitParameters); } finally { isBusy.endBusy(); } - if (this.applyIssuesFilter && !this.issuesFilterMode) { - const ownerFilter = this.filterOptions.find((f) => { - return f.Name === 'withowner'; - }); - - if (ownerFilter) { - ownerFilter.InitialValue = '0'; - } - } - - if (this.applyIssuesFilter && this.issuesFilterMode === 'requestable') { - const requestableFliter = this.filterOptions.find((f) => { - return f.Name === 'published'; - }); - - if (requestableFliter) { - requestableFliter.InitialValue = '0'; - } - } - await this.navigate(); } public ngOnDestroy(): void { @@ -195,13 +200,13 @@ export class DataExplorerGroupsComponent implements OnInit, OnDestroy, SideNavig public async updateConfig(config: ViewConfigData): Promise { await this.viewConfigService.putViewConfig(config); this.viewConfig = await this.viewConfigService.getDSTExtensionChanges(this.viewConfigPath); - this.dstSettings.viewConfig = this.viewConfig; + this.dataSource.viewConfig.set(this.viewConfig); } public async deleteConfigById(id: string): Promise { await this.viewConfigService.deleteViewConfig(id); this.viewConfig = await this.viewConfigService.getDSTExtensionChanges(this.viewConfigPath); - this.dstSettings.viewConfig = this.viewConfig; + this.dataSource.viewConfig.set(this.viewConfig); } public get itemsAreNotRequestable(): boolean { @@ -212,18 +217,7 @@ export class DataExplorerGroupsComponent implements OnInit, OnDestroy, SideNavig return this.selectedGroupsForUpdate.every((elem) => elem.Requestable.value); } - /** - * Occurs when the navigation state has changed - e.g. users clicks on the next page button. - * - */ - public async onNavigationStateChanged(newState?: CollectionLoadParameters): Promise { - if (newState) { - this.navigationState = newState; - } - await this.navigate(); - } - - public async onGroupChanged(group: PortalTargetsystemUnsGroup): Promise { + public async onGroupChanged(group: PortalTargetsystemUnsGroup | PortalRespUnsgroup): Promise { if (this.unsAccountIdFilter) { return; } @@ -232,13 +226,12 @@ export class DataExplorerGroupsComponent implements OnInit, OnDestroy, SideNavig this.logger.trace(this, `New group selected`, group); let data: GroupSidesheetData; - let busy: OverlayRef; - const isBusy = this.busyService.beginBusy(); + const hideOverlayRef = this.busyServiceElemental.show(); try { - const objKey = DbObjectKey.FromXml(group.XObjectKey.value); + const objKey = DbObjectKey.FromXml(group.GetEntity().GetColumn('XObjectKey').GetValue()); - const uidAccProduct = group.UID_AccProduct.value; + const uidAccProduct = group.GetEntity().GetColumn('UID_AccProduct').GetValue(); data = { uidAccProduct, @@ -248,31 +241,13 @@ export class DataExplorerGroupsComponent implements OnInit, OnDestroy, SideNavig isAdmin: this.isAdmin, }; } finally { - isBusy.endBusy(); + this.busyServiceElemental.hide(hideOverlayRef); } this.viewGroup(data); } - /** - * Occurs when user triggers search. - * - * @param keywords Search keywords. - */ - public async onSearch(keywords: string): Promise { - this.logger.debug(this, `Searching for: ${keywords}`); - this.navigationState.StartIndex = 0; - this.navigationState.search = keywords; - await this.navigate(); - } - - public async filterByTree(filters: FilterData[]): Promise { - this.navigationState.filter = filters; - this.navigationState.StartIndex = 0; - return this.navigate(); - } - - public onGroupSelected(selected: PortalTargetsystemUnsGroup[]): void { + public onGroupSelected(selected: Array): void { this.selectedGroupsForUpdate = selected; } @@ -292,7 +267,7 @@ export class DataExplorerGroupsComponent implements OnInit, OnDestroy, SideNavig title: await this.translate.get('#LDS#Heading View Assignment Analysis').toPromise(), subTitle: item.GetEntity().GetDisplay(), padding: '0px', - width: 'max(600px, 60%)', + width: calculateSidesheetWidth(), disableClose: false, testId: 'system-entitlement-role-membership-details', data, @@ -334,88 +309,42 @@ export class DataExplorerGroupsComponent implements OnInit, OnDestroy, SideNavig private async updateOwnerForSelectedGroups(selectedOwner: { uidPerson?: string; uidRole?: string }): Promise { let confirmMessage = ''; - let busy: OverlayRef; + if (this.busyServiceElemental.overlayRefs.length === 0) { + this.busyServiceElemental.show(); + } try { - setTimeout(() => (busy = this.busyServiceElemental.show())); confirmMessage = await this.groupsService.updateMultipleOwner( this.selectedGroupsForUpdate.map((elem) => elem.UID_AccProduct.value), - selectedOwner + selectedOwner, ); } finally { - setTimeout(() => this.busyServiceElemental.hide(busy)); + this.busyServiceElemental.hide(); } if (confirmMessage) { this.snackbar.open({ key: confirmMessage }); } - return this.navigate(); + return this.dataSource.updateState(); } private async updateSelectedGroups(updateData: EntityWriteDataBulk): Promise { this.selectedGroupsForUpdate.forEach((group: PortalTargetsystemUnsGroup) => { const serviceItemUid = group?.UID_AccProduct.value; - if (serviceItemUid?.length) { + if (serviceItemUid?.length && updateData.Keys) { updateData.Keys.push([serviceItemUid]); } }); - let busy: OverlayRef; + if (this.busyServiceElemental.overlayRefs.length === 0) { + this.busyServiceElemental.show(); + } try { - setTimeout(() => (busy = this.busyServiceElemental.show())); await this.groupsService.bulkUpdateGroupServiceItems(updateData); - await this.navigate(); - this.dataTable.clearSelection(); + await this.dataSource.updateState(); + this.dataSource.selection.clear(); this.requestableBulkUpdateCtrl.setValue(true, { emitEvent: false }); } finally { - setTimeout(() => this.busyServiceElemental.hide(busy)); - } - } - - private async navigate(): Promise { - const isBusy = this.busyService.beginBusy(); - - const getParams: GetGroupsOptionalParameters = this.navigationState; - - try { - if (this.unsAccountIdFilter) { - getParams.uid_unsaccount = this.unsAccountIdFilter; - } - - const data = - this.isAdmin || this.unsAccountIdFilter // Wenn wir filtern, muss auch der Admin-Endpoint genutzt werden - ? await this.groupsService.getGroups(getParams) - : await this.groupsService.getGroupsResp(getParams); - - const exportMethod = - this.isAdmin || this.unsAccountIdFilter - ? this.groupsService.exportGroups(getParams) - : this.groupsService.exportGroupsResp(getParams); - exportMethod.initialColumns = this.displayedColumns.map((col) => col.ColumnName); - - this.dstSettings = { - displayedColumns: this.displayedColumns, - dataSource: data, - entitySchema: this.entitySchemaUnsGroup, - navigationState: this.navigationState, - filters: this.filterOptions, - filterTree: { - filterMethode: async (parentkey) => { - return this.groupsService.getFilterTree({ - parentkey, - container: getParams.container, - system: getParams.system, - uid_unsaccount: getParams.uid_unsaccount, - }); - }, - multiSelect: false, - }, - dataModel: this.dataModel, - viewConfig: this.viewConfig, - exportMethod, - }; - this.logger.debug(this, `Head at ${data.Data.length + this.navigationState.StartIndex} of ${data.totalCount} item(s)`); - } finally { - isBusy.endBusy(); + this.busyServiceElemental.hide(); } } @@ -424,13 +353,13 @@ export class DataExplorerGroupsComponent implements OnInit, OnDestroy, SideNavig title: await this.translate.get('#LDS#Heading Edit System Entitlement').toPromise(), subTitle: data.group.GetEntity().GetDisplay(), padding: '0px', - width: `max(650px, ${this.sidesheetWidth})`, + width: calculateSidesheetWidth(1250, 0.7), icon: 'usergroup', data, testId: 'edit-system-entitlement-sidesheet', disableClose: true, }); // After the sidesheet closes, reload the current data to refresh any changes that might have been made - sidesheetRef.afterClosed().subscribe(() => this.navigate()); + sidesheetRef.afterClosed().subscribe(() => this.dataSource.updateState()); } } diff --git a/imxweb/projects/tsb/src/lib/groups/groups.models.ts b/imxweb/projects/tsb/src/lib/groups/groups.models.ts index dcdaa860f..820c5506c 100644 --- a/imxweb/projects/tsb/src/lib/groups/groups.models.ts +++ b/imxweb/projects/tsb/src/lib/groups/groups.models.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,10 +24,10 @@ * */ -import { CollectionLoadParameters } from 'imx-qbm-dbts'; -import { PortalTargetsystemUnsGroupServiceitem } from 'imx-api-tsb'; -import { GroupTypedEntity } from './group-typed-entity'; +import { PortalTargetsystemUnsGroupServiceitem } from '@imx-modules/imx-api-tsb'; +import { CollectionLoadParameters } from '@imx-modules/imx-qbm-dbts'; import { DbObjectKeyBase } from '../target-system/db-object-key-wrapper.interface'; +import { GroupTypedEntity } from './group-typed-entity'; export interface GetGroupsOptionalParameters extends CollectionLoadParameters { uid_unsaccount?: string; @@ -46,8 +46,8 @@ export interface GroupSidesheetData { } export interface GroupsFilterTreeParameters { - container: string; - system: string; - uid_unsaccount: string; - parentkey:string; + container: string | undefined; + system: string | undefined; + uid_unsaccount: string | undefined; + parentkey: string; } diff --git a/imxweb/projects/tsb/src/lib/groups/groups.module.ts b/imxweb/projects/tsb/src/lib/groups/groups.module.ts index 564d09b03..4e5f1557b 100644 --- a/imxweb/projects/tsb/src/lib/groups/groups.module.ts +++ b/imxweb/projects/tsb/src/lib/groups/groups.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,36 +24,37 @@ * */ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; import { TranslateModule } from '@ngx-translate/core'; import { + BusyIndicatorModule, + CdrModule, DataSourceToolbarModule, DataTableModule, - CdrModule, - LdsReplaceModule, DataTreeModule, - ExtModule, + DataViewModule, DynamicTabsModule, + ExtModule, + HelpContextualModule, + LdsReplaceModule, ObjectHistoryModule, - BusyIndicatorModule, SelectedElementsModule, - HelpContextualModule - } from 'qbm'; +} from 'qbm'; -import { GroupSidesheetComponent } from './group-sidesheet/group-sidesheet.component'; +import { IdentityRoleMembershipsModule, ObjectHyperviewModule, OwnerControlModule, ServiceItemsEditFormModule } from 'qer'; +import { DataFiltersModule } from '../data-filters/data-filters.module'; +import { NoDataModule } from '../no-data/no-data.module'; +import { GroupMembershipsExtComponent } from './group-memberships-ext/group-memberships-ext.component'; +import { ChildSystemEntitlementsComponent } from './group-sidesheet/child-system-entitlements/child-system-entitlements.component'; import { GroupMembersComponent } from './group-sidesheet/group-members/group-members.component'; +import { GroupSidesheetComponent } from './group-sidesheet/group-sidesheet.component'; import { DataExplorerGroupsComponent } from './groups.component'; -import { ChildSystemEntitlementsComponent } from './group-sidesheet/child-system-entitlements/child-system-entitlements.component'; -import { NoDataModule } from '../no-data/no-data.module'; -import { DataFiltersModule } from '../data-filters/data-filters.module'; import { ProductOwnerSidesheetComponent } from './product-owner-sidesheet/product-owner-sidesheet.component'; -import { IdentityRoleMembershipsModule, ObjectHyperviewModule, OwnerControlModule, ServiceItemsEditFormModule } from 'qer'; -import { GroupMembershipsExtComponent } from './group-memberships-ext/group-memberships-ext.component'; @NgModule({ declarations: [ @@ -88,7 +89,8 @@ import { GroupMembershipsExtComponent } from './group-memberships-ext/group-memb ObjectHistoryModule, IdentityRoleMembershipsModule, SelectedElementsModule, - HelpContextualModule + HelpContextualModule, + DataViewModule, ], exports: [DataExplorerGroupsComponent, ChildSystemEntitlementsComponent], }) diff --git a/imxweb/projects/tsb/src/lib/groups/groups.service.ts b/imxweb/projects/tsb/src/lib/groups/groups.service.ts index a915e274d..e649e0331 100644 --- a/imxweb/projects/tsb/src/lib/groups/groups.service.ts +++ b/imxweb/projects/tsb/src/lib/groups/groups.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,47 +25,49 @@ */ import { Injectable } from '@angular/core'; -import { ClassloggerService, DataSourceToolbarExportMethod } from 'qbm'; +import { + EntityWriteDataBulk, + PortalRespUnsgroup, + PortalTargetsystemUnsDirectmembers, + PortalTargetsystemUnsGroup, + PortalTargetsystemUnsGroupServiceitem, + PortalTargetsystemUnsGroupmembers, + PortalTargetsystemUnsNestedmembers, + V2ApiClientMethodFactory, +} from '@imx-modules/imx-api-tsb'; import { CollectionLoadParameters, - TypedEntityCollectionData, - FilterType, CompareOperator, + DataModel, DataModelFilter, + EntityCollectionData, EntitySchema, + FilterData, FilterTreeData, - DataModel, - EntityCollectionData, + FilterType, + MethodDefinition, MethodDescriptor, - MethodDefinition -} from 'imx-qbm-dbts'; -import { - PortalTargetsystemUnsGroup, - PortalTargetsystemUnsGroupServiceitem, - PortalTargetsystemUnsGroupmembers, - EntityWriteDataBulk, - PortalTargetsystemUnsDirectmembers, - PortalTargetsystemUnsNestedmembers, - PortalRespUnsgroup, - V2ApiClientMethodFactory -} from 'imx-api-tsb'; -import { GroupsFilterTreeParameters, GetGroupsOptionalParameters } from './groups.models'; + TypedEntityCollectionData, +} from '@imx-modules/imx-qbm-dbts'; +import { ClassloggerService, DataSourceToolbarExportMethod } from 'qbm'; +import { DbObjectKeyBase } from '../target-system/db-object-key-wrapper.interface'; +import { TargetSystemDynamicMethodService } from '../target-system/target-system-dynamic-method.service'; import { TsbApiService } from '../tsb-api-client.service'; import { GroupTypedEntity } from './group-typed-entity'; -import { TargetSystemDynamicMethodService } from '../target-system/target-system-dynamic-method.service'; -import { DbObjectKeyBase } from '../target-system/db-object-key-wrapper.interface'; +import { GetGroupsOptionalParameters, GroupsFilterTreeParameters } from './groups.models'; @Injectable({ providedIn: 'root' }) export class GroupsService { constructor( private readonly tsbClient: TsbApiService, private readonly logger: ClassloggerService, - private readonly dynamicMethod: TargetSystemDynamicMethodService - ) { } + private readonly dynamicMethod: TargetSystemDynamicMethodService, + ) {} public unsGroupsSchema(isAdmin: boolean): EntitySchema { - return isAdmin ? this.tsbClient.typedClient.PortalTargetsystemUnsGroup.GetSchema() : - this.tsbClient.typedClient.PortalRespUnsgroup.GetSchema(); + return isAdmin + ? this.tsbClient.typedClient.PortalTargetsystemUnsGroup.GetSchema() + : this.tsbClient.typedClient.PortalRespUnsgroup.GetSchema(); } public get UnsGroupMembersSchema(): EntitySchema { @@ -84,8 +86,11 @@ export class GroupsService { return this.tsbClient.client.portal_targetsystem_uns_group_filtertree_get(options); } - public async getGroups(navigationState: GetGroupsOptionalParameters): Promise> { - return this.tsbClient.typedClient.PortalTargetsystemUnsGroup.Get(navigationState); + public async getGroups( + navigationState: GetGroupsOptionalParameters, + signal: AbortSignal, + ): Promise> { + return this.tsbClient.typedClient.PortalTargetsystemUnsGroup.Get(navigationState, { signal }); } public exportGroups(navigationState: GetGroupsOptionalParameters): DataSourceToolbarExportMethod { @@ -94,17 +99,20 @@ export class GroupsService { getMethod: (withProperties: string, PageSize?: number) => { let method: MethodDescriptor; if (PageSize) { - method = factory.portal_targetsystem_uns_group_get({...navigationState, withProperties, PageSize, StartIndex: 0}) + method = factory.portal_targetsystem_uns_group_get({ ...navigationState, withProperties, PageSize, StartIndex: 0 }); } else { - method = factory.portal_targetsystem_uns_group_get({...navigationState, withProperties}) + method = factory.portal_targetsystem_uns_group_get({ ...navigationState, withProperties }); } return new MethodDefinition(method); - } - } + }, + }; } - public async getGroupsResp(navigationState: GetGroupsOptionalParameters): Promise> { - return this.tsbClient.typedClient.PortalRespUnsgroup.Get(navigationState); + public async getGroupsResp( + navigationState: GetGroupsOptionalParameters, + signal: AbortSignal, + ): Promise> { + return this.tsbClient.typedClient.PortalRespUnsgroup.Get(navigationState, { signal }); } public exportGroupsResp(navigationState: GetGroupsOptionalParameters): DataSourceToolbarExportMethod { @@ -113,13 +121,13 @@ export class GroupsService { getMethod: (withProperties: string, PageSize?: number) => { let method: MethodDescriptor; if (PageSize) { - method = factory.portal_resp_unsgroup_get({...navigationState, withProperties, PageSize, StartIndex: 0}) + method = factory.portal_resp_unsgroup_get({ ...navigationState, withProperties, PageSize, StartIndex: 0 }); } else { - method = factory.portal_resp_unsgroup_get({...navigationState, withProperties}) + method = factory.portal_resp_unsgroup_get({ ...navigationState, withProperties }); } return new MethodDefinition(method); - } - } + }, + }; } public async getGroupDetails(dbObjectKey: DbObjectKeyBase): Promise { @@ -131,14 +139,14 @@ export class GroupsService { } public async getGroupServiceItem(key: string): Promise { - const navigationState: CollectionLoadParameters = { filter: [] }; - navigationState.filter.push({ + const filter: FilterData[] = []; + filter.push({ ColumnName: 'UID_AccProduct', Type: FilterType.Compare, CompareOp: CompareOperator.Equal, Value1: key, }); - return (await this.tsbClient.typedClient.PortalTargetsystemUnsGroupServiceitem.Get(navigationState)).Data[0]; + return (await this.tsbClient.typedClient.PortalTargetsystemUnsGroupServiceitem.Get({ filter })).Data[0]; } public async bulkUpdateGroupServiceItems(updateData: EntityWriteDataBulk): Promise { @@ -147,20 +155,22 @@ export class GroupsService { public async getGroupDirectMembers( groupId: string, - navigationState: CollectionLoadParameters + navigationState: CollectionLoadParameters, + signal?: AbortSignal, ): Promise> { this.logger.debug(this, `Retrieving group direct memberships`); this.logger.trace('GroupId', groupId); - return this.tsbClient.typedClient.PortalTargetsystemUnsDirectmembers.Get(groupId, navigationState); + return this.tsbClient.typedClient.PortalTargetsystemUnsDirectmembers.Get(groupId, navigationState, { signal }); } public async getGroupNestedMembers( groupId: string, - navigationState: CollectionLoadParameters + navigationState: CollectionLoadParameters, + signal?: AbortSignal, ): Promise> { this.logger.debug(this, `Retrieving group nested memberships`); this.logger.trace('GroupId', groupId); - return this.tsbClient.typedClient.PortalTargetsystemUnsNestedmembers.Get(groupId, navigationState); + return this.tsbClient.typedClient.PortalTargetsystemUnsNestedmembers.Get(groupId, navigationState, { signal }); } public async deleteGroupMembers(dbObjectKey: DbObjectKeyBase, uidAccountList: string[]): Promise { @@ -171,20 +181,19 @@ export class GroupsService { const groupId = dbObjectKey.Keys[0]; - return Promise.all(uidAccountList.map(accountId => - this.dynamicMethod.delete( - dbObjectKey.TableName, - { + return Promise.all( + uidAccountList.map((accountId) => + this.dynamicMethod.delete(dbObjectKey.TableName, { path: '{groupId}/memberships/{accountId}', - parameters: { groupId, accountId } - } - ) - )); + parameters: { groupId, accountId }, + }), + ), + ); } public async getGroupsGroupMembers( groupId: string, - navigationState: CollectionLoadParameters + navigationState: CollectionLoadParameters, ): Promise> { this.logger.debug(this, `Retrieving groups group memberships`); this.logger.trace('GroupId', groupId); @@ -192,16 +201,19 @@ export class GroupsService { } public async getFilterOptions(forAdmin: boolean): Promise { - return forAdmin ? (await this.tsbClient.client.portal_targetsystem_uns_group_datamodel_get(undefined)).Filters + const filters = forAdmin + ? (await this.tsbClient.client.portal_targetsystem_uns_group_datamodel_get(undefined)).Filters : (await this.tsbClient.client.portal_resp_unsgroup_datamodel_get(undefined)).Filters; + return filters ?? []; } public async getDataModel(forAdmin: boolean): Promise { - return forAdmin ? this.tsbClient.client.portal_targetsystem_uns_group_datamodel_get(undefined) + return forAdmin + ? this.tsbClient.client.portal_targetsystem_uns_group_datamodel_get(undefined) : this.tsbClient.client.portal_resp_unsgroup_datamodel_get(undefined); } - public async updateMultipleOwner(uidAccProducts: string[], uidPerson: { uidPerson?: string; uidRole?: string; }): Promise { + public async updateMultipleOwner(uidAccProducts: string[], uidPerson: { uidPerson?: string; uidRole?: string }): Promise { let confirmMessage = '#LDS#The product owner has been successfully assigned.'; try { for (const data of uidAccProducts) { @@ -212,8 +224,6 @@ export class GroupsService { CopyAllMembers: true, }; confirmMessage = '#LDS#The product owner has been successfully assigned. It may take some time for the changes to take effect.'; - } else { - product.extendedData = undefined; } if (uidPerson.uidRole) { product.UID_OrgRuler.value = uidPerson.uidRole; diff --git a/imxweb/projects/tsb/src/lib/groups/product-owner-sidesheet/product-owner-sidesheet.component.html b/imxweb/projects/tsb/src/lib/groups/product-owner-sidesheet/product-owner-sidesheet.component.html index d901016b4..ab3fb909b 100644 --- a/imxweb/projects/tsb/src/lib/groups/product-owner-sidesheet/product-owner-sidesheet.component.html +++ b/imxweb/projects/tsb/src/lib/groups/product-owner-sidesheet/product-owner-sidesheet.component.html @@ -1,14 +1,8 @@ -
    -
    -
    - - -
    - - - -
    -
    \ No newline at end of file +
    + +
    +
    + +
    diff --git a/imxweb/projects/tsb/src/lib/groups/product-owner-sidesheet/product-owner-sidesheet.component.scss b/imxweb/projects/tsb/src/lib/groups/product-owner-sidesheet/product-owner-sidesheet.component.scss index fb54bb970..88ef5ff3c 100644 --- a/imxweb/projects/tsb/src/lib/groups/product-owner-sidesheet/product-owner-sidesheet.component.scss +++ b/imxweb/projects/tsb/src/lib/groups/product-owner-sidesheet/product-owner-sidesheet.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; .governance-sidesheet__tab-content { display: flex; diff --git a/imxweb/projects/tsb/src/lib/groups/product-owner-sidesheet/product-owner-sidesheet.component.ts b/imxweb/projects/tsb/src/lib/groups/product-owner-sidesheet/product-owner-sidesheet.component.ts index 138b1e391..7abe45a4a 100644 --- a/imxweb/projects/tsb/src/lib/groups/product-owner-sidesheet/product-owner-sidesheet.component.ts +++ b/imxweb/projects/tsb/src/lib/groups/product-owner-sidesheet/product-owner-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,18 +28,17 @@ import { Component, Inject, ViewChild } from '@angular/core'; import { AbstractControl } from '@angular/forms'; import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; -import { PortalTargetsystemUnsGroupServiceitem } from 'imx-api-tsb'; -import { IEntityColumn } from 'imx-qbm-dbts'; +import { PortalTargetsystemUnsGroupServiceitem } from '@imx-modules/imx-api-tsb'; +import { IEntityColumn } from '@imx-modules/imx-qbm-dbts'; import { BaseCdr } from 'qbm'; import { OwnerControlComponent } from 'qer'; import { ProductOwnerSidesheetService } from './product-owner-sidesheet.service'; @Component({ templateUrl: './product-owner-sidesheet.component.html', - styleUrls: ['./product-owner-sidesheet.component.scss'] + styleUrls: ['./product-owner-sidesheet.component.scss'], }) export class ProductOwnerSidesheetComponent { - public productOwnerCdr: BaseCdr; public column: IEntityColumn; @@ -50,7 +49,7 @@ export class ProductOwnerSidesheetComponent { constructor( private readonly sidesheetRef: EuiSidesheetRef, ownerService: ProductOwnerSidesheetService, - @Inject(EUI_SIDESHEET_DATA) sidesheetData: PortalTargetsystemUnsGroupServiceitem + @Inject(EUI_SIDESHEET_DATA) sidesheetData: PortalTargetsystemUnsGroupServiceitem, ) { this.column = ownerService.buildOrgRulerColumn(sidesheetData.GetEntity()); } @@ -64,5 +63,4 @@ export class ProductOwnerSidesheetComponent { public returnProductOwner(): void { this.sidesheetRef.close({ uidPerson: this.ownercontrol.uidPersonSelected, uidRole: this.ownercontrol.uidRoleSelected }); } - } diff --git a/imxweb/projects/tsb/src/lib/groups/product-owner-sidesheet/product-owner-sidesheet.service.ts b/imxweb/projects/tsb/src/lib/groups/product-owner-sidesheet/product-owner-sidesheet.service.ts index e8893da6d..44e867b8f 100644 --- a/imxweb/projects/tsb/src/lib/groups/product-owner-sidesheet/product-owner-sidesheet.service.ts +++ b/imxweb/projects/tsb/src/lib/groups/product-owner-sidesheet/product-owner-sidesheet.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,26 +26,24 @@ import { Injectable } from '@angular/core'; -import { IEntity, IEntityColumn } from 'imx-qbm-dbts'; +import { IEntity, IEntityColumn } from '@imx-modules/imx-qbm-dbts'; import { EntityService } from 'qbm'; import { TsbApiService } from '../../tsb-api-client.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ProductOwnerSidesheetService { - constructor( private readonly entityService: EntityService, - private readonly api: TsbApiService - ) { } + private readonly api: TsbApiService, + ) {} public buildOrgRulerColumn(ref: IEntity): IEntityColumn { const provider = ref.GetFkCandidateProvider().getProviderItem('UID_OrgRuler', 'AERole'); return this.entityService.createLocalEntityColumn( this.api.typedClient.PortalTargetsystemUnsGroupServiceitem.GetSchema().Columns.UID_OrgRuler, - [provider] + provider ? [provider] : undefined, ); } - } diff --git a/imxweb/projects/tsb/src/lib/guards/tsb-namespace-admin-guard.service.ts b/imxweb/projects/tsb/src/lib/guards/tsb-namespace-admin-guard.service.ts index 3ee6a4071..54f4bd4bc 100644 --- a/imxweb/projects/tsb/src/lib/guards/tsb-namespace-admin-guard.service.ts +++ b/imxweb/projects/tsb/src/lib/guards/tsb-namespace-admin-guard.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,7 +25,7 @@ */ import { Injectable, OnDestroy } from '@angular/core'; -import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router'; +import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; import { Observable, Subscription } from 'rxjs'; import { AppConfigService, AuthenticationService, ISessionState } from 'qbm'; @@ -34,25 +34,25 @@ import { TsbPermissionsService } from '../admin/tsb-permissions.service'; @Injectable({ providedIn: 'root', }) -export class TsbNamespaceAdminGuardService implements CanActivate, OnDestroy { +export class TsbNamespaceAdminGuardService implements OnDestroy { private onSessionResponse: Subscription; constructor( private readonly authentication: AuthenticationService, private readonly permissionService: TsbPermissionsService, private readonly appConfig: AppConfigService, - private readonly router: Router - ) { } + private readonly router: Router, + ) {} public canActivate(route: ActivatedRouteSnapshot, _: RouterStateSnapshot): Observable { return new Observable((observer) => { this.onSessionResponse = this.authentication.onSessionResponse.subscribe(async (sessionState: ISessionState) => { if (sessionState.IsLoggedIn) { // TODO Later: Berechtigungen sind noch nicht genau genug, non-admin-Pages lassen sich im Moment immer ansteuern - const navigateToAdmin = route.url[0].path === 'admin'; + const navigateToAdmin = route.url[0].path === 'admin'; const userIsAdmin = await this.permissionService.isTsbNameSpaceAdminBase(); if (navigateToAdmin && !userIsAdmin) { - this.router.navigate([this.appConfig.Config.routeConfig.start], { queryParams: {} }); + this.router.navigate([this.appConfig.Config?.routeConfig?.start], { queryParams: {} }); } observer.next(!navigateToAdmin || userIsAdmin); diff --git a/imxweb/projects/tsb/src/lib/init.service.ts b/imxweb/projects/tsb/src/lib/init.service.ts index b94eac55e..479937b53 100644 --- a/imxweb/projects/tsb/src/lib/init.service.ts +++ b/imxweb/projects/tsb/src/lib/init.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,8 +28,9 @@ import { Injectable } from '@angular/core'; import { Route, Router } from '@angular/router'; import { Subscription } from 'rxjs'; -import { UnsConfig } from 'imx-api-tsb'; -import { CachedPromise } from 'imx-qbm-dbts'; +import { ProjectConfig } from '@imx-modules/imx-api-qbm'; +import { UnsConfig } from '@imx-modules/imx-api-tsb'; +import { CachedPromise } from '@imx-modules/imx-qbm-dbts'; import { AuthenticationService, CacheService, @@ -38,7 +39,7 @@ import { HELP_CONTEXTUAL, ISessionState, MenuService, - TabItem + TabItem, } from 'qbm'; import { DataExplorerRegistryService, @@ -51,11 +52,10 @@ import { import { AccountsExtComponent } from './accounts/account-ext/accounts-ext.component'; import { DataExplorerAccountsComponent } from './accounts/accounts.component'; import { isTsbNameSpaceAdminBase } from './admin/tsb-permissions-helper'; +import { GroupMembershipsExtComponent } from './groups/group-memberships-ext/group-memberships-ext.component'; import { DataExplorerGroupsComponent } from './groups/groups.component'; import { ReportButtonExtComponent } from './report-button-ext/report-button-ext.component'; import { TsbApiService } from './tsb-api-client.service'; -import { GroupMembershipsExtComponent } from './groups/group-memberships-ext/group-memberships-ext.component'; -import { ProjectConfig } from 'imx-api-qbm'; @Injectable({ providedIn: 'root' }) export class InitService { @@ -73,7 +73,7 @@ export class InitService { private readonly extService: ExtService, private readonly cacheService: CacheService, private readonly myResponsibilitiesRegistryService: MyResponsibilitiesRegistryService, - private readonly permissions: QerPermissionsService + private readonly permissions: QerPermissionsService, ) {} public ngOnDestroy(): void { @@ -87,12 +87,12 @@ export class InitService { this.onSessionResponse = this.authentication.onSessionResponse.subscribe(async (sessionState: ISessionState) => { if (sessionState.IsLoggedIn) { - if(await this.permissions.isPersonManager()){ + if (await this.permissions.isPersonManager()) { this.extService.register('identityReportsManager', { instance: ReportButtonExtComponent, inputData: { - caption: '#LDS#Download report on user accounts of identities you are directly responsible for' - } + caption: '#LDS#View user accounts of identities you are directly responsible for', + }, }); } } @@ -107,16 +107,15 @@ export class InitService { sortOrder: 10, } as TabItem); - this.extService.register('identityAssignment', { - instance: GroupMembershipsExtComponent, - inputData: { - id: 'groups', - label: '#LDS#System entitlements', - checkVisibility: async (_) => true, - }, - sortOrder: 10, - } as TabItem); - + this.extService.register('identityAssignment', { + instance: GroupMembershipsExtComponent, + inputData: { + id: 'groups', + label: '#LDS#System entitlements', + checkVisibility: async (_) => true, + }, + sortOrder: 10, + } as TabItem); this.addRoutes(routes); this.setupMenu(); @@ -146,9 +145,9 @@ export class InitService { name: 'groups', caption: '#LDS#System entitlements', icon: 'usergroup', - contextId: HELP_CONTEXTUAL.DataExplorerGroups + contextId: HELP_CONTEXTUAL.DataExplorerGroups, }; - } + }, ); this.entlTypeService.Register(async () => [ @@ -160,17 +159,27 @@ export class InitService { name: 'UNSGroup', caption: '#LDS#System entitlements', icon: 'usergroup', - contextId: HELP_CONTEXTUAL.MyResponsibilitiesGroups + contextId: HELP_CONTEXTUAL.MyResponsibilitiesGroups, })); } private async loadUnsTypes(): Promise { const config = await this.cachedUnsConfig.get(); const types: IRequestableEntitlementType[] = []; + if (!config.ShopAssign) { + return types; + } for (const key of Object.keys(config.ShopAssign)) { - types.push( - new RequestableEntitlementType(key, this.tsbApiService.apiClient, config.ShopAssign[key].GroupColumnName, this.dynamicMethodService) - ); + if (config.ShopAssign[key]?.GroupColumnName) { + types.push( + new RequestableEntitlementType( + key, + this.tsbApiService.apiClient, + config.ShopAssign[key].GroupColumnName!, + this.dynamicMethodService, + ), + ); + } } return types; } @@ -187,7 +196,7 @@ export class InitService { this.menuService.addMenuFactories( (preProps: string[], __: string[]) => { if (!preProps.includes('ITSHOP')) { - return null; + return undefined; } return { @@ -206,7 +215,7 @@ export class InitService { }, (preProps: string[], features: string[], projectConfig: ProjectConfig, groups: string[]) => { if (!preProps.includes('ITSHOP') || !isTsbNameSpaceAdminBase(groups)) { - return null; + return undefined; } return { @@ -222,7 +231,7 @@ export class InitService { }, ], }; - } + }, ); } } diff --git a/imxweb/projects/tsb/src/lib/no-data/no-data.component.html b/imxweb/projects/tsb/src/lib/no-data/no-data.component.html index 8af2d414f..d0b294b45 100644 --- a/imxweb/projects/tsb/src/lib/no-data/no-data.component.html +++ b/imxweb/projects/tsb/src/lib/no-data/no-data.component.html @@ -1,12 +1,12 @@
    - -

    #LDS#Heading No Synchronization Configured

    + +

    #LDS#Heading No Synchronization Configured

    #LDS#Please configure a synchronization using the Synchronization Editor.

    - -

    #LDS#Heading No Data Synchronized

    + +

    #LDS#Heading No Data Synchronized

    #LDS#A synchronization has been configured in the Synchronization Editor, but not run yet.

    -
    \ No newline at end of file +
    diff --git a/imxweb/projects/tsb/src/lib/no-data/no-data.component.scss b/imxweb/projects/tsb/src/lib/no-data/no-data.component.scss index e298c049a..1e2aead52 100644 --- a/imxweb/projects/tsb/src/lib/no-data/no-data.component.scss +++ b/imxweb/projects/tsb/src/lib/no-data/no-data.component.scss @@ -1,4 +1,4 @@ -@import '@elemental-ui/core/src/styles/_palette.scss'; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; .data-explorer-sync-status { height: 100%; @@ -14,12 +14,11 @@ } .eui-icon { - font-size: 150px; - color: $black-9; + color: $color-gray-40; margin-bottom: 30px; } - h1 { + h2 { font-size: 32px; font-weight: 400; } @@ -28,6 +27,6 @@ font-size: 18px; margin-top: 10px; margin-bottom: 75px; - color: $black-9; + color: $color-gray-40; } } diff --git a/imxweb/projects/tsb/src/lib/no-data/no-data.component.ts b/imxweb/projects/tsb/src/lib/no-data/no-data.component.ts index 02cb8b589..edfa98766 100644 --- a/imxweb/projects/tsb/src/lib/no-data/no-data.component.ts +++ b/imxweb/projects/tsb/src/lib/no-data/no-data.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/tsb/src/lib/no-data/no-data.module.ts b/imxweb/projects/tsb/src/lib/no-data/no-data.module.ts index 7353dda03..0708e80c3 100644 --- a/imxweb/projects/tsb/src/lib/no-data/no-data.module.ts +++ b/imxweb/projects/tsb/src/lib/no-data/no-data.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,7 +24,6 @@ * */ - import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { EuiCoreModule } from '@elemental-ui/core'; @@ -33,16 +32,8 @@ import { TranslateModule } from '@ngx-translate/core'; import { DataExplorerNoDataComponent } from './no-data.component'; @NgModule({ - declarations: [ - DataExplorerNoDataComponent - ], - exports: [ - DataExplorerNoDataComponent - ], - imports: [ - CommonModule, - EuiCoreModule, - TranslateModule - ] + declarations: [DataExplorerNoDataComponent], + exports: [DataExplorerNoDataComponent], + imports: [CommonModule, EuiCoreModule, TranslateModule], }) -export class NoDataModule { } +export class NoDataModule {} diff --git a/imxweb/projects/tsb/src/lib/report-button-ext/report-button-ext.component.html b/imxweb/projects/tsb/src/lib/report-button-ext/report-button-ext.component.html index 1dc62b5f7..518e5a206 100644 --- a/imxweb/projects/tsb/src/lib/report-button-ext/report-button-ext.component.html +++ b/imxweb/projects/tsb/src/lib/report-button-ext/report-button-ext.component.html @@ -1,3 +1,3 @@ diff --git a/imxweb/projects/tsb/src/lib/report-button-ext/report-button-ext.component.scss b/imxweb/projects/tsb/src/lib/report-button-ext/report-button-ext.component.scss index e110d11b2..248a268c5 100644 --- a/imxweb/projects/tsb/src/lib/report-button-ext/report-button-ext.component.scss +++ b/imxweb/projects/tsb/src/lib/report-button-ext/report-button-ext.component.scss @@ -1 +1 @@ -// add styles if necessary \ No newline at end of file +// add styles if necessary diff --git a/imxweb/projects/tsb/src/lib/report-button-ext/report-button-ext.component.ts b/imxweb/projects/tsb/src/lib/report-button-ext/report-button-ext.component.ts index 989790196..2b3d52b72 100644 --- a/imxweb/projects/tsb/src/lib/report-button-ext/report-button-ext.component.ts +++ b/imxweb/projects/tsb/src/lib/report-button-ext/report-button-ext.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,19 +24,18 @@ * */ -import { Component, Injector, OnInit } from '@angular/core'; -import { EuiDownloadDirective, EuiDownloadOptions } from '@elemental-ui/core'; +import { Component, ElementRef, Injector, OnInit } from '@angular/core'; +import { EuiDownloadDirective, EuiDownloadOptions, EuiDownloadService } from '@elemental-ui/core'; +import { Overlay } from '@angular/cdk/overlay'; +import { HttpClient } from '@angular/common/http'; import { ElementalUiConfigService } from 'qbm'; -import { QerPermissionsService } from 'qer'; import { AccountsReportsService } from '../accounts/accounts-reports.service'; -import { HttpClient } from '@angular/common/http'; -import { Overlay } from '@angular/cdk/overlay'; @Component({ selector: 'imx-report-button-ext', templateUrl: './report-button-ext.component.html', - styleUrls: ['./report-button-ext.component.scss'] + styleUrls: ['./report-button-ext.component.scss'], }) export class ReportButtonExtComponent implements OnInit { public downloadOptions: EuiDownloadOptions; @@ -50,20 +49,26 @@ export class ReportButtonExtComponent implements OnInit { private readonly http: HttpClient, private readonly injector: Injector, private readonly overlay: Overlay, - - ) { } + private readonly downloadService: EuiDownloadService, + ) {} public async ngOnInit(): Promise { const url = this.service.accountsOwnedByManagedReport(30, this.referrer); this.downloadOptions = { ...this.elementalUiConfigService.Config.downloadOptions, - url + url, }; } - public viewReport():void{ - const directive = new EuiDownloadDirective(null, this.http, this.overlay, this.injector); + public viewReport(): void { + const directive = new EuiDownloadDirective( + new ElementRef('') /* no element */, + this.http, + this.overlay, + this.injector, + this.downloadService, + ); directive.downloadOptions = { ...this.downloadOptions, disableElement: false, diff --git a/imxweb/projects/tsb/src/lib/report-button-ext/report-button-ext.module.ts b/imxweb/projects/tsb/src/lib/report-button-ext/report-button-ext.module.ts index 85e6322dc..ba8b4614e 100644 --- a/imxweb/projects/tsb/src/lib/report-button-ext/report-button-ext.module.ts +++ b/imxweb/projects/tsb/src/lib/report-button-ext/report-button-ext.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -33,12 +33,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { ReportButtonExtComponent } from './report-button-ext.component'; @NgModule({ declarations: [ReportButtonExtComponent], - imports: [ - CommonModule, - MatMenuModule, - EuiCoreModule, - TranslateModule - ], - exports: [ReportButtonExtComponent] + imports: [CommonModule, MatMenuModule, EuiCoreModule, TranslateModule], + exports: [ReportButtonExtComponent], }) -export class ReportButtonExtModule { } +export class ReportButtonExtModule {} diff --git a/imxweb/projects/tsb/src/lib/target-system/db-object-key-wrapper.interface.ts b/imxweb/projects/tsb/src/lib/target-system/db-object-key-wrapper.interface.ts index d2a406628..b4725c516 100644 --- a/imxweb/projects/tsb/src/lib/target-system/db-object-key-wrapper.interface.ts +++ b/imxweb/projects/tsb/src/lib/target-system/db-object-key-wrapper.interface.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,9 +24,9 @@ * */ -import { DbObjectKey } from 'imx-qbm-dbts'; +import { DbObjectKey } from '@imx-modules/imx-qbm-dbts'; -export type DbObjectKeyBase = { TableName: string; Keys: ReadonlyArray; } | DbObjectKey; +export type DbObjectKeyBase = { TableName: string; Keys: ReadonlyArray } | DbObjectKey; export interface DbObjectKeyWrapper { dbObjectKey: DbObjectKeyBase; diff --git a/imxweb/projects/tsb/src/lib/target-system/path-parameter-wrapper.ts b/imxweb/projects/tsb/src/lib/target-system/path-parameter-wrapper.ts index ba160cd86..d7f586e51 100644 --- a/imxweb/projects/tsb/src/lib/target-system/path-parameter-wrapper.ts +++ b/imxweb/projects/tsb/src/lib/target-system/path-parameter-wrapper.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/tsb/src/lib/target-system/target-system-dynamic-method.service.ts b/imxweb/projects/tsb/src/lib/target-system/target-system-dynamic-method.service.ts index 14608d2be..9774239bd 100644 --- a/imxweb/projects/tsb/src/lib/target-system/target-system-dynamic-method.service.ts +++ b/imxweb/projects/tsb/src/lib/target-system/target-system-dynamic-method.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,26 +26,26 @@ import { Injectable } from '@angular/core'; -import { CompareOperator, ExtendedTypedEntityCollection, FilterType, IEntity, TypedEntity } from 'imx-qbm-dbts'; +import { CompareOperator, ExtendedTypedEntityCollection, FilterType, IEntity, TypedEntity } from '@imx-modules/imx-qbm-dbts'; import { DynamicCollectionLoadParameters, DynamicMethodService, MetadataService } from 'qbm'; import { TsbApiService } from '../tsb-api-client.service'; import { DbObjectKeyWrapper } from './db-object-key-wrapper.interface'; import { PathParameterWrapper } from './path-parameter-wrapper'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class TargetSystemDynamicMethodService { constructor( private readonly tsbClient: TsbApiService, private readonly dynamicMethod: DynamicMethodService, - private readonly metadata: MetadataService - ) { } + private readonly metadata: MetadataService, + ) {} public async getCollection( type: new (e: IEntity) => TEntity, tableName: string, - parameters: DynamicCollectionLoadParameters = {} + parameters: DynamicCollectionLoadParameters = {}, ): Promise> { const path = '/portal/targetsystem/' + tableName; return this.dynamicMethod.get(this.tsbClient.apiClient, { type, path }, parameters); @@ -53,35 +53,42 @@ export class TargetSystemDynamicMethodService { public async getById( type: new (e: IEntity) => TEntity, - dbObjectKeyWrapper: DbObjectKeyWrapper + dbObjectKeyWrapper: DbObjectKeyWrapper, ): Promise { const key = dbObjectKeyWrapper.dbObjectKey.Keys[0]; const tableName = dbObjectKeyWrapper.dbObjectKey.TableName.toLowerCase(); const path = '/portal/targetsystem/' + tableName + '/interactive'; - return (await this.dynamicMethod.getInteractive(this.tsbClient.apiClient, { type, key, path }, { - name: dbObjectKeyWrapper.columnName, value: key - })).Data[0]; + return ( + await this.dynamicMethod.getInteractive( + this.tsbClient.apiClient, + { type, key, path }, + { + name: dbObjectKeyWrapper.columnName!, + value: key, + }, + ) + ).Data[0]; } public async get( type: new (e: IEntity) => TEntity, - dbObjectKeyWrapper: DbObjectKeyWrapper + dbObjectKeyWrapper: DbObjectKeyWrapper, ): Promise { await this.metadata.updateNonExisting([dbObjectKeyWrapper.dbObjectKey.TableName]); const tableMetadata = this.metadata.tables[dbObjectKeyWrapper.dbObjectKey.TableName]; - const filter = [{ - ColumnName: dbObjectKeyWrapper.columnName ?? tableMetadata.PrimaryKeyColumns[0], - Type: FilterType.Compare, - CompareOp: CompareOperator.Equal, - Value1: dbObjectKeyWrapper.dbObjectKey.Keys[0] - }]; + const filter = [ + { + ColumnName: dbObjectKeyWrapper.columnName ?? tableMetadata?.PrimaryKeyColumns?.[0] ?? '', + Type: FilterType.Compare, + CompareOp: CompareOperator.Equal, + Value1: dbObjectKeyWrapper.dbObjectKey.Keys[0], + }, + ]; - return (await this.getCollection( - type, dbObjectKeyWrapper.dbObjectKey.TableName, { filter } - ))?.Data[0]; + return (await this.getCollection(type, dbObjectKeyWrapper.dbObjectKey.TableName, { filter }))?.Data[0]; } public async delete(tableName: string, pathParameterWrapper: PathParameterWrapper): Promise { diff --git a/imxweb/projects/tsb/src/lib/target-system/target-system.service.ts b/imxweb/projects/tsb/src/lib/target-system/target-system.service.ts index ad0fbfba9..d6e0e934e 100644 --- a/imxweb/projects/tsb/src/lib/target-system/target-system.service.ts +++ b/imxweb/projects/tsb/src/lib/target-system/target-system.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,14 +26,14 @@ import { Injectable } from '@angular/core'; -import { TsbUserConfig } from 'imx-api-tsb'; +import { TsbUserConfig } from '@imx-modules/imx-api-tsb'; import { TsbApiService } from '../tsb-api-client.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class TargetSystemService { - constructor(private readonly apiService: TsbApiService) { } + constructor(private readonly apiService: TsbApiService) {} public async getUserConfig(): Promise { return this.apiService.client.portal_targetsystem_userconfig_get(); diff --git a/imxweb/projects/tsb/src/lib/test/common-test-mocks.spec.ts b/imxweb/projects/tsb/src/lib/test/common-test-mocks.spec.ts index 35a577398..531c03138 100644 --- a/imxweb/projects/tsb/src/lib/test/common-test-mocks.spec.ts +++ b/imxweb/projects/tsb/src/lib/test/common-test-mocks.spec.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,96 +24,93 @@ * */ -import { IEntityColumn, IEntity } from 'imx-qbm-dbts'; +import { IEntityColumn, IEntity } from '@imx-modules/imx-qbm-dbts'; import { ISessionState } from 'qbm'; import { Subject } from 'rxjs'; export class TsbCommonTestData { - public static mockConfigService: any = { getConfig: () => { return Promise.resolve({ PersonConfig: { VI_MyData_WhitePages_ResultAttributes: { - Columns: ['col1', 'col2'] + Columns: ['col1', 'col2'], }, VI_PersonalData_Fields: { - Columns: ['col1', 'col2'] - } - } + Columns: ['col1', 'col2'], + }, + }, }); - } + }, }; public static mockAppConfigService: any = { Config: { Title: '', routeConfig: { - start: 'dashboard' - } + start: 'dashboard', + }, }, client: { imx_multilanguage_getcaptions_get: () => Promise.resolve({}), - imx_multilanguage_translations_get: () => Promise.resolve({}) - } + imx_multilanguage_translations_get: () => Promise.resolve({}), + }, }; public static mockAuthenticationServiceStub = { onSessionResponse: new Subject(), - update: jasmine.createSpy('update') + update: jasmine.createSpy('update'), }; public static mockSessionService: any = { TypedClient: { PortalTargetsystemUnsGroup: { - Get: () => Promise.resolve({}) + Get: () => Promise.resolve({}), }, PortalTargetsystemUnsAccount: { - Get: () => Promise.resolve({}) + Get: () => Promise.resolve({}), }, PortalPersonAll: { - Get: () => Promise.resolve({}) + Get: () => Promise.resolve({}), }, PortalAdminPerson: { - Get: () => Promise.resolve({}) + Get: () => Promise.resolve({}), }, PortalPerson: { - Get: () => Promise.resolve({Data: ['test1', 'test2']}) + Get: () => Promise.resolve({ Data: ['test1', 'test2'] }), }, - } + }, }; public static mockEntityColumn = { ColumnName: '', GetMetadata: () => { return { - CanEdit: () => false, + CanEdit: () => false, GetDisplay: () => '', - GetMinLength: () => 0 + GetMinLength: () => 0, }; }, GetDisplayValue: () => '', - GetValue: () => '', - PutValue: _ => {}, - + GetValue: () => '', + PutValue: (_) => {}, } as IEntityColumn; public static mockEntityColumnWithValue = { ColumnName: '', GetMetadata: () => { return { - CanEdit: () => false, + CanEdit: () => false, GetDisplay: () => '', - GetMinLength: () => 0 + GetMinLength: () => 0, }; }, - GetValue: () => 'Test value 1' - + GetValue: () => 'Test value 1', } as IEntityColumn; public static mockEntity = { GetDisplay: () => 'Display value', GetKeys: () => ['1'], - GetColumn: (name) => TsbCommonTestData.mockEntityColumn + GetColumn: (name) => TsbCommonTestData.mockEntityColumn, } as IEntity; } diff --git a/imxweb/projects/tsb/src/lib/tsb-api-client.service.ts b/imxweb/projects/tsb/src/lib/tsb-api-client.service.ts index e8d8d8f73..d0d149543 100644 --- a/imxweb/projects/tsb/src/lib/tsb-api-client.service.ts +++ b/imxweb/projects/tsb/src/lib/tsb-api-client.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,12 +25,12 @@ */ import { Injectable } from '@angular/core'; -import { V2Client, TypedClient } from 'imx-api-tsb'; -import { ApiClient } from 'imx-qbm-dbts'; +import { V2Client, TypedClient } from '@imx-modules/imx-api-tsb'; +import { ApiClient } from '@imx-modules/imx-qbm-dbts'; import { AppConfigService, ClassloggerService, ImxTranslationProviderService } from 'qbm'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class TsbApiService { private tc: TypedClient; @@ -50,7 +50,8 @@ export class TsbApiService { constructor( private readonly config: AppConfigService, private readonly logger: ClassloggerService, - private readonly translationProvider: ImxTranslationProviderService) { + private readonly translationProvider: ImxTranslationProviderService, + ) { try { this.logger.debug(this, 'Initializing TSB API service'); diff --git a/imxweb/projects/tsb/src/lib/tsb-config.module.ts b/imxweb/projects/tsb/src/lib/tsb-config.module.ts index 053a6d115..76cf6464c 100644 --- a/imxweb/projects/tsb/src/lib/tsb-config.module.ts +++ b/imxweb/projects/tsb/src/lib/tsb-config.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -46,21 +46,20 @@ const routes: Routes = [ path: 'claimgroup', component: ClaimGroupComponent, canActivate: [RouteGuardService], - data:{ - contextId: HELP_CONTEXTUAL.ClaimGroup - } + data: { + contextId: HELP_CONTEXTUAL.ClaimGroup, + }, }, { path: 'resp/UNSGroup', component: DataExplorerGroupsComponent, canActivate: [RouteGuardService], - resolve: [RouteGuardService] - } + resolve: [RouteGuardService], + }, ]; @NgModule({ - declarations: [ - ], + declarations: [], imports: [ AccountsModule, BusinessownerAddonTileModule, @@ -73,11 +72,9 @@ const routes: Routes = [ MatListModule, TileModule, TranslateModule, - ReportButtonExtModule + ReportButtonExtModule, ], - providers: [ - TsbNamespaceAdminGuardService - ] + providers: [TsbNamespaceAdminGuardService], }) export class TsbConfigModule { constructor(private readonly initializer: InitService) { diff --git a/imxweb/projects/tsb/src/public_api.ts b/imxweb/projects/tsb/src/public_api.ts index de3cc8424..01bf56b81 100644 --- a/imxweb/projects/tsb/src/public_api.ts +++ b/imxweb/projects/tsb/src/public_api.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/tsb/src/test.ts b/imxweb/projects/tsb/src/test.ts index 8a6ea30b8..e07515d40 100644 --- a/imxweb/projects/tsb/src/test.ts +++ b/imxweb/projects/tsb/src/test.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,27 +26,16 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'core-js/es7/reflect'; -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +// Zone must be imported first. Be careful with prettier import organizing +import 'zone.js'; +import 'zone.js/testing'; + import { getTestBed } from '@angular/core/testing'; -import { - BrowserDynamicTestingModule, - platformBrowserDynamicTesting -} from '@angular/platform-browser-dynamic/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; +import 'core-js/es/reflect'; import { TestHelperModule } from 'qbm'; -declare const require: any; - // First, initialize the Angular testing environment. -getTestBed().initTestEnvironment( - [BrowserDynamicTestingModule, TestHelperModule], - platformBrowserDynamicTesting(), - { - teardown: { destroyAfterEach: false } - } -); -// Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); -// And load the modules. -context.keys().map(context); +getTestBed().initTestEnvironment([BrowserDynamicTestingModule, TestHelperModule], platformBrowserDynamicTesting(), { + teardown: { destroyAfterEach: false }, +}); diff --git a/imxweb/projects/tsb/tsconfig.lib.json b/imxweb/projects/tsb/tsconfig.lib.json index 6bc419f84..534c734a0 100644 --- a/imxweb/projects/tsb/tsconfig.lib.json +++ b/imxweb/projects/tsb/tsconfig.lib.json @@ -1,16 +1,17 @@ /* To learn more about this file see: https://angular.io/config/tsconfig. */ { "extends": "../../tsconfig.json", + "angularCompilerOptions": { + "strictTemplates": true + }, "compilerOptions": { "outDir": "../../out-tsc/lib", + "strictNullChecks": true, "declaration": true, "declarationMap": true, "sourceMap": true, "inlineSources": true, "types": [] }, - "exclude": [ - "src/test.ts", - "**/*.spec.ts" - ] + "exclude": ["src/test.ts", "**/*.spec.ts"] } diff --git a/imxweb/projects/tsb/tsconfig.lib.prod.json b/imxweb/projects/tsb/tsconfig.lib.prod.json index 61c7592e7..fb40dbbb0 100644 --- a/imxweb/projects/tsb/tsconfig.lib.prod.json +++ b/imxweb/projects/tsb/tsconfig.lib.prod.json @@ -3,8 +3,5 @@ "extends": "./tsconfig.lib.json", "compilerOptions": { "declarationMap": false - }, - "angularCompilerOptions": { - "enableIvy": true } } diff --git a/imxweb/projects/tsb/tsconfig.spec.json b/imxweb/projects/tsb/tsconfig.spec.json index 16da33db0..6bd74e1f7 100644 --- a/imxweb/projects/tsb/tsconfig.spec.json +++ b/imxweb/projects/tsb/tsconfig.spec.json @@ -1,17 +1,10 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "strictNullChecks": false, "outDir": "../../out-tsc/spec", - "types": [ - "jasmine", - "node" - ] + "types": ["jasmine", "node"] }, - "files": [ - "src/test.ts" - ], - "include": [ - "**/*.spec.ts", - "**/*.d.ts" - ] + "files": ["src/test.ts"], + "include": ["**/*.spec.ts", "**/*.d.ts"] } diff --git a/imxweb/projects/tsb/tslint.json b/imxweb/projects/tsb/tslint.json deleted file mode 100644 index 8bab15f53..000000000 --- a/imxweb/projects/tsb/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "imx", - "camelCase" - ], - "component-selector": [ - true, - "element", - "imx", - "kebab-case" - ] - } -} diff --git a/imxweb/projects/uci/.compodocrc.json b/imxweb/projects/uci/.compodocrc.json index 235acc0e1..98cc812db 100644 --- a/imxweb/projects/uci/.compodocrc.json +++ b/imxweb/projects/uci/.compodocrc.json @@ -1,6 +1,6 @@ { "name": "IMX Web - UCI Library", - "output": "../../documentation/v92/uci", + "output": "../../documentation/v93/uci", "theme": "material", "assetsFolder": "../../compodoc/assets", "customLogo": "../../compodoc/assets/images/oneidentity-logo.png", diff --git a/imxweb/projects/uci/.eslintrc.json b/imxweb/projects/uci/.eslintrc.json new file mode 100644 index 000000000..23897dab8 --- /dev/null +++ b/imxweb/projects/uci/.eslintrc.json @@ -0,0 +1,35 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*", "**/*.spec.ts"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": ["imxweb/projects/uci/tsconfig.lib.json", "imxweb/projects/uci/tsconfig.spec.json"], + "createDefaultProgram": true + }, + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "imx", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "imx", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "rules": {} + } + ] +} diff --git a/imxweb/projects/uci/.vscode/settings.json b/imxweb/projects/uci/.vscode/settings.json index ed94f44b1..23830fb42 100644 --- a/imxweb/projects/uci/.vscode/settings.json +++ b/imxweb/projects/uci/.vscode/settings.json @@ -1,3 +1,3 @@ { "git.ignoreLimitWarning": true -} \ No newline at end of file +} diff --git a/imxweb/projects/uci/imx-plugin-config.json b/imxweb/projects/uci/imx-plugin-config.json index 2b08904c2..4da4a4d5c 100644 --- a/imxweb/projects/uci/imx-plugin-config.json +++ b/imxweb/projects/uci/imx-plugin-config.json @@ -1,8 +1,8 @@ { "qer-app-operationssupport": [ - { - "Container": "uci", - "Name": "UciConfigModule" - } + { + "Container": "uci", + "Name": "UciConfigModule" + } ] } diff --git a/imxweb/projects/uci/karma.conf.js b/imxweb/projects/uci/karma.conf.js index 211193c9d..3e53a3ac8 100644 --- a/imxweb/projects/uci/karma.conf.js +++ b/imxweb/projects/uci/karma.conf.js @@ -15,7 +15,7 @@ module.exports = function (config) { require('karma-junit-reporter'), ], client: { - clearContext: false // leave Jasmine Spec Runner output visible in browser + clearContext: false, // leave Jasmine Spec Runner output visible in browser }, coverageIstanbulReporter: { dir: require('path').join(__dirname, 'results'), @@ -23,7 +23,7 @@ module.exports = function (config) { fixWebpackSourcePaths: true, 'report-config': { html: { - subdir: 'coverage-html' + subdir: 'coverage-html', }, }, thresholds: { @@ -40,13 +40,14 @@ module.exports = function (config) { port: 9876, captureTimeout: 210000, browserDisconnectTolerance: 3, - browserDisconnectTimeout : 210000, - browserNoActivityTimeout : 210000, + browserDisconnectTimeout: 210000, + browserNoActivityTimeout: 210000, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], singleRun: false, - restartOnFileChange: true + failOnEmptyTestSuite: false, + restartOnFileChange: true, }); }; diff --git a/imxweb/projects/uci/ng-package.dynamic-ops.json b/imxweb/projects/uci/ng-package.dynamic-ops.json new file mode 100644 index 000000000..f0dd7ead5 --- /dev/null +++ b/imxweb/projects/uci/ng-package.dynamic-ops.json @@ -0,0 +1,26 @@ +{ + "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", + "allowedNonPeerDependencies": [ + "@angular/animations", + "@angular/cdk", + "@angular/common", + "@angular/compiler", + "@angular/core", + "@angular/forms", + "@angular/material", + "@angular/material-moment-adapter", + "@angular/platform-browser", + "@angular/platform-browser-dynamic", + "@angular/router", + "@elemental-ui/cadence-icon", + "@elemental-ui/core", + "@ngx-translate/core", + "@ngx-translate/http-loader", + "billboard.js" + ], + "dest": "../../html/qer-app-operationssupport/uci", + "lib": { + "entryFile": "src/public_api.ts", + "styleIncludePaths": ["../../shared/scss"] + } +} diff --git a/imxweb/projects/uci/ng-package.json b/imxweb/projects/uci/ng-package.json index 52dc5e513..6172a35c6 100644 --- a/imxweb/projects/uci/ng-package.json +++ b/imxweb/projects/uci/ng-package.json @@ -3,6 +3,6 @@ "dest": "../../dist/uci", "lib": { "entryFile": "src/public_api.ts", - "styleIncludePaths": ["../../shared/assets"] + "styleIncludePaths": ["../../shared/scss"] } } diff --git a/imxweb/projects/uci/package.json b/imxweb/projects/uci/package.json index 07b309c06..fb3b8ac0d 100644 --- a/imxweb/projects/uci/package.json +++ b/imxweb/projects/uci/package.json @@ -1,6 +1,6 @@ { "name": "uci", - "version": "9.2.1", + "version": "9.3.0", "private": true, "bundledDependencies": [ "imx-api-uci" diff --git a/imxweb/projects/uci/project.json b/imxweb/projects/uci/project.json new file mode 100644 index 000000000..e8a3ff980 --- /dev/null +++ b/imxweb/projects/uci/project.json @@ -0,0 +1,70 @@ +{ + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "name": "uci", + "sourceRoot": "projects/uci/src", + "implicitDependencies": [ + "qer" + ], + "projectType": "library", + "prefix": "imx", + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:ng-packagr", + "options": { + "tsConfig": "projects/uci/tsconfig.lib.json", + "project": "projects/uci/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "projects/uci/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "projects/uci/tsconfig.lib.json" + }, + "dynamic-ops": { + "project": "projects/uci/ng-package.dynamic-ops.json" + }, + "remote-dev": { + "tsConfig": "projects/uci/tsconfig.lib.json" + }, + "remote-qs": { + "tsConfig": "projects/uci/tsconfig.lib.json" + } + }, + "outputs": [ + "{workspaceRoot}/dist/uci" + ] + }, + "test": { + "executor": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/uci/src/test.ts", + "tsConfig": "projects/uci/tsconfig.spec.json", + "karmaConfig": "projects/uci/karma.conf.js", + "stylePreprocessorOptions": { + "includePaths": [ + "./shared/assets", + "./shared/scss", + "./node_modules", + "./node_modules/@elemental-ui/cadence-icon", + "./node_modules/@elemental-ui/core" + ] + } + } + }, + "lint": { + "executor": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": [ + "projects/uci/tsconfig.lib.json", + "projects/uci/tsconfig.spec.json" + ], + "exclude": [ + "**/node_modules/**", + "**/*.spec.ts", + "**/*.json" + ] + } + } + } +} diff --git a/imxweb/projects/uci/src/lib/changeview/change-sidesheet.component.html b/imxweb/projects/uci/src/lib/changeview/change-sidesheet.component.html index b89ec20ca..c2514a805 100644 --- a/imxweb/projects/uci/src/lib/changeview/change-sidesheet.component.html +++ b/imxweb/projects/uci/src/lib/changeview/change-sidesheet.component.html @@ -4,25 +4,28 @@

    {{ '#LDS#Heading Requested Operation' | translate }} ({{ idx + 1 }}/{{ changeDetail.length }}) - {{ '#LDS#Successful' | translate }} - {{ '#LDS#Pending' | translate }} - {{ '#LDS#Failed' | translate }} + {{ '#LDS#Successful' | translate }} + {{ '#LDS#Pending' | translate }} + {{ '#LDS#Failed' | translate }}

    - + {{ - '#LDS#Here you can see the details of the provisioning process. Additionally, you can change the status of the provisioning process if creating the object in the cloud application was successful or failed.' | translate + '#LDS#Here you can see the details of the provisioning process. Additionally, you can change the status of the provisioning process if creating the object in the cloud application was successful or failed.' + | translate }} {{ - '#LDS#Here you can see the details of the provisioning process. Additionally, you can change the status of the provisioning process if deleting the object in the cloud application was successful or failed.' | translate + '#LDS#Here you can see the details of the provisioning process. Additionally, you can change the status of the provisioning process if deleting the object in the cloud application was successful or failed.' + | translate }} {{ - '#LDS#Here you can see the details of the provisioning process. Additionally, you can change the status of the provisioning process if changing the object in the cloud application was successful or failed.' | translate + '#LDS#Here you can see the details of the provisioning process. Additionally, you can change the status of the provisioning process if changing the object in the cloud application was successful or failed.' + | translate }} @@ -31,9 +34,13 @@

    -
    - - +
    + +
    diff --git a/imxweb/projects/uci/src/lib/changeview/change-sidesheet.component.scss b/imxweb/projects/uci/src/lib/changeview/change-sidesheet.component.scss index 6b6d143c5..e97ff8366 100644 --- a/imxweb/projects/uci/src/lib/changeview/change-sidesheet.component.scss +++ b/imxweb/projects/uci/src/lib/changeview/change-sidesheet.component.scss @@ -1,4 +1,4 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; :host { @@ -8,22 +8,10 @@ height: 100%; } -.mat-card { +.mat-mdc-card { margin-bottom: 10px; } -.imx-helper-alert { - margin: 10px 0 20px auto; - display: block; -} - -.state-badge { - vertical-align: middle; -} - -.button-bar button:not(:last-child) { - margin-right: 1em; -} .eui-dark-theme { :host { diff --git a/imxweb/projects/uci/src/lib/changeview/change-sidesheet.component.ts b/imxweb/projects/uci/src/lib/changeview/change-sidesheet.component.ts index 5b820e25f..8d084fa74 100644 --- a/imxweb/projects/uci/src/lib/changeview/change-sidesheet.component.ts +++ b/imxweb/projects/uci/src/lib/changeview/change-sidesheet.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,10 +26,10 @@ import { Component, Inject } from '@angular/core'; -import { EuiSidesheetRef, EUI_SIDESHEET_DATA } from '@elemental-ui/core'; +import { EUI_SIDESHEET_DATA, EuiSidesheetRef } from '@elemental-ui/core'; -import { ManualChangeOperation, ManualChangeOperationData, OpsupportUciChangedetail } from 'imx-api-uci'; -import { ExtendedTypedEntityCollection, IEntityColumn, LocalEntityColumn } from 'imx-qbm-dbts'; +import { ManualChangeOperation, ManualChangeOperationData, OpsupportUciChangedetail } from '@imx-modules/imx-api-uci'; +import { ExtendedTypedEntityCollection, IEntityColumn, LocalEntityColumn } from '@imx-modules/imx-qbm-dbts'; import { ConfirmationService } from 'qbm'; import { UciApiService } from '../uci-api-client.service'; @@ -46,17 +46,16 @@ export class ChangeSidesheetComponent { constructor( @Inject(EUI_SIDESHEET_DATA) change: ExtendedTypedEntityCollection, private readonly uciApi: UciApiService, - private readonly sidesheetRef: EuiSidesheetRef, - private readonly confirmation: ConfirmationService - ) { - + private readonly sidesheetRef: EuiSidesheetRef, + private readonly confirmation: ConfirmationService, + ) { this.changeDetail = change.Data; - this.manualChangeData = change.extendedData.Operations; + this.manualChangeData = change?.extendedData?.Operations ?? []; // build entity columns from extended data this.changeProperties = this.manualChangeData.map((d) => { return d.map((c) => { - const prop = new LocalEntityColumn(c.Property, null, null, { + const prop = new LocalEntityColumn(c.Property!, undefined, undefined, { Value: c.DiffValue, }); @@ -66,22 +65,27 @@ export class ChangeSidesheetComponent { } public async markAsDone(detail: OpsupportUciChangedetail): Promise { - if (await this.confirmation.confirm({ - Title: '#LDS#Heading Mark As Successful', - Message: '#LDS#The provisioning process will be marked as successful. Are you sure you have made the requested change in the cloud application?' - })) { + if ( + await this.confirmation.confirm({ + Title: '#LDS#Heading Mark As Successful', + Message: + '#LDS#The provisioning process will be marked as successful. Are you sure you have made the requested change in the cloud application?', + }) + ) { await this.save(detail, true); - }; - + } } public async markAsError(detail: OpsupportUciChangedetail): Promise { - if (await this.confirmation.confirm({ - Title: '#LDS#Heading Mark As Failed', - Message: '#LDS#The provisioning process will be marked as failed. Are you sure you cannot make the requested change in the cloud application?' - })) { + if ( + await this.confirmation.confirm({ + Title: '#LDS#Heading Mark As Failed', + Message: + '#LDS#The provisioning process will be marked as failed. Are you sure you cannot make the requested change in the cloud application?', + }) + ) { await this.save(detail, false); - }; + } } public canMarkAsDone(detail: OpsupportUciChangedetail): boolean { diff --git a/imxweb/projects/uci/src/lib/changeview/change-view.component.html b/imxweb/projects/uci/src/lib/changeview/change-view.component.html index 9ed784a5d..45e5a3087 100644 --- a/imxweb/projects/uci/src/lib/changeview/change-view.component.html +++ b/imxweb/projects/uci/src/lib/changeview/change-view.component.html @@ -1,40 +1,47 @@
    -

    - {{'#LDS#Heading Pending Provisioning Processes' | translate}} -

    +

    + {{ '#LDS#Heading Pending Provisioning Processes' | translate }} +

    - - - - - -
    {{ prod.ObjectKeyElement.Column.GetDisplayValue() }}
    -
    {{ getTableDisplay(prod) }}
    -
    -
    - - -
    - {{'#LDS#Successful' | translate }} - {{'#LDS#Pending' | translate }} - {{'#LDS#Failed' | translate }} -
    -
    -
    - - - - -
    - - -
    \ No newline at end of file + + + + + +
    {{ prod.ObjectKeyElement.Column.GetDisplayValue() }}
    +
    {{ getTableDisplay(prod) }}
    +
    +
    + + +
    + {{ '#LDS#Successful' | translate }} + {{ '#LDS#Pending' | translate }} + {{ '#LDS#Failed' | translate }} +
    +
    +
    + + +
    + + diff --git a/imxweb/projects/uci/src/lib/changeview/change-view.component.scss b/imxweb/projects/uci/src/lib/changeview/change-view.component.scss index 5f738d967..cf23f5a10 100644 --- a/imxweb/projects/uci/src/lib/changeview/change-view.component.scss +++ b/imxweb/projects/uci/src/lib/changeview/change-view.component.scss @@ -1,18 +1,10 @@ -@import "@elemental-ui/core/src/styles/_palette.scss"; +@import '@elemental-ui/core/src/styles/_palette.scss'; :host { display: flex; flex-direction: column; overflow: hidden; flex-grow: 1; - -} - -.imx-table-container { - flex: 1 1 auto; - display: flex; - flex-direction: column; - overflow: auto; } .subtitle { @@ -23,17 +15,12 @@ white-space: nowrap; } -.state-badge { - vertical-align: middle; -} - .imx-table-container, .imx-table-card { display: flex; flex-direction: column; overflow: hidden; height: inherit; - flex: 1 1 auto; + flex: 1 1 auto; margin: 2px; } - diff --git a/imxweb/projects/uci/src/lib/changeview/change-view.component.ts b/imxweb/projects/uci/src/lib/changeview/change-view.component.ts index 017da9598..1cf6a28ab 100644 --- a/imxweb/projects/uci/src/lib/changeview/change-view.component.ts +++ b/imxweb/projects/uci/src/lib/changeview/change-view.component.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -28,9 +28,16 @@ import { Component, OnInit } from '@angular/core'; import { EuiSidesheetService } from '@elemental-ui/core'; import { TranslateService } from '@ngx-translate/core'; -import { ManualChangeOperationData, OpsupportUciChangedetail, OpsupportUciChanges } from 'imx-api-uci'; -import { CollectionLoadParameters, DataModel, DbObjectKey, EntitySchema, ExtendedTypedEntityCollection, TypedEntity, ValType } from 'imx-qbm-dbts'; -import { DataSourceToolbarFilter, DataSourceToolbarSettings, DataSourceWrapper, MetadataService } from 'qbm'; +import { ManualChangeOperationData, OpsupportUciChangedetail, OpsupportUciChanges } from '@imx-modules/imx-api-uci'; +import { + CollectionLoadParameters, + DataModel, + DbObjectKey, + EntitySchema, + ExtendedTypedEntityCollection, + TypedEntity, +} from '@imx-modules/imx-qbm-dbts'; +import { calculateSidesheetWidth, DataSourceToolbarFilter, DataSourceToolbarSettings, DataSourceWrapper, MetadataService } from 'qbm'; import { UciApiService } from '../uci-api-client.service'; import { ChangeSidesheetComponent } from './change-sidesheet.component'; import { ChangeViewService } from './change-view.service'; @@ -42,95 +49,98 @@ import { ChangeViewService } from './change-view.service'; }) export class ChangeViewComponent implements OnInit { public dstWrapper: DataSourceWrapper; - public dstSettings: DataSourceToolbarSettings; - public selectedChange: OpsupportUciChanges; + public dstSettings: DataSourceToolbarSettings; + public selectedChange: OpsupportUciChanges; public entitySchema: EntitySchema; - private filterOptions: DataSourceToolbarFilter[] = []; + private filterOptions: DataSourceToolbarFilter[] = []; - constructor( + constructor( private readonly translator: TranslateService, - private readonly uciApi: UciApiService, - private readonly changeviewService: ChangeViewService, - private readonly sidesheet: EuiSidesheetService, - private readonly metadatasvc: MetadataService, - ) { + private readonly uciApi: UciApiService, + private readonly changeviewService: ChangeViewService, + private readonly sidesheet: EuiSidesheetService, + private readonly metadatasvc: MetadataService, + ) { this.entitySchema = this.uciApi.typedClient.OpsupportUciChanges.GetSchema(); - } - - public async ngOnInit(): Promise { - const dataModel = await this.getDataModel(); - this.filterOptions = dataModel.Filters; - - // set initial value for state =0 (only pending processes) - const idx = this.filterOptions.findIndex(elem => elem.Name === 'state'); - if (idx > -1) { - this.filterOptions[idx].InitialValue = '0'; - } - - this.dstWrapper = new DataSourceWrapper( - state => this.uciApi.typedClient.OpsupportUciChanges.Get(state), - [ - this.entitySchema.Columns.ObjectKeyElement, - this.entitySchema.Columns.IsProcessed, - this.entitySchema.Columns.UID_UCIRoot, - this.entitySchema.Columns.XDateInserted - ], - this.entitySchema, - { - dataModel: dataModel, - } - ); - - await this.getData({ state: '0' }); - } - - public async getData(newState?: CollectionLoadParameters & { state?: string }): Promise { - this.changeviewService.handleOpenLoader(); - try { - const s = await this.dstWrapper.getDstSettings(newState); - - for (var d of s.dataSource.Data) { - const tableName = DbObjectKey.FromXml(d.GetEntity().GetColumn('ObjectKeyElement').GetValue()).TableName; - await this.metadatasvc.updateNonExisting([tableName]); - } - - this.dstSettings = s; - } finally { - this.changeviewService.handleCloseLoader(); - } - } - - public getTableDisplay(d: TypedEntity) { - const tableName = DbObjectKey.FromXml(d.GetEntity().GetColumn('ObjectKeyElement').GetValue()).TableName; - return this.metadatasvc.tables[tableName].DisplaySingular; - } - - public async viewDetails(change: OpsupportUciChanges): Promise { - this.changeviewService.handleOpenLoader(); - var details: ExtendedTypedEntityCollection; - try { - const uidChange = change.GetEntity().GetKeys()[0]; - details = await this.uciApi.typedClient.OpsupportUciChangedetail.Get(uidChange); - } - finally { - this.changeviewService.handleCloseLoader(); - } - - const result = await this.sidesheet.open(ChangeSidesheetComponent, { - title: await this.translator.get('#LDS#Heading View Provisioning Process Details').toPromise(), - subTitle: change.ObjectKeyElement.Column.GetDisplayValue(), - padding: '0', - width: '600px', - testId: 'changeview-details-sidesheet', - data: details - }).afterClosed().toPromise(); - - if (result) { - this.getData(); - } - } - - public async getDataModel(): Promise { - return this.uciApi.client.opsupport_uci_changes_datamodel_get(); - } + } + + public async ngOnInit(): Promise { + const dataModel = await this.getDataModel(); + this.filterOptions = dataModel.Filters ?? []; + + // set initial value for state =0 (only pending processes) + const idx = this.filterOptions.findIndex((elem) => elem.Name === 'state'); + if (idx > -1) { + this.filterOptions[idx].InitialValue = '0'; + } + + this.dstWrapper = new DataSourceWrapper( + (state) => this.uciApi.typedClient.OpsupportUciChanges.Get(state), + [ + this.entitySchema.Columns.ObjectKeyElement, + this.entitySchema.Columns.IsProcessed, + this.entitySchema.Columns.UID_UCIRoot, + this.entitySchema.Columns.XDateInserted, + ], + this.entitySchema, + { + dataModel: dataModel, + }, + ); + + await this.getData({ state: '0' }); + } + + public async getData(newState?: CollectionLoadParameters & { state?: string }): Promise { + this.changeviewService.handleOpenLoader(); + try { + const settings = await this.dstWrapper.getDstSettings(newState); + + if (settings) { + for (var d of settings.dataSource?.Data ?? []) { + const tableName = DbObjectKey.FromXml(d.GetEntity().GetColumn('ObjectKeyElement').GetValue()).TableName; + await this.metadatasvc.updateNonExisting([tableName]); + } + this.dstSettings = settings; + } + } finally { + this.changeviewService.handleCloseLoader(); + } + } + + public getTableDisplay(d: TypedEntity) { + const tableName = DbObjectKey.FromXml(d.GetEntity().GetColumn('ObjectKeyElement').GetValue()).TableName; + return this.metadatasvc.tables?.[tableName]?.DisplaySingular; + } + + public async viewDetails(change: TypedEntity): Promise { + this.changeviewService.handleOpenLoader(); + var details: ExtendedTypedEntityCollection; + try { + const uidChange = change.GetEntity().GetKeys()[0]; + details = await this.uciApi.typedClient.OpsupportUciChangedetail.Get(uidChange); + } finally { + this.changeviewService.handleCloseLoader(); + } + + const result = await this.sidesheet + .open(ChangeSidesheetComponent, { + title: await this.translator.get('#LDS#Heading View Provisioning Process Details').toPromise(), + subTitle: change.GetEntity().GetColumn('ObjectKeyElement').GetDisplayValue(), + padding: '0', + width: calculateSidesheetWidth(600, 0.4), + testId: 'changeview-details-sidesheet', + data: details, + }) + .afterClosed() + .toPromise(); + + if (result) { + this.getData(); + } + } + + public async getDataModel(): Promise { + return this.uciApi.client.opsupport_uci_changes_datamodel_get(); + } } diff --git a/imxweb/projects/uci/src/lib/changeview/change-view.service.ts b/imxweb/projects/uci/src/lib/changeview/change-view.service.ts index 7ea87617a..97531ea9e 100644 --- a/imxweb/projects/uci/src/lib/changeview/change-view.service.ts +++ b/imxweb/projects/uci/src/lib/changeview/change-view.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,31 +24,22 @@ * */ -import { OverlayRef } from "@angular/cdk/overlay"; -import { Injectable } from "@angular/core"; -import { EuiLoadingService } from "@elemental-ui/core"; +import { Injectable } from '@angular/core'; +import { EuiLoadingService } from '@elemental-ui/core'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ChangeViewService { - - constructor(private readonly busyService: EuiLoadingService) { } - - private busyIndicator: OverlayRef; + constructor(private readonly busyService: EuiLoadingService) {} public handleOpenLoader(): void { - if (!this.busyIndicator) { - setTimeout(() => this.busyIndicator = this.busyService.show()); + if (this.busyService.overlayRefs.length === 0) { + this.busyService.show(); } } public handleCloseLoader(): void { - if (this.busyIndicator) { - setTimeout(() => { - this.busyService.hide(this.busyIndicator); - this.busyIndicator = undefined; - }); - } + this.busyService.hide(); } -} \ No newline at end of file +} diff --git a/imxweb/projects/uci/src/lib/changeview/changeview.module.ts b/imxweb/projects/uci/src/lib/changeview/changeview.module.ts index 06e7e7662..fb9acf2c1 100644 --- a/imxweb/projects/uci/src/lib/changeview/changeview.module.ts +++ b/imxweb/projects/uci/src/lib/changeview/changeview.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -24,20 +24,17 @@ * */ -import { CommonModule } from "@angular/common"; -import { NgModule } from "@angular/core"; -import { MatCardModule } from "@angular/material/card"; -import { EuiCoreModule, EuiMaterialModule } from "@elemental-ui/core"; -import { TranslateModule } from "@ngx-translate/core"; -import { CdrModule, DataSourceToolbarModule, DataTableModule, QbmModule } from "qbm"; -import { ChangeSidesheetComponent } from "./change-sidesheet.component"; -import { ChangeViewComponent } from "./change-view.component"; +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { MatCardModule } from '@angular/material/card'; +import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core'; +import { TranslateModule } from '@ngx-translate/core'; +import { CdrModule, DataSourceToolbarModule, DataTableModule, QbmModule } from 'qbm'; +import { ChangeSidesheetComponent } from './change-sidesheet.component'; +import { ChangeViewComponent } from './change-view.component'; @NgModule({ - declarations: [ - ChangeViewComponent, - ChangeSidesheetComponent - ], + declarations: [ChangeViewComponent, ChangeSidesheetComponent], imports: [ CommonModule, EuiCoreModule, @@ -47,9 +44,7 @@ import { ChangeViewComponent } from "./change-view.component"; MatCardModule, CdrModule, QbmModule, - TranslateModule - ] + TranslateModule, + ], }) -export class ChangeViewModule { - -} \ No newline at end of file +export class ChangeViewModule {} diff --git a/imxweb/projects/uci/src/lib/init.service.ts b/imxweb/projects/uci/src/lib/init.service.ts index 7733933ff..21d4df9c9 100644 --- a/imxweb/projects/uci/src/lib/init.service.ts +++ b/imxweb/projects/uci/src/lib/init.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,15 +25,14 @@ */ import { Injectable } from '@angular/core'; -import { Router, Route } from '@angular/router'; +import { Route, Router } from '@angular/router'; import { MenuItem, MenuService } from 'qbm'; @Injectable({ providedIn: 'root' }) export class InitService { - constructor( private readonly router: Router, - private readonly menuService: MenuService + private readonly menuService: MenuService, ) { this.setupMenu(); } @@ -44,42 +43,38 @@ export class InitService { private addRoutes(routes: Route[]): void { const config = this.router.config; - routes.forEach(route => { + routes.forEach((route) => { config.unshift(route); }); this.router.resetConfig(config); } private setupMenu(): void { - this.menuService.addMenuFactories( - (preProps: string[], groups: string[]) => { - - const items: MenuItem[] = []; + this.menuService.addMenuFactories((preProps: string[], groups: string[]) => { + const items: MenuItem[] = []; - if (groups.includes('UCI_4_CLOUD_OPERATOR') - // || groups.includes('UCI_4_CLOUD_AUDITOR') - || groups.includes('UCI_4_CLOUD_ADMINISTRATOR') - ) { - items.push( - { - id: 'UCI_PendingOperations', - route: 'provisioning', - title: '#LDS#Menu Entry Pending provisioning processes', - sorting: '30-40', - }, - ); - } + if ( + groups.includes('UCI_4_CLOUD_OPERATOR') || + // || groups.includes('UCI_4_CLOUD_AUDITOR') + groups.includes('UCI_4_CLOUD_ADMINISTRATOR') + ) { + items.push({ + id: 'UCI_PendingOperations', + route: 'provisioning', + title: '#LDS#Menu Entry Pending provisioning processes', + sorting: '30-40', + }); + } - if (items.length === 0) { - return null; - } - return { - id: 'OpsWeb_ROOT_Synchronization', - title: '#LDS#Synchronization', - sorting: '30', - items - }; - }, - ); + if (items.length === 0) { + return undefined; + } + return { + id: 'OpsWeb_ROOT_Synchronization', + title: '#LDS#Synchronization', + sorting: '30', + items, + }; + }); } } diff --git a/imxweb/projects/uci/src/lib/uci-api-client.service.ts b/imxweb/projects/uci/src/lib/uci-api-client.service.ts index 6c86a29b8..8f243bd64 100644 --- a/imxweb/projects/uci/src/lib/uci-api-client.service.ts +++ b/imxweb/projects/uci/src/lib/uci-api-client.service.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -25,12 +25,12 @@ */ import { Injectable } from '@angular/core'; -import { V2Client, TypedClient } from 'imx-api-uci'; -import { ApiClient } from 'imx-qbm-dbts'; +import { V2Client, TypedClient } from '@imx-modules/imx-api-uci'; +import { ApiClient } from '@imx-modules/imx-qbm-dbts'; import { AppConfigService, ClassloggerService, ImxTranslationProviderService } from 'qbm'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class UciApiService { private tc: TypedClient; @@ -50,7 +50,8 @@ export class UciApiService { constructor( private readonly config: AppConfigService, private readonly logger: ClassloggerService, - private readonly translationProvider: ImxTranslationProviderService) { + private readonly translationProvider: ImxTranslationProviderService, + ) { try { this.logger.debug(this, 'Initializing UCI API service'); diff --git a/imxweb/projects/uci/src/lib/uci-config.module.ts b/imxweb/projects/uci/src/lib/uci-config.module.ts index 371d0bd47..772f9fe8a 100644 --- a/imxweb/projects/uci/src/lib/uci-config.module.ts +++ b/imxweb/projects/uci/src/lib/uci-config.module.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -38,20 +38,13 @@ const routes: Routes = [ path: 'provisioning', component: ChangeViewComponent, canActivate: [RouteGuardService], - resolve: [RouteGuardService] - } + resolve: [RouteGuardService], + }, ]; @NgModule({ - declarations: [ - - ], - imports: [ - CommonModule, - RouterModule.forChild(routes), - ChangeViewModule, - TranslateModule - ] + declarations: [], + imports: [CommonModule, RouterModule.forChild(routes), ChangeViewModule, TranslateModule], }) export class UciConfigModule { constructor(private readonly initializer: InitService) { diff --git a/imxweb/projects/uci/src/public_api.ts b/imxweb/projects/uci/src/public_api.ts index aff57fb89..108ed3473 100644 --- a/imxweb/projects/uci/src/public_api.ts +++ b/imxweb/projects/uci/src/public_api.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR diff --git a/imxweb/projects/uci/src/test.ts b/imxweb/projects/uci/src/test.ts index f7ac7d548..159c87d7d 100644 --- a/imxweb/projects/uci/src/test.ts +++ b/imxweb/projects/uci/src/test.ts @@ -9,7 +9,7 @@ * those terms. * * - * Copyright 2023 One Identity LLC. + * Copyright 2024 One Identity LLC. * ALL RIGHTS RESERVED. * * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR @@ -26,24 +26,14 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'core-js/es7/reflect'; -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +// Zone must be imported first. Be careful with prettier import organizing +import 'zone.js'; +import 'zone.js/testing'; + import { getTestBed } from '@angular/core/testing'; -import { - BrowserDynamicTestingModule, - platformBrowserDynamicTesting -} from '@angular/platform-browser-dynamic/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; +import 'core-js/es/reflect'; import { TestHelperModule } from 'qbm'; -declare const require: any; - // First, initialize the Angular testing environment. -getTestBed().initTestEnvironment( - [BrowserDynamicTestingModule, TestHelperModule], - platformBrowserDynamicTesting() -); -// Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); -// And load the modules. -context.keys().map(context); +getTestBed().initTestEnvironment([BrowserDynamicTestingModule, TestHelperModule], platformBrowserDynamicTesting()); diff --git a/imxweb/projects/uci/tsconfig.lib.json b/imxweb/projects/uci/tsconfig.lib.json index 6bc419f84..534c734a0 100644 --- a/imxweb/projects/uci/tsconfig.lib.json +++ b/imxweb/projects/uci/tsconfig.lib.json @@ -1,16 +1,17 @@ /* To learn more about this file see: https://angular.io/config/tsconfig. */ { "extends": "../../tsconfig.json", + "angularCompilerOptions": { + "strictTemplates": true + }, "compilerOptions": { "outDir": "../../out-tsc/lib", + "strictNullChecks": true, "declaration": true, "declarationMap": true, "sourceMap": true, "inlineSources": true, "types": [] }, - "exclude": [ - "src/test.ts", - "**/*.spec.ts" - ] + "exclude": ["src/test.ts", "**/*.spec.ts"] } diff --git a/imxweb/projects/uci/tsconfig.lib.prod.json b/imxweb/projects/uci/tsconfig.lib.prod.json index 61c7592e7..fb40dbbb0 100644 --- a/imxweb/projects/uci/tsconfig.lib.prod.json +++ b/imxweb/projects/uci/tsconfig.lib.prod.json @@ -3,8 +3,5 @@ "extends": "./tsconfig.lib.json", "compilerOptions": { "declarationMap": false - }, - "angularCompilerOptions": { - "enableIvy": true } } diff --git a/imxweb/projects/uci/tsconfig.spec.json b/imxweb/projects/uci/tsconfig.spec.json index 16da33db0..6bd74e1f7 100644 --- a/imxweb/projects/uci/tsconfig.spec.json +++ b/imxweb/projects/uci/tsconfig.spec.json @@ -1,17 +1,10 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "strictNullChecks": false, "outDir": "../../out-tsc/spec", - "types": [ - "jasmine", - "node" - ] + "types": ["jasmine", "node"] }, - "files": [ - "src/test.ts" - ], - "include": [ - "**/*.spec.ts", - "**/*.d.ts" - ] + "files": ["src/test.ts"], + "include": ["**/*.spec.ts", "**/*.d.ts"] } diff --git a/imxweb/projects/uci/tslint.json b/imxweb/projects/uci/tslint.json deleted file mode 100644 index 8bab15f53..000000000 --- a/imxweb/projects/uci/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "imx", - "camelCase" - ], - "component-selector": [ - true, - "element", - "imx", - "kebab-case" - ] - } -} diff --git a/imxweb/remove-local-package-locks.js b/imxweb/remove-local-package-locks.js index 7a9a83b05..f6e9b72cc 100644 --- a/imxweb/remove-local-package-locks.js +++ b/imxweb/remove-local-package-locks.js @@ -4,40 +4,38 @@ var data = require(path); var anyChanges = false; for (const name of [ - // these are the local packages that must not be included in package-lock.json. // This script removes the entries in the package-lock dependencies object // for these packages. // Add any custom API client libraries to this list (imx-api-ccc is already // included by default.) - 'imx-api-ccc', - 'imx-qbm-dbts', - 'imx-api-qbm', - 'imx-api-dpr', - 'imx-api-tsb', - 'imx-api-aob', - 'imx-api-apc', - 'imx-api-qer', - 'imx-api-rps', - 'imx-api-sac', - 'imx-api-cpl', - 'imx-api-pol', - 'imx-api-aad', - 'imx-api-o3t', - 'imx-api-rmb', - 'imx-api-rms', - 'imx-api-hds', - 'imx-api-att', - 'imx-api-uci', - 'imx-api-olg', - 'imx-api-o3e' + '@imx-modules/imx-api-ccc', + '@imx-modules/imx-qbm-dbts', + '@imx-modules/imx-api-qbm', + '@imx-modules/imx-api-dpr', + '@imx-modules/imx-api-tsb', + '@imx-modules/imx-api-aob', + '@imx-modules/imx-api-apc', + '@imx-modules/imx-api-qer', + '@imx-modules/imx-api-rps', + '@imx-modules/imx-api-sac', + '@imx-modules/imx-api-cpl', + '@imx-modules/imx-api-pol', + '@imx-modules/imx-api-aad', + '@imx-modules/imx-api-rmb', + '@imx-modules/imx-api-rms', + '@imx-modules/imx-api-hds', + '@imx-modules/imx-api-att', + '@imx-modules/imx-api-uci', + '@imx-modules/imx-api-olg', + '@elemental-ui/core', + '@elemental-ui/cadence-icon' ]) { if (data?.dependencies && data.dependencies[name]) { delete data.dependencies[name]; anyChanges = true; } - const nodeModuleName = 'node_modules/' + name; if (data.packages[nodeModuleName]) { delete data.packages[nodeModuleName]; @@ -62,5 +60,4 @@ fs.writeFile(path, toWrite, 'utf8', (err) => { } }); -if (error) - throw error; +if (error) throw error; diff --git a/imxweb/shared/assets/variables.scss b/imxweb/shared/assets/variables.scss deleted file mode 100644 index 1ba4d16ba..000000000 --- a/imxweb/shared/assets/variables.scss +++ /dev/null @@ -1,29 +0,0 @@ -@import '@elemental-ui/core/src/styles/_palette.scss'; - -$baseFontFamily: "Source Sans Pro", "Trebuchet MS", sans-serif; -$IMX_Mediaquery_Smartphone: "all and (max-width: 760px)"; -$IMX_Mediaquery_SmallDesktops: "all and (min-width: 761px) and (max-width: 1024px)"; -$PopupWidthLarge: 750; -$propcellminheight: 1.6em; -$propertylabelwidth: 220px; -$QBM_Primary_Black: #162c36; -$QBM_Primary_Orange: #FF9C00; -$VI_Common_Color_Blue_1: #69CCE9; -$VI_Common_Color_Blue_3: #03556D; -$VI_Common_Color_Button_Danger_Hover: #d82a30; -$VI_Common_Color_Button_Default_Hover: #0582a7; -$VI_Common_Color_Button_Font_Disabled: #b7b7b7; -$VI_Common_Color_Button_Outline: #6a6a6a; -$VI_Common_Color_Button_Success_Hover: #77af4c; -$VI_Common_Color_Font: #2f2f2f; -$VI_Common_Color_Font_Secondary: #282828; -$VI_Common_Color_Gray: #6A6A6A; -$VI_Common_Color_LightGray: #B7B7B7; -$Imx_White-two: #e9e9e9; -$Imx_Ellipse_Gray: rgba(204, 204, 204, 0.5); -$Imx_Checkbox: rgb(130, 213, 237); -$ImxLoginControlsMargin: 0.7em; - -/*To Do*/ -$link: test; -$permanentlink: test; diff --git a/imxweb/shared/scss/base/colors.scss b/imxweb/shared/scss/base/colors.scss new file mode 100644 index 000000000..8e856bf49 --- /dev/null +++ b/imxweb/shared/scss/base/colors.scss @@ -0,0 +1,46 @@ +.imx { + &-white { + color: $color-gray-0; + } + + &-black { + color: $color-gray-100; + } + + &-info { + color: $color-blue-60; + } + + &-new { + color: $color-green-60; + } + + &-warning { + color: $color-orange-60; + } + + &-error { + color: $color-red-60; + } +} + +.eui-dark-theme, +.eui-contrast-theme { + .imx { + &-info { + color: $color-blue-40; + } + + &-new { + color: $color-green-40; + } + + &-warning { + color: $color-orange-40; + } + + &-error { + color: $color-red-40; + } + } +} diff --git a/imxweb/shared/scss/base/fonts.scss b/imxweb/shared/scss/base/fonts.scss new file mode 100644 index 000000000..795364b22 --- /dev/null +++ b/imxweb/shared/scss/base/fonts.scss @@ -0,0 +1,6 @@ +@font-face { + font-family: 'fa-identity'; + src: url('../../assets/fonts/fa-identity.ttf'); + font-weight: normal; + font-style: normal; +} diff --git a/imxweb/shared/scss/base/margins-paddings.scss b/imxweb/shared/scss/base/margins-paddings.scss new file mode 100644 index 000000000..7fe2f7e9c --- /dev/null +++ b/imxweb/shared/scss/base/margins-paddings.scss @@ -0,0 +1,59 @@ +$spaceamounts: (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 16, 20, 24, 25, 30, 32, 35, 40, 45, 48, 50, 64, 75, 80, 100); +$sides: (top, bottom, left, right); + +@each $space in $spaceamounts { + @each $side in $sides { + .imx-margin-#{$side}-#{$space} { + margin-#{$side}: #{$space}px !important; + } + + .imx-padding-#{$side}-#{$space} { + padding-#{$side}: #{$space}px !important; + } + } + + .imx-margin-horizontal-#{$space} { + margin: 0 #{$space}px !important; + } + + .imx-margin-vertical-#{$space} { + margin: #{$space}px 0 !important; + } + + .imx-margin-#{$space} { + margin: #{$space}px !important; + } + + .imx-padding-horizontal-#{$space} { + padding: 0 #{$space}px !important; + } + + .imx-padding-vertical-#{$space} { + padding: #{$space}px 0 !important; + } + + .imx-padding-#{$space} { + padding: #{$space}px !important; + } +} + +@each $side in $sides { + .imx-margin-#{$side}-auto { + margin-#{$side}: auto !important; + } + + .imx-padding-#{$side}-auto { + padding-#{$side}: auto !important; + } +} + +@for $nLines from 1 through 4 { + .imx-text-overflow--#{$nLines} { + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: $nLines; /* number of lines to show */ + line-clamp: $nLines; + -webkit-box-orient: vertical; + } +} diff --git a/imxweb/shared/scss/base/mixins.scss b/imxweb/shared/scss/base/mixins.scss new file mode 100644 index 000000000..a32b194be --- /dev/null +++ b/imxweb/shared/scss/base/mixins.scss @@ -0,0 +1,237 @@ +@import '@elemental-ui/core/src/styles/_palette.scss'; + +@mixin eui-elevation-1 { + box-shadow: + 0px 1px 1px rgba(0, 0, 0, 0.14), + 0px 2px 1px rgba(0, 0, 0, 0.12), + 0px 1px 3px rgba(0, 0, 0, 0.2); +} + +@mixin eui-elevation-2 { + box-shadow: + 0px 4px 5px rgba(0, 0, 0, 0.14), + 0px 1px 10px rgba(0, 0, 0, 0.12), + 0px 2px 4px rgba(0, 0, 0, 0.2); +} + +@mixin eui-elevation-3 { + box-shadow: + 0px 24px 38px rgba(0, 0, 0, 0.14), + 0px 9px 46px rgba(0, 0, 0, 0.12), + 0px 11px 15px rgba(0, 0, 0, 0.2); +} + +@mixin ease-transition($duration: 0.4s) { + transition: all $duration ease; +} + +@mixin flex-column-container($overflow: null, $height: null, $width: null, $max-height: null, $max-width: null) { + display: flex; + flex-direction: column; + overflow: $overflow; + height: $height; + width: $width; + max-height: $max-height; + max-width: $max-width; +} + +@mixin flex-row-container($overflow: null, $height: null, $width: null, $max-height: null, $max-width: null) { + display: flex; + flex-direction: row; + overflow: $overflow; + height: $height; + width: $width; + max-height: $max-height; + max-width: $max-width; +} + +@mixin flex-column-container-fill { + @include flex-column-container($overflow: hidden); + flex: 1 1 auto; +} + +@mixin flex-row-container-fill { + @include flex-row-container(); + flex: 1 1 auto; +} + +@mixin align-button-content($text-color) { + color: $text-color; + + ::ng-deep .mat-mdc-button-wrapper { + display: flex; + align-items: center; + } +} + +@mixin image-button-icon { + eui-icon { + font-size: 14px; + margin-right: 4px; + } +} + +@mixin button-bar { + @include flex-row-container(); + justify-content: flex-end; + margin-top: 16px; + + > button:not(:last-of-type) { + margin-right: 16px; + } + + .imx-menu-spacer { + flex: 1 1 auto; + } + + .mat-mdc-stroked-button, + .mat-mdc-raised-button { + @include image-button-icon; + } +} + +@mixin button-column-right { + @include flex-row-container(); + justify-content: flex-end; + + .mat-mdc-stroked-button { + @include image-button-icon; + margin-right: 16px; + } +} + +@mixin text-overflow-ellipsis { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +@mixin transform-enlarge($scale: 1.03) { + transform: translate3D(0, -1px, 0) scale($scale); +} + +@mixin transform-shrink($scale: 0.97) { + transform: translate3D(0, -1px, 0) scale($scale); +} + +// $header-type: Values: body, title, label, emphasize, description +// $primary: Sets the text color, Values: Boolean, default: false +@mixin new-request-text($header-type: body, $primary: false) { + font-size: 14px; + font-weight: 600; + + @if $header-type == title { + font-size: 26px; + line-height: 33px; + } @else if $header-type == emphasize { + font-size: 20px; + line-height: 25px; + } @else if $header-type == label { + line-height: 18px; + } @else if $header-type == description { + color: $color-gray-40; + font-weight: 400; + line-height: 18px; + margin-left: 5px; + } +} + +@mixin new-request-orange-border($theme: eui-light-theme) { + border-radius: 4px; + border-width: 1px; + border-style: solid; + outline-width: 1px; + outline-style: solid; + + @if $theme == eui-light-theme { + outline-color: $color-orange-40; + border-color: $color-orange-40; + } @else if $theme == eui-dark-theme { + outline-color: $color-orange-60; + border-color: $color-orange-60; + } @else if $theme == eui-contrast-theme { + outline-color: $color-orange-60; + border-color: $color-orange-60; + } +} + +@mixin eui-sidesheet-actions__mixin_01( + $align-items: null, + $overflow-y: null, + $flex-wrap: null, + $padding: null, + $margin: null, + $margin-top: null, + $margin-right: null, + $margin-bottom: null, + $margin-left: null +) { + display: flex; + justify-content: flex-end; + align-items: $align-items; + overflow-y: $overflow-y; + flex-wrap: $flex-wrap; + padding: $padding; + margin: $margin; + > * { + margin-top: $margin-top; + margin-right: $margin-right; + margin-bottom: $margin-bottom; + margin-left: $margin-left; + } +} + +// Approval-workflow styles +@mixin Awm-Colors($theme: eui-light-theme) { + @if $theme == eui-light-theme { + --awm-node-level: #{$color-gray-20}; + --awm-node-step: #{$color-gray-2}; + --awm-edge-root: #{$color-gray-100}; + --awm-edge-approval: #{$color-green-60}; + --awm-edge-reject: #{$color-red-60}; + --awm-edge-escalation: #{$color-orange-60}; + --awm-edge-redirect: #{$color-blue-80}; + --awm-text: #{$color-gray-80}; + --awm-selected: #{$color-blue-40}; + } @else if $theme == eui-dark-theme { + --awm-node-level: #{$color-gray-50}; + --awm-node-step: #{$color-gray-70}; + --awm-edge-root: #{$color-gray-10}; + --awm-edge-approval: #{$color-green-40}; + --awm-edge-reject: #{$color-red-40}; + --awm-edge-escalation: #{$color-orange-40}; + --awm-edge-redirect: #{$color-blue-40}; + --awm-text: #{$color-gray-2}; + --awm-selected: #{$color-blue-60}; + } @else if $theme == eui-contrast-theme { + --awm-node-level: #{$color-gray-70}; + --awm-node-step: #{$color-gray-90}; + --awm-edge-root: #{$color-gray-0}; + --awm-edge-approval: #{$color-green-40}; + --awm-edge-reject: #{$color-red-40}; + --awm-edge-escalation: #{$color-orange-40}; + --awm-edge-redirect: #{$color-blue-40}; + --awm-text: #{$color-gray-0}; + --awm-selected: #{$color-blue-60}; + } @else { + @error "#{$theme} is not defined in approval-workflow-styles: Awm-Colors"; + } +} + +@mixin Awm-Form-Sidesheet { + :host { + .form-wrapper { + margin-top: 20px; + display: flex; + flex-direction: column; + overflow: auto; + .mat-mdc-card { + @include eui-elevation-1; + } + } + + .eui-sidesheet-actions { + column-gap: 15px; + } + } +} diff --git a/imxweb/shared/scss/theme.scss b/imxweb/shared/scss/base/theme.scss similarity index 99% rename from imxweb/shared/scss/theme.scss rename to imxweb/shared/scss/base/theme.scss index bd6e9dc82..7fedaea8a 100644 --- a/imxweb/shared/scss/theme.scss +++ b/imxweb/shared/scss/base/theme.scss @@ -1,3 +1,6 @@ + + + @use 'sass:map'; @use '@angular/material' as mat; @@ -7,11 +10,13 @@ $accent-palette: map.get($color-config, 'accent'); $warn-palette: map.get($color-config, 'warn'); $is-dark-theme: map.get($color-config, 'is-dark'); + body.#{unquote($theme-name)} { @include login-theme($primary-palette); } } + @mixin login-theme($primary-palette){ .imx-loginPage { background-color: mat.get-color-from-palette($primary-palette, default); diff --git a/imxweb/shared/scss/base/variables.scss b/imxweb/shared/scss/base/variables.scss new file mode 100644 index 000000000..b5f4d0784 --- /dev/null +++ b/imxweb/shared/scss/base/variables.scss @@ -0,0 +1,54 @@ +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import '@elemental-ui/core/src/styles/_palette.scss'; + +$IMX_Color_White: $color-gray-0; +$IMX_Color_Black: $color-gray-100; + +$IMX_Color_Light_Info: $color-blue-60; +$IMX_Color_Light_New: $color-green-60; +$IMX_Color_Light_Warning: $color-orange-60; +$IMX_Color_Light_Error: $color-red-60; + +$IMX_Color_Dark_Info: $color-blue-40; +$IMX_Color_Dark_New: $color-green-40; +$IMX_Color_Dark_Warning: $color-orange-40; +$IMX_Color_Dark_Error: $color-red-40; + +$IMX_Color_Contrast_Info: $color-blue-40; +$IMX_Color_Contrast_New: $color-green-40; +$IMX_Color_Contrast_Warning: $color-orange-40; +$IMX_Color_Contrast_Error: $color-red-40; + +$Path_Material_Icons_Font: '~node_modules/@elemental-ui/core/assets/MaterialIcons'; +$Path_Cadence_Font: '~node_modules/@elemental-ui/core/assets/Cadence'; +$Path_Source_Sans_Pro_Font: '~node_modules/@elemental-ui/core/assets/Source_Sans_Pro'; + +$IMX_Mediaquery_Smartphone: 'all and (max-width: 760px)'; +$IMX_Mediaquery_SmallDesktops: 'all and (min-width: 761px) and (max-width: 1024px)'; +$IMX_Base_FontFamily: 'Source Sans Pro', 'Trebuchet MS', sans-serif; +$IMX_White_2: #e9e9e9; +$IMX_Ellipse_Gray: rgba(204, 204, 204, 0.5); +$IMX_Checkbox: rgb(130, 213, 237); +$IMX_Material_Gray: rgba(#000, 0.38); +$IMX_Material_Gray_Hover: rgba(#2f3233, 0.87); +$IMX_Material_LightGray: rgba(#fff, 0.38); +$IMX_Material_LightGray_Hover: rgba(#fff, 0.87); +$IMX_Login_Controls_Margin: 0.7em; +$IMX_Login_Input_Width: calc(40vw + 2px); +$IMX_Identity_PhotoSize: 45px; +$IMX_OrgChart_HalfPhotoSize: calc($IMX_Identity_PhotoSize / 2); +$IMX_OrgChart_BorderWidth: 2px; +$IMX_OrgChart_Width: 100px; +/* Horizontal margin from one level to the next */ +$IMX_OrgChart_LevelMargin: 15px; +$IMX_OrgChart_PeersMargin: $IMX_Identity_PhotoSize + $IMX_OrgChart_LevelMargin; +$IMX_OrgChart_ReportsMargin: (2 * $IMX_Identity_PhotoSize) + (2 * $IMX_OrgChart_LevelMargin); +$IMX_OrgChart_BorderContainerReportsLeft: $IMX_OrgChart_PeersMargin + $IMX_OrgChart_HalfPhotoSize; +$IMX_OrgChart_ConnectorReportsLeft: $IMX_OrgChart_ReportsMargin + $IMX_OrgChart_HalfPhotoSize; +$IMX_MessageDialog_MaxWidth: 750px; +$IMX_PropCell_MinHeight: 1.6em; +$IMX_PropertyLabel_Width: 220px; +$IMX_Tile_Width: 395px; +$IMX_Application_Details_Component_Height: calc(100vh - 180px); +$IMX_SearchBar_Width: 535px; +$IMX_SearchBar_Height: 45px; diff --git a/imxweb/shared/scss/common-table.scss b/imxweb/shared/scss/common-table.scss deleted file mode 100644 index 03fac355c..000000000 --- a/imxweb/shared/scss/common-table.scss +++ /dev/null @@ -1,44 +0,0 @@ -@mixin imx-icon-for-image-button { - eui-icon { - font-size: 14px; - margin-right: 4px; - } -} - -@mixin imx-flex-fill-control-hidden-overflow{ - flex: 1 1 auto; - display: flex; - flex-direction: column; - overflow: hidden; -} - -@mixin imx-button-bar { - display: flex; - flex-direction: row; - justify-content: flex-end; - margin-top: 16px; - - > button:not(:last-of-type) { - margin-right: 16px; - } - - .imx-menu-spacer { - flex: 1 1 auto; - } - - .mat-stroked-button, - .mat-raised-button { - @include imx-icon-for-image-button; - } -} - -@mixin imx-button-column-right{ - display: flex; - flex-direction: row; - justify-content: flex-end; - - .mat-stroked-button { - @include imx-icon-for-image-button; - margin-right: 16px; - } -} \ No newline at end of file diff --git a/imxweb/shared/scss/common/captcha-login.scss b/imxweb/shared/scss/common/captcha-login.scss new file mode 100644 index 000000000..27ffa44bc --- /dev/null +++ b/imxweb/shared/scss/common/captcha-login.scss @@ -0,0 +1,68 @@ +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; + +.imx-loginInput { + margin: auto 12px; + + input { + box-sizing: content-box; + margin-bottom: 9px; + height: 30px; + width: calc(40vw - 6px); + } + .mat-mdc-card{ + color: $color-gray-80; + flex-direction: row; + } +} + +:host { + eui-alert{ + color: $color-gray-80; + } +} + +.buttonbar { + margin-top: 1.5em; + display: flex; + justify-content: center; + gap: 1em; +} + +.passcode, .pwd-answer { + margin-top: 1em; + margin-bottom: 1em; +} + +.eui-dark-theme, +.eui-contrast-theme { + :host { + eui-alert { + color: $color-gray-0; + } + } + .imx-loginInput { + .mat-mdc-card { + color: $color-gray-0; + } + } +} + +.eui-dark-theme { + :host { + .imx-loginInput { + .imx-input-login { + background-color: $color-gray-70 !important; + } + } + } +} + +.eui-contrast-theme { + :host { + .imx-loginInput { + .imx-input-login { + background-color: $color-gray-90 !important; + } + } + } +} diff --git a/imxweb/shared/scss/common/mitigating-controls.scss b/imxweb/shared/scss/common/mitigating-controls.scss new file mode 100644 index 000000000..4cdff3905 --- /dev/null +++ b/imxweb/shared/scss/common/mitigating-controls.scss @@ -0,0 +1,136 @@ +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/mixins'; + +:host { + height: 100%; + display: flex; + flex-direction: column; + justify-content: space-between; + + .eui-sidesheet-actions { + @include eui-sidesheet-actions__mixin_01($padding: 16px 20px); + } + + .control-container { + display: flex; + flex-direction: column; + overflow: auto; + } + + .imx-mitigating-control { + margin-bottom: 15px; + display: flex; + justify-content: space-between; + align-items: center; + + &.bordered { + border-bottom: 1px solid $color-gray-50; + } + + eui-select { + flex: 1 1 auto; + } + + &-properties { + display: flex; + flex-direction: column; + width: 100%; + margin-right: 15px; + gap: 15px; + } + + &-content { + flex: 1 1 auto; + margin: 3px; + display: flex; + flex-direction: column; + overflow: auto; + + .content { + overflow: auto; + display: flex; + flex: 1 1 auto; + flex-direction: column; + } + } + + .imx-delete-button { + margin-top: 15px; + align-self: flex-start; + } + } + + .imx-no-mitigating-controls { + flex: 1 1 auto; + margin: 3px; + display: flex; + align-items: center; + justify-content: center; + } + + .imx-no-results { + text-align: center; + margin: 20px 0; + justify-self: center; + + p { + margin: 0; + font-size: 18px; + } + + button { + margin-top: 10px; + } + } +} + +// Theming +:host { + .imx-no-results { + p { + color: $color-gray-50; + } + } + + .imx-no-mitigating-controls { + color: $color-gray-50; + } +} + +.eui-dark-theme { + :host { + .imx-no-results { + p { + color: $color-gray-10; + } + } + .imx-no-mitigating-controls { + color: $color-gray-10; + } + + .imx-mitigating-control { + .bordered { + border-bottom: 1px solid $color-gray-10; + } + } + } +} + +.eui-contrast-theme { + :host { + .imx-data-no-results { + p { + color: $color-gray-0; + } + } + .imx-no-mitigating-controls { + color: $color-gray-0; + } + + .imx-mitigating-control { + .bordered { + border-bottom: 1px solid $color-gray-0; + } + } + } +} diff --git a/imxweb/shared/scss/common/select.scss b/imxweb/shared/scss/common/select.scss new file mode 100644 index 000000000..f1ff68f91 --- /dev/null +++ b/imxweb/shared/scss/common/select.scss @@ -0,0 +1,35 @@ +:host { + display: flex; + flex-direction: column; + align-items: baseline; + + > * { + margin-right: 10px; + } +} + +input { + flex: 1; +} + +.imx-spacer { + height: 30px; +} + +.mat-mdc-button { + min-width: initial; +} + +.mat-mdc-error { + margin-top: 5px; + font-size: small; +} + +.imx-suffix-container { + display: flex; + + > *:not(:last-child) { + margin-right: 5px; + margin-top: 0; + } +} diff --git a/imxweb/shared/scss/components/alert.scss b/imxweb/shared/scss/components/alert.scss new file mode 100644 index 000000000..b67777ba4 --- /dev/null +++ b/imxweb/shared/scss/components/alert.scss @@ -0,0 +1,88 @@ +@import 'base/mixins'; + +/* Elemental UI */ +eui-alert { + display: flex; + width: auto; + margin-bottom: 16px; + max-height: max(50%, 200px); + + .eui-alert-header { + width: 100%; + } + + .eui-alert-content { + width: 100%; + overflow: auto; + } + + span { + display: block; + } +} + +/* Angular Material */ + +/* Imx */ +.imx-alert { + &-start { + &-50 { + width: 50%; + } + + &-70 { + width: 70%; + } + + &-100 { + width: 100%; + } + } + + &-end { + &-50 { + margin-left: auto; + width: 50%; + } + + &-70 { + margin-left: auto; + width: 70%; + } + } + + &-flex { + &-column { + @include flex-column-container(); + + &-end { + @extend .imx-alert-flex-column; + align-items: end; + } + } + } + + &-grid { + &-first-row { + grid-column: 1/3; + } + } + + &-shadow { + @include eui-elevation-1; + } + + &-content { + &-font-small { + font-size: 14px; + } + + &-end { + align-self: end; + } + } +} + +/* Dark theme */ + +/* High-contrast theme */ diff --git a/imxweb/shared/scss/components/autocomplete.scss b/imxweb/shared/scss/components/autocomplete.scss new file mode 100644 index 000000000..3f0147787 --- /dev/null +++ b/imxweb/shared/scss/components/autocomplete.scss @@ -0,0 +1,20 @@ +@import 'base/variables'; + +/* Elemental UI */ + +/* Angular Material */ + +/* Imx */ +.imx-autocomplete { + &-icon { + &.mat-mdc-autocomplete-panel.mat-mdc-autocomplete-visible { + min-width: $IMX_SearchBar_Width; + margin-left: -35px; + margin-top: 11px; + } + } +} + +/* Dark theme */ + +/* High-contrast theme */ diff --git a/imxweb/shared/scss/components/badge.scss b/imxweb/shared/scss/components/badge.scss new file mode 100644 index 000000000..7a71fbf77 --- /dev/null +++ b/imxweb/shared/scss/components/badge.scss @@ -0,0 +1,44 @@ +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; + +/* Elemental UI */ + +.eui-badge { + vertical-align: middle; +} + +.eui-badge .eui-badge-content { + font-size: 12px; + line-height: 12px; +} +/* Angular Material */ + +/* Imx */ +.imx-badge { + &-gray { + background-color: $color-gray-60; + } +} + +.mat-mdc-table { + .mat-mdc-cell { + .eui-badge { + .eui-badge-content { + white-space: nowrap; + } + } + } +} + +/* Dark theme */ + +/* High-contrast theme */ + +/* Dark theme + High-contrast theme */ +.eui-dark-theme, +.eui-contrast-theme { + .imx-badge { + &-gray { + background-color: $color-gray-40; + } + } +} diff --git a/imxweb/shared/scss/components/button-toggle.scss b/imxweb/shared/scss/components/button-toggle.scss new file mode 100644 index 000000000..e20953038 --- /dev/null +++ b/imxweb/shared/scss/components/button-toggle.scss @@ -0,0 +1,72 @@ +$icon-size: (14, 16, 24, 32, 36); + +/* Elemental UI */ + +/* Angular Material */ + +/* Imx */ +.imx-button-toggle { + &-group { + &-info { + &.mat-mdc-button-toggle-group { + .mat-mdc-button-toggle.mat-mdc-button-toggle-appearance-standard.mat-mdc-button-toggle-checked { + background-color: $color-blue-60; + color: $color-gray-0; + } + } + } + + &-warning { + &.mat-mdc-button-toggle-group { + .mat-mdc-button-toggle.mat-mdc-button-toggle-appearance-standard.mat-mdc-button-toggle-checked { + background-color: $color-orange-60; + color: $color-gray-0; + } + } + } + } + + @each $size in $icon-size { + &-icon-#{$size} { + &.mat-mdc-button-toggle-appearance-standard .mat-mdc-button-toggle-label-content { + padding: 0 calc(((#{$size}px - 24px) / -2) + 12px); + } + } + } +} +body { + .mat-button-toggle-group { + .mat-button-toggle-label-content { + .eui-icon { + vertical-align: middle; + } + } + } +} + +/* Dark theme */ +.eui-dark-theme { + .imx-button-toggle { + &-group { + &-info { + &.mat-mdc-button-toggle-group { + .mat-mdc-button-toggle.mat-mdc-button-toggle-appearance-standard.mat-mdc-button-toggle-checked { + background-color: $color-blue-40; + color: $color-gray-80; + } + } + } + + &-warning { + &.mat-mdc-button-toggle-group { + .mat-mdc-button-toggle.mat-mdc-button-toggle-appearance-standard.mat-mdc-button-toggle-checked { + background-color: $color-orange-40; + color: $color-gray-80; + } + } + } + } + } +} + +/* High-contrast theme */ diff --git a/imxweb/shared/scss/components/button.scss b/imxweb/shared/scss/components/button.scss new file mode 100644 index 000000000..bf1edc379 --- /dev/null +++ b/imxweb/shared/scss/components/button.scss @@ -0,0 +1,247 @@ +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/mixins'; +@import 'base/variables'; + +/* Elemental UI */ + +/* Angular Material */ +.mat-mdc-button, +.mat-mdc-stroked-button, +.mat-mdc-flat-button, +.mat-mdc-raised-button { + &:not(.mat-icon-button, .tree-item-button) { + // This is not applied to buttons that have a single mat-icon||eui-icon child + span.mat-mdc-button-wrapper:has(:not(mat-icon:only-child, eui-icon:only-child)) { + display: flex; + justify-content: center; + align-items: center; + } + } +} + +/* Imx */ +.imx-button { + color: $color-gray-0; + background-color: $color-blue-60; + + &-hover { + @include ease-transition(); + + &-info { + @extend .imx-button-hover; + + &:hover:not(:disabled) { + background-color: $color-blue-60; + } + } + + &-warning { + @extend .imx-button-hover; + + &:hover:not(:disabled) { + background-color: $color-orange-60; + } + } + } + + &-icon { + eui-icon.small, + eui-icon.medium, + eui-icon.large, + eui-icon.x-large { + margin-right: 4px; + } + + &-14 { + eui-icon.small, + eui-icon.medium, + eui-icon.large, + eui-icon.x-large { + font-size: 14px; + margin-right: 4px; + } + } + + &-20 { + eui-icon.small, + eui-icon.medium, + eui-icon.large, + eui-icon.x-large { + font-size: 20px; + margin-right: 4px; + } + } + + &-hover { + @include ease-transition(); + + &-info { + @extend .imx-button-hover; + + &:hover:not(:disabled) { + color: $color-blue-60; + } + } + + &-warning { + @extend .imx-button-hover; + + &:hover:not(:disabled) { + color: $color-orange-60; + } + } + } + } + + &-uppercase { + text-transform: uppercase; + } + + &-close-dialog { + right: -15px; + top: -15px; + } + + &-stepper { + @include flex-row-container(); + margin-top: 10px; + + button:not(:last-of-type) { + margin-right: 16px; + } + + &-vertical { + @include flex-column-container(); + + button:not(:last-of-type) { + margin-bottom: 5px; + } + } + } + + &-bar, + &-bar.mat-mdc-card { + @extend .imx-button-icon-14; + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-end; + margin-top: 16px; + border-top: 1px solid rgba(0, 0, 0, 0.12); + background-color: $color-gray-0; + + &-transparent { + @extend .imx-button-bar; + background-color: transparent; + border-top: none; + min-height: 48px; + } + + .justify-start { + margin-right: auto; + } + + .mat-mdc-checkbox:not(:last-child), + button:not(:last-child, .mdc-switch) { + margin-right: 16px; + } + } + + &-column { + @extend .imx-button-bar-transparent; + margin-top: 0; + margin-right: 16px; + + button:not(:last-of-type) { + margin-right: 5px; + } + } + + &-container { + @include flex-row-container(); + justify-content: flex-end; + + .justify-start { + margin-right: auto; + } + } + + &-refresh { + margin-right: 5px; + height: 50px; + } + + &:hover { + text-decoration: none; + } + + &:focus { + text-decoration: none; + outline: 1px $color-gray-60 dotted; + outline-offset: 1px; + } + + &:disabled { + cursor: not-allowed; + } +} + +/* Dark theme */ +.eui-dark-theme { + .imx-button { + &-bar { + background-color: $color-gray-70; + } + } +} + +/* High-contrast theme */ +.eui-contrast-theme { + .imx-button { + &-bar { + background-color: $color-gray-90; + } + } +} + +/* Dark theme + High-contrast theme */ +.eui-dark-theme, +.eui-contrast-theme { + .imx-button { + &-hover { + &-info { + &:hover:not(:disabled) { + background-color: $color-blue-40; + } + } + + &-warning { + &:hover:not(:disabled) { + background-color: $color-orange-40; + } + } + } + + &-icon { + &-hover { + &-info { + &:hover:not(:disabled) { + color: $color-blue-40; + } + } + + &-warning { + &:hover:not(:disabled) { + color: $color-orange-40; + } + } + } + } + + &-bar { + &-transparent { + background-color: transparent; + } + } + } +} diff --git a/imxweb/shared/scss/components/card.scss b/imxweb/shared/scss/components/card.scss new file mode 100644 index 000000000..e34cf968e --- /dev/null +++ b/imxweb/shared/scss/components/card.scss @@ -0,0 +1,160 @@ +@import 'base/mixins'; +@import 'components/icon'; + +/* Elemental UI */ + +/* Angular Material */ +.mat-mdc-card { + padding: 16px; + .mat-mdc-card-content, + .mat-mdc-card-header { + padding: 0 16px 0 0 !important; + } +} + +/* Imx */ +.mat-mdc-card.imx-card, +div.imx-card { + @include flex-column-container($overflow: hidden); + + &-border { + border: 1px solid $color-gray-20; + } + + &-fill { + @include flex-column-container-fill(); + } + + &-fill-sidesheet-margin { + @include flex-column-container-fill(); + margin: 20px; + } + + &-sidesheet { + height: 100%; + overflow: auto; + } + + &-data-explorer { + &-header { + background-color: $color-gray-0; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + z-index: 100; + border: 1px solid rgba($color-blue-60, 0.6); + margin-bottom: 20px; + + &-bg { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + background-color: $color-blue-20; + padding: 10px 24px; + display: flex; + align-items: center; + justify-content: flex-start; + height: 45px; + + & > h3 { + font-size: 20px; + font-weight: 400; + } + } + } + } + + &-select { + box-shadow: none; + border: 3px solid transparent; + cursor: pointer; + + &.selected { + border: 3px solid $color-blue-60; + } + } + + &-title { + font-weight: 600; + + &-s { + font-size: 16px; + } + + &-m { + font-size: 20px; + } + + &-l { + font-size: 24px; + } + + &-xl { + font-size: 28px; + } + } + + &-scroll-content { + @include flex-column-container($overflow: hidden); + .mat-mdc-card-content { + overflow: auto; + } + } +} + +.mat-mdc-card.imx-centered-filled-card { + flex: 1 1 auto; + display: flex; + flex-direction: column; + overflow: hidden; + margin: 3px; + display: flex; +} + +.imx-padding-for-cards { + padding-left: 3px; + padding-bottom: 3px; +} + +/* Dark theme */ +.eui-dark-theme { + .mat-mdc-card.imx-card, + div.imx-card { + &-border { + border: 1px solid $color-gray-60; + } + } +} + +/* High-contrast theme */ +.eui-contrast-theme { + .mat-mdc-card.imx-card, + div.imx-card { + &-border { + border: 1px solid $color-gray-0; + } + } +} + +/* Dark theme + High-contrast theme */ +.eui-dark-theme, +.eui-contrast-theme { + .mat-mdc-card.imx-card, + div.imx-card { + &-data-explorer { + &-header { + &-bg { + background-color: $color-blue-80; + } + + .eui-icon { + color: $color-gray-0; + } + } + } + + &-select { + &.selected { + border: 3px solid $color-blue-40; + } + } + } +} diff --git a/imxweb/shared/scss/components/chart.scss b/imxweb/shared/scss/components/chart.scss new file mode 100644 index 000000000..53ddd8a4b --- /dev/null +++ b/imxweb/shared/scss/components/chart.scss @@ -0,0 +1,63 @@ +/* Elemental UI */ + +/* Angular Material */ + +/* Imx */ + +.imx-chart { + &-container { + display: flex; + + &-center { + @extend .imx-chart-container; + justify-content: center; + } + + &-full { + width: 100%; + height: 100%; + margin: auto; + overflow: hidden; + } + } + + &-card { + display: flex; + flex-grow: 1; + + &-border { + @include eui-elevation-1; + @extend .imx-chart-card; + border: 1px solid $color-gray-10; + } + } + + &-title { + font-weight: 400; + margin-bottom: 10px; + } + + &-paginator { + display: flex; + align-items: center; + justify-content: flex-end; + } + + &-zoom { + cursor: crosshair; + } +} + +/* Dark theme */ +.eui-dark-theme { + .imx-chart-card-border { + border: 1px solid $color-gray-50; + } +} + +/* High-contrast theme */ +.eui-contrast-theme { + .imx-chart-card-border { + border: 1px solid $color-gray-70; + } +} diff --git a/imxweb/shared/scss/components/checkbox.scss b/imxweb/shared/scss/components/checkbox.scss new file mode 100644 index 000000000..12b9856c0 --- /dev/null +++ b/imxweb/shared/scss/components/checkbox.scss @@ -0,0 +1,67 @@ +/* Elemental UI */ + +/* Angular Material */ +.mat-mdc-checkbox-disabled { + cursor: not-allowed; +} + +/* Imx */ +.imx-checkbox { + &-info { + .mat-mdc-checkbox-frame { + color: $color-blue-60; + } + } + + &-new { + .mat-mdc-checkbox-frame { + color: $color-green-60; + } + } + + &-warning { + .mat-mdc-checkbox-frame { + color: $color-orange-60; + } + } + + &-error { + .mat-mdc-checkbox-frame { + color: $color-red-60; + } + } +} + +/* Dark theme */ + +/* High-contrast theme */ + +/* Dark theme + High-contrast theme */ +.eui-dark-theme, +.eui-contrast-theme { + .imx-checkbox { + &-info { + .mat-mdc-checkbox-frame { + color: $color-blue-40; + } + } + + &-new { + .mat-mdc-checkbox-frame { + color: $color-green-40; + } + } + + &-warning { + .mat-mdc-checkbox-frame { + color: $color-orange-40; + } + } + + &-error { + .mat-mdc-checkbox-frame { + color: $color-red-40; + } + } + } +} diff --git a/imxweb/shared/scss/components/chips.scss b/imxweb/shared/scss/components/chips.scss new file mode 100644 index 000000000..3c07e8355 --- /dev/null +++ b/imxweb/shared/scss/components/chips.scss @@ -0,0 +1,58 @@ +/* Elemental UI */ + +/* Angular Material */ +.mat-mdc-chip { + &-value { + max-width: 200px; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + } + + &-icon { + flex: 0 0 30px; + } +} +.mdc-evolution-chip-set__chips { + align-items: center; +} + +/* Imx */ +.imx-chip { + &-info { + &.mat-mdc-chip.mat-mdc-standard-chip { + background-color: $color-blue-60; + } + } +} + +/* Dark theme */ + +/* High-contrast theme */ +.eui-contrast-theme { + .mat-mdc-chip { + border: 1px solid $color-gray-0; + + span.tag-name { + color: $color-gray-0; + } + } +} + +/* Dark + High-contrast theme */ +.eui-dark-theme, +.eui-contrast-theme { + .imx-chip { + &-info { + &.mat-mdc-chip.mat-mdc-standard-chip { + background-color: $color-blue-40; + + span.tag-name, + .eui-icon.remove, + .imx-icon-chip-remove { + color: $color-gray-80; + } + } + } + } +} diff --git a/imxweb/shared/scss/components/clickable.scss b/imxweb/shared/scss/components/clickable.scss new file mode 100644 index 000000000..62f1d111e --- /dev/null +++ b/imxweb/shared/scss/components/clickable.scss @@ -0,0 +1,12 @@ +/* Elemental UI */ + +/* Angular Material */ + +/* Imx */ +.imx-clickable { + cursor: pointer; +} + +/* Dark theme */ + +/* High-contrast theme */ diff --git a/imxweb/shared/scss/components/data-source-toolbar.scss b/imxweb/shared/scss/components/data-source-toolbar.scss new file mode 100644 index 000000000..fb2138ed4 --- /dev/null +++ b/imxweb/shared/scss/components/data-source-toolbar.scss @@ -0,0 +1,21 @@ +/* Elemental UI */ + +/* Angular Material */ + +/* Imx */ +imx-data-source-toolbar { + &-custom { + display: flex; + align-items: center; + height: 100%; + margin-right: 5px; + + button { + height: 100%; + + &:not(:last-of-type) { + margin-right: 5px; + } + } + } +} diff --git a/imxweb/shared/scss/components/date-picker.scss b/imxweb/shared/scss/components/date-picker.scss new file mode 100644 index 000000000..f2d1431f4 --- /dev/null +++ b/imxweb/shared/scss/components/date-picker.scss @@ -0,0 +1,28 @@ +/* Elemental UI */ +eui-date-picker { + .mat-mdc-form-field { + .mat-mdc-form-field-wrapper { + padding: 0; + + .mat-mdc-form-field-subscript-wrapper { + margin: 0; + } + + .mat-mdc-form-field-flex { + padding: 0 0 0 10px; + + .mat-mdc-form-field-infix { + padding: 5px 0 5px 0; + } + } + } + } +} + +/* Angular Material */ + +/* Imx */ + +/* Dark theme */ + +/* High-contrast theme */ diff --git a/imxweb/shared/scss/components/dialog.scss b/imxweb/shared/scss/components/dialog.scss new file mode 100644 index 000000000..0fcec0e67 --- /dev/null +++ b/imxweb/shared/scss/components/dialog.scss @@ -0,0 +1,42 @@ +@import 'base/mixins'; + +/* Elemental UI */ + +/* Angular Material */ +.mat-mdc-dialog-container { + .mat-mdc-dialog-title { + display: flex; + align-items: baseline; + margin: 0 0 10px; + } + + .mat-mdc-dialog-actions { + justify-content: flex-end; + + .justify-start { + margin-right: auto; + } + } +} + +/* Imx */ +.imx-dialog { + &-title { + font-weight: 600; + font-size: 24px; + } + + &-content { + &-flex { + &-column { + &.mat-mdc-dialog-content { + @include flex-column-container($overflow: auto, $height: 100%); + } + } + } + } +} + +/* Dark theme */ + +/* High-contrast theme */ diff --git a/imxweb/shared/scss/components/divider.scss b/imxweb/shared/scss/components/divider.scss new file mode 100644 index 000000000..dd0ac9bc0 --- /dev/null +++ b/imxweb/shared/scss/components/divider.scss @@ -0,0 +1,19 @@ +/* Elemental UI */ + +/* Angular Material */ +mat-divider.mat-mdc-divider-horizontal { + position: inherit; +} + +/* Imx */ +.imx-divider { + &-dashed { + mat-divider.mat-mdc-divider-horizontal { + border-top-style: dashed; + } + } +} + +/* Dark theme */ + +/* High-contrast theme */ diff --git a/imxweb/shared/scss/components/download.scss b/imxweb/shared/scss/components/download.scss new file mode 100644 index 000000000..d8dff3dbd --- /dev/null +++ b/imxweb/shared/scss/components/download.scss @@ -0,0 +1,9 @@ +/* Elemental UI */ + +/* Angular Material */ + +/* Imx */ + +/* Dark theme */ + +/* High-contrast theme */ diff --git a/imxweb/shared/scss/components/expansion-panel.scss b/imxweb/shared/scss/components/expansion-panel.scss new file mode 100644 index 000000000..e1d69783c --- /dev/null +++ b/imxweb/shared/scss/components/expansion-panel.scss @@ -0,0 +1,109 @@ +@import 'base/mixins'; + +/* Elemental UI */ + +/* Angular Material */ + +/* Imx */ +mat-expansion-panel.imx-expansion-panel { + margin-bottom: 20px; + + &-violation { + @extend .imx-expansion-panel; + + .mat-expansion-panel-header-title { + font-size: 24px; + font-weight: 500; + } + } + + &-page { + .mat-expansion-panel-header, + .mat-expansion-panel-header.mat-expanded { + height: 50px; + } + .mat-expansion-panel-header-title { + font-size: 20px; + } + } + + &-filter { + .mat-expansion-panel-body { + @include flex-column-container(); + background-color: $color-gray-2; + padding-top: 10px; + } + + .mat-expansion-panel-header-title { + align-items: center; + margin-right: 0; + } + } + + &-bulk-item { + @extend .imx-expansion-panel; + margin-top: 20px; + + .mat-expansion-panel { + &-header { + min-height: 24px; + height: auto; + padding: 12px 24px; + + &-title { + flex-basis: auto; + } + + &-description { + justify-content: end; + } + } + } + } + + &-header { + &-fix { + .mat-expansion-panel-header, + .mat-expansion-panel-header.mat-expanded { + height: 50px; + } + } + } +} + +mat-expansion-panel.imx-expansion-panel { + &-page { + .mat-expansion-panel-header{ + background-color: $color-gray-20; + color: $color-gray-100; + } + .mat-expansion-panel-header.mat-expanded{ + background-color: $color-blue-40; + color: $color-gray-0; + + } + + } +} + +/* Dark theme */ +.eui-dark-theme { + mat-expansion-panel.imx-expansion-panel { + &-filter { + .mat-expansion-panel-body { + background-color: $color-gray-80; + } + } + } +} + +/* High-contrast theme */ +.eui-contrast-theme { + mat-expansion-panel.imx-expansion-panel { + &-filter { + .mat-expansion-panel-body { + background-color: $color-gray-100; + } + } + } +} diff --git a/imxweb/shared/scss/components/form-field.scss b/imxweb/shared/scss/components/form-field.scss new file mode 100644 index 000000000..48d6e2c5a --- /dev/null +++ b/imxweb/shared/scss/components/form-field.scss @@ -0,0 +1,100 @@ +/* Elemental UI */ +/* Angular Material */ + +mat-form-field:not(.eui-search, .eui-input--no-margin) { + &.mat-mdc-form-field { + .mat-mdc-text-field-wrapper { + margin-top: 5px; + } + &-prefix { + .mat-mdc-spinner { + margin-right: 10px; + } + + .mat-mdc-spinner, + .mat-mdc-spinner svg { + max-height: 16px; + max-width: 16px; + } + } + + &-underline { + position: static; + } + &-subscript-wrapper { + position: static; + } + .input-hidden { + display: none; + } + } +} +.eui-date-picker, .eui-form-field{ + .mat-mdc-form-field { + .mat-mdc-text-field-wrapper { + margin-top: 0px !important; + } + } +} +.mat-mdc-autocomplete-panel { + .eui-time-picker-option { + padding: 8px 6px; + } +} +.mdc-notched-outline__trailing { + margin-right: 1px; +} + +/* Imx */ +.imx-form-field { + &-width { + &-150 { + width: 150px; + } + + &-full { + width: 100%; + } + } + + &-flex.mat-mdc-form-field { + flex: 1; + } + + &-matsuffix { + &.mat-mdc-form-field { + [matsuffix] { + display: flex; + margin-left: 10px; // set margin to infix + + eui-icon { + margin-top: unset; // correct margin-top: -16px from _material_fixes.scss + display: block; + } + + mat-spinner { + display: inline-flex; + margin-right: 10px; + } + } + } + } +} + +/* Dark theme */ + +.eui-dark-theme { + .mat-mdc-form-field { + .mat-mdc-text-field-wrapper { + background-color: inherit !important; + } + } +} +/* High-contrast theme */ +.eui-contrast-theme { + .mat-mdc-form-field { + .mat-mdc-text-field-wrapper { + background-color: inherit !important; + } + } +} diff --git a/imxweb/shared/scss/components/icon.scss b/imxweb/shared/scss/components/icon.scss new file mode 100644 index 000000000..393f727f6 --- /dev/null +++ b/imxweb/shared/scss/components/icon.scss @@ -0,0 +1,216 @@ +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/mixins'; + +/* Elemental UI */ +/* https://elemental.dev.oneidentity.com/design/cadence-icons */ +/* size = + "s" => 16px + "m" => 24px (default) + "l" => 36px + "xl" => 48px */ + +.eui-sidesheet-actions { + button:not(.mat-icon-button) { + .mat-mdc-icon, + eui-icon.medium { + font-size: 14px; + margin-right: 4px; + } + } +} +/* Angular Material */ + +/* Imx */ +.imx-icon { + &-size { + @for $i from 1 through 100 { + &-#{$i} { + &.mat-mdc-icon { + font-size: #{$i}px; + width: #{$i}px; + height: #{$i}px; + } + + &.eui-icon.small, + &.eui-icon.medium, + &.eui-icon.large, + &.eui-icon.x-large { + font-size: #{$i}px; + } + } + } + } + + &-white { + color: $color-gray-0; + } + + &-black { + color: $color-gray-100; + } + + &-info { + color: $color-blue-60; + } + + &-new { + color: $color-green-60; + } + + &-warning { + color: $color-orange-60; + } + + &-error { + color: $color-red-60; + } + + &-disabled { + cursor: default; + color: $color-gray-30; + } + + &-hidden { + visibility: hidden; + } + + &-visible { + visibility: visible; + } + + &-upload { + &.eui-icon.medium { + font-size: 200px; + line-height: 200px; + } + } + + &-circle { + color: $color-gray-80; + background-color: $color-gray-20; + opacity: 0.6; + border-radius: 50%; + text-align: center; + line-height: 35px; + min-width: 35px; + min-height: 35px; + margin-right: 12px; + } + + &-table-container { + padding-right: 15px; + + .imx-icon-table { + display: flex; + align-items: center; + color: $color-gray-60; + font-weight: 600; + height: 14px; + + .mat-mdc-icon { + width: auto; + } + + .mat-mdc-icon, + .eui-icon { + padding-right: 6px; + } + + &:not(:first-of-type) { + margin-left: 8px; + border-left: 1px solid $color-gray-20; + padding-left: 8px; + } + } + } + + &-selector-list { + @include flex-row-container; + + eui-icon { + color: $color-gray-40; + user-select: none; + } + } + + &-chip-remove { + color: $color-gray-60; + opacity: 0.3; + font-size: 18px; + margin-right: -10px; + } +} + +.imx-no-results { + .eui-icon { + font-size: 100px !important; + line-height: 100px; + color: $color-gray-10; + } +} + +/* Dark theme */ +.eui-dark-theme { + .imx-no-results { + .eui-icon { + color: $color-gray-20; + } + } + + .imx-icon { + &-circle { + color: $color-gray-20; + background-color: $color-gray-80; + } + } +} + +/* High-contrast theme */ +.eui-contrast-theme { + .imx-no-results { + .eui-icon { + color: $color-gray-10; + } + } + + .imx-icon { + &-circle { + color: $color-gray-100; + background-color: $color-gray-0; + opacity: 1; + } + } +} + +/* Dark + High-contrast theme */ +.eui-dark-theme, +.eui-contrast-theme { + .imx-icon-table-container { + .imx-icon-table { + color: $color-gray-10; + } + } + + .imx-icon { + &-info { + color: $color-blue-40; + } + + &-new { + color: $color-green-40; + } + + &-warning { + color: $color-orange-40; + } + + &-error { + color: $color-red-40; + } + + &-chip-remove { + opacity: unset; + color: $color-gray-0; + } + } +} diff --git a/imxweb/shared/scss/components/input.scss b/imxweb/shared/scss/components/input.scss new file mode 100644 index 000000000..38a71b094 --- /dev/null +++ b/imxweb/shared/scss/components/input.scss @@ -0,0 +1,64 @@ +/* Elemental UI */ + +/* Angular Material */ + +/* Imx */ +.imx-input { + &-info { + color: $color-blue-60; + } + + &-file-hidden { + &[type='file'] { + display: none; + } + } + + &-login { + width: $IMX_PropertyLabel_Width; + box-sizing: content-box; + height: 30px; + padding-left: 6px; + width: calc(40vw - 6px); + } + + &-searchbar { + border: 0 solid transparent !important; + color: inherit; + background-color: transparent; + + &::placeholder { + font-style: italic; + } + + &::-ms-clear { + /* hide input-clear button (the 'X') in > IE 10 */ + display: none; + width: 0; + height: 0; + } + } +} + +.imx-textarea { + &-content-box { + &.mat-mdc-input-element.cdk-textarea-autosize { + box-sizing: content-box; + } + } +} + +/* Dark + High-contrast theme */ +.eui-dark-theme, +.eui-contrast-theme { + .imx-input { + &-info { + color: $color-blue-40; + } + + &-login { + border: none; + color: $color-gray-0; + } + } +} diff --git a/imxweb/shared/scss/components/layout-grid.scss b/imxweb/shared/scss/components/layout-grid.scss new file mode 100644 index 000000000..d8dff3dbd --- /dev/null +++ b/imxweb/shared/scss/components/layout-grid.scss @@ -0,0 +1,9 @@ +/* Elemental UI */ + +/* Angular Material */ + +/* Imx */ + +/* Dark theme */ + +/* High-contrast theme */ diff --git a/imxweb/shared/scss/components/list.scss b/imxweb/shared/scss/components/list.scss new file mode 100644 index 000000000..ebb530e2e --- /dev/null +++ b/imxweb/shared/scss/components/list.scss @@ -0,0 +1,103 @@ +/* Elemental UI */ + +/* Angular Material */ +.mat-mdc-list.mat-mdc-list-base { + .mat-mdc-list-option, + .mat-mdc-list-item { + height: auto; + } +} + +/* Imx */ +.imx-list { + &-subtitle { + color: $color-gray-40; + font-size: smaller; + font-style: italic; + } + + &-multi-action.mat-mdc-list-base { + overflow: auto; + + mat-list-item { + &.mat-mdc-2-line { + font-size: 14px; + margin-bottom: 10px; + height: auto; + } + + .mat-mdc-line { + white-space: normal; + } + + .imx-list-item-subtitle { + &.mat-mdc-line:nth-child(n + 2) { + @extend .imx-list-subtitle; + } + } + } + + mat-list-option { + font-size: smaller; + + .mat-mdc-list-text { + .imx-list-option-subtitle { + @extend .imx-list-subtitle; + font-size: 11px; + font-style: italic; + margin-bottom: 10px; + } + } + } + } + + &-data-tree { + .mat-mdc-list-option { + background-color: transparent; + + &:hover { + background-color: $color-gray-2; + } + } + } +} +.imx-list-container-reverse{ + .mat-mdc-list-option{ + flex-direction: row-reverse; + .mdc-list-item__end{ + margin-left: 0; + } + } +} + +/* Dark theme */ +.eui-dark-theme { + .imx-list { + &-subtitle { + color: $color-gray-10; + } + + &-data-tree { + .mat-mdc-list-option:hover { + background-color: $color-gray-60; + } + } + } +} + +/* High-contrast theme */ +.eui-contrast-theme { + .imx-list { + &-subtitle { + color: $color-gray-0; + } + + &-data-tree { + .mat-mdc-list-option:hover { + background-color: $color-gray-80; + } + } + } +} + +/* Dark + High-contrast theme */ diff --git a/imxweb/shared/scss/components/logo.scss b/imxweb/shared/scss/components/logo.scss new file mode 100644 index 000000000..73046b17d --- /dev/null +++ b/imxweb/shared/scss/components/logo.scss @@ -0,0 +1,14 @@ +/* Elemental UI */ +eui-logo { + &.no-logo { + display: none; + } +} + +/* Angular Material */ + +/* Imx */ + +/* Dark theme */ + +/* High-contrast theme */ diff --git a/imxweb/shared/scss/components/masthead.scss b/imxweb/shared/scss/components/masthead.scss new file mode 100644 index 000000000..d8c803bc2 --- /dev/null +++ b/imxweb/shared/scss/components/masthead.scss @@ -0,0 +1,41 @@ +/* Elemental UI */ + +/* Angular Material */ + +/* Imx */ + +/* Dark theme */ + +/* High-contrast theme */ + +/* Dark + High-contrast theme */ +.eui-dark-theme, +.eui-contrast-theme { + eui-masthead { + header.eui-masthead { + background-color: $color-blue-80; + } + + .mat-toolbar.mat-primary { + background-color: $color-blue-80; + color: $color-gray-0; + } + } +} + +/* Responsivity */ +@media only screen and (max-width: 768px) { + eui-masthead { + .mat-toolbar { + .mat-toolbar-row { + width: unset; + justify-content: center; + } + + .imx-masthead-space, + .imx-masthead--controls { + display: none; + } + } + } +} diff --git a/imxweb/shared/scss/components/menu.scss b/imxweb/shared/scss/components/menu.scss new file mode 100644 index 000000000..09c0bc828 --- /dev/null +++ b/imxweb/shared/scss/components/menu.scss @@ -0,0 +1,29 @@ +/* Elemental UI */ + +/* Angular Material */ + +/* Imx */ +.imx-menu { + &-with-spinner { + display: flex; + flex-direction: row; + align-items: center; + margin-right: 16px; + + .mat-mdc-progress-spinner { + overflow: visible; + } + } +} +.imx-config-menu{ + .mat-mdc-menu-item-text{ + display:flex; + align-items: center; + justify-content: space-between; + width: 100%; + } +} + +/* Dark theme */ + +/* High-contrast theme */ diff --git a/imxweb/shared/scss/components/paginator.scss b/imxweb/shared/scss/components/paginator.scss new file mode 100644 index 000000000..b9f05dbb6 --- /dev/null +++ b/imxweb/shared/scss/components/paginator.scss @@ -0,0 +1,26 @@ +/* Elemental UI */ + +/* Angular Material */ +mat-paginator { + box-shadow: none; + + &.hide-paginator { + display: none; + } + &.mat-mdc-paginator { + border-top: unset; + } +} + +.mat-mdc-paginator-navigation-next.mat-icon-button, +.mat-mdc-paginator-navigation-previous.mat-icon-button { + background: transparent; + width: 16px; + margin-right: 10px; +} + +/* Imx */ + +/* Dark theme */ + +/* High-contrast theme */ diff --git a/imxweb/shared/scss/components/progress-bar.scss b/imxweb/shared/scss/components/progress-bar.scss new file mode 100644 index 000000000..31a411226 --- /dev/null +++ b/imxweb/shared/scss/components/progress-bar.scss @@ -0,0 +1,66 @@ +@mixin standard-progress() { + flex: 1 1 auto; + width: auto; + align-self: center; +} + +/* Elemental UI */ + +/* Angular Material */ +mat-progress-bar { + @include standard-progress(); +} + +/* Imx */ + +.imx-child-progress-bar { + margin-left: 10px; + max-width: 60%; +} + +.imx-tree-text { + mat-progress-bar { + @include standard-progress(); + } +} + +imx-progress { + flex: 1 1 auto; +} + +td .imx-progress-bar { + &-container { + padding: 0 10px; + } +} + +.imx-progress-bar { + display: flex; + column-gap: 5px; + align-items: baseline; + + .mat-mdc-progress-bar { + width: 200px; + flex-grow: 0; + } + + &-container { + display: flex; + flex-direction: column; + } + + &-texts { + font-size: 12px; + text-align: left; + } + + &-line { + display: flex; + flex-direction: row; + align-items: center; + } +} + +/* Dark theme */ + +/* High-contrast theme */ diff --git a/imxweb/shared/scss/components/radio-button.scss b/imxweb/shared/scss/components/radio-button.scss new file mode 100644 index 000000000..9057a255f --- /dev/null +++ b/imxweb/shared/scss/components/radio-button.scss @@ -0,0 +1,61 @@ +/* Elemental UI */ + +/* Angular Material */ + +.mat-mdc-radio-group { + display: flex; + gap: 25px; + min-height: 30px; +} + +.mat-mdc-radio-button { + display: block; + margin-right: 5px; + .mdc-form-field{ + align-items: center !important; + } + .mdc-label { + display: flex; + flex-direction: column; + padding: 0; + .imx-radio-label-strong{ + font-weight: 600; + } + } +} + +/* Imx */ + +.imx-filter-radio-group { + display: block; +} + +.imx-radio-group-flex-column { + flex-direction: column; + margin-bottom: 15px; + gap: 15px; +} + +.imx-filter-radio-button.mat-mdc-radio-button { + display: block; + + label.mat-mdc-radio-label { + white-space: normal; + } +} + +.imx-fk-table-radio { + > *:not(:last-child) { + margin-right: 30px; + } +} + +/* Dark theme */ + +/* High-contrast theme */ + +.eui-contrast-theme { + ::ng-deep .mat-mdc-radio-outer-circle { + border-color: $color-gray-100; + } +} diff --git a/imxweb/shared/scss/components/ripple.scss b/imxweb/shared/scss/components/ripple.scss new file mode 100644 index 000000000..998e13925 --- /dev/null +++ b/imxweb/shared/scss/components/ripple.scss @@ -0,0 +1,13 @@ +/* Elemental UI */ + +/* Angular Material */ + +.mat-mdc-ripple-element { + background: transparent !important; +} + +/* Imx */ + +/* Dark theme */ + +/* High-contrast theme */ diff --git a/imxweb/shared/scss/components/scrollbar.scss b/imxweb/shared/scss/components/scrollbar.scss new file mode 100644 index 000000000..8f89bc2fe --- /dev/null +++ b/imxweb/shared/scss/components/scrollbar.scss @@ -0,0 +1,14 @@ +/* Elemental UI */ + +/* Angular Material */ + +/* Imx */ + +.imx-scroll-pad > .eui-scroll-container { + padding-right: 5px; +} + +.imx-scroll-pad-left > .eui-scroll-container { + padding-left: 5px; + padding-right: 10px; +} diff --git a/imxweb/shared/scss/components/search.scss b/imxweb/shared/scss/components/search.scss new file mode 100644 index 000000000..8ad49f9a1 --- /dev/null +++ b/imxweb/shared/scss/components/search.scss @@ -0,0 +1,19 @@ +/* Elemental UI */ + +.eui-search { + width: 100%; + + &.mat-mdc-form-field.mat-mdc-form-field-appearance-outline { + min-width: 280px; + flex: 1; + } +} + +/* Angular Material */ + +/* Imx */ + +.imx-multi-select { + width: 100%; + display: flex; +} diff --git a/imxweb/shared/scss/components/select.scss b/imxweb/shared/scss/components/select.scss new file mode 100644 index 000000000..2d8fb8b9c --- /dev/null +++ b/imxweb/shared/scss/components/select.scss @@ -0,0 +1,115 @@ +/* Elemental UI */ +eui-select { + .mat-mdc-form-field { + &-wrapper { + padding: 0; + } + } +} + +/* Angular Material */ + +.mat-mdc-form-field-infix .mat-mdc-select-trigger { + vertical-align: middle; + + .mat-mdc-select-arrow-wrapper { + vertical-align: bottom; + } +} + +// Until elemental changes the default to use the accent theme, important is needed +.mat-mdc-option.mdc-list-item--selected:not(.mdc-list-item--disabled) { + color: $color-orange-60 !important; +} + +.mat-primary .mat-pseudo-checkbox-checked.mat-pseudo-checkbox-full { + background-color: $color-orange-60 !important; +} + +.cdk-overlay-pane .mat-mdc-select-panel .mat-mdc-option { + line-height: normal; + + .mat-mdc-option-text { + white-space: normal; + margin: 5px 16px; + } +} + +/* Imx */ +.imx-data-source-filter-menu { + .mat-mdc-select .mat-mdc-select-trigger { + margin-top: 5px; + } + + .mat-mdc-select .mat-mdc-select-value { + max-width: none; + white-space: normal; + .mat-mdc-select-value-text { + white-space: normal; + } + } +} + +.mat-mdc-option.imx-candidate-option { + line-height: 1.5em; + display: flex; + width: 100%; + height: 50px; + + .imx-candidate-item { + width: 100%; + + .imx-candidate-content { + width: 100%; + overflow: hidden; + + .imx-candidate-display { + font-size: 14px; + line-height: 14px; + text-overflow: ellipsis; + overflow: hidden; + width: 100vw; + } + + .imx-candidate-longdisplay { + font-size: 12px; + text-overflow: ellipsis; + overflow: hidden; + } + } + } +} + +.imx-export-column--field { + width: 100%; + margin: 0 10px; +} + +.imx-eui-select-100 { + width: 100%; +} + +.imx-eui-select-toolbar { + width: 110px; +} + +.imx-container-search { + &--hidden { + display: none; + } +} + +/* Dark theme */ +.eui-dark-theme, +.eui-contrast-theme { + // Until elemental changes the default to use the accent theme, important is needed + .mat-option.mat-selected:not(.mat-option-disabled) { + color: $color-orange-40 !important; + } + + .mat-primary .mat-pseudo-checkbox-checked.mat-pseudo-checkbox-full { + background-color: $color-orange-40 !important; + } +} + +/* High-contrast theme */ diff --git a/imxweb/shared/scss/components/side-navigation.scss b/imxweb/shared/scss/components/side-navigation.scss new file mode 100644 index 000000000..fdf3bd668 --- /dev/null +++ b/imxweb/shared/scss/components/side-navigation.scss @@ -0,0 +1,438 @@ +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; +:host { + height: 100%; +} + +@mixin side() { + display: flex; + flex-direction: column; + height: 100%; +} + +/* Imx */ + +.imx-snavigation { + height: 100%; + + .mat-sidenav-container { + height: 100%; + + .mat-sidenav { + width: 230px; + + .imx-snavigation-side { + @include side(); + + .imx-snavigation-side-toggle { + display: none; + padding: 16px 12px 0; + margin-bottom: 10px; + + .mat-mdc-button { + min-width: 0; + padding: 0 4px; + height: 28px; + + .mat-mdc-icon { + margin-top: -12px; + } + } + } + + .imx-snavigation-side-content { + flex: 1; + padding: 20px; + padding-bottom: 16px; + overflow-x: hidden; + + .imx-snavigation-side-heading { + display: flex; + align-items: center; + margin-bottom: 10px; + white-space: nowrap; + h2{ + font-size: 14px; + font-weight: 700; + } + imx-help-contextual { + display: inline-block; + } + } + + .imx-snavigation-item { + margin: 0 -20px; + padding: 10px 20px; + display: flex; + align-items: center; + justify-content: flex-start; + cursor: pointer; + + & > .eui-icon { + margin-right: 8px; + } + + & > span { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + } + } + } + + .mat-sidenav-content { + padding: 20px; + position: relative; + display: flex; + flex-direction: column; + + &.imx-snavigation--backdrop-showing { + overflow: hidden; + } + + .imx-snavigation-backdrop { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba($color-gray-100, 0.32); + z-index: 200; + } + .mat-mdc-card{ + margin: 2px; + } + } + } +} + +.imx-flex-container { + display: flex; + align-items: stretch; + margin-top: 2em; + flex-direction: column; + + &-100 { + height: 100%; + } +} + +.imx-flex-container { + display: flex; + align-items: stretch; + margin-top: 2em; + flex-direction: column; +} + +imx-sidenav-tree { + .imx-snavigation { + width: 100% !important; + } +} + +/**/ + +.imx-snavigation-tree.mat-mdc-card { + height: 100%; + padding: 0; + + .mat-sidenav-container { + height: 100%; + border-radius: 4px; + + .mat-sidenav { + width: 100%; + overflow: hidden; + border-radius: 4px; + border-right: none; + + .mat-drawer-inner-container { + overflow: unset; + } + .imx-snavigation-side { + @include side(); + + .imx-snavigation-side-toggle { + display: flex; + justify-content: flex-end; + margin: 5px 0; + + .imx-snavigation-side-toggle-header { + margin: auto; + margin-left: 24px; + font-size: 16px; + line-height: 20px; + font-weight: 600; + } + + .toolbar--hidden { + width: 0; + } + + .expand-control-button { + .mdc-button__label { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; + } + + .rotate-90 { + margin: auto; + rotate: -90deg; + translate: 0 100%; + width: inherit; + -webkit-backface-visibility: hidden; // Handle the anti-aliasing from chrome on rotations + backface-visibility: hidden; + font-size: 16px; + white-space: nowrap; + } + } + } + + .imx-snavigation-side-content--center { + justify-content: center; + display: flex; + align-items: center; + } + + .imx-no-results { + display: flex; + flex-direction: column; + align-items: center; + } + + .imx-snavigation-side-content { + height: 100%; + overflow: auto; + margin: 0 24px 24px; + } + } + + &:not(.imx-snavigation-side--expanded) { + .imx-snavigation-side-toggle { + height: 100%; + margin: 0; + } + .imx-snavigation-side-toggle-header, + .imx-snavigation-side-content { + display: none; + } + .expand-control-button { + height: 100%; + width: 48px; + min-width: unset; + padding: 0; + display: flex; + justify-content: center; + flex-direction: column; + align-items: center; + border-radius: 0; + .mat-mdc-button-persistent-ripple { + border-radius: 0; + } + } + } + + &:is(.imx-snavigation-side--expanded) { + .mat-mdc-drawer-inner-container { + overflow: hidden; + } + .imx-snavigation-side-toggle { + align-items: center; + } + + .expand-control-button { + margin-right: 16px; + } + .rotate-90 { + display: none; + } + } + } + + .mat-sidenav-content { + padding: 32px; + position: relative; + display: flex; + flex-direction: column; + } + + // .mat-mdc-drawer-side seems to have a scss style - seems to leak from material + .mat-mdc-drawer-side { + border-right: none; + } + } + &.mat-mdc-card--hidden { + .mat-sidenav-container { + .mat-sidenav { + .imx-snavigation-side { + .imx-snavigation-side-content { + margin: 0; + } + } + } + } + } +} + +@media only screen and (max-width: 768px) { + .imx-snavigation .mat-sidenav-container { + .mat-sidenav { + transition: width 0.5s; + + .imx-snavigation-side { + .imx-snavigation-side-toggle { + display: block; + } + .imx-snavigation-side-content { + padding: 16px; + } + } + + &:not(.imx-snavigation-side--expanded) { + width: 58px; + + .imx-snavigation-side-content { + display: none; + } + } + } + + .mat-sidenav-content { + padding: 16px; + } + } +} + +// Theming + +.imx-snavigation { + .mat-sidenav-container { + background-color: $color-gray-2; + .mat-sidenav { + .imx-snavigation-side { + .imx-snavigation-side-content { + .imx-snavigation-item { + & > .eui-icon { + margin-right: 8px; + color: rgba($color-gray-100, 0.4); + } + + &:hover:not(.imx-snavigation-item--selected) { + background-color: rgba($color-blue-40, 0.5); + } + + &.imx-snavigation-item--selected { + background-color: $color-blue-60; + color: $color-gray-0; + } + } + } + } + } + } +} + +.imx-snavigation-tree.mat-mdc-card { + .imx-snavigation-side-toggle-header { + color: $color-gray-60; + } + + .mat-mdc-tree { + background-color: transparent; + } + + .imx-snavigation.imx-snavigation--expanded { + border: 1px solid $color-gray-20; + &.mat-mdc-card--hidden { + border: none; + box-shadow: none; + } + } + + .mat-sidenav:not(.imx-snavigation-side--expanded) { + .expand-control-button { + background-color: $color-blue-10; + border: 1px solid $color-blue-20; + } + } + .imx-snavigation-side-content { + background-color: $color-blue-10; + border: 1px solid $color-blue-20; + } +} + +/* Dark theme */ +.eui-dark-theme { + .imx-snavigation { + .mat-sidenav-container { + background-color: $color-gray-80; + } + } + + .mat-sidenav-content { + background: $color-gray-80; + } + + .imx-snavigation-tree.mat-mdc-card { + border: 1px solid $color-gray-60; + + .imx-snavigation-side-toggle-header { + color: $color-gray-20; + } + + .imx-snavigation.imx-snavigation--expanded { + border: 1px solid $color-gray-60; + } + + .mat-sidenav:not(.imx-snavigation-side--expanded) { + .expand-control-button { + background-color: $color-blue-80; + border: 1px solid $color-blue-60; + } + } + .imx-snavigation-side-content { + background-color: $color-blue-80; + border: 1px solid $color-blue-60; + } + } +} + +/* High-contrast theme */ +.eui-contrast-theme { + .imx-snavigation { + .mat-sidenav-container { + background-color: $color-gray-100; + } + } + + .mat-sidenav-content { + background: $color-gray-100; + } + .imx-snavigation-tree.mat-mdc-card { + border: 1px solid $color-gray-60; + + .imx-snavigation-side-toggle-header { + color: $color-gray-0; + } + + .imx-snavigation.imx-snavigation--expanded { + border: 1px solid $color-gray-60; + } + + .mat-sidenav:not(.imx-snavigation-side--expanded) { + .expand-control-button { + background-color: $color-gray-90; + border: 1px solid $color-gray-0; + } + } + .imx-snavigation-side-content { + background-color: $color-gray-90; + border: 1px solid $color-gray-0; + } + } +} diff --git a/imxweb/shared/scss/components/sidesheet.scss b/imxweb/shared/scss/components/sidesheet.scss new file mode 100644 index 000000000..cfcb854b9 --- /dev/null +++ b/imxweb/shared/scss/components/sidesheet.scss @@ -0,0 +1,61 @@ +@import 'base/mixins'; +@import 'base/margins-paddings'; + +/* Elemental UI */ +.eui-sidesheet { + .eui-sidesheet-actions { + &--flex { + display: flex; + flex-direction: row; + } + .justify-start { + margin-right: auto; + } + button:not(:last-child):not(.justify-start) { + margin-right: 10px; + } + .mat-mdc-button-base + .mat-mdc-button-base { + margin-left: 0; + } + } +} + +.eui-sidesheet { + max-width: 90% !important; + &__content { + width: 100% !important; + } +} + +/* Angular Material */ + +/* Imx */ +.imx-sidesheet { + &-content { + &-padding-0.eui-sidesheet-content { + padding: 0; + } + + &-flex-container.eui-sidesheet-content { + @include flex-column-container(); + } + + &-flex-container-row.eui-sidesheet-content { + @include flex-row-container(); + } + + &__overflow { + @include flex-column-container($overflow: hidden, $height: inherit); + } + + &__flex { + flex: 1 1 auto; + } + } +} + +@media screen and (max-width: 768px) { + .eui-sidesheet { + max-width: 100% !important; + } +} diff --git a/imxweb/shared/scss/components/slide-toggle.scss b/imxweb/shared/scss/components/slide-toggle.scss new file mode 100644 index 000000000..7f98db56a --- /dev/null +++ b/imxweb/shared/scss/components/slide-toggle.scss @@ -0,0 +1,41 @@ +@import 'base/mixins'; +/* Elemental UI */ + +/* Angular Material */ + +/* Imx */ +form { + .mat-mdc-slide-toggle { + margin-left: 8px; + } +} +.escalation-approver-toggle { + margin: 0.5em; +} + +.imx-slide-toggle { + margin-left: 10px; + margin-top: 13px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.imx-tab-group-slider { + align-self: center; + margin-right: 46px; + font-size: 14px; + font-weight: 400; +} + +.imx-flex-toggle-container { + margin-bottom: 20px; + @include flex-row-container(); + > * { + margin-right: 20px; + } +} + +/* Dark theme */ + +/* High-contrast theme */ diff --git a/imxweb/shared/scss/components/slider.scss b/imxweb/shared/scss/components/slider.scss new file mode 100644 index 000000000..d787059ce --- /dev/null +++ b/imxweb/shared/scss/components/slider.scss @@ -0,0 +1,55 @@ +/* Elemental UI */ + +/* Angular Material */ + +/* Imx */ +.imx-risk-slider { + .mat-mdc-slider-thumb-container { + .mat-mdc-slider-thumb-label { + transform: rotate(45deg); + border-radius: 50% 50% 0; + } + .mat-mdc-slider-thumb { + transform: scale(0); + } + .mat-mdc-slider-thumb-label-text { + opacity: 1; + } + } +} + +.slider-container { + display: flex; + align-items: center; + + .mat-mdc-slider { + width: 300px; + flex-grow: 2; + } + + .slider-label { + &__main { + display: block; + } + &__prefix { + font-size: 14px; + margin-right: 8px; + } + &__suffix { + font-size: 14px; + margin-left: 8px; + } + } +} + +@media only screen and (max-width: 320px) { + .slider-container { + .mat-mdc-slider-horizontal { + min-width: unset; + } + } +} + +/* Dark theme */ + +/* High-contrast theme */ diff --git a/imxweb/shared/scss/components/snackbar.scss b/imxweb/shared/scss/components/snackbar.scss new file mode 100644 index 000000000..d8dff3dbd --- /dev/null +++ b/imxweb/shared/scss/components/snackbar.scss @@ -0,0 +1,9 @@ +/* Elemental UI */ + +/* Angular Material */ + +/* Imx */ + +/* Dark theme */ + +/* High-contrast theme */ diff --git a/imxweb/shared/scss/components/sort-header.scss b/imxweb/shared/scss/components/sort-header.scss new file mode 100644 index 000000000..d8dff3dbd --- /dev/null +++ b/imxweb/shared/scss/components/sort-header.scss @@ -0,0 +1,9 @@ +/* Elemental UI */ + +/* Angular Material */ + +/* Imx */ + +/* Dark theme */ + +/* High-contrast theme */ diff --git a/imxweb/shared/scss/components/spinner.scss b/imxweb/shared/scss/components/spinner.scss new file mode 100644 index 000000000..8cff8498a --- /dev/null +++ b/imxweb/shared/scss/components/spinner.scss @@ -0,0 +1,31 @@ +/* Elemental UI */ + +/* Angular Material */ +mat-spinner { + margin: auto; + align-self: center; +} + +/* Imx */ +.mat-mdc-progress-spinner.imx-spinner-inline { + display: inline-block; +} + +.mat-mdc-progress-spinner.imx-spinner-inline-flex { + display: inline-flex; +} + +.mat-mdc-progress-spinner.imx-sprinner-tile, +.mat-mdc-progress-spinner.imx-sprinner-tile svg { + max-height: 40px; + max-width: 40px; +} + +.imx-small-spinner { + font-size: 16px; + line-height: 16px; +} + +/* Dark theme */ + +/* High-contrast theme */ diff --git a/imxweb/shared/scss/components/stepper.scss b/imxweb/shared/scss/components/stepper.scss new file mode 100644 index 000000000..1be7f2a7a --- /dev/null +++ b/imxweb/shared/scss/components/stepper.scss @@ -0,0 +1,34 @@ +/* Elemental UI */ + +/* Angular Material */ + +/* Imx */ +.imx-stepper-samples.mat-mdc-stepper-vertical { + @include flex-column-container($max-height: 100%); + + .mat-mdc-step { + @include flex-column-container($max-height: calc(100% - 144px)); + flex: 1 1 auto; + + .mat-mdc-vertical-content-container { + @include flex-column-container($height: calc(100% - 72px)); + + .mat-mdc-vertical-content { + @include flex-column-container-fill(); + height: 100%; + + imx-pick-category-select-identities { + @include flex-column-container($max-height: calc(100% - 36px)); + } + } + } + } + + .imx-summary-intro { + margin: 15px 0; + } +} + +/* Dark theme */ + +/* High-contrast theme */ diff --git a/imxweb/shared/scss/components/table.scss b/imxweb/shared/scss/components/table.scss new file mode 100644 index 000000000..3148218c2 --- /dev/null +++ b/imxweb/shared/scss/components/table.scss @@ -0,0 +1,328 @@ +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; +@import 'base/mixins'; + +/* Elemental UI */ + +/* Angular Material */ + +/* Angular Material - table*/ + +.mat-mdc-table { + tbody { + .mat-mdc-row { + .mat-mdc-cell { + .imx-table-column-ellipsis { + width: 120px; + display: block; + white-space: nowrap; + overflow-x: hidden; + text-overflow: ellipsis; + } + } + } + } +} + +table.mat-mdc-table { + box-shadow: none; + padding: 1px; +} +/* Angular Material - cell */ + +td.mat-mdc-cell { + padding-right: 10px; + .mdc-button { + white-space: nowrap; + } +} +td.imx-table-cell{ + div{ + @include flex-row-container($height: fit-content, $width: fit-content); + } +} + +// Handle cursor icons, make sure divs are minimal so we don't have the text icon too much +.imx-data-table-highlightedActive { + .mat-mdc-row { + &:hover { + cursor: pointer; + } + } + .mat-mdc-cell { + // Make sure to exclude checkboxes as they behave differently with content fitting + *:not(.mat-mdc-checkbox, .mdc-button, .mdc-button *, .mdc-icon-button, imx-info-badge, imx-info-badge *) { + &:hover { + cursor: text; + } + &:only-child{ + @include flex-row-container($height: fit-content, $width: fit-content); + } + } + + > .mdc-button, + .mat-mdc-stroked-button, + .mat-mdc-raised-button, + .mat-mdc-flat-button, + .mat-icon-button { + &:hover{ + cursor: pointer; + } + & *:hover{ + cursor: inherit !important; + } + } + } +} + +/* Imx */ + +.mat-mdc-card.imx-table-container, +.div.imx-table-container, +.imx-table-container { + @include flex-column-container-fill(); + height: inherit; + + > imx-data-table { + flex-grow: 2; + } +} + +.imx-table-container.overflow-auto { + overflow: auto; +} + +.imx-table-container-for-simple-table { + @include flex-column-container-fill(); + overflow: auto; +} + +.imx-simple-table { + width: 100%; + max-height: 100%; + box-shadow: none; + overflow: auto; +} + +/* Imx - table */ +.imx-data-table-grouped { + @include flex-column-container-fill(); + + .spaced-left { + margin-left: 5px; + } + + td.mat-mdc-cell { + padding-top: 6px; + padding-bottom: 6px; + + div { + overflow: hidden; + + &.group-row-expanded { + margin-bottom: 15px; + } + } + table { + td.mat-mdc-cell { + div { + overflow: unset; + } + } + } + } + + .imx-data-table-grouped-content { + flex: 1 1 auto; + overflow: auto; + } +} + +.imx-table-action-row{ + display:flex; + align-items: baseline; + justify-content: space-between; +} + +/* Imx - row*/ +.imx-hidden-header.mat-mdc-header-row { + display: none; +} + +.mat-mdc-row.imx_MatTableRow { + min-height: 60px; +} + +/* Imx - column*/ + +.imx-table-column.mat-mdc-column-select { + overflow: initial; + max-width: 50px; + min-width: 50px; + width: 50px; + margin-right: 15px; +} + +.imx-table-column { + padding-right: 15px; + .mat-mdc-checkbox { + &:hover{ + cursor: pointer; + } + & *:hover{ + cursor: inherit !important; + } + } +} + +.imx-long-title-table .mat-mdc-row .mat-mdc-cell { + padding: 0 10px; + div[imxTitle] { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + .button-row { + border-top-style: solid; + border-top-width: 0px; + @include flex-row-container(); + justify-content: flex-end; + } + } +} + +.imx-table-column { + padding-right: 15px; +} + +td.mat-mdc-cell div[subtitle] { + font-size: smaller; + color: $color-gray-40; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 500px; +} + +.mat-mdc-cell.imx_ColumnCell { + text-overflow: ellipsis; +} + +.imx_ColumnHeader { + overflow: hidden; + text-overflow: ellipsis; +} + +.mat-mdc-header-cell.imx_CheckableCell { + flex: 0 0 45px; +} + +.mat-mdc-cell.imx_CheckableCell { + flex: 0 0 45px; +} + +/* Media Screens*/ + +@media screen and (max-width: 768px) { + td.mat-mdc-cell div[subtitle] { + max-width: 250px; + } +} + +/* Default theme */ + +.imx-data-table-row-highlighted { + background-color: $color-gray-5; +} + +.imx-data-table-row-conditional { + background-color: $color-red-20; +} + +.imx-data-table-highlightedActive .mat-mdc-row:hover, +.mat-mdc-row:hover { + background-color: $color-blue-10; +} + +/* Dark theme */ + +.eui-dark-theme { + .mat-mdc-header-cell { + color: $color-gray-10; + border-bottom-color: $color-gray-60; + } + .mat-mdc-table { + background-color: $color-gray-70; + } + + .custom-row td { + border-bottom-color: $color-gray-60; + } + + .imx-data-table-row-highlighted, + .imx-data-table-highlightedActive.mat-mdc-row:hover, + .mat-mdc-row:hover { + background-color: $color-gray-60; + } + + .custom-row:hover { + background-color: $color-gray-60 !important; + } + + .imx-data-table-row-conditional { + background-color: $color-red-80; + } +} + +/* High-contrast theme */ + +.eui-contrast-theme { + .mat-mdc-header-cell { + color: $color-gray-0; + border-bottom-color: $color-gray-60; + } + + .mat-mdc-table { + background-color: $color-gray-90; + } + + .imx-data-table-row-highlighted, + .imx-data-table-highlightedActive .mat-mdc-row:hover, + .mat-mdc-row:hover { + background-color: $color-gray-0 !important; + + td.mat-mdc-cell { + color: $color-gray-100; + } + + .mat-mdc-checkbox-frame { + border-color: $color-gray-100; + } + } + + table { + border-top: none; + } + + th:after, + th:before { + content: ''; + position: absolute; + left: 0; + width: 100%; + } + th:before { + top: 0; + border-top: 1px solid white; + } + th:after { + bottom: 0; + border-bottom: 1px solid white; + } + + .imx-data-table-row-conditional { + background-color: $color-red-80; + } + + .imx-data-table-selection-info { + color: $color-blue-40; + } +} diff --git a/imxweb/shared/scss/components/tabs.scss b/imxweb/shared/scss/components/tabs.scss new file mode 100644 index 000000000..b3a916c9e --- /dev/null +++ b/imxweb/shared/scss/components/tabs.scss @@ -0,0 +1,262 @@ +@import 'base/mixins'; +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; + +/* Elemental UI */ + +/* Angular Material */ + +.mat-mdc-tab-group { + flex-grow: 1; + overflow: auto; + height: 100%; + .mat-mdc-tab-header { + padding: 0; + } +} + +.mat-mdc-tab-body-content { + @include flex-column-container(); +} + +.mat-mdc-tab-body-wrapper { + height: 100%; + flex: 1 1 auto; + .mat-mdc-tab-body { + display: flex; + flex-direction: column; + flex: 1 1 auto; + } +} + +/* Imx */ + +.imx-tab-content { + @include flex-column-container($height: 100%); + + .imx-tab-content-body { + @include flex-column-container-fill(); + + .mat-mdc-card { + > form { + padding-right: 10px; + } + } + } +} + +.imx-tab-content-sidesheet-with-table { + height: 100%; + padding: 0; + + .imx-sidesheet-content__overflow { + @include flex-column-container-fill(); + + .imx-sidesheet-content__overflow { + imx-data-table { + height: 100%; + } + } + } +} + +.imx-tab-margin { + .mat-mdc-tab-body-content { + margin: 20px; + } +} + +.imx-tab-header-indent { + .mat-mdc-tab-header { + margin-left: 20px; + } +} + +.imx-tab-card.imx-card-fill { + margin: 20px; +} + +.imx-tab-container { + padding: 20px; + + &-full { + @include flex-column-container-fill(); + height: 100%; + } +} + +.imx-tab-group-wrapper { + @include flex-row-container-fill(); + overflow: auto; + + .mat-mdc-tab-group { + width: 100%; + } +} + +.imx-nested-group { + &.eui-sidesheet-content { + padding: 20px; + .mat-mdc-card { + height: 100%; + padding: 0; + .mat-mdc-card:not(.imx-card-shadow) { + box-shadow: none; + border-style: none; + } + ::ng-deep .mat-mdc-tab-group { + border-radius: 4px; + imx-object-hyperview { + .imx-tab-content-body { + padding: 0; + } + } + .mat-mdc-ink-bar { + background-color: $color-blue-90; + } + .governance-sidesheet { + &__tab-content-body { + ng-component { + height: 100%; + .mat-mdc-card { + padding: 0; + box-shadow: none; + } + } + } + } + } + } + } +} + +/* IMX ~ menu*/ +.imx-menu-like-tabs { + .mat-mdc-tab-link { + font-style: normal; + font-stretch: normal; + line-height: normal; + letter-spacing: normal; + color: $color-gray-60; + min-width: 10px !important; + } + + .mat-mdc-tab-link, + .mat-mdc-tab-link:focus, + .mat-mdc-tab-link:hover { + text-decoration: none; + } + + .mat-mdc-tab-link.mat-mdc-tab-label-active { + font-weight: bold; + } + + .mat-mdc-tab-nav-bar { + background-color: $color-gray-0; + + .mat-mdc-tab-link { + font-size: 16px; + color: $color-gray-60; + } + } +} + +/* Light theme */ + +.mat-mdc-tab-group.imx-card-heading { + .mat-mdc-tab-header { + background-color: $color-gray-0; + } +} + +/* Dark theme */ + +.eui-dark-theme { + .mat-mdc-tab-group { + .mat-mdc-tab-header { + background-color: $color-gray-80; + } + } + + .mat-mdc-tab-group.imx-card-heading { + .mat-mdc-tab-header { + background-color: $color-gray-70; + } + } + + .mat-mdc-tab-group.imx-aob { + .mat-mdc-tab-body { + background-color: $color-gray-70; + } + .mat-mdc-tab-header { + background-color: $color-gray-70; + } + } + + .imx-nested-group { + .mat-mdc-tab-group { + .mat-mdc-tab-header { + background-color: $color-gray-70; + .mat-mdc-ink-bar { + background-color: $color-gray-0; + } + } + } + } + + .imx-menu-like-tabs { + background-color: $color-gray-80; + + .mat-mdc-tab-link { + color: $color-gray-5; + } + + .mat-mdc-tab-nav-bar { + background-color: $color-gray-80; + + .mat-mdc-tab-link { + color: $color-gray-5; + } + } + } +} + +/* High-contrast theme */ + +.eui-contrast-theme { + .mat-mdc-tab-group { + .mat-mdc-tab-header { + background-color: $color-gray-90; + } + } + + .mat-mdc-tab-group.imx-card-heading { + .mat-mdc-tab-header { + background-color: $color-gray-100; + } + } + .imx-nested-group { + .mat-mdc-tab-group { + .mat-mdc-tab-header { + background-color: $color-gray-90; + .mat-mdc-ink-bar { + background-color: $color-gray-0; + } + } + } + } + .imx-menu-like-tabs { + background-color: $color-gray-100; + + .mat-mdc-tab-link { + color: $color-gray-0; + } + + .mat-mdc-tab-nav-bar { + background-color: $color-gray-100; + + .mat-mdc-tab-link { + color: $color-gray-0; + } + } + } +} diff --git a/imxweb/shared/scss/components/theme-switcher.scss b/imxweb/shared/scss/components/theme-switcher.scss new file mode 100644 index 000000000..20b8c6df7 --- /dev/null +++ b/imxweb/shared/scss/components/theme-switcher.scss @@ -0,0 +1,15 @@ +/* Elemental UI */ +eui-theme-switcher { + width: 100%; + + .mat-mdc-form-field { + width: 100%; + } +} +/* Angular Material */ + +/* Imx */ + +/* Dark theme */ + +/* High-contrast theme */ diff --git a/imxweb/shared/scss/components/time-picker.scss b/imxweb/shared/scss/components/time-picker.scss new file mode 100644 index 000000000..88b666063 --- /dev/null +++ b/imxweb/shared/scss/components/time-picker.scss @@ -0,0 +1,39 @@ +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; +/* Elemental UI */ +eui-time-picker { + .eui-time-picker-container { + display: flex !important; + align-items: center; + .mdc-text-field--outlined{ + padding: 0px 10px; + } + } +} + +/* Angular Material */ + +/* Imx */ +.imx-time-picker-container { + padding: 5px; +} + +.imx-time-picker-container { + background: $color-gray-0; + border: 1px solid $color-gray-60; +} + +/* Dark theme */ +.eui-dark-theme { + .imx-time-picker-container { + background: $color-gray-60; + border: 1px solid $color-gray-90; + } +} + +/* High-contrast theme */ +.eui-contrast-theme { + .imx-time-picker-container { + background: $color-gray-100; + border: 1px solid $color-gray-0; + } +} diff --git a/imxweb/shared/scss/components/toolbar.scss b/imxweb/shared/scss/components/toolbar.scss new file mode 100644 index 000000000..942870fc0 --- /dev/null +++ b/imxweb/shared/scss/components/toolbar.scss @@ -0,0 +1,63 @@ +/* Elemental UI */ + +eui-sidesheet { + .eui-sidesheet { + .mat-toolbar { + justify-content: start; + .eui-sidesheet__heading-text { + flex-basis: auto; + } + imx-help-contextual { + .mat-icon-button { + color: $color-gray-0; + } + } + .eui-sidesheet-close { + margin-left: auto; + } + } + } +} + +/* Angular Material */ + +.mat-toolbar { + background-color: inherit; +} + +.mat-toolbar > button { + margin-left: 5px; +} +/* Imx */ + +/* Light theme */ +.mat-toolbar { + color: $color-gray-100; +} + +/* Dark theme */ + +.eui-dark-theme { + .mat-toolbar { + color: $color-gray-10; + } + + .eui-sidesheet { + .mat-toolbar { + background-color: $color-blue-80; + } + } +} + +/* High-contrast theme */ +.eui-contrast-theme { + .mat-toolbar { + color: $color-gray-0; + } + + .eui-sidesheet { + .mat-toolbar { + background-color: $color-blue-90; + } + } +} diff --git a/imxweb/shared/scss/components/tooltip.scss b/imxweb/shared/scss/components/tooltip.scss new file mode 100644 index 000000000..f7c3b33ff --- /dev/null +++ b/imxweb/shared/scss/components/tooltip.scss @@ -0,0 +1,24 @@ +@import '@elemental-ui/core/src/styles/_eui_palette.scss'; + +/* Elemental UI */ + +/* Angular Material */ +.mat-mdc-tooltip-component .mat-mdc-tooltip.custom-tooltip { + background: $color-gray-0; + color: $color-gray-100; + margin: 5px; + max-width: unset; + font-size: 12px; + border: 1px solid $color-gray-80; + border-radius: 4px; +} + +.mat-mdc-tooltip-hide { + display: none !important; +} + +/* Imx */ + +/* Dark theme */ + +/* High-contrast theme */ diff --git a/imxweb/shared/scss/components/top-navigation.scss b/imxweb/shared/scss/components/top-navigation.scss new file mode 100644 index 000000000..cf2428140 --- /dev/null +++ b/imxweb/shared/scss/components/top-navigation.scss @@ -0,0 +1,25 @@ +/* Elemental UI */ + +@media only screen and (max-width: 768px) { + .eui-top-navigation-mobile { + .eui-top-navigation-mobile-footer { + .mat-mdc-button { + &.imx-masthead--icon-button { + width: 100%; + + .mat-mdc-button-wrapper span { + display: inline-block; + } + } + } + } + } +} + +/* Angular Material */ + +/* Imx */ + +/* Dark theme */ + +/* High-contrast theme */ diff --git a/imxweb/shared/scss/components/tree.scss b/imxweb/shared/scss/components/tree.scss new file mode 100644 index 000000000..322b3226b --- /dev/null +++ b/imxweb/shared/scss/components/tree.scss @@ -0,0 +1,117 @@ +/* Elemental UI */ + +/* Angular Material */ +.mat-tree-node .mat-icon-button.mat-mdc-button-base { + flex: 0 0 40px; +} + +.mat-tree { + flex-grow: 1; +} + +/* Imx */ + +.imx-snavigation-side-content { + flex: 1 1 auto; + mat-tree { + background-color: transparent !important; + } + + .mat-tree-node { + border-radius: 4px; + } + + /* + * This padding sets alignment of the nested nodes. + */ + .tree .mat-nested-tree-node div[role='group'] { + margin-left: 20px; + } + + .tree-item-button { + width: 100%; + padding: 0; + color: unset; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + + .mdc-button__label { + width: 100%; + } + } + .tree-invisible { + display: none; + } +} + +.imx-upper-case { + text-transform: uppercase; + align-items: center; +} + +/* Dark theme */ + +.eui-light-theme { + .mat-mdc-button.mat-primary.tree-item-button:not(.mat-tree-node--selected) { + color: unset; + } + + .mat-tree-node--selected { + background-color: $color-gray-0; + border: 1px solid $color-blue-40; + color: $color-orange-60; + } + + .imx-tree-control { + .imx-snavigation-side-content { + background-color: $color-blue-10; + border: 1px solid $color-blue-20; + } + } +} + +.eui-dark-theme { + .mat-tree-node--selected { + background-color: $color-gray-80; + border: 1px solid $color-blue-40; + color: $color-orange-40; + + .imx-tree-root { + color: $color-orange-40; + } + } + + .imx-tree-control { + .imx-snavigation-side-content { + background-color: $color-blue-80; + border: 1px solid $color-blue-40; + } + } +} + +/* High-contrast theme */ + +.eui-contrast-theme { + .mat-tree-node--selected { + background-color: $color-gray-20; + border: 1px solid $color-blue-40; + color: $color-orange-80; + } + .imx-tree-root { + color: $color-gray-0; + } + .imx-tree-root--selected { + color: $color-orange-80; + .imx-icon-warning { + color: $color-orange-80; + } + } + + .imx-tree-control { + .imx-snavigation-side-content { + background-color: $color-gray-100; + border: 1px solid $color-gray-0; + } + } +} diff --git a/imxweb/shared/scss/side-navigation.scss b/imxweb/shared/scss/side-navigation.scss deleted file mode 100644 index cf039aac1..000000000 --- a/imxweb/shared/scss/side-navigation.scss +++ /dev/null @@ -1,176 +0,0 @@ -@import '@elemental-ui/core/src/styles/_eui_palette.scss'; -:host { - height: 100%; -} - -.snavigation { - height: 100%; - - .mat-sidenav-container { - height: 100%; - - .mat-sidenav { - width: 230px; - - .snavigation-side { - display: flex; - flex-direction: column; - height: 100%; - - .snavigation-side-toggle { - display: none; - padding: 16px 12px 0; - margin-bottom: 10px; - - .mat-button { - min-width: 0; - padding: 0 4px; - height: 28px; - - .mat-icon { - margin-top: -12px; - } - } - } - - .snavigation-side-content { - flex: 1; - padding: 20px; - padding-bottom: 16px; - - .snavigation-side-heading { - font-size: 14px; - font-weight: bold; - margin-bottom: 10px; - white-space: nowrap; - imx-help-contextual{ - display: inline-block; - } - } - - .snavigation-item { - margin: 0 -20px; - padding: 10px 20px; - display: flex; - align-items: center; - justify-content: flex-start; - cursor: pointer; - - & > .eui-icon { - margin-right: 8px; - } - - & > span { - flex: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - } - } - } - } - - .mat-sidenav-content { - padding: 20px; - position: relative; - display: flex; - flex-direction: column; - - &.snavigation--backdrop-showing { - overflow: hidden; - } - - .snavigation-backdrop { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba($color-gray-100, 0.32); - z-index: 200; - } - - .mat-table tr:hover td { - background-color: rgba($color-gray-100, 0.04); - cursor: pointer; - } - } - } -} - -@media only screen and (max-width: 768px) { - .snavigation .mat-sidenav-container { - .mat-sidenav { - transition: width 0.5s; - - .snavigation-side { - .snavigation-side-toggle { - display: block; - } - .snavigation-side-content { - padding: 16px; - } - } - - &:not(.snavigation-side--expanded) { - width: 58px; - - .snavigation-side-content { - display: none; - } - } - } - - .mat-sidenav-content { - padding: 16px; - } - } -} - -// Theming - -.snavigation { - .mat-sidenav-container { - background-color: $color-gray-2; - .mat-sidenav { - .snavigation-side { - .snavigation-side-content { - .snavigation-item { - & > .eui-icon { - margin-right: 8px; - color: rgba($color-gray-100, 0.4); - } - - &:hover:not(.snavigation-item--selected) { - background-color: rgba($color-blue-40, 0.5); - } - - &.snavigation-item--selected { - background-color: $color-blue-60; - color: $color-gray-0; - } - } - } - } - } - } -} -.eui-dark-theme { - :host { - .snavigation { - .mat-sidenav-container { - background-color: $color-gray-80; - } - } - } -} -.eui-contrast-theme { - :host { - .snavigation { - .mat-sidenav-container { - background-color: $color-gray-100; - } - } - } -} diff --git a/imxweb/shared/assets/styles.scss b/imxweb/shared/scss/styles.scss similarity index 64% rename from imxweb/shared/assets/styles.scss rename to imxweb/shared/scss/styles.scss index b4c5b11d3..df2d3bb50 100644 --- a/imxweb/shared/assets/styles.scss +++ b/imxweb/shared/scss/styles.scss @@ -1,32 +1,77 @@ -@import 'variables'; @import '@elemental-ui/core/src/styles/_eui_palette.scss'; -/* Elemental UI */ -.imx-eui-icon { - margin: 10px; +@import 'base/colors'; +@import 'base/fonts'; +@import 'base/margins-paddings'; +@import 'base/mixins'; +@import 'base/variables'; + +@import 'components/alert'; +@import 'components/autocomplete'; +@import 'components/badge'; +@import 'components/button'; +@import 'components/button-toggle'; +@import 'components/card'; +@import 'components/chart'; +@import 'components/checkbox'; +@import 'components/chips'; +@import 'components/scrollbar'; +@import 'components/search'; +@import 'components/sidesheet'; +@import 'components/side-navigation'; +@import 'components/clickable'; +@import 'components/data-source-toolbar'; +@import 'components/date-picker'; +@import 'components/dialog'; +@import 'components/divider'; +@import 'components/download'; +@import 'components/expansion-panel'; +@import 'components/form-field'; +@import 'components/icon'; +@import 'components/input'; +@import 'components/layout-grid'; +@import 'components/list'; +@import 'components/logo'; +@import 'components/masthead'; +@import 'components/menu'; +@import 'components/paginator'; +@import 'components/progress-bar'; +@import 'components/radio-button'; +@import 'components/ripple'; +@import 'components/spinner'; +@import 'components/select'; +@import 'components/slide-toggle'; +@import 'components/slider'; +@import 'components/stepper'; +@import 'components/table'; +@import 'components/tabs'; +@import 'components/theme-switcher'; +@import 'components/time-picker'; +@import 'components/toolbar'; +@import 'components/tooltip'; +@import 'components/tree'; +@import 'components/top-navigation'; + +/* Imx */ +.imx-flex-container-100 { + display: flex; + flex-direction: column; + align-items: stretch; + height: 100%; +} + +.imx-no-results { + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.imx-hidden { + display: none !important; } -/* Elemental UI END */ - -/* #region VI_Styles_RendererSettings_Default (FontFamily) */ -@font-face { - font-family: 'fa-identity'; - src: url('fonts/fa-identity.ttf'); - font-weight: normal; - font-style: normal; -} -/*#endregion*/ - -/* #region VI_Styles_RendererSettings_Default (Icons) */ -.imx-icon { - background-position: 0 center; - display: inline-block; - min-height: 18px; - background-repeat: no-repeat; - padding-left: 20px; -} -/*#endregion*/ - /* #region VI_Styles_RendererSettings_Default (Flex styles) */ /* not a smartphone and not a tablet... */ @media not #{$IMX_Mediaquery_Smartphone} { @@ -39,6 +84,10 @@ .imx-flex-child { flex: 1 1 auto; overflow: auto; + &-hidden { + flex: 1 1 auto; + overflow: hidden; + } } } @@ -48,10 +97,14 @@ flex-basis: auto; } - .imx-flex-child { flex: 1 1 auto; overflow: auto; + + &-hidden { + flex: 1 1 auto; + overflow: hidden; + } } .k-ie10 .imx-flex-child { @@ -61,32 +114,6 @@ /*#endregion*/ -/* #region VI_Styles_Button_Default (CSS) */ -.imx-button { - color: $white; - background-color: $iris-blue; -} - -.imx-button:hover { - background-color: $VI_Common_Color_Button_Default_Hover; - text-decoration: none; -} - -.imx-button:focus { - background-color: $VI_Common_Color_Button_Default_Hover; - text-decoration: none; - outline: 1px $VI_Common_Color_Button_Outline dotted; - outline-offset: 1px; -} - -.imx-button:Disabled { - color: $VI_Common_Color_Button_Font_Disabled; - // background-color: mat-color($asher-gray-palette, 900); - cursor: not-allowed; -} - -/*#endregion*/ - /* #region VI_Styles_RendererSettings_Default (Icon font) */ /* CUI Icon Font */ @@ -101,7 +128,7 @@ .cux-icon, .cux-pane__collapse-icon, [data-icon]::before { - font-family: "fa-identity" !important; + font-family: 'fa-identity' !important; -moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: antialiased; -webkit-backface-visibility: hidden; @@ -113,27 +140,27 @@ text-transform: none; } .cux-icon--caret-down:before { - content: "\f0d7"; + content: '\f0d7'; } .cux-icon--caret-right:before { - content: "\f0da"; + content: '\f0da'; } .cux-icon--shopping-cart:before { - content: "\f07a"; + content: '\f07a'; } .cux-icon--chain-broken:before { - content: "\f127"; + content: '\f127'; } .cux-icon--tasks:before { - content: "\f0ae"; + content: '\f0ae'; } .cux-icon--table:before { - content: "\f0ce"; + content: '\f0ce'; } /*#endregion*/ @@ -162,9 +189,9 @@ input[accesskey]:after, label[accesskey]:after, legend[accesskey]:after, textarea[accesskey]:after { - padding-left: .3em; + padding-left: 0.3em; display: inline; - content: "[" attr(accesskey) "]"; + content: '[' attr(accesskey) ']'; color: $white; } /*#endregion*/ @@ -183,7 +210,7 @@ div.timeline-content { } div.timeline-axis { - border-color: $VI_Common_Color_Gray; + border-color: $color-gray-60; border-top-style: solid; border-width: 1px; box-sizing: border-box; @@ -201,7 +228,7 @@ div.timeline-axis-grid-minor { } div.timeline-axis-text { - color: $VI_Common_Color_Font; + color: $color-gray-80; padding: 3px; white-space: nowrap; font-size: 14px; @@ -217,9 +244,9 @@ div.timeline-event { } div.timeline-event-selected { - background-color: $VI_Common_Color_Blue_1; + background-color: $color-blue-40; border-color: $black-6; - color: $VI_Common_Color_Font; + color: $color-gray-80; z-index: 999; } @@ -229,12 +256,12 @@ div.timeline-event-dot { } div.timeline-event-cluster { - background: $VI_Common_Color_Blue_1; - color: $VI_Common_Color_Font; + background: $color-blue-40; + color: $color-gray-80; } div.timeline-event-cluster div.timeline-event-dot { - border-color: $VI_Common_Color_Blue_1; + border-color: $color-blue-40; } div.timeline-event-box { @@ -280,7 +307,7 @@ div.timeline-event-content { } div.timeline-groups-axis { - border-color: $VI_Common_Color_Gray; + border-color: $color-gray-60; border-width: 1px; box-sizing: border-box; } @@ -294,13 +321,13 @@ div.timeline-groups-axis-onright { } div.timeline-groups-text { - color: $VI_Common_Color_Font_Secondary; + color: $color-gray-80; padding-left: 10px; padding-right: 10px; } div.timeline-currenttime { - background-color: $QBM_Primary_Orange; + background-color: $color-orange-60; box-sizing: border-box; width: 2px; } @@ -333,13 +360,13 @@ div.timeline-navigation-new { /* separator between new and navigation buttons */ div.timeline-navigation-new-line { - border-right: 1px solid $VI_Common_Color_Gray; + border-right: 1px solid $color-gray-60; } div.imx-timeline-event-objectcreate, div.imx-timeline-event-propertychange { border-color: $iris-blue; - background-color: $VI_Common_Color_Blue_1; + background-color: $color-blue-40; } div.imx-timeline-event-addaccount, @@ -358,7 +385,7 @@ div.imx-timeline-event-removemembership, div.imx-timeline-event-removepermission, div.imx-timeline-event-removeresponsibility { border-color: $phoenix-red; - background-color: $QBM_Primary_Orange; + background-color: $color-orange-60; } .imx-timeline-detail-header { @@ -402,8 +429,6 @@ div.imx-timeline-event-removeresponsibility { } /*#endregion*/ - - /* #region VI_Styles_RendererSettings_Mobile (Performance improvements) */ * { text-shadow: none; @@ -413,18 +438,10 @@ div.imx-timeline-event-removeresponsibility { /*#endregion*/ -/* #region QBM-Page styles (CSS) */ - -body { - background-color: $Imx_White-two -} - -/*#endregion*/ - /* #region Angular Material Komponenten*/ -.mat-primary .mat-pseudo-checkbox-checked, -.mat-primary .mat-pseudo-checkbox-indeterminate { +.mat-primary .mat-mdc-pseudo-checkbox-checked, +.mat-primary .mat-mdc-pseudo-checkbox-indeterminate { background-color: $iris-blue; } @@ -434,7 +451,7 @@ textarea:focus { } .simpleText { - color: $VI_Common_Color_Font; + color: $color-gray-80; } .mat-icon-button.imx-searchbar-tablecellsmall { @@ -447,10 +464,9 @@ textarea:focus { } /*#endregion*/ -.mat-paginator, -.mat-table { +.mat-mdc-table { box-shadow: 0 4px 4px 0 rgba(0, 0, 0, 0.25); - font-family: $baseFontFamily; + font-family: $IMX_Base_FontFamily; } .imx-title { @@ -461,7 +477,7 @@ textarea:focus { font-stretch: normal; line-height: 1.5; letter-spacing: normal; - color: $VI_Common_Color_Font; + color: $color-gray-80; } .imx-subtitle { @@ -472,7 +488,7 @@ textarea:focus { font-stretch: normal; line-height: 1.71; letter-spacing: normal; - color: $VI_Common_Color_Gray; + color: $color-gray-60; } .imx_processTitle { @@ -483,7 +499,7 @@ textarea:focus { font-stretch: normal; line-height: 1.6; letter-spacing: normal; - color: $VI_Common_Color_Gray; + color: $color-gray-60; } .imx-normalCell { @@ -494,7 +510,7 @@ textarea:focus { font-stretch: normal; line-height: 2; letter-spacing: normal; - color: $VI_Common_Color_Gray; + color: $color-gray-60; } /* #region overrides Angular Material styles*/ @@ -508,20 +524,59 @@ textarea:focus { .imx-messageDialog { margin: auto auto; - max-width: $PopupWidthLarge + px; + max-width: $IMX_MessageDialog_MaxWidth; min-width: 550px; font-size: 14px; top: 100px; position: absolute !important; } +/* Extends the properties of .imx-messageDialog */ +.imx-messageDialog-error { + @extend .imx-messageDialog; + border-left: 4px solid $color-red-60; + border-radius: 6px; +} + +/* Sets the error icon style in the error dialog */ +.imx-error-icon-in-error-dialog { + display: inline-block; + color: $color-red-60; + margin-right: 10px; +} + +/* Sets the h2 tag style in the error dialog */ +.h2-in-error-dialog, +.h2-in-session-expired-dialog { + display: inline-block !important; + vertical-align: sub; +} + +.h2-in-session-expired-dialog { + color: $color-red-60; +} + +.eui-dark-theme { + .imx-messageDialog-error { + border-left-color: $color-red-40; + } + + .imx-error-icon-in-error-dialog { + color: $color-red-40; + } + + .h2-in-session-expired-dialog { + color: $color-red-40; + } +} + /* #region Elemental UI fixes */ -.mat-form-field-infix .mat-select-trigger { +.mat-mdc-form-field-infix .mat-mdc-select-trigger { vertical-align: baseline; - .mat-select-arrow-wrapper { - vertical-align: middle; + .mat-mdc-select-arrow-wrapper { + transform: none; } } @@ -529,91 +584,82 @@ button { letter-spacing: normal; } -.mat-card-title { +.mat-mdc-card-title { margin-bottom: 16px; } /*#endregion*/ /* #region Fix for the bug with mat-hint of Angular Material */ -.imx-form .mat-form-field .mat-form-field-wrapper { +.imx-form .mat-mdc-form-field .mat-mdc-form-field-wrapper { padding-bottom: 0; - .mat-form-field-underline { + .mat-mdc-form-field-underline { position: initial; display: block; margin-top: -1px; } - .mat-form-field-subscript-wrapper, - .mat-form-field-ripple { + .mat-mdc-form-field-subscript-wrapper, + .mat-mdc-form-field-ripple { position: relative; display: inline-flex; - .mat-form-field-hint-wrapper { + .mat-mdc-form-field-hint-wrapper { flex-wrap: wrap; } } - .mat-form-field-subscript-wrapper { + .mat-mdc-form-field-subscript-wrapper { min-height: calc(1em + 1px); } } /* #endregion */ -/* #region mat-paginator styling */ -.mat-paginator-navigation-next.mat-icon-button, -.mat-paginator-navigation-previous.mat-icon-button { - background: transparent; - width: 16px; - margin-right: 10px; -} -/* #endregion */ - /* bugfix for the mat-error overlapping error for more information see: https://github.com/angular/components/issues/4580#issuecomment-510290576 */ -mat-form-field { - &.ng-valid { - .mat-form-field-wrapper { - padding-bottom: 1.25em; - } - } - - &.ng-invalid, - &.mat-form-field-invalid { - .mat-form-field-wrapper { - padding-bottom: 7px; - } - } - - &.ng-untouched { - .mat-form-field-wrapper { - padding-bottom: 1.25em; - } - } - - .mat-form-field { - &-underline { - position: static; - } - - &-subscript-wrapper { - position: static; - } - } -} +// mat-form-field { +// &.ng-valid { +// .mat-mdc-form-field-wrapper { +// padding-bottom: 1.25em; +// } +// } + +// &.ng-invalid, +// &.mat-mdc-form-field-invalid { +// .mat-mdc-form-field-wrapper { +// padding-bottom: 7px; +// } +// } + +// &.ng-untouched { +// .mat-mdc-form-field-wrapper { +// padding-bottom: 1.25em; +// } +// } + +// .mat-mdc-form-field { +// &-underline { +// position: static; +// } + +// &-subscript-wrapper { +// position: static; +// } +// } +// } /* remove the max-width of the menu-panel #304731*/ -.cdk-overlay-pane .mat-menu-panel { - max-width: initial; +.cdk-overlay-pane .mat-mdc-menu-panel { + max-width: initial; } .eui-dark-theme { - .imx-title { - color: $color-gray-5; - } + .imx-title { + color: $color-gray-5; + } - .simpleText { - color: $color-gray-5; - } + .simpleText { + color: $color-gray-5; + } } .eui-contrast-theme { @@ -625,7 +671,3 @@ mat-form-field { color: $color-gray-0; } } - -// Fix tooltip flash bug on delayed tooltips: https://github.com/angular/components/issues/24614 -.mat-tooltip-hide { display: none!important; } - diff --git a/imxweb/tsconfig.json b/imxweb/tsconfig.json index 670d2b69c..bdfe5949f 100644 --- a/imxweb/tsconfig.json +++ b/imxweb/tsconfig.json @@ -1,4 +1,3 @@ -/* To learn more about this file see: https://angular.io/config/tsconfig. */ { "compileOnSave": false, "compilerOptions": { @@ -6,55 +5,51 @@ "outDir": "./dist/out-tsc", "forceConsistentCasingInFileNames": true, "strict": false, - "strictNullChecks": false, + "strictNullChecks": true, "noImplicitOverride": false, + "esModuleInterop": true, "noPropertyAccessFromIndexSignature": false, "noImplicitReturns": false, - "allowSyntheticDefaultImports": true, "resolveJsonModule": true, "allowJs": true, "paths": { + "aad": ["dist/aad"], + "aad/*": ["dist/aad/*"], + "aob": ["dist/aob"], + "aob/*": ["dist/aob/*"], + "apc": ["dist/apc"], + "apc/*": ["dist/apc/*"], + "att": ["dist/att"], + "att/*": ["dist/att/*"], "dpr": ["dist/dpr"], "dpr/*": ["dist/dpr/*"], + "o3t": ["dist/o3t"], + "o3t/*": ["dist/o3t/*"], "qbm": ["dist/qbm"], "qbm/*": ["dist/qbm/*"], "qer": ["dist/qer"], "qer/*": ["dist/qer/*"], - "aob": ["dist/aob"], - "aob/*": ["dist/aob/*"], - "tsb": ["dist/tsb"], - "tsb/*": ["dist/tsb/*"], - "att": ["dist/att"], - "att/*": ["dist/att/*"], "rps": ["dist/rps"], "rps/*": ["dist/rps/*"], - "o3t": ["dist/o3t"], - "o3t/*": ["dist/o3t/*"], - "aad": ["dist/aad"], - "aad/*": ["dist/aad/*"], - "apc": ["dist/apc"], - "apc/*": ["dist/apc/*"] + "tsb": ["dist/tsb"], + "tsb/*": ["dist/tsb/*"] }, "noFallthroughCasesInSwitch": true, "sourceMap": true, "declaration": false, - "downlevelIteration": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "moduleResolution": "node", "importHelpers": true, - "target": "es2020", - "module": "es2020", - "lib": [ - "es2020", - "dom" - ] + "target": "es2022", + "module": "es2022", + "lib": ["es2022", "dom"] }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, "strictInjectionParameters": false, "strictInputAccessModifiers": true, - "strictTemplates": false, - "strictNullInputTypes": false, + "strictTemplates": true, + "strictNullInputTypes": false } } diff --git a/imxweb/tslint.json b/imxweb/tslint.json deleted file mode 100644 index 68c99bfeb..000000000 --- a/imxweb/tslint.json +++ /dev/null @@ -1,158 +0,0 @@ -{ - "extends": "tslint:recommended", - "linterOptions": { - "exclude": [ - "**/*.spec.ts" - ] - }, - "rulesDirectory": [ - "codelyzer" - ], - "rules": { - "align": { - "options": [ - "parameters", - "statements" - ] - }, - "array-type": false, - "arrow-parens": false, - "arrow-return-shorthand": true, - "deprecation": { - "severity": "warn" - }, - "curly": true, - "import-blacklist": [ - true, - "rxjs/Rx" - ], - "interface-name": false, - "eofline": true, - "max-classes-per-file": [ - true, - 1, - "exclude-class-expressions" - ], - "import-spacing": true, - "indent": { - "options": [ - "spaces" - ] - }, - "max-line-length": [ - true, - { - "limit": 140, - "ignore-pattern": "^import [^,]+ from" - } - ], - "member-access": true, - "member-ordering": [ - true, - { - "order": [ - "static-field", - "public-instance-field", - "protected-instance-field", - "private-instance-field", - "constructor", - "public-instance-method", - "protected-instance-method", - "private-instance-method", - "static-method" - ] - } - ], - "no-consecutive-blank-lines": false, - "no-console": [ - true, - "debug", - "info", - "time", - "timeEnd", - "trace" - ], - "no-empty": true, - "no-inferrable-types": [ - true, - "ignore-params" - ], - "no-non-null-assertion": true, - "no-redundant-jsdoc": true, - "no-return-await": true, - "no-shadowed-variable": true, - "no-switch-case-fall-through": true, - "no-var-requires": false, - "object-literal-key-quotes": [ - true, - "as-needed" - ], - "object-literal-sort-keys": false, - "ordered-imports": false, - "quotemark": [ - true, - "single" - ], - "trailing-comma": false, - "typedef": [ - true, - "call-signature", - "parameter", - "property-declaration" - ], - "variable-name": [ true, "ban-keywords", "check-format" ], - "no-output-on-prefix": true, - "no-inputs-metadata-property": true, - "no-outputs-metadata-property": true, - "no-host-metadata-property": true, - "no-input-rename": true, - "no-output-rename": true, - "semicolon": { - "options": [ - "always" - ] - }, - "space-before-function-paren": { - "options": { - "anonymous": "never", - "asyncArrow": "always", - "constructor": "never", - "method": "never", - "named": "never" - } - }, - "use-lifecycle-interface": true, - "use-pipe-transform-interface": true, - "typedef-whitespace": { - "options": [ - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - }, - { - "call-signature": "onespace", - "index-signature": "onespace", - "parameter": "onespace", - "property-declaration": "onespace", - "variable-declaration": "onespace" - } - ] - }, - "component-class-suffix": true, - "directive-class-suffix": true, - "prefer-const": true, - "whitespace": { - "options": [ - "check-branch", - "check-decl", - "check-operator", - "check-separator", - "check-type", - "check-typecast" - ] - } - } -}

    8((_E+OMp)Ypl)5c?s+4K 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 diff --git a/imxweb/imx-modules/imx-api-APC.tgz-hash b/imxweb/imx-modules/imx-api-APC.tgz-hash new file mode 100644 index 000000000..d5d8189bf --- /dev/null +++ b/imxweb/imx-modules/imx-api-APC.tgz-hash @@ -0,0 +1,8 @@ +E6573E34E07D56A25D5A5E5AE4B4B8944C02BAC5479D6A7870003C698401BC6E7D3B8FC6E11FA25628D36EAA6DD0F9A71021A0114B808ADF1FC86E7A17246DCE tsconfig.json +52822F64AEA2B42445A1A01278F864061078D3E60DF1F530F19FEA5E077643DE3CAC8B4A4F065F238F2ED260EF8A0AEC94E5711DF64D75DC9CEB1A4C35A865D8 rollup.config.js +565D30DE7CA344FE6BB6724E1208DEE942D0CEDCE31BDD06F11B8C9E8DA76466100FA64B33FB780C2822F4EAC1B170EF1A2E73BDE93498BCFA185923278A07EE .npmrc +616A2983C375DD7F53176DBF8536C3577236C80EACCC3039DFCA06C20EDD44495C0C77EB405A6FA5854BAAB94016AA603E0EEF7DD9B4FF8646E744091E4CF39A package-lock.json +605B864ADAD5C8F41B700A4D885B1ED0256D2826844C5374820CA8B28D6BE2AE599EF2F8E3FE4B7C5EA8EE4204E68DA5E56072E81F89F6C67896572669CC71FE imx-api-apc.es5.js +7EDF257327F7610C453C0823AEE7A38FE25495D9558BCA21807A803208282D4E604BCF36B27DD5E44D9D30E9947736B919E6A90977BB90CB10D70B044BD35F31 imx-api-apc.umd.js +A9C0B61DDB3B45B9F836C8BF15DA912EC954166DEEC833354ADD50790761401B38B559EDFCF7794D6FD4747F668376F6FF86DC5AB0DE5415BFAC3FC5451815A9 index.d.ts +F0AD6A0E89D6B7F4E107491BEB533BB5427BEB7F604B47E045F8F5AEA7B438397A37BB96758D5E3E79E5FAF52B94625D9A453F3CA28FB6E5D50B16A834F70E88 TypedClient.d.ts \ No newline at end of file diff --git a/imxweb/imx-modules/imx-api-APC.tgz-version b/imxweb/imx-modules/imx-api-APC.tgz-version new file mode 100644 index 000000000..dd5a52084 --- /dev/null +++ b/imxweb/imx-modules/imx-api-APC.tgz-version @@ -0,0 +1 @@ +9.3.12 \ No newline at end of file diff --git a/imxweb/imx-modules/imx-api-aad.tgz b/imxweb/imx-modules/imx-api-aad.tgz deleted file mode 100644 index b9da9f74a6a7306a737e89df8303dd05a0e47638..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43355 zcmZttLzpIAuy73*yNoV&*|u%lwryK?*|u%lwr!hTc76Sv_nd$9=P0hs!OlTu?1;5j z;6*|L{da-z=E-Iyx!SqKWep z8zSlh&1nwr|M;E)`-AHL`r{wuR@t(G#8G>5K3eOg%jNT=p_l1o^>OL;KJzZS8tt_B zv;UdKzwY<7KU%tB_kA}QE0@dj@prVeY-`bMw%6|&^!2){fQ++f05`07L+OFek-@$CANiC(f@o6-*rhAw3TY4}TQ%zPM zJeKMDHU~9Lt@l?rLOS1fSF82lqbcUs1M3{<(h(LJJ&zBnx}LP|I>O4zr`{^VF8hn^ z)=xBUp5%=5$J>L{HUl+iGs>#F*)ObBcat5h7*zF|LYyYA*=c|gIwqN%VWN275kga{^-+Rw*G7OKC$ndD}8*4wdPG++WPfv zb2q#M9n=s|WsQv6Ue^l`k7C?6(!H1^_*J2tT0L{{i_(YdnlCk_GV}&qOK{&~9jm1K z;%@WwvuquHUwe55x6*X(+0{pbYxuJ`am>8;NCi$Wt)y>5Z+|771JWhwTU%M!bk`UH9CpncN;0mp!#2 z_je+#FD1{yAKk84?kSBqc{zTu-!0zL4xlotsCdC$zN$Ilk3g{xf|=B4KCm=sJP^|bPKCR0nM1Rh*G+mLaZCw zG(=Y;Q9z>X`Yhxt9AT2Ly8vXH0m?j+&+0dlDp_yHwSh)#`=6F5DmmLQ%3XZu_$ynQ z-7MM0>uGt^sdd2EaPiH{bGON6i37IR9#6r~M?ib#5LhmkIN|`IJN1Snf-05`QVd!v z9|5Qd8P^Q}jr`*92I}`2SuW*^(1wul?KS=p;rsO#e+YU#&w<6O1$$DY{RvWT4g}U> z`cPH+Fqky*fOH2}44%wjyxzO58Q(n^l-&Au>3NdlBYF30st|Rjb)n(8$x%FAuhgo$ z^qQMBSFdGD5A!w5^||!Jd>rjThHz*&ONZCH#7FkCHReXz-VFw$U;f!VA3PIxIB&~7 z)AaV+Qrc|OM|udhseclQ#m`ESne=1u^YCLzV(DT!+Kl__IMXK-4X)3lw174Cu-_Wk-R5^7_ z!vJyU{LV@B*AI$a{oa^TObnB4(UVzsVbfLZYf$65S>%;h1EY0P#z>_KehD{zP61~= zAtwGdq&&VYoa~8HaZub_!W&;fN4Z{t+z?N(DyZ_|q^yGykPBqI-@7@6*&f6TQa$e> z%3;v@3j7BFLs(YZ%%&faSMqoYEW`n2sBWRY-H~DuyoH{x6e^Q`bzuznB&8Pje`h6M zWUkM_zh2gZMLF&SwFql`eF?~azJgoW$teGR?2Zb4Cuf|)NX|+R9^s43+bHkjTb_ps zOT{LAscVo2%gx)3aTHi=XBLotUk0M-W)Wp`?zVFs&CTJdFBkMWX7HB?h?!mye#i{D zaOyPmr;C=(ux7i|&41TECrv%+e2%+qFme7Q9ScAB4k4gg0`YoH!Aiwy)HNkP+JHPA4e;~}6U&Z5?JX4{q$9KS5i1wYiR%e{KR6znx zT6>Paz4ss;G|Sf=cH(erS0@u^nbtHk!`EAtBwzGSP!OCAqOw}(WymGLsYBVpH^W>M zBV3egcpJ%$XbJRTZ|x9xcUaUPiSiygb{D)jGEZ+9b{0i6c&quV1y=bOF@f81YyXT$ zBJF9RB@R@md$#vW^x}d}tejmV-mi{q5odSh?+c`aPd)6S)EauAm7V;b z^3Bu^1NEHn`Rl^djT0`XPG%lIqicIaVR{{f_FgYYoHSkATRP$FS{}FF$#dqfFNy3o zDikCP+=I)F@pC14X^GX6Ze2;|Y)V(n)sNyIkK9#@4*sVbTeh2<;>#^9s|3b684#rU zyR;Y+%${?fdexJemFyw>qe>OL4G4h7bf2HFziDR5$&mg%^vWVnqHR1bl#~hXBpt2vj|6L+aYxK_e8~)7k4JNxz;^qZ z#$2p!Ca_M`J66mHso5x@*`l;hM&VE8oBR34%EtP_UgR!fDRt@akK46|l@xc@otNx1 z+c#Xs?@0}q%Mp^-W*)0buUL;}3#gs4-SZu@#n*KaP3uf!&tNd)i2Dt%VhG4QLK{eF zp)C}x2sSR<1*8Uiikzf2$WmTPBMdwR2QBpw-BJ9O zvT~zwVXORLb@~d#4fv{;+woRjMHHon@%NM#$5yT^eYh%`ck5<`R!$88VoPyV==k(J z8WhV1hpN^l0#^o>!gHz)v}mWJ@hG^;SoE$WGM(XXk=+dR@PS5a1#3#W0+TzZ4*W5S z=Mg`8QHExuhs^4wIc2ix_91cPV;G2Vsmi&V!qGVS3#0uL=k&i4!$ii= zY{UIYy@`zn(!nR}=`N)UK_(Z>jJmFHw9Ywh<32j4wL4ooUEQCf2Pa=o)5{+SKd;C4 z+o-zewB)3_cbk`bx~VqV$`gi3wEz(UsaaEk$gjvhf3ROWBcKf+!^6{PEQq-Y4Pp&$ zX}8KQ~fHNf!YV0z(?O`|a2(z^zr*TYg9 zMV&!z7pqa3ICVe?wZg0k8m_gBGgl15%>N~VFf>BV{|h!Uh&Gr%%h1M61qlSeGHFYx z9j7hYyz!T1i!zuZi~$+6Y0=WdSkQ9%^?CD#&(M^DZP5FafJz@2HSh^o1uEfc#Ad-I z=>H2eot7y136=xZ6q<;`48VtzgBymg)!^GVpsXVy5~J+EkB1FxpYJk2o=Y*4PdzFK z0quw@fS({})(q?^|Q~DZ?5w z{f#3B$e=5ucbzn86J%LZ=ucyaQYy=&N`sdd*Q{1exW36koAUSMhYW17;oZg6bi3Hz zA*Lb$2vSpP&rHl(ll-wJY#oBSpy41$?v?Txl{p+0XI!u+*)C7okcruhZBICE`u;TsN6iFL1mXwwf5!7qNS;&*Agiyw03sWvMB-0;@Jy2QR>Dv(v1$b)UmQdP zl~7a!;b*i>w(h$6Sw@Q@o0_cBfJ7Z#6?}e&{UT1&c#E>?=OhqBV+c*9 z458h@lO0&g8Eo7kM9KfG0265}=Hokmf@0iN-axb=nmOvDNPfy(UiZmK%4b`Zlq|_5 zZzKW!4?Us&kSBwo8N%=WU!%03>xnOH6Y2UQYbl_S#tODAmOk!mt#l;pl|+Bo-SolD z*|pR_&`0g`E3;@f%!57hQGp+xF&+vyx=4_cCzUlTK+jSlu<JLfWFC$KQzQ2$j!@hCgpRcHExfd~w-UDthm~_Hn$I{T=II zb-Wf~wKO)KvBi$-VnW7ij|`v=aH{|w_~h}~&Kzq2%=aH#GH{fKOj?P4oRG3W*Vp%h zN6N@#mxjGwr?7hph z#+07U33}dCf@CVQaSm3i;Cd;!Ih5D5BmcA>v=jI)8)8?zFU{3Y-zzOVrW5qFP0-)` zP3{{o5?U{~KHSiZfd@_jr-o5)c|0s4kwtKz1ol-4)myIG$G)z_tfTbvJyY*smG?Y2 zC$L4P37kbT3ym3WtZgQ<^)g#g`MJsOKcRaVYBI}(r8akIXrK{(hfT)6^tx6$C zv2V@vm{VJ*nb?SP?v&%ZVesu@hY|Xg(x^bP@C;q3b3?QF%JiLk-rfE)%nS#j1P%^*ye6e-lHm%jzIDSVmU#CoocNa^g`}S>TA84uAo)OlUJUkwfDx>Q_*zjML*rwf z*?!vg^$PUP@A5P#68g`PzOj*_;B^OF*&GsLrEm!W>vRjQZJ`)o4X_{2P`;IGj0Eb2 zOE8~3Gg4YD-47^@YPuQ|b2$ZzXf+@@QBg2qIEL&ZO>ytU8cd9-;O{)GveY@|g^f`^ z@B(zG)O-j}$zKN{t0h5KLT#SR^hg zxe2(Vm58@{%qzcNEXHq2qAzqJwhjTX%R-ZPWbURqV`~aBCED^(lo_eBrKUZeU65nK zZ>CH9)zM5bRCZATt04O@kc|rmz|omE2823yM-4|0Arcj57c-HjgR^1-2z-qhe2h3X zdrJvUCVx5@d^AsM`Bd#O8zlj(RZfjU9gqlNSu+epe7B&8>aBG^ORHdzg zXjUK1^Ae3A!>`u06s@DmlrfqF7amDE&I z5w00oN&UX*r|Ie6`QQ+%Z9&Lo$g|Dcw%VW1$KB1OgH19G=n%C?C2pp@ak>r3A?n?U?1AV4A8h% z5e5kKe-~!%osHc6+hM7-y)GLmz9Vj|a~eQp!uJDu-RO<>IF8KBRYmcy+la$I7d{Uo zq=x}BBm{~g^`@We6HHHY%-p2x<{V?{XRe~oM^UIS8ID!+heuXpsFuOBjH(m|n7E_C zBe0TOQ%&}x9{%L)eiD!j3#{kNh#t^pDgsi}X-<@BPO9{$ddLl~X{R|}GKSeoXBQ~M zDuY>+c`7-<(?aXW!>kOMP`n^Z*NjUvrvvn0v>fd~A;_RCLsv_PAB{<~i%BqsAC5%V z%FJ|Pw#nu^u@uvzr^Ue(3`G7g9g#5iGN|zF{jHTFr^ij_rg%m7TZkeEVuSR5_C&^y z7z4*x_?xu(7Ev?*fjZp2LOI4=q!OqrN4DHBFXBd+6E9+xIFd{PR(g5&R(khS?0g zp6muh*^zo(d4I3{->{V^C(?AhT0jOqneCH+sL6iqEZ9bJVp45@&eNOt zyL6B{VGzk7Jq>U(YIg4}kJ=})bs>tMO6A-2Xq_PEFZmHFX(TszWHdQLiLZd_kf;9E zCYH$l0OoLz$IQr^NX)Wq&suI}NyQ#Lls-*O4KaLOf4~S4ha$sTRxzM0CuwBj#Jesg zg=*#pz=7pJ0MN`Z$O#PC3^5zKy8mrWs8Tq&OQ%cZtB7M&WbNpV=x50tI*21op0PZ` z%~hm?)Tl;o&}7uMq3?A`Gb3pf4wMSX=qc`S)F_}_O*YYHYtT(Npa8q~U;YSP=IEiw z1_D*ykp`kE^gYM2y39)F0O!f2-7RBQBjE5++y-85)h1@O z2R${eKM1FZBcTWLq{bSrGdb=nFliqZs?f9|cMn5aUeBz3p7=2gdLvL${hj!Z!wCaM z_8=CDw+e}2w
    0>G7%iRrE#Uu`j`3D#r}XC8K=9oKNTIV&Nvi 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 diff --git a/imxweb/imx-modules/imx-api-aad.tgz-hash b/imxweb/imx-modules/imx-api-aad.tgz-hash new file mode 100644 index 000000000..65e565fd0 --- /dev/null +++ b/imxweb/imx-modules/imx-api-aad.tgz-hash @@ -0,0 +1,8 @@ +E6573E34E07D56A25D5A5E5AE4B4B8944C02BAC5479D6A7870003C698401BC6E7D3B8FC6E11FA25628D36EAA6DD0F9A71021A0114B808ADF1FC86E7A17246DCE tsconfig.json +52822F64AEA2B42445A1A01278F864061078D3E60DF1F530F19FEA5E077643DE3CAC8B4A4F065F238F2ED260EF8A0AEC94E5711DF64D75DC9CEB1A4C35A865D8 rollup.config.js +565D30DE7CA344FE6BB6724E1208DEE942D0CEDCE31BDD06F11B8C9E8DA76466100FA64B33FB780C2822F4EAC1B170EF1A2E73BDE93498BCFA185923278A07EE .npmrc +507EA85D039F06742AEBA49AFC07F8F74ED8914BE94546CF741E75DD613EA1C5A672D1904EBB6C301885DB0C9343C891D75B9F6EC281D51303A29B7E6B01E51B package-lock.json +9A2E8B6035879F27E699CB0D886311D42BE245C585F66BFF47B0FD35DCAB0502415FDB31002BD79C7637D4C1105F289F6E11E401D9A66454E1A44A0C09F78F1B imx-api-aad.es5.js +06BAB4D8CDE952E267A312D85C482E1E89BCF1F67371EECB4571AC988DE599F34C5EB9BCF24B942D312C5184111BD7D856B303298987BA63AAE36F261C2BBB0A imx-api-aad.umd.js +A9C0B61DDB3B45B9F836C8BF15DA912EC954166DEEC833354ADD50790761401B38B559EDFCF7794D6FD4747F668376F6FF86DC5AB0DE5415BFAC3FC5451815A9 index.d.ts +A9D47CD5E9766CB8D1E86A27FFC593869C554F1916DFBDF4D1CCE1DA7DD089F3FFDC95A2754C2A50362509401563E49F16FDDBCC0EA77F1F82F3BA970E88EAA3 TypedClient.d.ts \ No newline at end of file diff --git a/imxweb/imx-modules/imx-api-aad.tgz-version b/imxweb/imx-modules/imx-api-aad.tgz-version new file mode 100644 index 000000000..b83a0529d --- /dev/null +++ b/imxweb/imx-modules/imx-api-aad.tgz-version @@ -0,0 +1 @@ +9.3.14 \ No newline at end of file diff --git a/imxweb/imx-modules/imx-api-aob.tgz b/imxweb/imx-modules/imx-api-aob.tgz deleted file mode 100644 index c47c2f7cdefde14ca5cab49cda353cb66e57f545..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57653 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