From e070ccdb0ac9359ab0e2fc5422a2460fa5a882fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20B=C3=B8rsheim?= Date: Tue, 18 Feb 2025 10:46:46 +0100 Subject: [PATCH] Upgrade to angular v19 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * build: remove unecessary file .yarnrc * build: remove browserlist configuration * build: simplify configuration * ci: update actions * test: fix wrong expected date * fix: -- is deprecated in yarn test * feat: replace moment with dayjs * fix: remove non-null assertion in dayjs test * update angular core and cli * upgrade angular-material * feat: localization of time and date using dayjs * feat: replace deprecated ngx-matdatetimepicker with material Date and Timepicker * refactor: removed workaround. * chore: update dependencies and remove dependencies no longer in use * fix: format duration to same format with dayjs as with moment. update tests * fix: change oicd response type * feat: use casl abilityService instead of able pipe * chore: update dependencies * fix: settings format in dateadapter to match calendar input. Update tests to support changes * fix: update after changing casl ability implementation * fix: update tests to work with new dayjs dateadapter * cleanup: remove console.log statements * fix: replace logo image with smaller size images to get rid of warning during render * fix: dayjs-date-adapter * feat: remove deprecated APP_INITIALIZER * fix: turn off eslint prefer standalone Turned off prefer-standalone option in eslint to pass linting step. Can be turned back on again if we at some point refactor all components ++ to be standalone. * fix: remove unused lifecycle hook to please linter * fix: remove optional locale in _createDayJs. Set locale to nb instead of nb-NO in tests --------- Co-authored-by: Marius André Elsfjordstrand Beck --- .browserslistrc | 14 - .eslintrc.json | 6 +- .github/workflows/release.yml | 14 +- .github/workflows/test.yml | 24 +- .yarnrc | 2 - Dockerfile | 22 +- angular.json | 4 +- nginx/{default.template.conf => default.conf} | 4 +- package.json | 87 +- .../about-dialog/about-dialog.component.ts | 7 +- src/app/components/app/app.component.html | 175 +- src/app/components/app/app.component.ts | 24 +- .../crawlerstatus-dialog.component.ts | 9 +- .../crawlerstatus.component.html | 6 +- .../crawlerstatus/crawlerstatus.component.ts | 16 +- src/app/components/dialog/dialog.component.ts | 7 +- .../error-dialog/error-dialog.component.ts | 7 +- src/app/components/home/home.component.html | 9 +- src/app/components/home/home.component.ts | 13 +- .../schedule-event-dialog.component.ts | 7 +- .../schedule-overview.component.ts | 32 +- src/app/components/time/time.component.ts | 9 +- src/assets/config.json | 2 +- .../logo/veidemann_horizontal_white.png | Bin 24054 -> 15326 bytes .../logo/veidemann_logo_inline_black.png | Bin 62217 -> 7979 bytes src/main.ts | 1 - src/modules/commons/commons.module.ts | 3 - .../commons/components/base-list/base-list.ts | 12 +- .../commons/directives/action.directive.ts | 3 +- .../commons/directives/extra.directive.ts | 3 +- .../commons/directives/filter.directive.ts | 3 +- .../commons/directives/prismjs.directive.ts | 3 +- .../commons/directives/shortcut.directive.ts | 3 +- src/modules/commons/material.module.ts | 29 +- .../pipes/duration-format.pipe.spec.ts | 28 - .../commons/pipes/duration-format.pipe.ts | 3 +- src/modules/commons/pipes/url-format.pipe.ts | 3 +- .../annotation/annotation.component.html | 130 +- .../annotation/annotation.component.spec.ts | 3 +- .../annotation/annotation.component.ts | 17 +- .../script-annotation.component.html | 4 +- .../script-annotation.component.spec.ts | 3 +- .../script-annotation.component.ts | 14 +- .../browserconfig-details.component.spec.ts | 2 - .../browserconfig-details.component.ts | 7 +- .../browserconfig-dialog.component.spec.ts | 2 - .../browserconfig-dialog.component.ts | 7 +- .../browserconfig-multi-dialog.component.ts | 9 +- .../browserconfig-preview.component.spec.ts | 3 +- .../browserconfig-preview.component.ts | 7 +- .../browserscript-details.component.spec.ts | 2 - .../browserscript-details.component.ts | 9 +- .../browserscript-dialog.component.spec.ts | 2 - .../browserscript-dialog.component.ts | 7 +- .../browserscript-multi-dialog.component.ts | 7 +- .../browserscript-preview.component.ts | 7 +- .../collection-meta.component.spec.ts | 2 - .../collection-meta.component.ts | 15 +- .../collection-details.component.spec.ts | 2 - .../collection-details.component.ts | 9 +- .../collection-dialog.component.spec.ts | 3 +- .../collection-dialog.component.ts | 9 +- .../collection-preview.component.ts | 7 +- .../config-list/config-list.component.ts | 41 +- .../config-query/config-query.component.ts | 9 +- .../crawl-execution-status.component.ts | 10 +- .../crawlconfig-details.component.spec.ts | 2 - .../crawlconfig-details.component.ts | 7 +- .../crawlconfig-dialog.component.spec.ts | 3 +- .../crawlconfig-dialog.component.ts | 7 +- .../crawlconfig-multi-dialog.component.ts | 7 +- .../crawlconfig-preview.component.ts | 7 +- ...lhostgroupconfig-details.component.spec.ts | 2 - .../crawlhostgroupconfig-details.component.ts | 9 +- ...wlhostgroupconfig-dialog.component.spec.ts | 3 +- .../crawlhostgroupconfig-dialog.component.ts | 7 +- ...groupconfig-multi-dialog.component.spec.ts | 3 +- ...lhostgroupconfig-multi-dialog.component.ts | 7 +- .../crawlhostgroupconfig-preview.component.ts | 7 +- .../crawl-job-details.component.spec.ts | 2 - .../crawl-job-details.component.ts | 7 +- .../crawljob-dialog.component.spec.ts | 3 +- .../crawljob-dialog.component.ts | 7 +- .../crawljobs-multi-dialog.component.ts | 7 +- .../crawljob-preview.component.ts | 7 +- .../delete-dialog/delete-dialog.component.ts | 5 +- .../delete-multi-dialog.component.ts | 7 +- .../durationpicker/duration-picker.ts | 40 +- .../entity-details.component.spec.ts | 2 - .../entity-details.component.ts | 7 +- .../entity-dialog/entity-dialog.component.ts | 7 +- .../entity-multi-dialog.component.ts | 5 +- .../entity-view/entity-view.component.ts | 7 +- .../filesize-input.component.ts | 31 +- .../job-status.component.ts | 9 +- .../label-multi/label-multi.component.ts | 7 +- .../components/label/label.component.ts | 11 +- .../meta-preview/meta-preview.component.ts | 7 +- .../components/meta/meta.component.spec.ts | 2 - .../config/components/meta/meta.component.ts | 17 +- ...politenessconfig-details.component.spec.ts | 2 - .../politenessconfig-details.component.ts | 7 +- .../politenessconfig-dialog.component.spec.ts | 3 +- .../politenessconfig-dialog.component.ts | 7 +- ...politenessconfig-multi-dialog.component.ts | 7 +- .../politenessconfig-preview.component.ts | 7 +- .../components/preview/preview.component.ts | 9 +- .../rolemapping-details.component.ts | 7 +- .../rolemapping-dialog.component.ts | 7 +- .../rolemapping-list.component.ts | 21 +- .../rolemapping-multi-dialog.component.ts | 7 +- .../run-crawl-dialog.component.ts | 7 +- .../schedule-details.component.html | 1 + .../schedule-details.component.spec.ts | 63 +- .../schedule-details.component.ts | 7 +- .../schedule-dialog.component.spec.ts | 3 +- .../schedule-dialog.component.ts | 7 +- .../schedule-multi-dialog.component.ts | 7 +- .../schedule-preview.component.ts | 7 +- .../seed-meta-preview.component.ts | 7 +- .../seed-meta/seed-meta.component.spec.ts | 2 - .../seed-meta/seed-meta.component.ts | 17 +- .../seed-details/seed-details.component.ts | 7 +- .../seed/seed-dialog/seed-dialog.component.ts | 7 +- .../seed-multi-dialog.component.ts | 7 +- .../seed-preview.component.spec.ts | 1 - .../seed-preview/seed-preview.component.ts | 7 +- .../components/selector/selector.component.ts | 11 +- .../action-shortcut.component.html | 10 +- .../action-shortcut.component.spec.ts | 3 +- .../action-shortcut.component.ts | 11 +- .../filter-shortcut.component.html | 26 +- .../filter-shortcut.component.spec.ts | 3 +- .../filter-shortcut.component.ts | 11 +- .../shortcut-list.component.html | 18 +- .../shortcut-list.component.spec.ts | 3 +- .../shortcut-list/shortcut-list.component.ts | 10 +- .../components/shortcut/shortcut.component.ts | 7 +- .../config-nav-list.component.html | 35 +- .../config-nav-list.component.ts | 7 +- .../containers/config/config.component.ts | 9 +- .../configuration/configuration.component.ts | 9 +- .../configurations.component.html | 53 +- .../configurations.component.ts | 15 +- .../directives/config-query.directive.ts | 3 +- .../config/pipe/browser-config-name.pipe.ts | 3 +- .../config/pipe/browser-script-name.pipe.ts | 3 +- .../config/pipe/collection.name.pipe.ts | 3 +- .../config/pipe/crawl-config-name.pipe.ts | 3 +- .../pipe/crawl-execution-status.pipe.ts | 3 +- .../config/pipe/crawl-schedule-name.pipe.ts | 3 +- .../pipe/crawljob-disabled-status.pipe.ts | 3 +- src/modules/config/pipe/entity-name.pipe.ts | 3 +- .../config/pipe/job-execution-state.pipe.ts | 3 +- .../config/pipe/job-execution-status.pipe.ts | 3 +- .../pipe/politeness-config-name.pipe.ts | 3 +- .../config/pipe/script-annotations.pipe.ts | 3 +- src/modules/config/pipe/to-array.pipe.ts | 3 +- src/modules/core/core.module.ts | 50 +- src/modules/core/core.testing.module.ts | 11 +- .../services/api/report-api.service.spec.ts | 3 +- .../core/services/auth/auth.service.spec.ts | 18 +- .../core/services/auth/auth.service.ts | 10 +- .../core/services/auth/guard.service.spec.ts | 3 +- src/modules/core/services/index.ts | 1 + src/modules/core/services/locale.service.ts | 41 + .../dayjs-date-adapter.spec.ts | 889 ++ .../dayjs-date-adapter/dayjs-date-adapter.ts | 338 + .../dayjs-date-adapter/dayjs-date-formats.ts | 20 + src/modules/dayjs-date-adapter/index.ts | 32 + .../logconfig/loglevel.component.ts | 9 +- .../abort-crawl-dialog.component.ts | 7 +- .../crawl-execution-preview.component.ts | 7 +- .../crawl-execution-shortcuts.component.html | 90 +- ...rawl-execution-shortcuts.component.spec.ts | 5 +- .../crawl-execution-shortcuts.component.ts | 12 +- .../crawl-execution-status-list.component.ts | 43 +- ...rawl-execution-status-query.component.html | 36 +- .../crawl-execution-status-query.component.ts | 9 +- .../crawl-execution-status.component.ts | 11 +- .../crawl-log-list.component.ts | 43 +- .../crawl-log-preview.component.ts | 7 +- .../crawl-log-query.component.ts | 7 +- .../crawl-log-shortcuts.component.html | 8 +- .../crawl-log-shortcuts.component.spec.ts | 2 - .../crawl-log-shortcuts.component.ts | 13 +- .../crawl-log-status.component.ts | 11 +- .../job-execution-preview.component.ts | 9 +- .../job-execution-shortcuts.component.html | 67 +- .../job-execution-shortcuts.component.spec.ts | 3 +- .../job-execution-shortcuts.component.ts | 13 +- .../job-execution-status-list.component.ts | 43 +- .../job-execution-status-query.component.html | 30 +- .../job-execution-status-query.component.ts | 9 +- .../job-execution-status.component.ts | 11 +- .../page-log-list/page-log-list.component.ts | 29 +- .../page-log-query.component.ts | 7 +- .../page-log-shortcuts.component.html | 6 +- .../page-log-shortcuts.component.spec.ts | 3 +- .../page-log-shortcuts.component.ts | 15 +- .../page-log-status.component.ts | 9 +- .../components/resource/resource.component.ts | 23 +- .../crawl-execution-detail.component.ts | 9 +- .../crawl-execution.component.html | 14 +- .../crawl-execution.component.ts | 24 +- .../crawl-log-detail.component.ts | 7 +- .../crawl-log/crawl-log.component.html | 8 +- .../crawl-log/crawl-log.component.ts | 8 +- .../job-execution-detail.component.ts | 7 +- .../job-execution.component.html | 106 +- .../job-execution/job-execution.component.ts | 15 +- .../page-log-detail.component.ts | 7 +- .../page-log/pagelog.component.html | 6 +- .../containers/page-log/pagelog.component.ts | 27 +- .../report-navigation-list.component.html | 57 +- .../report-navigation-list.component.ts | 14 +- .../report/report.component.spec.ts | 4 +- .../containers/report/report.component.ts | 7 +- .../query-crawl-execution-status.directive.ts | 3 +- .../directives/query-crawl-log.directive.ts | 3 +- .../query-job-execution-status.directive.ts | 3 +- .../directives/query-page-log.directive.ts | 3 +- .../report/pipe/crawlexecution-fetch.pipe.ts | 3 +- .../report/pipe/execution-queue-count.pipe.ts | 3 +- src/modules/report/pipe/job-name.pipe.ts | 3 +- .../report/pipe/jobexecution-fetch.pipe.ts | 3 +- .../pipe/jobexecution-total-queue.pipe.ts | 3 +- src/modules/report/pipe/sed.pipe.ts | 3 +- src/modules/report/pipe/seed-name.pipe.ts | 3 +- src/modules/report/report.module.ts | 2 - src/shared/func/datetime/datetime.spec.ts | 24 + src/shared/func/datetime/datetime.ts | 63 +- src/styles/styles.scss | 3 +- yarn.lock | 8171 ++++++++--------- 234 files changed, 6811 insertions(+), 5553 deletions(-) delete mode 100644 .browserslistrc delete mode 100644 .yarnrc rename nginx/{default.template.conf => default.conf} (52%) delete mode 100644 src/modules/commons/pipes/duration-format.pipe.spec.ts create mode 100644 src/modules/core/services/locale.service.ts create mode 100644 src/modules/dayjs-date-adapter/dayjs-date-adapter.spec.ts create mode 100644 src/modules/dayjs-date-adapter/dayjs-date-adapter.ts create mode 100644 src/modules/dayjs-date-adapter/dayjs-date-formats.ts create mode 100644 src/modules/dayjs-date-adapter/index.ts create mode 100644 src/shared/func/datetime/datetime.spec.ts diff --git a/.browserslistrc b/.browserslistrc deleted file mode 100644 index 04c200922..000000000 --- a/.browserslistrc +++ /dev/null @@ -1,14 +0,0 @@ -# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. -# For additional information regarding the format and rule options, please see: -# https://github.com/browserslist/browserslist#queries - -# You can see what browsers were selected by your queries by running: -# npx browserslist - -last 2 Chrome version -last 2 Firefox version -last 2 Edge major versions -last 2 Safari major version -last 2 iOS major versions -Firefox ESR -not IE 9-11 # For IE 9-11 support, remove 'not'. diff --git a/.eslintrc.json b/.eslintrc.json index c28709165..c492c7a0d 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -18,8 +18,10 @@ "plugin:@angular-eslint/recommended", "plugin:@angular-eslint/template/process-inline-templates" ], - "rules": {} - }, + "rules": { + "@angular-eslint/prefer-standalone": "off" + } + }, { "files": [ "*.html" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9bfb58fc4..fc4bf205b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: docker/metadata-action@v3 + - uses: docker/metadata-action@v5 name: Extract metadata (tags, labels) and establish version id: meta with: @@ -30,13 +30,13 @@ jobs: type=ref,event=branch - name: Log in to the container registry (${{ env.REGISTRY }}) - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - uses: docker/build-push-action@v2 + - uses: docker/build-push-action@v6 name: Build and push image with: build-args: | @@ -50,9 +50,9 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - - uses: docker/metadata-action@v3 + - uses: docker/metadata-action@v5 name: Extract metadata (tags, labels) and establish version id: meta with: @@ -63,13 +63,13 @@ jobs: type=ref,event=pr - name: Log in to the container registry (${{ env.REGISTRY }}) - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - uses: docker/build-push-action@v2 + - uses: docker/build-push-action@v6 name: Build and push image with: build-args: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 94206fc94..25d18fe92 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,17 +14,17 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Use Node.js - uses: actions/setup-node@v2 + uses: actions/setup-node@v4 with: - node-version: 18.x + node-version: 20.x - id: yarn-cache-dir-path run: echo "::set-output name=dir::$(yarn cache dir)" - - uses: actions/cache@v2 + - uses: actions/cache@v4 id: yarn-cache with: path: ${{ steps.yarn-cache-dir-path.outputs.dir }} @@ -36,23 +36,23 @@ jobs: run: yarn install --non-interactive --frozen-lockfile --prefer-offline - name: Test - run: yarn test -- --watch=false --browsers=ChromeHeadless + run: yarn test --watch=false --browsers=ChromeHeadless lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Use Node.js - uses: actions/setup-node@v2 + uses: actions/setup-node@v4 with: - node-version: 18.x + node-version: 20.x - id: yarn-cache-dir-path run: echo "::set-output name=dir::$(yarn cache dir)" - - uses: actions/cache@v2 + - uses: actions/cache@v4 id: yarn-cache with: path: ${{ steps.yarn-cache-dir-path.outputs.dir }} @@ -71,9 +71,9 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - - uses: docker/metadata-action@v3 + - uses: docker/metadata-action@v5 name: Extract metadata (tags, labels) and establish version id: meta with: @@ -81,7 +81,7 @@ jobs: tags: | type=ref,event=pr - - uses: docker/build-push-action@v2 + - uses: docker/build-push-action@v6 name: Build and push image with: build-args: | diff --git a/.yarnrc b/.yarnrc deleted file mode 100644 index 8c08f9074..000000000 --- a/.yarnrc +++ /dev/null @@ -1,2 +0,0 @@ -network-timeout 100000 -registry "https://registry.npmjs.org" diff --git a/Dockerfile b/Dockerfile index 1872a2773..05fad14e1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,33 +1,23 @@ -FROM node:20.11-alpine as build +FROM node:20-alpine as build -ARG BASE_HREF=/veidemann/ ARG VERSION -# Install git because git dependency in package.json -# Install gettext because envsubst -RUN apk add --update --no-cache git gettext - -COPY package.json yarn.lock .yarnrc /usr/src/app/ WORKDIR /usr/src/app + +COPY package.json yarn.lock ./ + RUN yarn install --non-interactive --frozen-lockfile COPY . . -# Give the application a version RUN sed -i "s/version: ''/version: '${VERSION}'/" src/environments/*.ts -RUN yarn ng build \ ---configuration production \ ---base-href=${BASE_HREF} \ ---output-path=dist/${BASE_HREF} - -# Configure nginx location -RUN envsubst \$BASE_HREF < nginx/default.template.conf > nginx/default.conf +RUN yarn ng build --configuration production FROM nginx:stable-alpine LABEL maintainer="nettarkivet@nb.no" -COPY --from=build /usr/src/app/dist /usr/share/nginx/app +COPY --from=build /usr/src/app/dist /usr/share/nginx COPY --from=build /usr/src/app/nginx/default.conf /etc/nginx/conf.d/ diff --git a/angular.json b/angular.json index a7d54fbb9..5a2e2b7a6 100644 --- a/angular.json +++ b/angular.json @@ -17,8 +17,10 @@ "build": { "builder": "@angular-devkit/build-angular:application", "options": { + "baseHref": "/veidemann/", "outputPath": { - "base": "dist" + "base": "dist/app", + "browser": "veidemann" }, "index": "src/index.html", "tsConfig": "tsconfig.app.json", diff --git a/nginx/default.template.conf b/nginx/default.conf similarity index 52% rename from nginx/default.template.conf rename to nginx/default.conf index 106189f70..218ecc28a 100644 --- a/nginx/default.template.conf +++ b/nginx/default.conf @@ -3,7 +3,7 @@ server { root /usr/share/nginx/app; index index.html; - location ${BASE_HREF} { - try_files $uri $uri/ ${BASE_HREF}/index.html =404; + location /veidemann { + try_files $uri $uri/ /veidemann/index.html =404; } } diff --git a/package.json b/package.json index 8fe2edf63..0fe3b0cea 100644 --- a/package.json +++ b/package.json @@ -16,78 +16,71 @@ "extract-i18n": "ng xi18n --output-path src/locale --locale no && xliffmerge --profile xliffmerge.json no en" }, "dependencies": { - "@angular-material-components/datetime-picker": "^16.0.1", - "@angular-material-components/moment-adapter": "^16.0.1", - "@angular/animations": "^18.1.4", - "@angular/cdk": "^18.1.4", - "@angular/common": "^18.1.4", - "@angular/compiler": "^18.1.4", - "@angular/core": "^18.1.4", + "@angular/animations": "^19.1.5", + "@angular/cdk": "^19.1.3", + "@angular/common": "^19.1.5", + "@angular/compiler": "^19.1.5", + "@angular/core": "^19.1.5", "@angular/flex-layout": "^15.0.0-beta.42", - "@angular/forms": "^18.1.4", - "@angular/localize": "^18.1.4", - "@angular/material": "^18.1.4", - "@angular/material-moment-adapter": "^18.1.4", - "@angular/platform-browser": "^18.1.4", - "@angular/platform-browser-dynamic": "^18.1.4", - "@angular/router": "^18.1.4", - "@casl/ability": "^6.7.1", - "@casl/angular": "^8.2.7", - "@fullcalendar/angular": "^6.1.15", + "@angular/forms": "^19.1.5", + "@angular/localize": "^19.1.5", + "@angular/material": "^19.1.3", + "@angular/platform-browser": "^19.1.5", + "@angular/platform-browser-dynamic": "^19.1.5", + "@angular/router": "^19.1.5", + "@casl/ability": "^6.7.3", + "@casl/angular": "^9.0.3", + "@fullcalendar/angular": "^6.1.16", "@fullcalendar/core": "^6.1.15", "@fullcalendar/daygrid": "^6.1.15", "@fullcalendar/interaction": "^6.1.15", "@fullcalendar/timegrid": "^6.1.15", "@materia-ui/ngx-monaco-editor": "^6.0.0", - "@ngneat/spectator": "^19.0.0", - "@types/moment-duration-format": "^2.2.6", - "@types/prismjs": "^1.26.4", - "angular-oauth2-oidc": "~17.0.2", + "@ngneat/spectator": "^19.4.1", + "@types/prismjs": "^1.26.5", + "angular-oauth2-oidc": "~19.0.0", "angular-oauth2-oidc-jwks": "^17.0.2", "chroma-js": "^2.6.0", "cron-parser": "^4.9.0", - "echarts": "^5.5.1", + "dayjs": "^1.11.13", + "echarts": "^5.6.0", "filesize": "^9.0.11", - "google-protobuf": "^3.21.2", + "google-protobuf": "^3.21.4", "grpc-web": "^1.5.0", - "hammerjs": "^2.0.8", "ip-address": "^9.0.5", "minimist": "~1.2.8", - "moment": "^2.30.1", - "moment-duration-format": "^2.3.2", - "moment-timezone": "^0.5.45", - "monaco-editor": "^0.50.0", + "monaco-editor": "^0.52.2", "ng-keyboard-shortcuts": "^13.0.8", - "ng-mocks": "^14.13.0", - "ngx-echarts": "^18.0.0", + "ng-mocks": "^14.13.2", + "ngx-echarts": "^19.0.0", "ngx-filesize": "^3.0.4", "prismjs": "^1.29.0", "rxjs": "~7.8.1", - "tslib": "^2.6.3", - "zone.js": "~0.14.10" + "tslib": "^2.8.1", + "zone.js": "~0.15.0" }, "resolutions": { "minimist": "~1.2.5" }, "devDependencies": { - "@angular-devkit/build-angular": "^18.1.4", - "@angular-eslint/builder": "^18.3.0", - "@angular-eslint/eslint-plugin": "^18.3.0", - "@angular-eslint/eslint-plugin-template": "^18.3.0", - "@angular-eslint/schematics": "^18.2.0", - "@angular-eslint/template-parser": "^18.3.0", - "@angular/cli": "^18.1.4", - "@angular/compiler-cli": "^18.1.4", - "@angular/language-service": "^18.1.4", + "@angular-devkit/build-angular": "^19.1.6", + "@angular-eslint/builder": "^19.1.0", + "@angular-eslint/eslint-plugin": "^19.1.0", + "@angular-eslint/eslint-plugin-template": "^19.1.0", + "@angular-eslint/schematics": "^19.1.0", + "@angular-eslint/template-parser": "^19.1.0", + "@angular/cli": "^19.1.6", + "@angular/compiler-cli": "^19.1.5", + "@angular/language-service": "^19.1.5", "@juggle/resize-observer": "^3.4.0", "@types/google-protobuf": "^3.15.12", - "@types/jasmine": "~5.1.4", + "@types/jasmine": "~5.1.5", "@types/jasminewd2": "~2.0.13", - "@types/node": "^20.8.9", - "@typescript-eslint/eslint-plugin": "^8.1.0", - "@typescript-eslint/parser": "^8.1.0", - "eslint": "^9.9.0", - "jasmine-core": "~5.2.0", + "@types/node": "^22.0.3", + "@typescript-eslint/eslint-plugin": "^8.24.0", + "@typescript-eslint/parser": "^8.24.0", + "eslint": "^9.20.0", + "jasmine-core": "~5.6.0", "jasmine-spec-reporter": "~7.0.0", "karma": "~6.4.4", "karma-chrome-launcher": "~3.2.0", diff --git a/src/app/components/about-dialog/about-dialog.component.ts b/src/app/components/about-dialog/about-dialog.component.ts index 76e957e39..7ed28821c 100644 --- a/src/app/components/about-dialog/about-dialog.component.ts +++ b/src/app/components/about-dialog/about-dialog.component.ts @@ -2,9 +2,10 @@ import {Component} from '@angular/core'; import {environment} from '../../../environments/environment'; @Component({ - selector: 'app-about-dialog', - templateUrl: './about-dialog.component.html', - styleUrls: ['./about-dialog.component.css'] + selector: 'app-about-dialog', + templateUrl: './about-dialog.component.html', + styleUrls: ['./about-dialog.component.css'], + standalone: false }) export class AboutDialogComponent { diff --git a/src/app/components/app/app.component.html b/src/app/components/app/app.component.html index bca8334f6..c1d1b1902 100644 --- a/src/app/components/app/app.component.html +++ b/src/app/components/app/app.component.html @@ -1,101 +1,104 @@
- - - - - + + + + + + - + - + - + - - help - - - Docs - About - + + help + + + Docs + About + - + - - + + - -
-

Loading module...

- -
+
+

Loading module...

+ +
- -
- - - -
+ +
+ + + +
+
diff --git a/src/app/components/app/app.component.ts b/src/app/components/app/app.component.ts index 7704ac620..038d6654b 100644 --- a/src/app/components/app/app.component.ts +++ b/src/app/components/app/app.component.ts @@ -1,22 +1,30 @@ import {AfterViewInit, ChangeDetectionStrategy, Component, OnInit} from '@angular/core'; -import {ActivatedRoute, RouteConfigLoadEnd, RouteConfigLoadStart, Router, RouterEvent} from '@angular/router'; +import {ActivatedRoute, RouteConfigLoadEnd, RouteConfigLoadStart, Router} from '@angular/router'; import {Observable} from 'rxjs'; import {filter, map} from 'rxjs/operators'; -import {AppInitializerService, ControllerApiService, ErrorService, SnackBarService} from '../../../modules/core/services'; +import { + AppInitializerService, + ControllerApiService, + ErrorService, + SnackBarService +} from '../../../modules/core/services'; import {AuthService, GuardService} from '../../../modules/core/services/auth'; import {MatDialog} from '@angular/material/dialog'; import {AboutDialogComponent} from '../about-dialog/about-dialog.component'; import {ShortcutEventOutput, ShortcutInput} from 'ng-keyboard-shortcuts'; import {ScheduleOverviewComponent} from '../schedule-overview/schedule-overview.component'; +import {AbilityService} from "@casl/angular"; @Component({ - selector: 'app-root', - templateUrl: './app.component.html', - styleUrls: ['./app.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class AppComponent implements OnInit, AfterViewInit { + readonly ability$: Observable; isModuleLoading$: Observable; private moduleLoadSemaphore = 0; @@ -30,7 +38,9 @@ export class AppComponent implements OnInit, AfterViewInit { private guardService: GuardService, private snackBarService: SnackBarService, private dialog: MatDialog, - private errorService: ErrorService) { + private errorService: ErrorService, + private abilityService: AbilityService) { + this.ability$ = this.abilityService.ability$; this.isModuleLoading$ = this.router.events.pipe( filter(event => event instanceof RouteConfigLoadStart || event instanceof RouteConfigLoadEnd), map(event => { diff --git a/src/app/components/crawlerstatus-dialog/crawlerstatus-dialog.component.ts b/src/app/components/crawlerstatus-dialog/crawlerstatus-dialog.component.ts index 28fc295d6..5045f7fa0 100644 --- a/src/app/components/crawlerstatus-dialog/crawlerstatus-dialog.component.ts +++ b/src/app/components/crawlerstatus-dialog/crawlerstatus-dialog.component.ts @@ -2,10 +2,11 @@ import {ChangeDetectionStrategy, Component, Inject} from '@angular/core'; import {MAT_DIALOG_DATA} from '@angular/material/dialog'; @Component({ - selector: 'app-crawlerstatus-dialog', - templateUrl: './crawlerstatus-dialog.component.html', - styleUrls: ['./crawlerstatus-dialog.component.css'], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-crawlerstatus-dialog', + templateUrl: './crawlerstatus-dialog.component.html', + styleUrls: ['./crawlerstatus-dialog.component.css'], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class CrawlerStatusDialogComponent { shouldPause: boolean; diff --git a/src/app/components/crawlerstatus/crawlerstatus.component.html b/src/app/components/crawlerstatus/crawlerstatus.component.html index 15fd67758..f7a7f8bb7 100644 --- a/src/app/components/crawlerstatus/crawlerstatus.component.html +++ b/src/app/components/crawlerstatus/crawlerstatus.component.html @@ -1,11 +1,12 @@
+

Crawler status

RUNNING

diff --git a/src/app/components/crawlerstatus/crawlerstatus.component.ts b/src/app/components/crawlerstatus/crawlerstatus.component.ts index e24c546d1..7e784926a 100644 --- a/src/app/components/crawlerstatus/crawlerstatus.component.ts +++ b/src/app/components/crawlerstatus/crawlerstatus.component.ts @@ -1,16 +1,24 @@ import {ChangeDetectionStrategy, Component, EventEmitter, Input, Output} from '@angular/core'; import {RunStatus} from '../../../shared/models/controller'; import {CrawlerStatus} from '../../../shared/models/controller/controller.model'; +import {Observable} from "rxjs"; +import {AbilityService} from "@casl/angular"; @Component({ - selector: 'app-crawlerstatus', - templateUrl: './crawlerstatus.component.html', - styleUrls: ['./crawlerstatus.component.css'], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-crawlerstatus', + templateUrl: './crawlerstatus.component.html', + styleUrls: ['./crawlerstatus.component.css'], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class CrawlerStatusComponent { readonly RunStatus = RunStatus; + readonly ability$: Observable; + + constructor(private abilityService: AbilityService) { + this.ability$ = this.abilityService.ability$; + } @Input() crawlerStatus: CrawlerStatus; diff --git a/src/app/components/dialog/dialog.component.ts b/src/app/components/dialog/dialog.component.ts index 884973c97..de9f2f8fb 100644 --- a/src/app/components/dialog/dialog.component.ts +++ b/src/app/components/dialog/dialog.component.ts @@ -9,9 +9,10 @@ import {ErrorService} from '../../../modules/core/services'; @Component({ - selector: 'app-dialog', - template: '', - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-dialog', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class DialogComponent implements OnInit, OnDestroy { diff --git a/src/app/components/error-dialog/error-dialog.component.ts b/src/app/components/error-dialog/error-dialog.component.ts index 1247e1343..872fbdb9c 100644 --- a/src/app/components/error-dialog/error-dialog.component.ts +++ b/src/app/components/error-dialog/error-dialog.component.ts @@ -2,14 +2,15 @@ import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; import {ChangeDetectionStrategy, Component, Inject} from '@angular/core'; @Component({ - selector: 'app-error-dialog', - template: `

{{title}}

+ selector: 'app-error-dialog', + template: `

{{title}}

{{content}}
`, - changeDetection: ChangeDetectionStrategy.OnPush, + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class ErrorDialogComponent { title: string; diff --git a/src/app/components/home/home.component.html b/src/app/components/home/home.component.html index 3eb6422a5..deb60af5e 100644 --- a/src/app/components/home/home.component.html +++ b/src/app/components/home/home.component.html @@ -1,9 +1,12 @@ -
+ +
- + (changeRunStatus)="onChangeRunStatus($event)"> +
+
diff --git a/src/app/components/home/home.component.ts b/src/app/components/home/home.component.ts index 50a349afa..81fa7f84d 100644 --- a/src/app/components/home/home.component.ts +++ b/src/app/components/home/home.component.ts @@ -5,20 +5,25 @@ import {ControllerApiService, ErrorService} from '../../../modules/core/services import {CrawlerStatusDialogComponent} from '../index'; import {MatDialog} from '@angular/material/dialog'; import {CrawlerStatus} from '../../../shared/models/controller/controller.model'; +import {AbilityService} from "@casl/angular"; @Component({ - selector: 'app-home', - templateUrl: './home.component.html', - styleUrls: ['./home.component.css'] + selector: 'app-home', + templateUrl: './home.component.html', + styleUrls: ['./home.component.css'], + standalone: false }) export class HomeComponent implements OnInit { + readonly ability$: Observable; updateRunStatus: Subject = new Subject(); crawlerStatus$: Observable; constructor(private errorService: ErrorService, private controllerApiService: ControllerApiService, - private dialog: MatDialog) { + private dialog: MatDialog, + private abilityService: AbilityService) { + this.ability$ = this.abilityService.ability$; } ngOnInit(): void { diff --git a/src/app/components/schedule-event-dialog/schedule-event-dialog.component.ts b/src/app/components/schedule-event-dialog/schedule-event-dialog.component.ts index 84c557277..d36914c45 100644 --- a/src/app/components/schedule-event-dialog/schedule-event-dialog.component.ts +++ b/src/app/components/schedule-event-dialog/schedule-event-dialog.component.ts @@ -2,9 +2,10 @@ import {Component, Inject} from '@angular/core'; import {MAT_DIALOG_DATA} from '@angular/material/dialog'; @Component({ - selector: 'app-schedule-event-dialog', - templateUrl: './schedule-event-dialog.component.html', - styleUrls: ['./schedule-event-dialog.component.css'] + selector: 'app-schedule-event-dialog', + templateUrl: './schedule-event-dialog.component.html', + styleUrls: ['./schedule-event-dialog.component.css'], + standalone: false }) export class ScheduleEventDialogComponent { diff --git a/src/app/components/schedule-overview/schedule-overview.component.ts b/src/app/components/schedule-overview/schedule-overview.component.ts index b090d6c50..a5bf6bf48 100644 --- a/src/app/components/schedule-overview/schedule-overview.component.ts +++ b/src/app/components/schedule-overview/schedule-overview.component.ts @@ -16,14 +16,17 @@ import {takeUntil, toArray} from 'rxjs/operators'; import * as cronParser from 'cron-parser'; import {MatDialog} from '@angular/material/dialog'; import {ScheduleEventDialogComponent} from '../schedule-event-dialog/schedule-event-dialog.component'; -import * as momentTimezone from 'moment-timezone'; -import moment from 'moment'; import {colorScales} from './colors'; import {DateClickArg} from '@fullcalendar/interaction'; import {CalendarOptions, EventClickArg} from '@fullcalendar/core'; import dayGridPlugin from '@fullcalendar/daygrid'; import timeGridPlugin from '@fullcalendar/timegrid'; import interactionPlugin from '@fullcalendar/interaction'; +import dayjs from 'dayjs' +import timezone from 'dayjs/plugin/timezone' +import isBetween from 'dayjs/plugin/isBetween' +dayjs.extend(timezone) +dayjs.extend(isBetween) interface ScheduledJob { crawlJobName: string; @@ -40,12 +43,13 @@ interface ScheduleValidRange { } @Component({ - selector: 'app-schedule-overview', - templateUrl: './schedule-overview.component.html', - styleUrls: ['./schedule-overview.component.css'], - providers: [ConfigApiService], - changeDetection: ChangeDetectionStrategy.OnPush, - encapsulation: ViewEncapsulation.Emulated, + selector: 'app-schedule-overview', + templateUrl: './schedule-overview.component.html', + styleUrls: ['./schedule-overview.component.css'], + providers: [ConfigApiService], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.Emulated, + standalone: false }) export class ScheduleOverviewComponent implements OnInit, OnDestroy { private crawlJobs: ConfigObject[]; @@ -189,7 +193,7 @@ export class ScheduleOverviewComponent implements OnInit, OnDestroy { endDate: new Date(this.viewDate.getFullYear(), this.viewDate.getMonth() + 1, 1), utc: true, iterator: true, - tz: momentTimezone.tz.guess(), + tz: dayjs.tz.guess(), }; const prevOptions = { @@ -197,7 +201,7 @@ export class ScheduleOverviewComponent implements OnInit, OnDestroy { endDate: this.viewDate, iterator: true, utc: true, - tz: momentTimezone.tz.guess(), + tz: dayjs.tz.guess(), }; const schedule = []; @@ -312,9 +316,9 @@ export class ScheduleOverviewComponent implements OnInit, OnDestroy { } private isDateInRange(startDate: string, validRange: ScheduleValidRange) { - const eventStart = moment(startDate); - const validFrom = validRange.validFrom ? moment(validRange.validFrom) : moment().startOf('year'); - const validTo = validRange.validTo ? moment(validRange.validTo) : moment().endOf('year'); - return moment(eventStart).isBetween(validFrom, validTo); + const eventStart = dayjs(startDate); + const validFrom = validRange.validFrom ? dayjs(validRange.validFrom) : dayjs().startOf('year'); + const validTo = validRange.validTo ? dayjs(validRange.validTo) : dayjs().endOf('year'); + return dayjs(eventStart).isBetween(validFrom, validTo); } } diff --git a/src/app/components/time/time.component.ts b/src/app/components/time/time.component.ts index ff4c1462d..be57f09ad 100644 --- a/src/app/components/time/time.component.ts +++ b/src/app/components/time/time.component.ts @@ -4,10 +4,11 @@ import {map} from 'rxjs/operators'; @Component({ - selector: 'app-time', - template: `{{time$ | async | date: 'long': 'UTC'}}`, - styleUrls: ['./time.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush + selector: 'app-time', + template: `{{time$ | async | date: 'long': 'UTC'}}`, + styleUrls: ['./time.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class TimeComponent { time$: Observable; diff --git a/src/assets/config.json b/src/assets/config.json index 46dbc8038..d9c316ce9 100644 --- a/src/assets/config.json +++ b/src/assets/config.json @@ -5,7 +5,7 @@ "redirectUri": "https://localhost:4200/", "requestAccessToken": false, "oidc": true, - "responseType": "id_token", + "responseType": "code", "clientId": "veidemann-dashboard", "scope": "openid profile email groups offline_access audience:server:client_id:veidemann-api", "requireHttps": true diff --git a/src/assets/logo/veidemann_horizontal_white.png b/src/assets/logo/veidemann_horizontal_white.png index dbb7b4f860e48194ae3a0ad121e198212f9770c7..a85807b56a892f7584c6732ad9b11455ea07bb48 100644 GIT binary patch literal 15326 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 diff --git a/src/assets/logo/veidemann_logo_inline_black.png b/src/assets/logo/veidemann_logo_inline_black.png index 6b6b66e2a682e8e01e1ca7a7456d0717eeccc52a..313d2d1c8296e7b6eae62e21eae09e5c9b068439 100644 GIT binary patch literal 7979 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 lfR|PXOZX!dM++OcHr&Dr<_sl55e{{g|Dn{NOB literal 62217 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≥GK}A)h9F(zO4E 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 diff --git a/src/main.ts b/src/main.ts index cf93fdd31..d80a61d4d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,7 +2,6 @@ import {enableProdMode} from '@angular/core'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; -import 'hammerjs'; import {AppModule} from './app/app.module'; import {environment} from './environments/environment'; diff --git a/src/modules/commons/commons.module.ts b/src/modules/commons/commons.module.ts index bec6365a0..ae8e2b4ba 100644 --- a/src/modules/commons/commons.module.ts +++ b/src/modules/commons/commons.module.ts @@ -9,7 +9,6 @@ import {ActionDirective, ExtraDirective, FilterDirective, HighlightDirective, Sh import {NgxFilesizeModule} from 'ngx-filesize'; import {DurationFormatPipe} from './pipes/duration-format.pipe'; import {UrlFormatPipe} from './pipes/url-format.pipe'; -import {AbilityModule} from '@casl/angular'; import {KeyboardShortcutsModule} from 'ng-keyboard-shortcuts'; @@ -32,7 +31,6 @@ import {KeyboardShortcutsModule} from 'ng-keyboard-shortcuts'; RouterModule, DragDropModule, NgxFilesizeModule, - AbilityModule, KeyboardShortcutsModule, ], exports: [ @@ -50,7 +48,6 @@ import {KeyboardShortcutsModule} from 'ng-keyboard-shortcuts'; DurationFormatPipe, HighlightDirective, UrlFormatPipe, - AbilityModule, KeyboardShortcutsModule, ], providers: [ diff --git a/src/modules/commons/components/base-list/base-list.ts b/src/modules/commons/components/base-list/base-list.ts index 892161356..6c2ff6553 100644 --- a/src/modules/commons/components/base-list/base-list.ts +++ b/src/modules/commons/components/base-list/base-list.ts @@ -24,7 +24,7 @@ import {ShortcutEventOutput, ShortcutInput} from 'ng-keyboard-shortcuts'; @Directive() // eslint-disable-next-line @angular-eslint/directive-class-suffix -export abstract class BaseListComponent implements OnChanges, AfterViewInit { +export abstract class BaseListComponent implements AfterViewInit { readonly Kind = Kind; length$: BehaviorSubject; @@ -117,16 +117,6 @@ export abstract class BaseListComponent implements OnChanges this.length$ = new BehaviorSubject(0); } - ngOnChanges(changes: SimpleChanges): void { - if (changes.sortDirection && changes.sortActive) { - // ugly hack see https://github.com/angular/components/issues/10242 and https://github.com/angular/components/issues/10524 - const sortHeader = (this.matSort.sortables.get(this.sortActive) as MatSortHeader); - if (sortHeader) { - sortHeader._setAnimationTransitionState({toState: 'active'}); - } - } - } - ngAfterViewInit() { this.shortcuts.push( diff --git a/src/modules/commons/directives/action.directive.ts b/src/modules/commons/directives/action.directive.ts index 6e5483fde..1ca6dd5fe 100644 --- a/src/modules/commons/directives/action.directive.ts +++ b/src/modules/commons/directives/action.directive.ts @@ -1,7 +1,8 @@ import {Directive} from '@angular/core'; @Directive({ - selector: '[appAction]' + selector: '[appAction]', + standalone: false }) export class ActionDirective { } diff --git a/src/modules/commons/directives/extra.directive.ts b/src/modules/commons/directives/extra.directive.ts index 915aefc96..632dfcf16 100644 --- a/src/modules/commons/directives/extra.directive.ts +++ b/src/modules/commons/directives/extra.directive.ts @@ -1,7 +1,8 @@ import {Directive} from '@angular/core'; @Directive({ - selector: '[appExtra]' + selector: '[appExtra]', + standalone: false }) export class ExtraDirective { } diff --git a/src/modules/commons/directives/filter.directive.ts b/src/modules/commons/directives/filter.directive.ts index 98c3e048b..150f87804 100644 --- a/src/modules/commons/directives/filter.directive.ts +++ b/src/modules/commons/directives/filter.directive.ts @@ -1,7 +1,8 @@ import {Directive} from '@angular/core'; @Directive({ - selector: '[appFilter]' + selector: '[appFilter]', + standalone: false }) export class FilterDirective { diff --git a/src/modules/commons/directives/prismjs.directive.ts b/src/modules/commons/directives/prismjs.directive.ts index feb80687f..c76eb9cb2 100644 --- a/src/modules/commons/directives/prismjs.directive.ts +++ b/src/modules/commons/directives/prismjs.directive.ts @@ -7,7 +7,8 @@ import 'prismjs/components/prism-python'; // import 'prismjs/plugins/match-braces/prism-match-braces'; @Directive({ - selector: '[appHighlight]' + selector: '[appHighlight]', + standalone: false }) export class HighlightDirective implements AfterViewChecked { private done = false; diff --git a/src/modules/commons/directives/shortcut.directive.ts b/src/modules/commons/directives/shortcut.directive.ts index a38dc532e..bd31a56f9 100644 --- a/src/modules/commons/directives/shortcut.directive.ts +++ b/src/modules/commons/directives/shortcut.directive.ts @@ -1,7 +1,8 @@ import {Directive} from '@angular/core'; @Directive({ - selector: '[appShortcut]' + selector: '[appShortcut]', + standalone: false }) export class ShortcutDirective { } diff --git a/src/modules/commons/material.module.ts b/src/modules/commons/material.module.ts index 211d3c1ec..76a5777af 100644 --- a/src/modules/commons/material.module.ts +++ b/src/modules/commons/material.module.ts @@ -24,14 +24,15 @@ import {MatSortModule} from '@angular/material/sort'; import {MatTableModule} from '@angular/material/table'; import {MatToolbarModule} from '@angular/material/toolbar'; import {MatTooltipModule} from '@angular/material/tooltip'; -import {MAT_MOMENT_DATE_ADAPTER_OPTIONS, MatMomentDateModule} from '@angular/material-moment-adapter'; import {MatAutocompleteModule} from '@angular/material/autocomplete'; import {MatTreeModule} from '@angular/material/tree'; import {MatTabsModule} from '@angular/material/tabs'; import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; -import {NgxMatDatetimePickerModule, NgxMatTimepickerModule} from '@angular-material-components/datetime-picker'; -import {NgxMatMomentModule} from '@angular-material-components/moment-adapter'; -import {MAT_FORM_FIELD_DEFAULT_OPTIONS} from '@angular/material/form-field'; +import {MatTimepickerModule} from "@angular/material/timepicker"; +import {DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE} from "@angular/material/core"; +import {DayjsDateAdapter, MAT_DAYJS_DATE_ADAPTER_OPTIONS, MAT_DAYJS_DATE_FORMATS} from "../dayjs-date-adapter"; +import {MAT_FORM_FIELD_DEFAULT_OPTIONS} from "@angular/material/form-field"; +import {LocaleService} from "../core/services"; const modules = [ MatTabsModule, @@ -56,7 +57,6 @@ const modules = [ MatPaginatorModule, MatSortModule, MatDatepickerModule, - MatMomentDateModule, MatButtonToggleModule, MatProgressBarModule, MatBadgeModule, @@ -64,17 +64,26 @@ const modules = [ MatAutocompleteModule, MatTreeModule, MatProgressSpinnerModule, - NgxMatDatetimePickerModule, - NgxMatTimepickerModule, - NgxMatMomentModule, + MatTimepickerModule, ]; @NgModule({ imports: modules, exports: modules, providers: [ - {provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS, useValue: 'no-NO'}, - {provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, useValue: {floatLabel: 'auto'}} + { + provide: DateAdapter, + useClass: DayjsDateAdapter, + deps: [MAT_DATE_LOCALE, MAT_DAYJS_DATE_ADAPTER_OPTIONS] + }, + {provide: MAT_DAYJS_DATE_ADAPTER_OPTIONS, useValue: {useUtc: false, strict: true}}, + {provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, useValue: {floatLabel: 'auto'}}, + {provide: MAT_DATE_FORMATS, useValue: MAT_DAYJS_DATE_FORMATS}, + { + provide: MAT_DATE_LOCALE, + useFactory: (localeService: LocaleService) => localeService.getLocale(), + deps: [LocaleService] + }, ] }) export class MaterialModule { diff --git a/src/modules/commons/pipes/duration-format.pipe.spec.ts b/src/modules/commons/pipes/duration-format.pipe.spec.ts deleted file mode 100644 index 810ff005a..000000000 --- a/src/modules/commons/pipes/duration-format.pipe.spec.ts +++ /dev/null @@ -1,28 +0,0 @@ -import {DurationFormatPipe} from './duration-format.pipe'; - -describe('DurationFormatPipe', () => { - // This pipe is a pure, stateless function so no need for BeforeEach - // TODO: Would be better to test the datetime-function. - const pipe = new DurationFormatPipe(); - - it('transforms 10 to 10s', () => { - expect(pipe.transform(10, 's')).toBe('10s'); - }); - - it('transforms 70 to 1min:10s', () => { - expect(pipe.transform(70, 's')).toBe('1min:10s'); - }); - - it('transforms 31536000 s to 1 year', () => { - expect(pipe.transform(31536000, 's')).toBe('365days'); - }); - - it('transforms 100 ms to 100ms', () => { - expect(pipe.transform(100, 'ms')).toBe('100ms'); - }); - - it('transforms 2000 ms to 2s', () => { - expect(pipe.transform(2000, 'ms')).toBe('2s'); - }); - -}); diff --git a/src/modules/commons/pipes/duration-format.pipe.ts b/src/modules/commons/pipes/duration-format.pipe.ts index ee12ddbda..c659b39a1 100644 --- a/src/modules/commons/pipes/duration-format.pipe.ts +++ b/src/modules/commons/pipes/duration-format.pipe.ts @@ -2,7 +2,8 @@ import {Pipe, PipeTransform} from '@angular/core'; import {timeToDuration} from '../../../shared/func'; @Pipe({ - name: 'durationFormat' + name: 'durationFormat', + standalone: false }) export class DurationFormatPipe implements PipeTransform { diff --git a/src/modules/commons/pipes/url-format.pipe.ts b/src/modules/commons/pipes/url-format.pipe.ts index 31b3c12c7..e36acd66d 100644 --- a/src/modules/commons/pipes/url-format.pipe.ts +++ b/src/modules/commons/pipes/url-format.pipe.ts @@ -3,7 +3,8 @@ import {DomSanitizer, SafeHtml} from '@angular/platform-browser'; import {SedPipe} from '../../report/pipe/sed.pipe'; @Pipe({ - name: 'urlFormat' + name: 'urlFormat', + standalone: false }) export class UrlFormatPipe implements PipeTransform { diff --git a/src/modules/config/components/annotation/annotation.component.html b/src/modules/config/components/annotation/annotation.component.html index bf1c04027..0b4076207 100644 --- a/src/modules/config/components/annotation/annotation.component.html +++ b/src/modules/config/components/annotation/annotation.component.html @@ -1,66 +1,68 @@ -
-
- - Annotation - - - - {{group.key + ':' + value}} - cancel - - - - - -
+ +
+
+ + Annotation + + + + {{ group.key + ':' + value }} + cancel + + + + + +
-
- - - note - Edit annotation - - - - - - Can't be empty or contain a colon. - - - - - - Can't be empty. - - - + + + + note + Edit annotation + + + + + + Can't be empty or contain a colon. + + + + + + Can't be empty. + + + - - - - - - -
+ + + + + + +
+ diff --git a/src/modules/config/components/annotation/annotation.component.spec.ts b/src/modules/config/components/annotation/annotation.component.spec.ts index 3ed7c2d07..ad10e56ff 100644 --- a/src/modules/config/components/annotation/annotation.component.spec.ts +++ b/src/modules/config/components/annotation/annotation.component.spec.ts @@ -4,7 +4,6 @@ import {AnnotationComponent} from './annotation.component'; import {UntypedFormBuilder} from '@angular/forms'; import {AuthService} from '../../../core/services/auth'; import {CoreTestingModule} from '../../../core/core.testing.module'; -import {AbilityModule} from '@casl/angular'; describe('AnnotationComponent', () => { let component: AnnotationComponent; @@ -12,7 +11,7 @@ describe('AnnotationComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - imports: [CoreTestingModule.forRoot(), AbilityModule], + imports: [CoreTestingModule.forRoot()], providers: [UntypedFormBuilder, { provide: AuthService, diff --git a/src/modules/config/components/annotation/annotation.component.ts b/src/modules/config/components/annotation/annotation.component.ts index e3b2aedce..e01e932ec 100644 --- a/src/modules/config/components/annotation/annotation.component.ts +++ b/src/modules/config/components/annotation/annotation.component.ts @@ -15,15 +15,18 @@ import {MatChipInputEvent} from '@angular/material/chips'; import {NO_COLON} from '../../../../shared/validation/patterns'; import {BehaviorSubject, Observable} from 'rxjs'; import {AuthService} from '../../../core/services/auth'; +import {AbilityService} from "@casl/angular"; @Component({ - selector: 'app-annotation', - templateUrl: './annotation.component.html', - styleUrls: ['./annotation.component.css'], - providers: [{provide: NG_VALUE_ACCESSOR, useExisting: AnnotationComponent, multi: true}], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-annotation', + templateUrl: './annotation.component.html', + styleUrls: ['./annotation.component.css'], + providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: AnnotationComponent, multi: true }], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class AnnotationComponent implements ControlValueAccessor, OnInit { + readonly ability$: Observable; @Input() removable = true; @@ -49,8 +52,10 @@ export class AnnotationComponent implements ControlValueAccessor, OnInit { constructor(protected fb: UntypedFormBuilder, protected cdr: ChangeDetectorRef, - protected authService: AuthService) { + protected authService: AuthService, + private abilityService: AbilityService) { this.createForm(); + this.ability$ = this.abilityService.ability$; } get canEdit(): boolean { diff --git a/src/modules/config/components/annotation/script-annotation/script-annotation.component.html b/src/modules/config/components/annotation/script-annotation/script-annotation.component.html index 2ac7275f0..404e916ec 100644 --- a/src/modules/config/components/annotation/script-annotation/script-annotation.component.html +++ b/src/modules/config/components/annotation/script-annotation/script-annotation.component.html @@ -1,7 +1,9 @@ -
+ +
{{annotation.key}}:{{annotation.value}}
+
diff --git a/src/modules/config/components/annotation/script-annotation/script-annotation.component.spec.ts b/src/modules/config/components/annotation/script-annotation/script-annotation.component.spec.ts index 0caf72d39..f19d1a94b 100644 --- a/src/modules/config/components/annotation/script-annotation/script-annotation.component.spec.ts +++ b/src/modules/config/components/annotation/script-annotation/script-annotation.component.spec.ts @@ -1,7 +1,6 @@ import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing'; import {UntypedFormBuilder} from '@angular/forms'; -import {AbilityModule} from '@casl/angular'; import {AuthService} from '../../../../core/services/auth'; import {CoreTestingModule} from '../../../../core/core.testing.module'; import {ScriptAnnotationComponent} from './script-annotation.component'; @@ -12,7 +11,7 @@ describe('ScriptAnnotationComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - imports: [CoreTestingModule.forRoot(), AbilityModule], + imports: [CoreTestingModule.forRoot()], providers: [UntypedFormBuilder, { provide: AuthService, diff --git a/src/modules/config/components/annotation/script-annotation/script-annotation.component.ts b/src/modules/config/components/annotation/script-annotation/script-annotation.component.ts index 44af9591e..8ac445993 100644 --- a/src/modules/config/components/annotation/script-annotation/script-annotation.component.ts +++ b/src/modules/config/components/annotation/script-annotation/script-annotation.component.ts @@ -1,16 +1,20 @@ import {ChangeDetectionStrategy, Component, Input, OnInit} from '@angular/core'; import {Annotation} from '../../../../../shared/models/config'; +import {Observable} from "rxjs"; +import {AbilityService} from "@casl/angular"; @Component({ - selector: 'app-script-annotation', - templateUrl: './script-annotation.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-script-annotation', + templateUrl: './script-annotation.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class ScriptAnnotationComponent { - +readonly ability$: Observable; @Input() annotations: Annotation[]; - constructor() { + constructor(abilityService: AbilityService) { + this.ability$ = abilityService.ability$; } } diff --git a/src/modules/config/components/browserconfig/browserconfig-details/browserconfig-details.component.spec.ts b/src/modules/config/components/browserconfig/browserconfig-details/browserconfig-details.component.spec.ts index f31cb0000..5099479a5 100644 --- a/src/modules/config/components/browserconfig/browserconfig-details/browserconfig-details.component.spec.ts +++ b/src/modules/config/components/browserconfig/browserconfig-details/browserconfig-details.component.spec.ts @@ -19,7 +19,6 @@ import { Label, Meta } from '../../../../../shared/models'; -import {AbilityModule} from '@casl/angular'; import {AuthService} from '../../../../core/services'; import {ReactiveFormsModule} from '@angular/forms'; import {MockComponent} from 'ng-mocks'; @@ -122,7 +121,6 @@ describe('BrowserConfigDetailsComponent', () => { TestBed.configureTestingModule({ imports: [ ReactiveFormsModule, - AbilityModule, MaterialModule, RouterTestingModule, CoreTestingModule.forRoot(), diff --git a/src/modules/config/components/browserconfig/browserconfig-details/browserconfig-details.component.ts b/src/modules/config/components/browserconfig/browserconfig-details/browserconfig-details.component.ts index e16cc8c45..9714aa097 100644 --- a/src/modules/config/components/browserconfig/browserconfig-details/browserconfig-details.component.ts +++ b/src/modules/config/components/browserconfig/browserconfig-details/browserconfig-details.component.ts @@ -7,9 +7,10 @@ import {UnitOfTime} from '../../../../../shared/models/duration/unit-time.model' @Component({ - selector: 'app-browserconfig-details', - templateUrl: './browserconfig-details.component.html', - changeDetection: ChangeDetectionStrategy.OnPush + selector: 'app-browserconfig-details', + templateUrl: './browserconfig-details.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class BrowserConfigDetailsComponent implements OnChanges { readonly Kind = Kind; diff --git a/src/modules/config/components/browserconfig/browserconfig-dialog/browserconfig-dialog.component.spec.ts b/src/modules/config/components/browserconfig/browserconfig-dialog/browserconfig-dialog.component.spec.ts index c90f33122..964916ed9 100644 --- a/src/modules/config/components/browserconfig/browserconfig-dialog/browserconfig-dialog.component.spec.ts +++ b/src/modules/config/components/browserconfig/browserconfig-dialog/browserconfig-dialog.component.spec.ts @@ -11,7 +11,6 @@ import {ConfigDialogData} from '../../../func'; import {LabelService} from '../../../services'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; import {of} from 'rxjs'; -import {AbilityModule} from '@casl/angular'; import {CoreTestingModule} from '../../../../core/core.testing.module'; import {AuthService} from '../../../../core/services'; @@ -31,7 +30,6 @@ describe('BrowserConfigDialogComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [CoreTestingModule.forRoot(), - AbilityModule, CommonsModule, NoopAnimationsModule, MatDialogModule, ReactiveFormsModule], diff --git a/src/modules/config/components/browserconfig/browserconfig-dialog/browserconfig-dialog.component.ts b/src/modules/config/components/browserconfig/browserconfig-dialog/browserconfig-dialog.component.ts index 5b634bf46..bee863ac1 100644 --- a/src/modules/config/components/browserconfig/browserconfig-dialog/browserconfig-dialog.component.ts +++ b/src/modules/config/components/browserconfig/browserconfig-dialog/browserconfig-dialog.component.ts @@ -7,9 +7,10 @@ import {ConfigDialogData} from '../../../func'; import {ConfigObject} from '../../../../../shared/models/config'; @Component({ - selector: 'app-browserconfig-dialog', - templateUrl: './browserconfig-dialog.component.html', - styleUrls: ['./browserconfig-dialog.component.css'] + selector: 'app-browserconfig-dialog', + templateUrl: './browserconfig-dialog.component.html', + styleUrls: ['./browserconfig-dialog.component.css'], + standalone: false }) export class BrowserConfigDialogComponent extends BrowserConfigDetailsComponent implements OnInit { diff --git a/src/modules/config/components/browserconfig/browserconfig-multi-dialog/browserconfig-multi-dialog.component.ts b/src/modules/config/components/browserconfig/browserconfig-multi-dialog/browserconfig-multi-dialog.component.ts index 891df58e0..7c896c4a8 100644 --- a/src/modules/config/components/browserconfig/browserconfig-multi-dialog/browserconfig-multi-dialog.component.ts +++ b/src/modules/config/components/browserconfig/browserconfig-multi-dialog/browserconfig-multi-dialog.component.ts @@ -9,10 +9,11 @@ import {NUMBER_OR_EMPTY_STRING} from '../../../../../shared/validation/patterns' import {LabelMultiComponent} from '../../label/label-multi/label-multi.component'; @Component({ - selector: 'app-browserconfig-multi-dialog', - templateUrl: './browserconfig-multi-dialog.component.html', - styleUrls: ['./browserconfig-multi-dialog.component.css'], - changeDetection: ChangeDetectionStrategy.OnPush + selector: 'app-browserconfig-multi-dialog', + templateUrl: './browserconfig-multi-dialog.component.html', + styleUrls: ['./browserconfig-multi-dialog.component.css'], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class BrowserConfigMultiDialogComponent extends BrowserConfigDetailsComponent implements OnInit { diff --git a/src/modules/config/components/browserconfig/browserconfig-preview/browserconfig-preview.component.spec.ts b/src/modules/config/components/browserconfig/browserconfig-preview/browserconfig-preview.component.spec.ts index 8c6cbf699..7f7833696 100644 --- a/src/modules/config/components/browserconfig/browserconfig-preview/browserconfig-preview.component.spec.ts +++ b/src/modules/config/components/browserconfig/browserconfig-preview/browserconfig-preview.component.spec.ts @@ -4,7 +4,6 @@ import {BrowserconfigPreviewComponent} from './browserconfig-preview.component'; import {CommonsModule} from '../../../../commons'; import {ConfigObject, Kind} from '../../../../../shared/models'; import {ShortcutListComponent} from '../../shortcut/shortcut-list/shortcut-list.component'; -import {AbilityModule} from '@casl/angular'; import {CoreTestingModule} from '../../../../core/core.testing.module'; describe('BrowserconfigPreviewComponent', () => { @@ -13,7 +12,7 @@ describe('BrowserconfigPreviewComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - imports: [CommonsModule, CoreTestingModule.forRoot(), AbilityModule], + imports: [CommonsModule, CoreTestingModule.forRoot()], declarations: [BrowserconfigPreviewComponent, ShortcutListComponent], providers: [] }) diff --git a/src/modules/config/components/browserconfig/browserconfig-preview/browserconfig-preview.component.ts b/src/modules/config/components/browserconfig/browserconfig-preview/browserconfig-preview.component.ts index 7c1d6751b..fb497c363 100644 --- a/src/modules/config/components/browserconfig/browserconfig-preview/browserconfig-preview.component.ts +++ b/src/modules/config/components/browserconfig/browserconfig-preview/browserconfig-preview.component.ts @@ -2,9 +2,10 @@ import {Component, Input} from '@angular/core'; import {ConfigObject} from '../../../../../shared/models/config'; @Component({ - selector: 'app-browserconfig-preview', - templateUrl: './browserconfig-preview.component.html', - styleUrls: ['./browserconfig-preview.component.css'] + selector: 'app-browserconfig-preview', + templateUrl: './browserconfig-preview.component.html', + styleUrls: ['./browserconfig-preview.component.css'], + standalone: false }) export class BrowserconfigPreviewComponent { @Input() diff --git a/src/modules/config/components/browserscript/browserscript-details/browserscript-details.component.spec.ts b/src/modules/config/components/browserscript/browserscript-details/browserscript-details.component.spec.ts index 37a8b96e5..3a4ebf98d 100644 --- a/src/modules/config/components/browserscript/browserscript-details/browserscript-details.component.spec.ts +++ b/src/modules/config/components/browserscript/browserscript-details/browserscript-details.component.spec.ts @@ -18,7 +18,6 @@ import {LabelService} from '../../../services'; import {of} from 'rxjs'; import {AuthService} from '../../../../core/services/auth'; import {AnnotationComponent, LabelComponent, MetaComponent} from '../..'; -import {AbilityModule} from '@casl/angular'; import {HarnessLoader} from '@angular/cdk/testing'; import {MatButtonHarness} from '@angular/material/button/testing'; import {MatSelectHarness} from '@angular/material/select/testing'; @@ -66,7 +65,6 @@ describe('BrowserScriptDetailsComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ - AbilityModule, RouterTestingModule, CommonsModule, NoopAnimationsModule, diff --git a/src/modules/config/components/browserscript/browserscript-details/browserscript-details.component.ts b/src/modules/config/components/browserscript/browserscript-details/browserscript-details.component.ts index 46d949a22..fb47221d5 100644 --- a/src/modules/config/components/browserscript/browserscript-details/browserscript-details.component.ts +++ b/src/modules/config/components/browserscript/browserscript-details/browserscript-details.component.ts @@ -16,10 +16,11 @@ import {MatChipInputEvent} from '@angular/material/chips'; import {MonacoEditorConstructionOptions, MonacoEditorLoaderService, MonacoStandaloneCodeEditor} from '@materia-ui/ngx-monaco-editor'; @Component({ - changeDetection: ChangeDetectionStrategy.OnPush, - selector: 'app-browserscript-details', - templateUrl: './browserscript-details.component.html', - styleUrls: ['./browserscript-details.component.scss'] + changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-browserscript-details', + templateUrl: './browserscript-details.component.html', + styleUrls: ['./browserscript-details.component.scss'], + standalone: false }) export class BrowserScriptDetailsComponent implements OnChanges { readonly BrowserScriptType = BrowserScriptType; diff --git a/src/modules/config/components/browserscript/browserscript-dialog/browserscript-dialog.component.spec.ts b/src/modules/config/components/browserscript/browserscript-dialog/browserscript-dialog.component.spec.ts index 0e5121584..ad720a283 100644 --- a/src/modules/config/components/browserscript/browserscript-dialog/browserscript-dialog.component.spec.ts +++ b/src/modules/config/components/browserscript/browserscript-dialog/browserscript-dialog.component.spec.ts @@ -11,7 +11,6 @@ import {LabelService} from '../../../services'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; import {of} from 'rxjs'; import {AnnotationComponent} from '../../annotation/annotation.component'; -import {AbilityModule} from '@casl/angular'; import {CoreTestingModule} from '../../../../core/core.testing.module'; import {AuthService} from '../../../../core/services'; import {MonacoEditorComponent} from '@materia-ui/ngx-monaco-editor'; @@ -29,7 +28,6 @@ describe('BrowserScriptDialogComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ - AbilityModule, CoreTestingModule.forRoot(), CommonsModule, NoopAnimationsModule diff --git a/src/modules/config/components/browserscript/browserscript-dialog/browserscript-dialog.component.ts b/src/modules/config/components/browserscript/browserscript-dialog/browserscript-dialog.component.ts index 4db8b8910..4b72a85e5 100644 --- a/src/modules/config/components/browserscript/browserscript-dialog/browserscript-dialog.component.ts +++ b/src/modules/config/components/browserscript/browserscript-dialog/browserscript-dialog.component.ts @@ -8,9 +8,10 @@ import {ConfigObject} from '../../../../../shared/models/config'; import {MonacoEditorLoaderService} from '@materia-ui/ngx-monaco-editor'; @Component({ - selector: 'app-browserscript-dialog', - templateUrl: './browserscript-dialog.component.html', - styleUrls: ['./browserscript-dialog.component.css'] + selector: 'app-browserscript-dialog', + templateUrl: './browserscript-dialog.component.html', + styleUrls: ['./browserscript-dialog.component.css'], + standalone: false }) export class BrowserScriptDialogComponent extends BrowserScriptDetailsComponent implements OnInit { diff --git a/src/modules/config/components/browserscript/browserscript-multi-dialog/browserscript-multi-dialog.component.ts b/src/modules/config/components/browserscript/browserscript-multi-dialog/browserscript-multi-dialog.component.ts index 6863c8729..dcee09cd2 100644 --- a/src/modules/config/components/browserscript/browserscript-multi-dialog/browserscript-multi-dialog.component.ts +++ b/src/modules/config/components/browserscript/browserscript-multi-dialog/browserscript-multi-dialog.component.ts @@ -9,9 +9,10 @@ import {LabelMultiComponent} from '../../label/label-multi/label-multi.component import {MonacoEditorLoaderService} from '@materia-ui/ngx-monaco-editor'; @Component({ - selector: 'app-browserscript-multi-dialog', - templateUrl: './browserscript-multi-dialog.component.html', - styleUrls: ['./browserscript-multi-dialog.component.css'] + selector: 'app-browserscript-multi-dialog', + templateUrl: './browserscript-multi-dialog.component.html', + styleUrls: ['./browserscript-multi-dialog.component.css'], + standalone: false }) export class BrowserScriptMultiDialogComponent extends BrowserScriptDetailsComponent implements OnInit { diff --git a/src/modules/config/components/browserscript/browserscript-preview/browserscript-preview.component.ts b/src/modules/config/components/browserscript/browserscript-preview/browserscript-preview.component.ts index 349057f33..389e40efe 100644 --- a/src/modules/config/components/browserscript/browserscript-preview/browserscript-preview.component.ts +++ b/src/modules/config/components/browserscript/browserscript-preview/browserscript-preview.component.ts @@ -3,9 +3,10 @@ import {BrowserScriptType, ConfigObject} from '../../../../../shared/models/conf import {MonacoEditorConstructionOptions, MonacoStandaloneCodeEditor} from '@materia-ui/ngx-monaco-editor'; @Component({ - selector: 'app-browserscript-preview', - templateUrl: './browserscript-preview.component.html', - styleUrls: ['./browserscript-preview.component.css'] + selector: 'app-browserscript-preview', + templateUrl: './browserscript-preview.component.html', + styleUrls: ['./browserscript-preview.component.css'], + standalone: false }) export class BrowserscriptPreviewComponent implements OnInit { readonly BrowserScriptType = BrowserScriptType; diff --git a/src/modules/config/components/collection-meta/collection-meta.component.spec.ts b/src/modules/config/components/collection-meta/collection-meta.component.spec.ts index 4edf2cdf7..f2b7fb108 100644 --- a/src/modules/config/components/collection-meta/collection-meta.component.spec.ts +++ b/src/modules/config/components/collection-meta/collection-meta.component.spec.ts @@ -15,7 +15,6 @@ import {AnnotationComponent} from '../annotation/annotation.component'; import {LabelService} from '../../services'; import {of} from 'rxjs'; import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; -import {AbilityModule} from '@casl/angular'; import {AuthService} from '../../../core/services'; import {expect} from '@angular/flex-layout/_private-utils/testing'; import {VALID_COLLECTION_NAME} from '../../../../shared/validation/patterns'; @@ -32,7 +31,6 @@ describe('CollectionMetaComponent', () => { TestBed.configureTestingModule({ imports: [ ReactiveFormsModule, - AbilityModule, MaterialModule, RouterTestingModule, CommonsModule, diff --git a/src/modules/config/components/collection-meta/collection-meta.component.ts b/src/modules/config/components/collection-meta/collection-meta.component.ts index 7f23952cc..867b88be8 100644 --- a/src/modules/config/components/collection-meta/collection-meta.component.ts +++ b/src/modules/config/components/collection-meta/collection-meta.component.ts @@ -8,13 +8,14 @@ import {Observable, of} from 'rxjs'; import {first, map, tap} from 'rxjs/operators'; @Component({ - selector: 'app-collection-meta', - templateUrl: './collection-meta.component.html', - styleUrls: ['./collection-meta.component.css'], - providers: [ - {provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => CollectionMetaComponent), multi: true}, - ], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-collection-meta', + templateUrl: './collection-meta.component.html', + styleUrls: ['./collection-meta.component.css'], + providers: [ + { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => CollectionMetaComponent), multi: true }, + ], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class CollectionMetaComponent extends MetaComponent { diff --git a/src/modules/config/components/collection/collection-details/collection-details.component.spec.ts b/src/modules/config/components/collection/collection-details/collection-details.component.spec.ts index f95d8ba47..3c51734d2 100644 --- a/src/modules/config/components/collection/collection-details/collection-details.component.spec.ts +++ b/src/modules/config/components/collection/collection-details/collection-details.component.spec.ts @@ -19,7 +19,6 @@ import { RotationPolicy, subCollectionTypes } from '../../../../../shared/models'; -import {AbilityModule} from '@casl/angular'; import {AuthService} from '../../../../core/services'; import {HarnessLoader} from '@angular/cdk/testing'; import {MatButtonHarness} from '@angular/material/button/testing'; @@ -78,7 +77,6 @@ describe('CollectionDetailsComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ - AbilityModule, RouterTestingModule, CommonsModule, NoopAnimationsModule, diff --git a/src/modules/config/components/collection/collection-details/collection-details.component.ts b/src/modules/config/components/collection/collection-details/collection-details.component.ts index 92af373a3..ba2f28975 100644 --- a/src/modules/config/components/collection/collection-details/collection-details.component.ts +++ b/src/modules/config/components/collection/collection-details/collection-details.component.ts @@ -14,10 +14,11 @@ import {VALID_COLLECTION_NAME} from '../../../../../shared/validation/patterns'; @Component({ - selector: 'app-collection-details', - templateUrl: './collection-details.component.html', - styleUrls: ['./collection-details.component.css'], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-collection-details', + templateUrl: './collection-details.component.html', + styleUrls: ['./collection-details.component.css'], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class CollectionDetailsComponent implements OnChanges { diff --git a/src/modules/config/components/collection/collection-dialog/collection-dialog.component.spec.ts b/src/modules/config/components/collection/collection-dialog/collection-dialog.component.spec.ts index 87d11acf1..4d43d37e7 100644 --- a/src/modules/config/components/collection/collection-dialog/collection-dialog.component.spec.ts +++ b/src/modules/config/components/collection/collection-dialog/collection-dialog.component.spec.ts @@ -12,7 +12,6 @@ import {AnnotationComponent, FilesizeInputComponent, LabelComponent, MetaCompone import {LabelService} from '../../../services'; import {of} from 'rxjs'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; -import {AbilityModule} from '@casl/angular'; import {AuthService} from '../../../../core/services'; import {CollectionMetaComponent} from '../../collection-meta/collection-meta.component'; @@ -31,7 +30,7 @@ describe('CollectionDialogComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - imports: [CommonsModule, CoreTestingModule.forRoot(), MatDialogModule, MatIconModule, NoopAnimationsModule, AbilityModule], + imports: [CommonsModule, CoreTestingModule.forRoot(), MatDialogModule, MatIconModule, NoopAnimationsModule], declarations: [CollectionDialogComponent, CollectionMetaComponent, FilesizeInputComponent, LabelComponent, AnnotationComponent], providers: [UntypedFormBuilder, { diff --git a/src/modules/config/components/collection/collection-dialog/collection-dialog.component.ts b/src/modules/config/components/collection/collection-dialog/collection-dialog.component.ts index 02d175565..749876912 100644 --- a/src/modules/config/components/collection/collection-dialog/collection-dialog.component.ts +++ b/src/modules/config/components/collection/collection-dialog/collection-dialog.component.ts @@ -7,10 +7,11 @@ import {ConfigDialogData} from '../../../func'; import {ConfigObject} from '../../../../../shared/models/config'; @Component({ - selector: 'app-collection-dialog', - templateUrl: './collection-dialog.component.html', - styleUrls: ['./collection-dialog.component.css'], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-collection-dialog', + templateUrl: './collection-dialog.component.html', + styleUrls: ['./collection-dialog.component.css'], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class CollectionDialogComponent extends CollectionDetailsComponent implements OnInit { diff --git a/src/modules/config/components/collection/collection-preview/collection-preview.component.ts b/src/modules/config/components/collection/collection-preview/collection-preview.component.ts index 155ebad2a..30d97b7a5 100644 --- a/src/modules/config/components/collection/collection-preview/collection-preview.component.ts +++ b/src/modules/config/components/collection/collection-preview/collection-preview.component.ts @@ -2,9 +2,10 @@ import {Component, Input, OnInit} from '@angular/core'; import {ConfigObject, RotationPolicy, SubCollectionType} from '../../../../../shared/models/config'; @Component({ - selector: 'app-collection-preview', - templateUrl: './collection-preview.component.html', - styleUrls: ['./collection-preview.component.css'] + selector: 'app-collection-preview', + templateUrl: './collection-preview.component.html', + styleUrls: ['./collection-preview.component.css'], + standalone: false }) export class CollectionPreviewComponent { readonly RotationPolicy = RotationPolicy; diff --git a/src/modules/config/components/config-list/config-list.component.ts b/src/modules/config/components/config-list/config-list.component.ts index 034581d0f..bd21bbb51 100644 --- a/src/modules/config/components/config-list/config-list.component.ts +++ b/src/modules/config/components/config-list/config-list.component.ts @@ -5,26 +5,27 @@ import {BASE_LIST} from '../../../../shared/directives'; import {animate, state, style, transition, trigger} from '@angular/animations'; @Component({ - selector: 'app-config-list', - templateUrl: '../../../commons/components/base-list/base-list.html', - styleUrls: [ - '../../../commons/components/base-list/base-list.scss', - '../../../commons/components/base-list/base-list-odd-preview.scss', - ], - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - { - provide: BASE_LIST, - useExisting: forwardRef(() => ConfigListComponent) - } - ], - animations: [ - trigger('detailExpand', [ - state('collapsed', style({height: '0px', minHeight: '0', opacity: 0})), - state('expanded', style({height: '*', opacity: 1})), - transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')), - ]), - ], + selector: 'app-config-list', + templateUrl: '../../../commons/components/base-list/base-list.html', + styleUrls: [ + '../../../commons/components/base-list/base-list.scss', + '../../../commons/components/base-list/base-list-odd-preview.scss', + ], + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [ + { + provide: BASE_LIST, + useExisting: forwardRef(() => ConfigListComponent) + } + ], + animations: [ + trigger('detailExpand', [ + state('collapsed', style({ height: '0px', minHeight: '0', opacity: 0 })), + state('expanded', style({ height: '*', opacity: 1 })), + transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')), + ]), + ], + standalone: false }) export class ConfigListComponent extends BaseListComponent { diff --git a/src/modules/config/components/config-query/config-query.component.ts b/src/modules/config/components/config-query/config-query.component.ts index 7ea423457..9625b825d 100644 --- a/src/modules/config/components/config-query/config-query.component.ts +++ b/src/modules/config/components/config-query/config-query.component.ts @@ -17,10 +17,11 @@ import {ShortcutEventOutput, ShortcutInput} from 'ng-keyboard-shortcuts'; @Component({ - selector: 'app-config-query', - styleUrls: ['config-query.component.scss'], - templateUrl: './config-query.component.html', - changeDetection: ChangeDetectionStrategy.OnPush + selector: 'app-config-query', + styleUrls: ['config-query.component.scss'], + templateUrl: './config-query.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class ConfigQueryComponent extends QueryComponent implements OnChanges, AfterViewInit { readonly Kind = Kind; diff --git a/src/modules/config/components/crawl-execution-status/crawl-execution-status.component.ts b/src/modules/config/components/crawl-execution-status/crawl-execution-status.component.ts index 4589dfb66..e5f409f37 100644 --- a/src/modules/config/components/crawl-execution-status/crawl-execution-status.component.ts +++ b/src/modules/config/components/crawl-execution-status/crawl-execution-status.component.ts @@ -2,11 +2,11 @@ import {ChangeDetectionStrategy, Component, Input} from '@angular/core'; import {CrawlExecutionState, CrawlExecutionStatus} from '../../../../shared/models/report'; @Component({ - selector: 'app-config-crawl-execution-status', - templateUrl: './crawl-execution-status.component.html', - styleUrls: ['./crawl-execution-status.component.css'], - changeDetection: ChangeDetectionStrategy.OnPush - + selector: 'app-config-crawl-execution-status', + templateUrl: './crawl-execution-status.component.html', + styleUrls: ['./crawl-execution-status.component.css'], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class CrawlExecutionStatusComponent { readonly CrawlExecutionState = CrawlExecutionState; diff --git a/src/modules/config/components/crawlconfig/crawlconfig-details/crawlconfig-details.component.spec.ts b/src/modules/config/components/crawlconfig/crawlconfig-details/crawlconfig-details.component.spec.ts index a0123ac22..11de56463 100644 --- a/src/modules/config/components/crawlconfig/crawlconfig-details/crawlconfig-details.component.spec.ts +++ b/src/modules/config/components/crawlconfig/crawlconfig-details/crawlconfig-details.component.spec.ts @@ -9,7 +9,6 @@ import {LabelComponent} from '../../label/label.component'; import {LabelService} from '../../../services'; import {of} from 'rxjs'; import {AnnotationComponent} from '../../annotation/annotation.component'; -import {AbilityModule} from '@casl/angular'; import {AuthService} from '../../../../core/services'; import {HarnessLoader} from '@angular/cdk/testing'; import {ExtraConfig} from '../../../../../shared/models/config/crawlconfig.model'; @@ -68,7 +67,6 @@ describe('CrawlConfigDetailsComponent', () => { TestBed.configureTestingModule({ imports: [ ReactiveFormsModule, - AbilityModule, MaterialModule, RouterTestingModule, CoreTestingModule.forRoot(), diff --git a/src/modules/config/components/crawlconfig/crawlconfig-details/crawlconfig-details.component.ts b/src/modules/config/components/crawlconfig/crawlconfig-details/crawlconfig-details.component.ts index 6c4857586..711129518 100644 --- a/src/modules/config/components/crawlconfig/crawlconfig-details/crawlconfig-details.component.ts +++ b/src/modules/config/components/crawlconfig/crawlconfig-details/crawlconfig-details.component.ts @@ -5,9 +5,10 @@ import {ConfigObject, ConfigRef, CrawlConfig, Kind, Meta} from '../../../../../s import {AuthService} from '../../../../core/services/auth'; @Component({ - selector: 'app-crawlconfig-details', - templateUrl: './crawlconfig-details.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-crawlconfig-details', + templateUrl: './crawlconfig-details.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class CrawlConfigDetailsComponent implements OnChanges { readonly Kind = Kind; diff --git a/src/modules/config/components/crawlconfig/crawlconfig-dialog/crawlconfig-dialog.component.spec.ts b/src/modules/config/components/crawlconfig/crawlconfig-dialog/crawlconfig-dialog.component.spec.ts index 226572f1b..b536f7982 100644 --- a/src/modules/config/components/crawlconfig/crawlconfig-dialog/crawlconfig-dialog.component.spec.ts +++ b/src/modules/config/components/crawlconfig/crawlconfig-dialog/crawlconfig-dialog.component.spec.ts @@ -12,7 +12,6 @@ import {LabelService} from '../../../services'; import {of} from 'rxjs'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; import {AnnotationComponent} from '../../annotation/annotation.component'; -import {AbilityModule} from '@casl/angular'; import {AuthService} from '../../../../core/services'; describe('CrawlConfigDialogComponent', () => { @@ -26,7 +25,7 @@ describe('CrawlConfigDialogComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - imports: [AbilityModule, CoreTestingModule.forRoot(), CommonsModule, NoopAnimationsModule], + imports: [CoreTestingModule.forRoot(), CommonsModule, NoopAnimationsModule], declarations: [CrawlConfigDialogComponent, MetaComponent, LabelComponent, AnnotationComponent], providers: [ { diff --git a/src/modules/config/components/crawlconfig/crawlconfig-dialog/crawlconfig-dialog.component.ts b/src/modules/config/components/crawlconfig/crawlconfig-dialog/crawlconfig-dialog.component.ts index 9e3b5a158..4cb34e78f 100644 --- a/src/modules/config/components/crawlconfig/crawlconfig-dialog/crawlconfig-dialog.component.ts +++ b/src/modules/config/components/crawlconfig/crawlconfig-dialog/crawlconfig-dialog.component.ts @@ -7,9 +7,10 @@ import {ConfigDialogData} from '../../../func'; import {ConfigObject} from '../../../../../shared/models/config'; @Component({ - selector: 'app-crawlconfig-dialog', - templateUrl: './crawlconfig-dialog.component.html', - styleUrls: ['./crawlconfig-dialog.component.css'] + selector: 'app-crawlconfig-dialog', + templateUrl: './crawlconfig-dialog.component.html', + styleUrls: ['./crawlconfig-dialog.component.css'], + standalone: false }) export class CrawlConfigDialogComponent extends CrawlConfigDetailsComponent implements OnInit { diff --git a/src/modules/config/components/crawlconfig/crawlconfig-multi-dialog/crawlconfig-multi-dialog.component.ts b/src/modules/config/components/crawlconfig/crawlconfig-multi-dialog/crawlconfig-multi-dialog.component.ts index fd8d7dac2..5b8087d04 100644 --- a/src/modules/config/components/crawlconfig/crawlconfig-multi-dialog/crawlconfig-multi-dialog.component.ts +++ b/src/modules/config/components/crawlconfig/crawlconfig-multi-dialog/crawlconfig-multi-dialog.component.ts @@ -9,9 +9,10 @@ import {NUMBER_OR_EMPTY_STRING} from '../../../../../shared/validation/patterns' import {LabelMultiComponent} from '../../label/label-multi/label-multi.component'; @Component({ - selector: 'app-crawlconfig-multi-dialog', - templateUrl: './crawlconfig-multi-dialog.component.html', - styleUrls: ['./crawlconfig-multi-dialog.component.css'] + selector: 'app-crawlconfig-multi-dialog', + templateUrl: './crawlconfig-multi-dialog.component.html', + styleUrls: ['./crawlconfig-multi-dialog.component.css'], + standalone: false }) export class CrawlConfigMultiDialogComponent extends CrawlConfigDetailsComponent implements OnInit { diff --git a/src/modules/config/components/crawlconfig/crawlconfig-preview/crawlconfig-preview.component.ts b/src/modules/config/components/crawlconfig/crawlconfig-preview/crawlconfig-preview.component.ts index 26519e3bd..6b48885ac 100644 --- a/src/modules/config/components/crawlconfig/crawlconfig-preview/crawlconfig-preview.component.ts +++ b/src/modules/config/components/crawlconfig/crawlconfig-preview/crawlconfig-preview.component.ts @@ -2,9 +2,10 @@ import {Component, Input, OnInit} from '@angular/core'; import {ConfigObject} from '../../../../../shared/models/config'; @Component({ - selector: 'app-crawlconfig-preview', - templateUrl: './crawlconfig-preview.component.html', - styleUrls: ['./crawlconfig-preview.component.css'] + selector: 'app-crawlconfig-preview', + templateUrl: './crawlconfig-preview.component.html', + styleUrls: ['./crawlconfig-preview.component.css'], + standalone: false }) export class CrawlconfigPreviewComponent { @Input() diff --git a/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-details/crawlhostgroupconfig-details.component.spec.ts b/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-details/crawlhostgroupconfig-details.component.spec.ts index a65cd5716..8b6121498 100644 --- a/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-details/crawlhostgroupconfig-details.component.spec.ts +++ b/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-details/crawlhostgroupconfig-details.component.spec.ts @@ -9,7 +9,6 @@ import {CoreTestingModule} from '../../../../core/core.testing.module'; import {LabelService} from '../../../services'; import {of} from 'rxjs'; import {AnnotationComponent, DurationPickerComponent, LabelComponent, MetaComponent} from '../..'; -import {AbilityModule} from '@casl/angular'; import {AuthService} from '../../../../core/services'; import {HarnessLoader} from '@angular/cdk/testing'; import {MatButtonHarness} from '@angular/material/button/testing'; @@ -65,7 +64,6 @@ describe('CrawlHostGroupConfigDetailsComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ - AbilityModule, CommonsModule, RouterTestingModule, NoopAnimationsModule, diff --git a/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-details/crawlhostgroupconfig-details.component.ts b/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-details/crawlhostgroupconfig-details.component.ts index 1b710b0ce..b99fd11d7 100644 --- a/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-details/crawlhostgroupconfig-details.component.ts +++ b/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-details/crawlhostgroupconfig-details.component.ts @@ -12,10 +12,11 @@ import { import {UnitOfTime} from 'src/shared/models/duration/unit-time.model'; @Component({ - selector: 'app-crawlhostgroupconfig-details', - templateUrl: './crawlhostgroupconfig-details.component.html', - styleUrls: ['./crawlhostgroupconfig-details.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-crawlhostgroupconfig-details', + templateUrl: './crawlhostgroupconfig-details.component.html', + styleUrls: ['./crawlhostgroupconfig-details.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class CrawlHostGroupConfigDetailsComponent implements OnChanges { readonly UnitOfTime = UnitOfTime; diff --git a/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-dialog/crawlhostgroupconfig-dialog.component.spec.ts b/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-dialog/crawlhostgroupconfig-dialog.component.spec.ts index 293188008..6ab27d2ca 100644 --- a/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-dialog/crawlhostgroupconfig-dialog.component.spec.ts +++ b/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-dialog/crawlhostgroupconfig-dialog.component.spec.ts @@ -12,7 +12,6 @@ import {LabelService} from '../../../services'; import {of} from 'rxjs'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; import {AnnotationComponent} from '../../annotation/annotation.component'; -import {AbilityModule} from '@casl/angular'; import {AuthService} from '../../../../core/services'; import {DurationPickerComponent} from '../../durationpicker/duration-picker'; @@ -27,7 +26,7 @@ describe('CrawlHostGroupConfigDialogComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - imports: [CoreTestingModule.forRoot(), AbilityModule, CommonsModule, NoopAnimationsModule], + imports: [CoreTestingModule.forRoot(), CommonsModule, NoopAnimationsModule], providers: [ { provide: LabelService, diff --git a/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-dialog/crawlhostgroupconfig-dialog.component.ts b/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-dialog/crawlhostgroupconfig-dialog.component.ts index 8438cbac5..a0ba3a4a9 100644 --- a/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-dialog/crawlhostgroupconfig-dialog.component.ts +++ b/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-dialog/crawlhostgroupconfig-dialog.component.ts @@ -7,9 +7,10 @@ import {ConfigObject} from '../../../../../shared/models/config'; import {CrawlHostGroupConfigDetailsComponent} from '..'; @Component({ - selector: 'app-crawlhostgroupconfig-dialog', - templateUrl: './crawlhostgroupconfig-dialog.component.html', - styleUrls: ['./crawlhostgroupconfig-dialog.component.css'] + selector: 'app-crawlhostgroupconfig-dialog', + templateUrl: './crawlhostgroupconfig-dialog.component.html', + styleUrls: ['./crawlhostgroupconfig-dialog.component.css'], + standalone: false }) export class CrawlHostGroupConfigDialogComponent extends CrawlHostGroupConfigDetailsComponent implements OnInit { diff --git a/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-multi-dialog/crawlhostgroupconfig-multi-dialog.component.spec.ts b/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-multi-dialog/crawlhostgroupconfig-multi-dialog.component.spec.ts index ae523f759..757b54c09 100644 --- a/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-multi-dialog/crawlhostgroupconfig-multi-dialog.component.spec.ts +++ b/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-multi-dialog/crawlhostgroupconfig-multi-dialog.component.spec.ts @@ -12,7 +12,6 @@ import {AuthService} from '../../../../core/services'; import {DurationPickerComponent} from '../../durationpicker/duration-picker'; import {CommonsModule} from '../../../../commons'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; -import {AbilityModule} from '@casl/angular'; import {of} from 'rxjs'; describe('CrawlHostGroupConfigMultiDialogComponent', () => { @@ -26,7 +25,7 @@ describe('CrawlHostGroupConfigMultiDialogComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - imports: [CoreTestingModule.forRoot(), AbilityModule, CommonsModule, NoopAnimationsModule], + imports: [CoreTestingModule.forRoot(), CommonsModule, NoopAnimationsModule], providers: [UntypedFormBuilder, { provide: LabelService, diff --git a/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-multi-dialog/crawlhostgroupconfig-multi-dialog.component.ts b/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-multi-dialog/crawlhostgroupconfig-multi-dialog.component.ts index e3ae04691..fedf241db 100644 --- a/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-multi-dialog/crawlhostgroupconfig-multi-dialog.component.ts +++ b/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-multi-dialog/crawlhostgroupconfig-multi-dialog.component.ts @@ -9,9 +9,10 @@ import {LabelMultiComponent} from '../../label/label-multi/label-multi.component import {ANY_DECIMAL_NUMBER_OR_EMPTY_STRING, NUMBER_OR_EMPTY_STRING} from '../../../../../shared/validation/patterns'; @Component({ - selector: 'app-crawlhostgroupconfig-multi-dialog', - templateUrl: './crawlhostgroupconfig-multi-dialog.component.html', - styleUrls: ['./crawlhostgroupconfig-multi-dialog.component.css'] + selector: 'app-crawlhostgroupconfig-multi-dialog', + templateUrl: './crawlhostgroupconfig-multi-dialog.component.html', + styleUrls: ['./crawlhostgroupconfig-multi-dialog.component.css'], + standalone: false }) export class CrawlHostGroupConfigMultiDialogComponent extends CrawlHostGroupConfigDetailsComponent implements OnInit { diff --git a/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-preview/crawlhostgroupconfig-preview.component.ts b/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-preview/crawlhostgroupconfig-preview.component.ts index f99b7dee6..bda0936fe 100644 --- a/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-preview/crawlhostgroupconfig-preview.component.ts +++ b/src/modules/config/components/crawlhostgroupconfig/crawlhostgroupconfig-preview/crawlhostgroupconfig-preview.component.ts @@ -2,9 +2,10 @@ import {Component, Input} from '@angular/core'; import {ConfigObject} from '../../../../../shared/models/config'; @Component({ - selector: 'app-crawlhostgroupconfig-preview', - templateUrl: './crawlhostgroupconfig-preview.component.html', - styleUrls: ['./crawlhostgroupconfig-preview.component.css'] + selector: 'app-crawlhostgroupconfig-preview', + templateUrl: './crawlhostgroupconfig-preview.component.html', + styleUrls: ['./crawlhostgroupconfig-preview.component.css'], + standalone: false }) export class CrawlhostgroupconfigPreviewComponent { @Input() diff --git a/src/modules/config/components/crawljobs/crawljob-details/crawl-job-details.component.spec.ts b/src/modules/config/components/crawljobs/crawljob-details/crawl-job-details.component.spec.ts index b2001c315..d0442c582 100644 --- a/src/modules/config/components/crawljobs/crawljob-details/crawl-job-details.component.spec.ts +++ b/src/modules/config/components/crawljobs/crawljob-details/crawl-job-details.component.spec.ts @@ -27,7 +27,6 @@ import { Label, Meta } from '../../../../../shared/models'; -import {AbilityModule} from '@casl/angular'; import {AuthService} from '../../../../core/services'; import {HarnessLoader} from '@angular/cdk/testing'; import {MatButtonHarness} from '@angular/material/button/testing'; @@ -158,7 +157,6 @@ describe('CrawljobDetailsComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ - AbilityModule, CommonsModule, RouterTestingModule, NoopAnimationsModule, diff --git a/src/modules/config/components/crawljobs/crawljob-details/crawl-job-details.component.ts b/src/modules/config/components/crawljobs/crawljob-details/crawl-job-details.component.ts index 4f5a4fc82..c6401a606 100644 --- a/src/modules/config/components/crawljobs/crawljob-details/crawl-job-details.component.ts +++ b/src/modules/config/components/crawljobs/crawljob-details/crawl-job-details.component.ts @@ -7,9 +7,10 @@ import {UnitOfTime} from '../../../../../shared/models/duration/unit-time.model' @Component({ - selector: 'app-crawljob-details', - templateUrl: './crawl-job-details.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-crawljob-details', + templateUrl: './crawl-job-details.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class CrawlJobDetailsComponent implements OnChanges { readonly Kind = Kind; diff --git a/src/modules/config/components/crawljobs/crawljob-dialog/crawljob-dialog.component.spec.ts b/src/modules/config/components/crawljobs/crawljob-dialog/crawljob-dialog.component.spec.ts index cce4e5593..cdc11c4af 100644 --- a/src/modules/config/components/crawljobs/crawljob-dialog/crawljob-dialog.component.spec.ts +++ b/src/modules/config/components/crawljobs/crawljob-dialog/crawljob-dialog.component.spec.ts @@ -15,7 +15,6 @@ import { } from '../..'; import {CommonsModule} from '../../../../commons'; import {LabelService} from '../../../services'; -import {AbilityModule} from '@casl/angular'; import {of} from 'rxjs'; import {AuthService} from '../../../../core/services'; @@ -30,7 +29,7 @@ describe('CrawlJobDialogComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - imports: [AbilityModule, CommonsModule, NoopAnimationsModule, CoreTestingModule.forRoot()], + imports: [CommonsModule, NoopAnimationsModule, CoreTestingModule.forRoot()], declarations: [ MetaComponent, CrawlJobDialogComponent, diff --git a/src/modules/config/components/crawljobs/crawljob-dialog/crawljob-dialog.component.ts b/src/modules/config/components/crawljobs/crawljob-dialog/crawljob-dialog.component.ts index e264b898f..87d7978ac 100644 --- a/src/modules/config/components/crawljobs/crawljob-dialog/crawljob-dialog.component.ts +++ b/src/modules/config/components/crawljobs/crawljob-dialog/crawljob-dialog.component.ts @@ -7,9 +7,10 @@ import {ConfigDialogData} from '../../../func'; import {BrowserScriptType, ConfigObject} from '../../../../../shared/models/config'; @Component({ - selector: 'app-crawljob-dialog', - templateUrl: './crawljob-dialog.component.html', - styleUrls: ['./crawljob-dialog.component.css'] + selector: 'app-crawljob-dialog', + templateUrl: './crawljob-dialog.component.html', + styleUrls: ['./crawljob-dialog.component.css'], + standalone: false }) export class CrawlJobDialogComponent extends CrawlJobDetailsComponent implements OnInit { diff --git a/src/modules/config/components/crawljobs/crawljob-multi-dialog/crawljobs-multi-dialog.component.ts b/src/modules/config/components/crawljobs/crawljob-multi-dialog/crawljobs-multi-dialog.component.ts index 7fd7b64c0..6ee1b7c8a 100644 --- a/src/modules/config/components/crawljobs/crawljob-multi-dialog/crawljobs-multi-dialog.component.ts +++ b/src/modules/config/components/crawljobs/crawljob-multi-dialog/crawljobs-multi-dialog.component.ts @@ -9,9 +9,10 @@ import {NUMBER_OR_EMPTY_STRING} from '../../../../../shared/validation/patterns' import {LabelMultiComponent} from '../../label/label-multi/label-multi.component'; @Component({ - selector: 'app-crawljobs-multi-dialog', - templateUrl: './crawljobs-multi-dialog.component.html', - styleUrls: ['./crawljobs-multi-dialog.component.css'] + selector: 'app-crawljobs-multi-dialog', + templateUrl: './crawljobs-multi-dialog.component.html', + styleUrls: ['./crawljobs-multi-dialog.component.css'], + standalone: false }) export class CrawlJobMultiDialogComponent extends CrawlJobDetailsComponent implements OnInit { diff --git a/src/modules/config/components/crawljobs/crawljob-preview/crawljob-preview.component.ts b/src/modules/config/components/crawljobs/crawljob-preview/crawljob-preview.component.ts index a0c333965..ad8264778 100644 --- a/src/modules/config/components/crawljobs/crawljob-preview/crawljob-preview.component.ts +++ b/src/modules/config/components/crawljobs/crawljob-preview/crawljob-preview.component.ts @@ -2,9 +2,10 @@ import {Component, Input} from '@angular/core'; import {ConfigObject} from '../../../../../shared/models/config'; @Component({ - selector: 'app-crawljob-preview', - templateUrl: './crawljob-preview.component.html', - styleUrls: ['./crawljob-preview.component.css'] + selector: 'app-crawljob-preview', + templateUrl: './crawljob-preview.component.html', + styleUrls: ['./crawljob-preview.component.css'], + standalone: false }) export class CrawljobPreviewComponent { diff --git a/src/modules/config/components/delete-dialog/delete-dialog.component.ts b/src/modules/config/components/delete-dialog/delete-dialog.component.ts index 6a061a036..008b10a14 100644 --- a/src/modules/config/components/delete-dialog/delete-dialog.component.ts +++ b/src/modules/config/components/delete-dialog/delete-dialog.component.ts @@ -3,8 +3,9 @@ import {MAT_DIALOG_DATA} from '@angular/material/dialog'; import {ConfigObject, Kind} from '../../../../shared/models/config'; @Component({ - selector: 'app-delete-dialog', - templateUrl: 'delete-dialog.component.html' + selector: 'app-delete-dialog', + templateUrl: 'delete-dialog.component.html', + standalone: false }) export class DeleteDialogComponent { readonly Kind = Kind; diff --git a/src/modules/config/components/delete-multi-dialog/delete-multi-dialog.component.ts b/src/modules/config/components/delete-multi-dialog/delete-multi-dialog.component.ts index f9552c84f..2e1d415d0 100644 --- a/src/modules/config/components/delete-multi-dialog/delete-multi-dialog.component.ts +++ b/src/modules/config/components/delete-multi-dialog/delete-multi-dialog.component.ts @@ -6,9 +6,10 @@ export interface DeleteDialogData { } @Component({ - selector: 'app-delete-multi-dialog', - styleUrls: ['delete-multi-dialog.component.scss'], - templateUrl: 'delete-multi-dialog.component.html' + selector: 'app-delete-multi-dialog', + styleUrls: ['delete-multi-dialog.component.scss'], + templateUrl: 'delete-multi-dialog.component.html', + standalone: false }) export class DeleteMultiDialogComponent { diff --git a/src/modules/config/components/durationpicker/duration-picker.ts b/src/modules/config/components/durationpicker/duration-picker.ts index feeccf9cb..3a1daa1a5 100644 --- a/src/modules/config/components/durationpicker/duration-picker.ts +++ b/src/modules/config/components/durationpicker/duration-picker.ts @@ -13,24 +13,28 @@ import {Duration} from '../../../../shared/models/duration/duration.model'; import {takeUntil} from 'rxjs/operators'; import {Subject} from 'rxjs'; import {NUMBER_OR_EMPTY_STRING} from '../../../../shared/validation/patterns'; -import * as moment from 'moment'; +import dayjs from 'dayjs'; +import duration from 'dayjs/plugin/duration'; +dayjs.extend(duration); @Component({ - selector: 'app-duration-picker', - templateUrl: './duration-picker.component.html', - styleUrls: ['./duration-picker.component.scss'], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => DurationPickerComponent), - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => DurationPickerComponent), - multi: true - }], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-duration-picker', + templateUrl: './duration-picker.component.html', + styleUrls: ['./duration-picker.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => DurationPickerComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => DurationPickerComponent), + multi: true + } + ], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class DurationPickerComponent implements ControlValueAccessor, OnInit, AfterViewInit, OnDestroy { @@ -149,7 +153,7 @@ export class DurationPickerComponent implements ControlValueAccessor, OnInit, Af } secondsToDuration(seconds: number): Duration { - const s = moment.duration(seconds, 'seconds'); + const s = dayjs.duration(seconds, 'seconds'); return new Duration({ days: s.days(), hours: s.hours(), @@ -160,7 +164,7 @@ export class DurationPickerComponent implements ControlValueAccessor, OnInit, Af } msToDuration(milliseconds: number): Duration { - const ms = moment.duration(milliseconds); + const ms = dayjs.duration(milliseconds); return new Duration({ days: null, hours: null, diff --git a/src/modules/config/components/entity/entity-details/entity-details.component.spec.ts b/src/modules/config/components/entity/entity-details/entity-details.component.spec.ts index 0a95fe049..bc2c3b7d4 100644 --- a/src/modules/config/components/entity/entity-details/entity-details.component.spec.ts +++ b/src/modules/config/components/entity/entity-details/entity-details.component.spec.ts @@ -5,7 +5,6 @@ import {LabelService} from '../../../services'; import {of} from 'rxjs'; import {AnnotationComponent, LabelComponent, MetaComponent} from '../..'; import {Annotation, ConfigObject, CrawlEntity, Kind, Label, Meta} from '../../../../../shared/models'; -import {AbilityModule} from '@casl/angular'; import {CoreTestingModule} from '../../../../core/core.testing.module'; import {AuthService} from '../../../../core/services'; import {RouterTestingModule} from '@angular/router/testing'; @@ -46,7 +45,6 @@ describe('EntityDetailsComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ - AbilityModule, CommonsModule, RouterTestingModule, NoopAnimationsModule, diff --git a/src/modules/config/components/entity/entity-details/entity-details.component.ts b/src/modules/config/components/entity/entity-details/entity-details.component.ts index eb62b06d3..2f4cb66e0 100644 --- a/src/modules/config/components/entity/entity-details/entity-details.component.ts +++ b/src/modules/config/components/entity/entity-details/entity-details.component.ts @@ -4,9 +4,10 @@ import {AuthService} from '../../../../core/services/auth'; import {ConfigObject, Kind, Meta} from '../../../../../shared/models'; @Component({ - selector: 'app-entity-details', - templateUrl: './entity-details.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-entity-details', + templateUrl: './entity-details.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class EntityDetailsComponent implements OnChanges { diff --git a/src/modules/config/components/entity/entity-dialog/entity-dialog.component.ts b/src/modules/config/components/entity/entity-dialog/entity-dialog.component.ts index 244042d6d..40cddb743 100644 --- a/src/modules/config/components/entity/entity-dialog/entity-dialog.component.ts +++ b/src/modules/config/components/entity/entity-dialog/entity-dialog.component.ts @@ -7,9 +7,10 @@ import {ConfigDialogData} from '../../../func'; import {ConfigObject} from '../../../../../shared/models/config'; @Component({ - selector: 'app-entity-dialog', - templateUrl: './entity-dialog.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-entity-dialog', + templateUrl: './entity-dialog.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class EntityDialogComponent extends EntityDetailsComponent implements OnInit { constructor(protected fb: UntypedFormBuilder, diff --git a/src/modules/config/components/entity/entity-multi-dialog/entity-multi-dialog.component.ts b/src/modules/config/components/entity/entity-multi-dialog/entity-multi-dialog.component.ts index d80a10318..70bd40d50 100644 --- a/src/modules/config/components/entity/entity-multi-dialog/entity-multi-dialog.component.ts +++ b/src/modules/config/components/entity/entity-multi-dialog/entity-multi-dialog.component.ts @@ -8,8 +8,9 @@ import {ConfigObject, Kind, Label} from '../../../../../shared/models/config'; import {LabelMultiComponent} from '../../label/label-multi/label-multi.component'; @Component({ - selector: 'app-entity-multi-dialog', - templateUrl: './entity-multi-dialog.component.html', + selector: 'app-entity-multi-dialog', + templateUrl: './entity-multi-dialog.component.html', + standalone: false }) export class EntityMultiDialogComponent extends EntityDetailsComponent implements OnInit { diff --git a/src/modules/config/components/entity/entity-view/entity-view.component.ts b/src/modules/config/components/entity/entity-view/entity-view.component.ts index a7b728c8b..ed3df7de4 100644 --- a/src/modules/config/components/entity/entity-view/entity-view.component.ts +++ b/src/modules/config/components/entity/entity-view/entity-view.component.ts @@ -2,9 +2,10 @@ import {ChangeDetectionStrategy, Component, Input} from '@angular/core'; import {ConfigObject, Label} from '../../../../../shared/models'; @Component({ - selector: 'app-entity-view', - templateUrl: './entity-view.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-entity-view', + templateUrl: './entity-view.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class EntityViewComponent { diff --git a/src/modules/config/components/filesize-input/filesize-input.component.ts b/src/modules/config/components/filesize-input/filesize-input.component.ts index 09abfbe64..906fa3731 100644 --- a/src/modules/config/components/filesize-input/filesize-input.component.ts +++ b/src/modules/config/components/filesize-input/filesize-input.component.ts @@ -33,21 +33,22 @@ const incrementBases = { }; @Component({ - selector: 'app-filesize-input', - templateUrl: './filesize-input.component.html', - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => FilesizeInputComponent), - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => FilesizeInputComponent), - multi: true - } - ], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-filesize-input', + templateUrl: './filesize-input.component.html', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => FilesizeInputComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => FilesizeInputComponent), + multi: true + } + ], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class FilesizeInputComponent implements ControlValueAccessor, AfterViewInit, OnDestroy { diff --git a/src/modules/config/components/job-execution-status/job-status.component.ts b/src/modules/config/components/job-execution-status/job-status.component.ts index eebefd4d6..670c8660f 100644 --- a/src/modules/config/components/job-execution-status/job-status.component.ts +++ b/src/modules/config/components/job-execution-status/job-status.component.ts @@ -2,10 +2,11 @@ import {ChangeDetectionStrategy, Component, Input} from '@angular/core'; import {JobExecutionState, JobExecutionStatus} from '../../../../shared/models/report'; @Component({ - selector: 'app-config-job-execution-status', - templateUrl: './job-status.component.html', - styleUrls: ['./job-status.component.css'], - changeDetection: ChangeDetectionStrategy.OnPush + selector: 'app-config-job-execution-status', + templateUrl: './job-status.component.html', + styleUrls: ['./job-status.component.css'], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class JobStatusComponent { readonly JobExecutionState = JobExecutionState; diff --git a/src/modules/config/components/label/label-multi/label-multi.component.ts b/src/modules/config/components/label/label-multi/label-multi.component.ts index 2b767704b..1a154487b 100644 --- a/src/modules/config/components/label/label-multi/label-multi.component.ts +++ b/src/modules/config/components/label/label-multi/label-multi.component.ts @@ -9,9 +9,10 @@ import {LabelService} from '../../../services/label.service'; import {UntypedFormControl} from '@angular/forms'; @Component({ - selector: 'app-label-multi', - templateUrl: './label-multi.component.html', - styleUrls: ['./label-multi.component.css'] + selector: 'app-label-multi', + templateUrl: './label-multi.component.html', + styleUrls: ['./label-multi.component.css'], + standalone: false }) export class LabelMultiComponent implements OnInit { @Input() diff --git a/src/modules/config/components/label/label.component.ts b/src/modules/config/components/label/label.component.ts index 9ae797c6a..f1d663933 100644 --- a/src/modules/config/components/label/label.component.ts +++ b/src/modules/config/components/label/label.component.ts @@ -26,11 +26,12 @@ import {LabelService} from '../../services/label.service'; @Component({ - selector: 'app-labels', - templateUrl: './label.component.html', - styleUrls: ['./label.component.scss'], - providers: [{provide: NG_VALUE_ACCESSOR, useExisting: LabelComponent, multi: true}], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-labels', + templateUrl: './label.component.html', + styleUrls: ['./label.component.scss'], + providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: LabelComponent, multi: true }], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class LabelComponent implements ControlValueAccessor, OnInit { diff --git a/src/modules/config/components/meta/meta-preview/meta-preview.component.ts b/src/modules/config/components/meta/meta-preview/meta-preview.component.ts index 5f364db77..1383e4874 100644 --- a/src/modules/config/components/meta/meta-preview/meta-preview.component.ts +++ b/src/modules/config/components/meta/meta-preview/meta-preview.component.ts @@ -3,9 +3,10 @@ import {ConfigObject} from '../../../../../shared/models/config'; import {AuthService, SnackBarService} from '../../../../core/services'; @Component({ - selector: 'app-meta-preview', - templateUrl: './meta-preview.component.html', - styleUrls: ['./meta-preview.component.css'] + selector: 'app-meta-preview', + templateUrl: './meta-preview.component.html', + styleUrls: ['./meta-preview.component.css'], + standalone: false }) export class MetaPreviewComponent { diff --git a/src/modules/config/components/meta/meta.component.spec.ts b/src/modules/config/components/meta/meta.component.spec.ts index 0c573e97a..6f1918bad 100644 --- a/src/modules/config/components/meta/meta.component.spec.ts +++ b/src/modules/config/components/meta/meta.component.spec.ts @@ -9,7 +9,6 @@ import {CoreTestingModule} from '../../../core/core.testing.module'; import {CommonsModule} from '../../../commons'; import {LabelComponent} from '../label/label.component'; import {AnnotationComponent} from '../annotation/annotation.component'; -import {AbilityModule} from '@casl/angular'; import {AuthService} from '../../../core/services'; describe('MetaComponent', () => { @@ -20,7 +19,6 @@ describe('MetaComponent', () => { TestBed.configureTestingModule({ declarations: [MetaComponent, LabelComponent, AnnotationComponent], imports: [ - AbilityModule, CoreTestingModule.forRoot(), CommonsModule, NoopAnimationsModule diff --git a/src/modules/config/components/meta/meta.component.ts b/src/modules/config/components/meta/meta.component.ts index 07e171d03..f8e944edb 100644 --- a/src/modules/config/components/meta/meta.component.ts +++ b/src/modules/config/components/meta/meta.component.ts @@ -17,14 +17,15 @@ import {takeUntil} from 'rxjs/operators'; @Component({ - selector: 'app-meta', - templateUrl: './meta.component.html', - styleUrls: ['./meta.component.css'], - providers: [ - {provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => MetaComponent), multi: true}, - {provide: NG_VALIDATORS, useExisting: forwardRef(() => MetaComponent), multi: true}, - ], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-meta', + templateUrl: './meta.component.html', + styleUrls: ['./meta.component.css'], + providers: [ + { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => MetaComponent), multi: true }, + { provide: NG_VALIDATORS, useExisting: forwardRef(() => MetaComponent), multi: true }, + ], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class MetaComponent implements AfterViewInit, OnInit, OnDestroy, ControlValueAccessor, Validator { diff --git a/src/modules/config/components/politenessconfig/politenessconfig-details/politenessconfig-details.component.spec.ts b/src/modules/config/components/politenessconfig/politenessconfig-details/politenessconfig-details.component.spec.ts index 3b766634a..6d674e640 100644 --- a/src/modules/config/components/politenessconfig/politenessconfig-details/politenessconfig-details.component.spec.ts +++ b/src/modules/config/components/politenessconfig/politenessconfig-details/politenessconfig-details.component.spec.ts @@ -17,7 +17,6 @@ import { RobotsPolicy } from '../../../../../shared/models'; import {AnnotationComponent, DurationPickerComponent, LabelComponent, MetaComponent} from '../..'; -import {AbilityModule} from '@casl/angular'; import {AuthService} from '../../../../core/services'; import {HarnessLoader} from '@angular/cdk/testing'; import {MatButtonHarness} from '@angular/material/button/testing'; @@ -68,7 +67,6 @@ describe('PolitenessConfigDetailsComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ - AbilityModule, RouterTestingModule, NoopAnimationsModule, CoreTestingModule.forRoot(), diff --git a/src/modules/config/components/politenessconfig/politenessconfig-details/politenessconfig-details.component.ts b/src/modules/config/components/politenessconfig/politenessconfig-details/politenessconfig-details.component.ts index 3ff9315b1..12d6bd41e 100644 --- a/src/modules/config/components/politenessconfig/politenessconfig-details/politenessconfig-details.component.ts +++ b/src/modules/config/components/politenessconfig/politenessconfig-details/politenessconfig-details.component.ts @@ -7,9 +7,10 @@ import {UnitOfTime} from '../../../../../shared/models/duration/unit-time.model' @Component({ - selector: 'app-politenessconfig-details', - templateUrl: './politenessconfig-details.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-politenessconfig-details', + templateUrl: './politenessconfig-details.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class PolitenessConfigDetailsComponent implements OnChanges { diff --git a/src/modules/config/components/politenessconfig/politenessconfig-dialog/politenessconfig-dialog.component.spec.ts b/src/modules/config/components/politenessconfig/politenessconfig-dialog/politenessconfig-dialog.component.spec.ts index cc7866c78..b8350d518 100644 --- a/src/modules/config/components/politenessconfig/politenessconfig-dialog/politenessconfig-dialog.component.spec.ts +++ b/src/modules/config/components/politenessconfig/politenessconfig-dialog/politenessconfig-dialog.component.spec.ts @@ -17,7 +17,6 @@ import {CommonsModule} from '../../../../commons'; import {LabelService} from '../../../services'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; import {of} from 'rxjs'; -import {AbilityModule} from '@casl/angular'; import {AuthService} from '../../../../core/services'; describe('PolitenessConfigDialogComponent', () => { @@ -32,7 +31,7 @@ describe('PolitenessConfigDialogComponent', () => { }; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - imports: [AbilityModule, CoreTestingModule.forRoot(), MatDialogModule, CommonsModule, NoopAnimationsModule], + imports: [CoreTestingModule.forRoot(), MatDialogModule, CommonsModule, NoopAnimationsModule], declarations: [PolitenessConfigDialogComponent, DurationPickerComponent, SelectorComponent, diff --git a/src/modules/config/components/politenessconfig/politenessconfig-dialog/politenessconfig-dialog.component.ts b/src/modules/config/components/politenessconfig/politenessconfig-dialog/politenessconfig-dialog.component.ts index ab86eebf8..aa4e6ba4a 100644 --- a/src/modules/config/components/politenessconfig/politenessconfig-dialog/politenessconfig-dialog.component.ts +++ b/src/modules/config/components/politenessconfig/politenessconfig-dialog/politenessconfig-dialog.component.ts @@ -7,9 +7,10 @@ import {ConfigDialogData} from '../../../func'; import {ConfigObject} from '../../../../../shared/models/config'; @Component({ - selector: 'app-politenessconfig-dialog', - templateUrl: './politenessconfig-dialog.component.html', - styleUrls: ['./politenessconfig-dialog.component.css'] + selector: 'app-politenessconfig-dialog', + templateUrl: './politenessconfig-dialog.component.html', + styleUrls: ['./politenessconfig-dialog.component.css'], + standalone: false }) export class PolitenessConfigDialogComponent extends PolitenessConfigDetailsComponent implements OnInit { diff --git a/src/modules/config/components/politenessconfig/politenessconfig-multi-dialog/politenessconfig-multi-dialog.component.ts b/src/modules/config/components/politenessconfig/politenessconfig-multi-dialog/politenessconfig-multi-dialog.component.ts index 4650ae08d..9c408dc93 100644 --- a/src/modules/config/components/politenessconfig/politenessconfig-multi-dialog/politenessconfig-multi-dialog.component.ts +++ b/src/modules/config/components/politenessconfig/politenessconfig-multi-dialog/politenessconfig-multi-dialog.component.ts @@ -9,9 +9,10 @@ import {NUMBER_OR_EMPTY_STRING} from '../../../../../shared/validation/patterns' import {LabelMultiComponent} from '../../label/label-multi/label-multi.component'; @Component({ - selector: 'app-politenessconfig-multi-dialog', - templateUrl: './politenessconfig-multi-dialog.component.html', - styleUrls: ['./politenessconfig-multi-dialog.component.css'] + selector: 'app-politenessconfig-multi-dialog', + templateUrl: './politenessconfig-multi-dialog.component.html', + styleUrls: ['./politenessconfig-multi-dialog.component.css'], + standalone: false }) export class PolitenessConfigMultiDialogComponent extends PolitenessConfigDetailsComponent implements OnInit { diff --git a/src/modules/config/components/politenessconfig/politenessconfig-preview/politenessconfig-preview.component.ts b/src/modules/config/components/politenessconfig/politenessconfig-preview/politenessconfig-preview.component.ts index aa9bce820..bb2297c55 100644 --- a/src/modules/config/components/politenessconfig/politenessconfig-preview/politenessconfig-preview.component.ts +++ b/src/modules/config/components/politenessconfig/politenessconfig-preview/politenessconfig-preview.component.ts @@ -2,9 +2,10 @@ import {Component, Input} from '@angular/core'; import {ConfigObject, RobotsPolicy} from '../../../../../shared/models/config'; @Component({ - selector: 'app-politenessconfig-preview', - templateUrl: './politenessconfig-preview.component.html', - styleUrls: ['./politenessconfig-preview.component.css'] + selector: 'app-politenessconfig-preview', + templateUrl: './politenessconfig-preview.component.html', + styleUrls: ['./politenessconfig-preview.component.css'], + standalone: false }) export class PolitenessconfigPreviewComponent { readonly RobotsPolicy = RobotsPolicy; diff --git a/src/modules/config/components/preview/preview.component.ts b/src/modules/config/components/preview/preview.component.ts index 484417bf3..4880fbbcf 100644 --- a/src/modules/config/components/preview/preview.component.ts +++ b/src/modules/config/components/preview/preview.component.ts @@ -4,10 +4,11 @@ import {ActivatedRoute, Router} from '@angular/router'; import {ErrorService} from '../../../core/services'; @Component({ - selector: 'app-preview', - templateUrl: './preview.component.html', - styleUrls: ['./preview.component.css'], - changeDetection: ChangeDetectionStrategy.OnPush + selector: 'app-preview', + templateUrl: './preview.component.html', + styleUrls: ['./preview.component.css'], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class PreviewComponent { readonly Kind = Kind; diff --git a/src/modules/config/components/rolemapping/rolemapping-details/rolemapping-details.component.ts b/src/modules/config/components/rolemapping/rolemapping-details/rolemapping-details.component.ts index 585c89202..71d6d3441 100644 --- a/src/modules/config/components/rolemapping/rolemapping-details/rolemapping-details.component.ts +++ b/src/modules/config/components/rolemapping/rolemapping-details/rolemapping-details.component.ts @@ -5,9 +5,10 @@ import {ConfigObject, Kind, Meta, Role, RoleMapping} from '../../../../../shared @Component({ - selector: 'app-rolemapping-details', - templateUrl: './rolemapping-details.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-rolemapping-details', + templateUrl: './rolemapping-details.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class RoleMappingDetailsComponent implements OnChanges { diff --git a/src/modules/config/components/rolemapping/rolemapping-dialog/rolemapping-dialog.component.ts b/src/modules/config/components/rolemapping/rolemapping-dialog/rolemapping-dialog.component.ts index 24313112d..0afe8f167 100644 --- a/src/modules/config/components/rolemapping/rolemapping-dialog/rolemapping-dialog.component.ts +++ b/src/modules/config/components/rolemapping/rolemapping-dialog/rolemapping-dialog.component.ts @@ -7,9 +7,10 @@ import {ConfigDialogData} from '../../../func'; import {ConfigObject} from '../../../../../shared/models/config'; @Component({ - selector: 'app-rolemapping-dialog', - templateUrl: './rolemapping-dialog.component.html', - styleUrls: ['./rolemapping-dialog.component.css'] + selector: 'app-rolemapping-dialog', + templateUrl: './rolemapping-dialog.component.html', + styleUrls: ['./rolemapping-dialog.component.css'], + standalone: false }) export class RoleMappingDialogComponent extends RoleMappingDetailsComponent implements OnInit { diff --git a/src/modules/config/components/rolemapping/rolemapping-list/rolemapping-list.component.ts b/src/modules/config/components/rolemapping/rolemapping-list/rolemapping-list.component.ts index 12a20a274..e93729c0f 100644 --- a/src/modules/config/components/rolemapping/rolemapping-list/rolemapping-list.component.ts +++ b/src/modules/config/components/rolemapping/rolemapping-list/rolemapping-list.component.ts @@ -5,16 +5,17 @@ import {BASE_LIST} from '../../../../../shared/directives'; @Component({ - selector: 'app-rolemapping-list', - templateUrl: './rolemapping-list.component.html', - styleUrls: ['../../../../commons/components/base-list/base-list.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - { - provide: BASE_LIST, - useExisting: forwardRef(() => RoleMappingListComponent) - } - ] + selector: 'app-rolemapping-list', + templateUrl: './rolemapping-list.component.html', + styleUrls: ['../../../../commons/components/base-list/base-list.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [ + { + provide: BASE_LIST, + useExisting: forwardRef(() => RoleMappingListComponent) + } + ], + standalone: false }) export class RoleMappingListComponent extends BaseListComponent { diff --git a/src/modules/config/components/rolemapping/rolemapping-multi-dialog/rolemapping-multi-dialog.component.ts b/src/modules/config/components/rolemapping/rolemapping-multi-dialog/rolemapping-multi-dialog.component.ts index c2e5ecf86..2d3575f41 100644 --- a/src/modules/config/components/rolemapping/rolemapping-multi-dialog/rolemapping-multi-dialog.component.ts +++ b/src/modules/config/components/rolemapping/rolemapping-multi-dialog/rolemapping-multi-dialog.component.ts @@ -7,9 +7,10 @@ import {CustomValidators} from '../../../../../shared/validation'; import {ConfigObject, Kind} from '../../../../../shared/models/config'; @Component({ - selector: 'app-rolemapping-multi-dialog', - templateUrl: './rolemapping-multi-dialog.component.html', - styleUrls: ['./rolemapping-multi-dialog.component.css'] + selector: 'app-rolemapping-multi-dialog', + templateUrl: './rolemapping-multi-dialog.component.html', + styleUrls: ['./rolemapping-multi-dialog.component.css'], + standalone: false }) export class RoleMappingMultiDialogComponent extends RoleMappingDetailsComponent implements OnInit { diff --git a/src/modules/config/components/run-crawl-dialog/run-crawl-dialog.component.ts b/src/modules/config/components/run-crawl-dialog/run-crawl-dialog.component.ts index 800b74d1e..3236c50b5 100644 --- a/src/modules/config/components/run-crawl-dialog/run-crawl-dialog.component.ts +++ b/src/modules/config/components/run-crawl-dialog/run-crawl-dialog.component.ts @@ -4,9 +4,10 @@ import {RunCrawlReply, RunCrawlRequest} from '../../../../shared/models/controll import {ConfigObject, Kind} from '../../../../shared/models/config'; @Component({ - selector: 'app-run-crawl-dialog', - templateUrl: './run-crawl-dialog.component.html', - styleUrls: ['./run-crawl-dialog.component.css'] + selector: 'app-run-crawl-dialog', + templateUrl: './run-crawl-dialog.component.html', + styleUrls: ['./run-crawl-dialog.component.css'], + standalone: false }) export class RunCrawlDialogComponent { diff --git a/src/modules/config/components/schedule/schedule-details/schedule-details.component.html b/src/modules/config/components/schedule/schedule-details/schedule-details.component.html index 60350b4dd..9719f58c1 100644 --- a/src/modules/config/components/schedule/schedule-details/schedule-details.component.html +++ b/src/modules/config/components/schedule/schedule-details/schedule-details.component.html @@ -78,6 +78,7 @@

Time limit

+ {{validFrom.errors | json}} diff --git a/src/modules/config/components/schedule/schedule-details/schedule-details.component.spec.ts b/src/modules/config/components/schedule/schedule-details/schedule-details.component.spec.ts index 646f65050..8ed6586c0 100644 --- a/src/modules/config/components/schedule/schedule-details/schedule-details.component.spec.ts +++ b/src/modules/config/components/schedule/schedule-details/schedule-details.component.spec.ts @@ -7,7 +7,6 @@ import {CoreTestingModule} from '../../../../core/core.testing.module'; import {LabelService} from '../../../services'; import {of} from 'rxjs'; import {AnnotationComponent, LabelComponent, MetaComponent} from '../..'; -import {AbilityModule} from '@casl/angular'; import {AuthService} from '../../../../core/services'; import {HarnessLoader} from '@angular/cdk/testing'; import {MatButtonHarness} from '@angular/material/button/testing'; @@ -19,7 +18,8 @@ import { MatDatepickerToggleHarness } from '@angular/material/datepicker/testing'; import {MatFormFieldHarness} from '@angular/material/form-field/testing'; -import moment from 'moment'; +import dayjs from 'dayjs'; +import {MAT_DATE_LOCALE} from "@angular/material/core"; const exampleCrawlSchedule: ConfigObject = { id: 'configObject_id', @@ -72,7 +72,6 @@ describe('ScheduleDetailsComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ - AbilityModule, CommonsModule, NoopAnimationsModule, CoreTestingModule.forRoot(), @@ -84,6 +83,10 @@ describe('ScheduleDetailsComponent', () => { AnnotationComponent ], providers: [ + { + provide: MAT_DATE_LOCALE, + useValue: 'nb', + }, { provide: AuthService, useValue: { @@ -384,22 +387,7 @@ describe('ScheduleDetailsComponent', () => { }); }); - // TODO: - // use no-nb as locale when inputting dates ? - // Use updateButton.click() instead of component.onUpdate().(Gets error fixture already destroyed when using click()) - - /** Testing datepicker component. - * When checking validation for entering invalid dates, output from calendar is in en-US locale. - * Same goes for when typing in input. - * ex: writing dd.mm.yyyy in test (24.12.2022), is invalid. - * Tested on live page (where locale is set), and works as expected (24.12.2022 is valid and 12.24.2022 is invalid). - * - * At the end of each test the updated configObject (from prepareSave), is validated to be as expected. - * - */ - it('setting valid from date in calendar sets timestamp to beginning of day', async () => { - let update: ConfigObject | undefined; component.update.subscribe((config: ConfigObject) => { update = config; @@ -411,42 +399,49 @@ describe('ScheduleDetailsComponent', () => { await fromCal.selectCell(dates[0]); await validFromToggle.closeCalendar(); fixture.detectChanges(); - const expectedFromDate = moment().add(1, 'M').month() + '/1/' + moment().year(); - expect(await validFrom.getValue()).toEqual(expectedFromDate); + + // Calculate the expected date and timestamp reliably + const expectedDate = dayjs().locale('nb').startOf('month'); + const expected = expectedDate.format('l'); + + expect(await validFrom.getValue()).toEqual(expected); expect(component.canUpdate).toBeTruthy(); + fixture.detectChanges(); component.onUpdate(); - const expectedTimestamp = moment().startOf('month').format('YYYY-MM-DD') + 'T00:00:00.000Z'; + + const expectedTimestamp = expectedDate.format('YYYY-MM-DD') + 'T00:00:00.000Z'; expect(update.crawlScheduleConfig.validFrom).toBe(expectedTimestamp); }); - it('setting valid from date in input sets timestamp to end of day', async () => { + it('setting valid from date in input sets timestamp to start of day', async () => { let update: ConfigObject | undefined; component.update.subscribe((config: ConfigObject) => { update = config; }); - await validFrom.setValue('1.32.2022'); + await validFrom.setValue('32.1.2022'); fixture.detectChanges(); await validFrom.blur(); expect(await validFromFormField.isControlValid()).toBeFalsy(); expect(component.canUpdate).toBeFalsy(); - await validFrom.setValue('13.1.2022'); + + await validFrom.setValue('1.13.2022'); await validFrom.blur(); expect(await validFromFormField.isControlValid()).toBeFalsy(); expect(component.canUpdate).toBeFalsy(); + await validFrom.setValue('1.1.2022'); await validFrom.blur(); - expect(await validFromFormField.isControlValid()).toBeTruthy(); expect(component.canUpdate).toBeTruthy(); component.onUpdate(); + expect(update.crawlScheduleConfig.validFrom).toBe('2022-01-01T00:00:00.000Z'); }); it('setting valid to date in calendar sets timestamp to end of day', async () => { - let update: ConfigObject | undefined; component.update.subscribe((config: ConfigObject) => { update = config; @@ -455,18 +450,18 @@ describe('ScheduleDetailsComponent', () => { await validToToggle.openCalendar(); const toCal = await validToToggle.getCalendar(); const daysInMonth = await toCal.getCells(); - await toCal.selectCell({text: (daysInMonth.length).toString()}); + await daysInMonth[daysInMonth.length -1].select(); await validToToggle.closeCalendar(); fixture.detectChanges(); - const expectedToDate = moment().add(1, 'M').month() + '/' + daysInMonth.length + '/' + moment().year(); + const expectedToDate = dayjs().locale('nb').endOf('month').format('D.M.YYYY'); expect(await validTo.getValue()).toEqual(expectedToDate); expect(component.canUpdate).toBeTruthy(); component.onUpdate(); - const expectedTimestamp = moment().endOf('month').format('YYYY-MM-DD') + 'T23:59:59.999Z'; + const expectedTimestamp = dayjs().locale('nb').endOf('month').format('YYYY-MM-DD') + 'T23:59:59.999Z'; expect(update.crawlScheduleConfig.validTo).toBe(expectedTimestamp); }); - it('setting valid to date in input sets timestamp to beginning of day', async () => { + it('setting valid to date in input sets timestamp to end of day', async () => { let update: ConfigObject | undefined; component.update.subscribe((config: ConfigObject) => { update = config; @@ -477,16 +472,18 @@ describe('ScheduleDetailsComponent', () => { await validTo.blur(); expect(await validToFormField.isControlValid()).toBeFalsy(); expect(component.canUpdate).toBeFalsy(); - await validTo.setValue('13.1.2022'); + + await validTo.setValue('32.1.2022'); + fixture.detectChanges(); await validTo.blur(); expect(await validToFormField.isControlValid()).toBeFalsy(); expect(component.canUpdate).toBeFalsy(); + await validTo.setValue('1.1.2022'); + fixture.detectChanges(); await validTo.blur(); - expect(await validToFormField.isControlValid()).toBeTruthy(); expect(component.canUpdate).toBeTruthy(); - component.onUpdate(); expect(update.crawlScheduleConfig.validTo).toBe('2022-01-01T23:59:59.999Z'); }); diff --git a/src/modules/config/components/schedule/schedule-details/schedule-details.component.ts b/src/modules/config/components/schedule/schedule-details/schedule-details.component.ts index f80b417b3..fe2f0ba90 100644 --- a/src/modules/config/components/schedule/schedule-details/schedule-details.component.ts +++ b/src/modules/config/components/schedule/schedule-details/schedule-details.component.ts @@ -22,9 +22,10 @@ import {DateTime} from '../../../../../shared/func'; @Component({ - selector: 'app-schedule-details', - templateUrl: './schedule-details.component.html', - styleUrls: ['./schedule-details.component.css'], + selector: 'app-schedule-details', + templateUrl: './schedule-details.component.html', + styleUrls: ['./schedule-details.component.css'], + standalone: false }) export class ScheduleDetailsComponent implements OnChanges { @Input() diff --git a/src/modules/config/components/schedule/schedule-dialog/schedule-dialog.component.spec.ts b/src/modules/config/components/schedule/schedule-dialog/schedule-dialog.component.spec.ts index be3d26df4..f716ac1a4 100644 --- a/src/modules/config/components/schedule/schedule-dialog/schedule-dialog.component.spec.ts +++ b/src/modules/config/components/schedule/schedule-dialog/schedule-dialog.component.spec.ts @@ -9,7 +9,6 @@ import {AnnotationComponent, LabelComponent, MetaComponent} from '../..'; import {ConfigDialogData} from '../../../func'; import {LabelService} from '../../../services'; import {of} from 'rxjs'; -import {AbilityModule} from '@casl/angular'; import {AuthService} from '../../../../core/services'; describe('ScheduleDialogComponent', () => { @@ -24,7 +23,7 @@ describe('ScheduleDialogComponent', () => { { component: ScheduleDialogComponent, declarations: [MetaComponent, LabelComponent, AnnotationComponent], - imports: [AbilityModule, CoreTestingModule.forRoot(), MatDialogModule, CommonsModule], + imports: [CoreTestingModule.forRoot(), MatDialogModule, CommonsModule], providers: [UntypedFormBuilder, { provide: LabelService, diff --git a/src/modules/config/components/schedule/schedule-dialog/schedule-dialog.component.ts b/src/modules/config/components/schedule/schedule-dialog/schedule-dialog.component.ts index 9c08bbfd1..3cf1e8d58 100644 --- a/src/modules/config/components/schedule/schedule-dialog/schedule-dialog.component.ts +++ b/src/modules/config/components/schedule/schedule-dialog/schedule-dialog.component.ts @@ -7,9 +7,10 @@ import {ConfigObject} from '../../../../../shared/models/config'; import {ScheduleDetailsComponent} from '..'; @Component({ - selector: 'app-schedule-dialog', - templateUrl: './schedule-dialog.component.html', - styleUrls: ['./schedule-dialog.component.css'] + selector: 'app-schedule-dialog', + templateUrl: './schedule-dialog.component.html', + styleUrls: ['./schedule-dialog.component.css'], + standalone: false }) export class ScheduleDialogComponent extends ScheduleDetailsComponent implements OnInit { diff --git a/src/modules/config/components/schedule/schedule-multi-dialog/schedule-multi-dialog.component.ts b/src/modules/config/components/schedule/schedule-multi-dialog/schedule-multi-dialog.component.ts index 4eea315df..b54e24589 100644 --- a/src/modules/config/components/schedule/schedule-multi-dialog/schedule-multi-dialog.component.ts +++ b/src/modules/config/components/schedule/schedule-multi-dialog/schedule-multi-dialog.component.ts @@ -9,9 +9,10 @@ import {DateTime} from '../../../../../shared/func'; import {LabelMultiComponent} from '../../label/label-multi/label-multi.component'; @Component({ - selector: 'app-schedule-multi-dialog', - templateUrl: './schedule-multi-dialog.component.html', - styleUrls: ['./schedule-multi-dialog.component.css'] + selector: 'app-schedule-multi-dialog', + templateUrl: './schedule-multi-dialog.component.html', + styleUrls: ['./schedule-multi-dialog.component.css'], + standalone: false }) export class ScheduleMultiDialogComponent extends ScheduleDetailsComponent implements OnInit { diff --git a/src/modules/config/components/schedule/schedule-preview/schedule-preview.component.ts b/src/modules/config/components/schedule/schedule-preview/schedule-preview.component.ts index 9b016531b..f98be8df9 100644 --- a/src/modules/config/components/schedule/schedule-preview/schedule-preview.component.ts +++ b/src/modules/config/components/schedule/schedule-preview/schedule-preview.component.ts @@ -2,9 +2,10 @@ import {Component, Input} from '@angular/core'; import {ConfigObject} from '../../../../../shared/models/config'; @Component({ - selector: 'app-schedule-preview', - templateUrl: './schedule-preview.component.html', - styleUrls: ['./schedule-preview.component.css'] + selector: 'app-schedule-preview', + templateUrl: './schedule-preview.component.html', + styleUrls: ['./schedule-preview.component.css'], + standalone: false }) export class SchedulePreviewComponent { @Input() diff --git a/src/modules/config/components/seed-meta/seed-meta-preview/seed-meta-preview.component.ts b/src/modules/config/components/seed-meta/seed-meta-preview/seed-meta-preview.component.ts index 44295eeaf..4999650c4 100644 --- a/src/modules/config/components/seed-meta/seed-meta-preview/seed-meta-preview.component.ts +++ b/src/modules/config/components/seed-meta/seed-meta-preview/seed-meta-preview.component.ts @@ -3,9 +3,10 @@ import {ConfigObject} from '../../../../../shared/models'; import {AuthService, SnackBarService} from '../../../../core/services'; @Component({ - selector: 'app-seed-meta-preview', - templateUrl: './seed-meta-preview.component.html', - styleUrls: ['./seed-meta-preview.component.css'] + selector: 'app-seed-meta-preview', + templateUrl: './seed-meta-preview.component.html', + styleUrls: ['./seed-meta-preview.component.css'], + standalone: false }) export class SeedMetaPreviewComponent { diff --git a/src/modules/config/components/seed-meta/seed-meta.component.spec.ts b/src/modules/config/components/seed-meta/seed-meta.component.spec.ts index a1a9d655d..39a553c36 100644 --- a/src/modules/config/components/seed-meta/seed-meta.component.spec.ts +++ b/src/modules/config/components/seed-meta/seed-meta.component.spec.ts @@ -7,7 +7,6 @@ import {LabelService} from '../../services'; import {of} from 'rxjs'; import {AnnotationComponent, LabelComponent} from '..'; import {CoreTestingModule} from '../../../core/core.testing.module'; -import {AbilityModule} from '@casl/angular'; import {CommonsModule} from '../../../commons'; import {HarnessLoader} from '@angular/cdk/testing'; import {MatFormFieldHarness} from '@angular/material/form-field/testing'; @@ -104,7 +103,6 @@ describe('SeedMetaComponent', () => { TestBed.configureTestingModule({ imports: [ ReactiveFormsModule, - AbilityModule, MaterialModule, CommonsModule, CoreTestingModule.forRoot(), diff --git a/src/modules/config/components/seed-meta/seed-meta.component.ts b/src/modules/config/components/seed-meta/seed-meta.component.ts index f9a010ab7..33fd917d2 100644 --- a/src/modules/config/components/seed-meta/seed-meta.component.ts +++ b/src/modules/config/components/seed-meta/seed-meta.component.ts @@ -32,14 +32,15 @@ export interface Parcel { } @Component({ - selector: 'app-seed-meta', - templateUrl: './seed-meta.component.html', - styleUrls: ['./seed-meta.component.css'], - providers: [ - {provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SeedMetaComponent), multi: true}, - {provide: NG_ASYNC_VALIDATORS, useExisting: forwardRef(() => SeedMetaComponent), multi: true} - ], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-seed-meta', + templateUrl: './seed-meta.component.html', + styleUrls: ['./seed-meta.component.css'], + providers: [ + { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SeedMetaComponent), multi: true }, + { provide: NG_ASYNC_VALIDATORS, useExisting: forwardRef(() => SeedMetaComponent), multi: true } + ], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class SeedMetaComponent extends MetaComponent implements AsyncValidator { diff --git a/src/modules/config/components/seed/seed-details/seed-details.component.ts b/src/modules/config/components/seed/seed-details/seed-details.component.ts index d458a4fe7..cdb30497e 100644 --- a/src/modules/config/components/seed/seed-details/seed-details.component.ts +++ b/src/modules/config/components/seed/seed-details/seed-details.component.ts @@ -18,9 +18,10 @@ import {Parcel} from '../..'; import {configRefIdRequired} from '../../../../../shared/validation/configref'; @Component({ - selector: 'app-seed-details', - templateUrl: './seed-details.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-seed-details', + templateUrl: './seed-details.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class SeedDetailsComponent implements OnChanges, OnDestroy { diff --git a/src/modules/config/components/seed/seed-dialog/seed-dialog.component.ts b/src/modules/config/components/seed/seed-dialog/seed-dialog.component.ts index 76c6d160e..0fe6dcbae 100644 --- a/src/modules/config/components/seed/seed-dialog/seed-dialog.component.ts +++ b/src/modules/config/components/seed/seed-dialog/seed-dialog.component.ts @@ -7,9 +7,10 @@ import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; import {ConfigDialogData} from '../../../func'; @Component({ - selector: 'app-entity-dialog', - templateUrl: './seed-dialog.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-entity-dialog', + templateUrl: './seed-dialog.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class SeedDialogComponent extends SeedDetailsComponent implements OnInit { crawlJobs: ConfigObject[]; diff --git a/src/modules/config/components/seed/seed-multi-dialog/seed-multi-dialog.component.ts b/src/modules/config/components/seed/seed-multi-dialog/seed-multi-dialog.component.ts index c103cb4f8..70f062435 100644 --- a/src/modules/config/components/seed/seed-multi-dialog/seed-multi-dialog.component.ts +++ b/src/modules/config/components/seed/seed-multi-dialog/seed-multi-dialog.component.ts @@ -8,9 +8,10 @@ import {ConfigObject, ConfigRef, Kind, Label} from '../../../../../shared/models import {LabelMultiComponent} from '../../label/label-multi/label-multi.component'; @Component({ - selector: 'app-seed-multi-dialog', - templateUrl: './seed-multi-dialog.component.html', - styleUrls: ['./seed-multi-dialog.component.css'] + selector: 'app-seed-multi-dialog', + templateUrl: './seed-multi-dialog.component.html', + styleUrls: ['./seed-multi-dialog.component.css'], + standalone: false }) export class SeedMultiDialogComponent extends SeedDetailsComponent implements OnInit { diff --git a/src/modules/config/components/seed/seed-preview/seed-preview.component.spec.ts b/src/modules/config/components/seed/seed-preview/seed-preview.component.spec.ts index 2a09052ae..563bbac12 100644 --- a/src/modules/config/components/seed/seed-preview/seed-preview.component.spec.ts +++ b/src/modules/config/components/seed/seed-preview/seed-preview.component.spec.ts @@ -2,7 +2,6 @@ import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing'; import {SeedPreviewComponent} from './seed-preview.component'; import {RouterTestingModule} from '@angular/router/testing'; -import {MatDialogModule} from '@angular/material/dialog'; import {CoreTestingModule} from '../../../../core/core.testing.module'; import {CommonsModule} from '../../../../commons'; diff --git a/src/modules/config/components/seed/seed-preview/seed-preview.component.ts b/src/modules/config/components/seed/seed-preview/seed-preview.component.ts index 9124bd8d9..67121b2dc 100644 --- a/src/modules/config/components/seed/seed-preview/seed-preview.component.ts +++ b/src/modules/config/components/seed/seed-preview/seed-preview.component.ts @@ -6,9 +6,10 @@ import {AuthService, SnackBarService} from '../../../../core/services'; @Component({ - selector: 'app-seed-preview', - templateUrl: './seed-preview.component.html', - styleUrls: ['./seed-preview.component.css'] + selector: 'app-seed-preview', + templateUrl: './seed-preview.component.html', + styleUrls: ['./seed-preview.component.css'], + standalone: false }) export class SeedPreviewComponent { readonly CrawlExecutionState = CrawlExecutionState; diff --git a/src/modules/config/components/selector/selector.component.ts b/src/modules/config/components/selector/selector.component.ts index 66d3e2f35..60f61412b 100644 --- a/src/modules/config/components/selector/selector.component.ts +++ b/src/modules/config/components/selector/selector.component.ts @@ -6,11 +6,12 @@ import {LabelComponent} from '../label/label.component'; @Component({ - selector: 'app-selector', - templateUrl: '../label/label.component.html', - styleUrls: ['../label/label.component.scss'], - providers: [{provide: NG_VALUE_ACCESSOR, useExisting: SelectorComponent, multi: true}], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-selector', + templateUrl: '../label/label.component.html', + styleUrls: ['../label/label.component.scss'], + providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: SelectorComponent, multi: true }], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class SelectorComponent extends LabelComponent implements OnInit { diff --git a/src/modules/config/components/shortcut/action-shortcut/action-shortcut.component.html b/src/modules/config/components/shortcut/action-shortcut/action-shortcut.component.html index 27c358dfe..ff6c7c03f 100644 --- a/src/modules/config/components/shortcut/action-shortcut/action-shortcut.component.html +++ b/src/modules/config/components/shortcut/action-shortcut/action-shortcut.component.html @@ -1,4 +1,4 @@ - +
Actions
@@ -6,7 +6,7 @@ @@ -17,7 +17,7 @@ play_arrow Crawl seed @@ -26,7 +26,7 @@ play_arrow Run crawljob @@ -35,7 +35,7 @@ file_copy Clone diff --git a/src/modules/config/components/shortcut/action-shortcut/action-shortcut.component.spec.ts b/src/modules/config/components/shortcut/action-shortcut/action-shortcut.component.spec.ts index f1be3024d..5b40ef91d 100644 --- a/src/modules/config/components/shortcut/action-shortcut/action-shortcut.component.spec.ts +++ b/src/modules/config/components/shortcut/action-shortcut/action-shortcut.component.spec.ts @@ -1,5 +1,4 @@ import {ActionShortcutComponent} from './action-shortcut.component'; -import {AbilityModule} from '@casl/angular'; import {CoreTestingModule} from '../../../../core/core.testing.module'; import {ConfigObject, Kind} from '../../../../../shared/models'; import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing'; @@ -11,7 +10,7 @@ describe('ActionShortcutComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - imports: [CoreTestingModule.forRoot(), AbilityModule, MatListModule], + imports: [CoreTestingModule.forRoot(), MatListModule], declarations: [ActionShortcutComponent], providers: [] }) diff --git a/src/modules/config/components/shortcut/action-shortcut/action-shortcut.component.ts b/src/modules/config/components/shortcut/action-shortcut/action-shortcut.component.ts index 5d85804e5..571f27123 100644 --- a/src/modules/config/components/shortcut/action-shortcut/action-shortcut.component.ts +++ b/src/modules/config/components/shortcut/action-shortcut/action-shortcut.component.ts @@ -1,12 +1,16 @@ import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; import {ConfigObject, Kind} from '../../../../../shared/models/config'; +import {Observable} from "rxjs"; +import {AbilityService} from "@casl/angular"; @Component({ - selector: 'app-action-shortcut', - templateUrl: './action-shortcut.component.html', + selector: 'app-action-shortcut', + templateUrl: './action-shortcut.component.html', + standalone: false }) export class ActionShortcutComponent { readonly Kind = Kind; + readonly ability$: Observable @Input() configObject: ConfigObject; @@ -21,7 +25,8 @@ export class ActionShortcutComponent { clone = new EventEmitter(); - constructor() { + constructor(private ableService: AbilityService) { + this.ability$ = this.ableService.ability$; } onClone() { diff --git a/src/modules/config/components/shortcut/filter-shortcut/filter-shortcut.component.html b/src/modules/config/components/shortcut/filter-shortcut/filter-shortcut.component.html index 61374140e..183433bd0 100644 --- a/src/modules/config/components/shortcut/filter-shortcut/filter-shortcut.component.html +++ b/src/modules/config/components/shortcut/filter-shortcut/filter-shortcut.component.html @@ -1,11 +1,11 @@ - +
Filters
link @@ -18,7 +18,7 @@ Filters
{ @@ -12,7 +11,7 @@ describe('FilterShortcutComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - imports: [CoreTestingModule.forRoot(), AbilityModule, MatListModule], + imports: [CoreTestingModule.forRoot(), MatListModule], declarations: [FilterShortcutComponent], providers: [] }) diff --git a/src/modules/config/components/shortcut/filter-shortcut/filter-shortcut.component.ts b/src/modules/config/components/shortcut/filter-shortcut/filter-shortcut.component.ts index ac71618ae..c649b78f5 100644 --- a/src/modules/config/components/shortcut/filter-shortcut/filter-shortcut.component.ts +++ b/src/modules/config/components/shortcut/filter-shortcut/filter-shortcut.component.ts @@ -1,13 +1,17 @@ import {Component, EventEmitter, Input, Output} from '@angular/core'; import {ConfigObject, Kind} from '../../../../../shared/models/config'; import {Params} from '@angular/router'; +import {Observable} from "rxjs"; +import {AbilityService} from "@casl/angular"; @Component({ - selector: 'app-filter-shortcut', - templateUrl: './filter-shortcut.component.html', + selector: 'app-filter-shortcut', + templateUrl: './filter-shortcut.component.html', + standalone: false }) export class FilterShortcutComponent { readonly Kind = Kind; + readonly ability$: Observable; @Input() configObject: ConfigObject; @@ -15,7 +19,8 @@ export class FilterShortcutComponent { @Output() clone = new EventEmitter(); - constructor() { + constructor(private abilityService: AbilityService) { + this.ability$ = this.abilityService.ability$; } getJobRefListQueryParams(configObject: ConfigObject): Params { diff --git a/src/modules/config/components/shortcut/shortcut-list/shortcut-list.component.html b/src/modules/config/components/shortcut/shortcut-list/shortcut-list.component.html index 559342150..2989fc469 100644 --- a/src/modules/config/components/shortcut/shortcut-list/shortcut-list.component.html +++ b/src/modules/config/components/shortcut/shortcut-list/shortcut-list.component.html @@ -1,4 +1,4 @@ - +
Entity
@@ -6,7 +6,7 @@ [routerLink]="['/config', 'entity', configObject.seed.entityRef.id]"> business {{configObject.seed.entityRef.id | getEntityName | async}}
- +
Crawljobs
- +
Schedule
@@ -30,7 +30,7 @@ {{configObject | getCrawlScheduleName | async}}
- +
CrawlConfig
@@ -38,7 +38,7 @@ {{configObject | getCrawlConfigName | async}}
- +
Scope script
web_asset @@ -50,7 +50,7 @@ - +
Collection
@@ -58,7 +58,7 @@ {{configObject | getCollectionName | async}}
- +
BrowserConfig
@@ -66,7 +66,7 @@ {{configObject | getBrowserConfigName | async}}
- +
PolitenessConfig
@@ -79,7 +79,7 @@ + *ngIf="configObject.browserConfig.scriptRefList.length > 0 && ability.can('read', Kind[Kind.BROWSERSCRIPT])">
BrowserScripts
diff --git a/src/modules/config/components/shortcut/shortcut-list/shortcut-list.component.spec.ts b/src/modules/config/components/shortcut/shortcut-list/shortcut-list.component.spec.ts index 94b66ef16..5a04fcf97 100644 --- a/src/modules/config/components/shortcut/shortcut-list/shortcut-list.component.spec.ts +++ b/src/modules/config/components/shortcut/shortcut-list/shortcut-list.component.spec.ts @@ -2,7 +2,6 @@ import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing'; import {ShortcutListComponent} from './shortcut-list.component'; import {ConfigObject, Kind} from '../../../../../shared/models'; -import {AbilityModule} from '@casl/angular'; import {CoreTestingModule} from '../../../../core/core.testing.module'; import {CommonsModule} from '../../../../commons'; import {EntityNamePipe} from '../../../pipe'; @@ -17,7 +16,7 @@ describe('ShortcutListComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ declarations: [ShortcutListComponent, EntityNamePipe], - imports: [AbilityModule, CoreTestingModule.forRoot(), RouterTestingModule, CommonsModule], + imports: [CoreTestingModule.forRoot(), RouterTestingModule, CommonsModule], providers: [ { provide: ConfigService, useValue: { diff --git a/src/modules/config/components/shortcut/shortcut-list/shortcut-list.component.ts b/src/modules/config/components/shortcut/shortcut-list/shortcut-list.component.ts index 9a223a618..e21c06ab6 100644 --- a/src/modules/config/components/shortcut/shortcut-list/shortcut-list.component.ts +++ b/src/modules/config/components/shortcut/shortcut-list/shortcut-list.component.ts @@ -1,16 +1,20 @@ import {Component, Input} from '@angular/core'; import {ConfigObject, Kind} from 'src/shared/models'; +import {AbilityService} from "@casl/angular"; @Component({ - selector: 'app-shortcut-list', - templateUrl: './shortcut-list.component.html', + selector: 'app-shortcut-list', + templateUrl: './shortcut-list.component.html', + standalone: false }) export class ShortcutListComponent { readonly Kind = Kind; + readonly ability$: any; @Input() configObject: ConfigObject; - constructor() { + constructor(private abilityService: AbilityService) { + this.ability$ = this.abilityService.ability$; } } diff --git a/src/modules/config/components/shortcut/shortcut.component.ts b/src/modules/config/components/shortcut/shortcut.component.ts index e4412c75d..0b274d69d 100644 --- a/src/modules/config/components/shortcut/shortcut.component.ts +++ b/src/modules/config/components/shortcut/shortcut.component.ts @@ -5,9 +5,10 @@ import {ErrorService} from '../../../core/services'; import {JobExecutionState} from '../../../../shared/models/report'; @Component({ - selector: 'app-shortcut', - templateUrl: './shortcut.component.html', - styleUrls: ['./shortcut.component.scss'] + selector: 'app-shortcut', + templateUrl: './shortcut.component.html', + styleUrls: ['./shortcut.component.scss'], + standalone: false }) export class ShortcutComponent { readonly Kind = Kind; diff --git a/src/modules/config/containers/config-nav-list/config-nav-list.component.html b/src/modules/config/containers/config-nav-list/config-nav-list.component.html index 8a240775a..8e7b254b5 100644 --- a/src/modules/config/containers/config-nav-list/config-nav-list.component.html +++ b/src/modules/config/containers/config-nav-list/config-nav-list.component.html @@ -1,62 +1,71 @@ - - + business Entity - link Seed - work Crawljobs - schedule Schedule - settings_system_daydream CrawlConfig - collections_bookmark Collection - web BrowserConfig - web_asset BrowserScript - sentiment_very_satisfied Politeness - group_work CrawlHostGroup - people Users - diff --git a/src/modules/config/containers/config-nav-list/config-nav-list.component.ts b/src/modules/config/containers/config-nav-list/config-nav-list.component.ts index 8b9a6a0cf..96fae8ec1 100644 --- a/src/modules/config/containers/config-nav-list/config-nav-list.component.ts +++ b/src/modules/config/containers/config-nav-list/config-nav-list.component.ts @@ -5,16 +5,20 @@ import {AuthService} from '../../../core/services/auth'; import {NavigationListComponent} from '../../../commons/components'; import {ShortcutEventOutput, ShortcutInput} from 'ng-keyboard-shortcuts'; import {Router} from '@angular/router'; +import {Observable} from 'rxjs'; +import {AbilityService} from '@casl/angular'; @Component({ selector: 'app-config-navigation-list', templateUrl: './config-nav-list.component.html', styleUrls: ['../../../commons/components/navigation-list/navigation-list.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class ConfigNavListComponent extends NavigationListComponent { readonly ConfigPath = ConfigPath; readonly Kind = Kind; + readonly ability$: Observable shortcuts: ShortcutInput[] = []; @@ -24,7 +28,8 @@ export class ConfigNavListComponent extends NavigationListComponent { @Input() options: ConfigOptions; - constructor(protected authService: AuthService, private router: Router) { + constructor(protected authService: AuthService, private router: Router, private abilityService: AbilityService) { super(authService); + this.ability$ = this.abilityService.ability$; } } diff --git a/src/modules/config/containers/config/config.component.ts b/src/modules/config/containers/config/config.component.ts index 98de6593d..ec9b8365e 100644 --- a/src/modules/config/containers/config/config.component.ts +++ b/src/modules/config/containers/config/config.component.ts @@ -33,10 +33,11 @@ export interface ConfigOptions { } @Component({ - selector: 'app-configs', - templateUrl: './config.component.html', - styleUrls: ['./config.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-configs', + templateUrl: './config.component.html', + styleUrls: ['./config.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class ConfigComponent implements OnInit { readonly Kind = Kind; diff --git a/src/modules/config/containers/configuration/configuration.component.ts b/src/modules/config/containers/configuration/configuration.component.ts index fc8d1b1ae..e8fe274f1 100644 --- a/src/modules/config/containers/configuration/configuration.component.ts +++ b/src/modules/config/containers/configuration/configuration.component.ts @@ -44,10 +44,11 @@ export interface ConfigOptions { } @Component({ - selector: 'app-configuration', - templateUrl: './configuration.component.html', - styleUrls: ['./configuration.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-configuration', + templateUrl: './configuration.component.html', + styleUrls: ['./configuration.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false }) export class ConfigurationComponent implements OnDestroy { readonly Kind = Kind; diff --git a/src/modules/config/containers/configurations/configurations.component.html b/src/modules/config/containers/configurations/configurations.component.html index 32a164245..0464a8fca 100644 --- a/src/modules/config/containers/configurations/configurations.component.html +++ b/src/modules/config/containers/configurations/configurations.component.html @@ -1,4 +1,4 @@ -
+
@@ -6,7 +6,7 @@ -