diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..c194ecc1a --- /dev/null +++ b/.eslintignore @@ -0,0 +1,7 @@ +.angular +.dev +.husky +dist +coverage +node_modules +bamboo-specs diff --git a/.eslintrc.json b/.eslintrc.json index a305aede8..670fd062e 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -45,12 +45,14 @@ "plugin:@angular-eslint/recommended", "plugin:@angular-eslint/recommended--extra", "plugin:@angular-eslint/template/process-inline-templates", - "plugin:prettier/recommended" + "plugin:prettier/recommended", + "plugin:rxjs/recommended" ], "parserOptions": { "project": "./tsconfig.json" }, "rules": { + "rxjs/no-ignored-subscription": "error", "@angular-eslint/no-conflicting-lifecycle": "error", "@angular-eslint/no-host-metadata-property": "error", "@angular-eslint/no-input-rename": "error", diff --git a/.prettierignore b/.prettierignore index 81d117d52..b8ec6a7f0 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,5 +1,7 @@ # Add files here to ignore them from prettier formatting - +/.angular +/.dev +/.husky /dist /coverage /node_modules diff --git a/apps/red-ui/jest.config.ts b/apps/red-ui/jest.config.ts new file mode 100644 index 000000000..d48e080e9 --- /dev/null +++ b/apps/red-ui/jest.config.ts @@ -0,0 +1,26 @@ +import type { Config } from 'jest'; +import { defaults } from 'jest-config'; + +export default { + ...defaults, + displayName: 'red-ui', + preset: '../../jest.preset.js', + setupFilesAfterEnv: ['jest-preset-angular/setup-jest.js', 'jest-extended/all'], + coverageDirectory: '../../coverage/apps/angular-jest', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '../../tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + testEnvironment: 'jest-environment-jsdom', + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +} as Config; diff --git a/apps/red-ui/src/app/app-routing.module.ts b/apps/red-ui/src/app/app-routing.module.ts index 0033f3247..a42c3cb55 100644 --- a/apps/red-ui/src/app/app-routing.module.ts +++ b/apps/red-ui/src/app/app-routing.module.ts @@ -146,6 +146,7 @@ const routes: IqserRoutes = [ }, { path: 'downloads', + // TODO: transform into a lazy loaded module component: DownloadsListScreenComponent, canActivate: [CompositeRouteGuard, IqserPermissionsGuard], data: { @@ -161,7 +162,7 @@ const routes: IqserRoutes = [ loadChildren: () => import('./modules/search/search.module').then(m => m.SearchModule), canActivate: [CompositeRouteGuard, IqserPermissionsGuard], data: { - routeGuards: [IqserAuthGuard, RedRoleGuard], + routeGuards: [IqserAuthGuard, RedRoleGuard, DossiersGuard], permissions: { allow: [ROLES.search], redirectTo: '/auth-error', @@ -224,7 +225,7 @@ const routes: IqserRoutes = [ @NgModule({ imports: [RouterModule.forRoot(routes, { scrollPositionRestoration: 'enabled' })], - providers: [{ provide: RouteReuseStrategy, useClass: CustomRouteReuseStrategy }], + providers: [{ provide: RouteReuseStrategy, useExisting: CustomRouteReuseStrategy }], exports: [RouterModule], }) export class AppRoutingModule {} diff --git a/apps/red-ui/src/app/app.component.ts b/apps/red-ui/src/app/app.component.ts index edb0cd879..9a11ac19b 100644 --- a/apps/red-ui/src/app/app.component.ts +++ b/apps/red-ui/src/app/app.component.ts @@ -1,4 +1,4 @@ -import { Component, Inject, Renderer2, ViewContainerRef } from '@angular/core'; +import { Component, Inject, OnDestroy, Renderer2, ViewContainerRef } from '@angular/core'; import { RouterHistoryService } from '@services/router-history.service'; import { REDDocumentViewer } from './modules/pdf-viewer/services/document-viewer.service'; import { DossiersChangesService } from '@services/dossiers/dossier-changes.service'; @@ -7,6 +7,8 @@ import { UserPreferenceService } from '@users/user-preference.service'; import { getConfig, IqserPermissionsService } from '@iqser/common-ui'; import { ROLES } from '@users/roles'; import { AppConfig } from '@red/domain'; +import { ActivatedRoute, ParamMap, Router } from '@angular/router'; +import { Subscription } from 'rxjs'; function loadCustomTheme() { const cssFileName = getConfig().THEME; @@ -27,24 +29,49 @@ function loadCustomTheme() { selector: 'redaction-root', templateUrl: './app.component.html', }) -export class AppComponent { +export class AppComponent implements OnDestroy { + readonly #subscription = new Subscription(); + constructor( /** ViewContainerRef needs to be injected for the color picker to work */ readonly viewContainerRef: ViewContainerRef, /** RouterHistoryService needs to be injected for last dossiers screen to be updated on first app load */ private readonly _routerHistoryService: RouterHistoryService, - private readonly _userPreferenceService: UserPreferenceService, + userPreferenceService: UserPreferenceService, readonly documentViewer: REDDocumentViewer, - private readonly _dossierChangesService: DossiersChangesService, - @Inject(DOCUMENT) private readonly _document: Document, - private readonly _renderer: Renderer2, - private readonly _permissionsService: IqserPermissionsService, + dossierChangesService: DossiersChangesService, + @Inject(DOCUMENT) document: Document, + renderer: Renderer2, + permissionsService: IqserPermissionsService, + private readonly _router: Router, + route: ActivatedRoute, ) { - this._renderer.addClass(this._document.body, _userPreferenceService.getTheme()); + renderer.addClass(document.body, userPreferenceService.getTheme()); loadCustomTheme(); // TODO: Find a better place to initialize dossiers refresh - if (_permissionsService.has(ROLES.dossiers.read)) { - _dossierChangesService.initializeRefresh(); + if (permissionsService.has(ROLES.dossiers.read)) { + const refreshSub = dossierChangesService.initializeRefresh().subscribe(); + this.#subscription.add(refreshSub); + } + + const sub = route.queryParamMap.subscribe(queryParams => this.#navigate(queryParams)); + this.#subscription.add(sub); + } + + ngOnDestroy() { + this.#subscription.unsubscribe(); + } + + #navigate(queryParams: ParamMap) { + if (queryParams.has('code') || queryParams.has('state') || queryParams.has('session_state')) { + return this._router.navigate([], { + queryParams: { + state: null, + session_state: null, + code: null, + }, + queryParamsHandling: 'merge', + }); } } } diff --git a/apps/red-ui/src/app/app.module.ts b/apps/red-ui/src/app/app.module.ts index 0bfe6c1e0..4da89584c 100644 --- a/apps/red-ui/src/app/app.module.ts +++ b/apps/red-ui/src/app/app.module.ts @@ -1,7 +1,6 @@ import { BrowserModule } from '@angular/platform-browser'; import { APP_INITIALIZER, ErrorHandler, NgModule } from '@angular/core'; import { AppComponent } from './app.component'; -import { ActivatedRoute, Router } from '@angular/router'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { BaseScreenComponent } from '@components/base-screen/base-screen.component'; @@ -9,18 +8,27 @@ import { MissingTranslationHandler } from '@ngx-translate/core'; import { BASE_HREF, CachingModule, + CircleButtonComponent, CommonUiModule, + EmptyStateComponent, + HiddenActionDirective, + InputWithActionComponent, + IqserAllowDirective, + IqserDenyDirective, IqserHelpModeModule, + IqserListingModule, IqserLoadingModule, - IqserPermissionsModule, IqserPermissionsService, - IqserSharedModule, IqserTranslateModule, IqserUsersModule, LanguageService, + LogoComponent, MAX_RETRIES_ON_SERVER_ERROR, + RoundCheckboxComponent, SERVER_ERROR_SKIP_PATHS, ServerErrorInterceptor, + SkeletonComponent, + StopPropagationDirective, ToastComponent, } from '@iqser/common-ui'; import { ToastrModule } from 'ngx-toastr'; @@ -101,7 +109,6 @@ export const appModuleFactory = (config: AppConfig) => { existingUserService: UserService, existingRoleGuard: RedRoleGuard, }), - IqserSharedModule, CachingModule.forRoot(UI_CACHES), IqserHelpModeModule.forRoot(links), PdfViewerModule, @@ -114,7 +121,6 @@ export const appModuleFactory = (config: AppConfig) => { }), IqserTranslateModule.forRoot({ pathPrefix: config.BASE_TRANSLATIONS_DIRECTORY || '/assets/i18n/redact/' }), IqserLoadingModule.forRoot(), - IqserPermissionsModule.forRoot(), ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }), LoggerModule.forRoot(undefined, { ruleProvider: { @@ -125,7 +131,7 @@ export const appModuleFactory = (config: AppConfig) => { provide: TOKEN_LOGGER_CONFIG, useValue: { level: environment.production ? NgxLoggerLevel.ERROR : NgxLoggerLevel.DEBUG, - enableSourceMaps: true, + enableSourceMaps: false, timestampFormat: 'mm:ss:SSS', disableFileDetails: true, features: { @@ -138,7 +144,7 @@ export const appModuleFactory = (config: AppConfig) => { enabled: false, }, PDF: { - enabled: true, + enabled: false, }, FILE: { enabled: false, @@ -153,6 +159,17 @@ export const appModuleFactory = (config: AppConfig) => { } as ILoggerConfig, }, }), + CircleButtonComponent, + EmptyStateComponent, + SkeletonComponent, + LogoComponent, + HiddenActionDirective, + StopPropagationDirective, + InputWithActionComponent, + RoundCheckboxComponent, + IqserAllowDirective, + IqserDenyDirective, + IqserListingModule, ], providers: [ { @@ -223,26 +240,7 @@ export const appModuleFactory = (config: AppConfig) => { ], bootstrap: [AppComponent], }) - class AppModule { - constructor(private readonly _router: Router, private readonly _route: ActivatedRoute) { - this._configureKeyCloakRouteHandling(); - } - - private _configureKeyCloakRouteHandling() { - this._route.queryParamMap.subscribe(queryParams => { - if (queryParams.has('code') || queryParams.has('state') || queryParams.has('session_state')) { - this._router.navigate([], { - queryParams: { - state: null, - session_state: null, - code: null, - }, - queryParamsHandling: 'merge', - }); - } - }); - } - } + class AppModule {} return AppModule; }; diff --git a/apps/red-ui/src/app/components/base-screen/base-screen.component.html b/apps/red-ui/src/app/components/base-screen/base-screen.component.html index 4fffdc4e0..f9f73cfb2 100644 --- a/apps/red-ui/src/app/components/base-screen/base-screen.component.html +++ b/apps/red-ui/src/app/components/base-screen/base-screen.component.html @@ -11,9 +11,7 @@ @@ -29,7 +27,10 @@ - + diff --git a/apps/red-ui/src/app/components/downloads-list-screen/downloads-list-screen.component.html b/apps/red-ui/src/app/components/downloads-list-screen/downloads-list-screen.component.html index 9915adfb9..78c12e1ac 100644 --- a/apps/red-ui/src/app/components/downloads-list-screen/downloads-list-screen.component.html +++ b/apps/red-ui/src/app/components/downloads-list-screen/downloads-list-screen.component.html @@ -5,11 +5,11 @@
@@ -26,6 +26,7 @@ > +
@@ -42,7 +43,7 @@
- {{ download.creationDate | date: 'd MMM yyyy, hh:mm a' }} + {{ download.creationDate | date : 'd MMM yyyy, hh:mm a' }}
diff --git a/apps/red-ui/src/app/components/notifications/notifications.component.html b/apps/red-ui/src/app/components/notifications/notifications.component.html index 518a46c85..517a15f89 100644 --- a/apps/red-ui/src/app/components/notifications/notifications.component.html +++ b/apps/red-ui/src/app/components/notifications/notifications.component.html @@ -1,4 +1,9 @@ - + @@ -13,17 +18,25 @@
{{ group.date }}
-
+
{{ 'notifications.mark-all-as-read' | translate }}
@@ -31,11 +44,14 @@
{{ notification.creationDate | date : 'exactDate' }}
+
diff --git a/apps/red-ui/src/app/components/notifications/notifications.component.ts b/apps/red-ui/src/app/components/notifications/notifications.component.ts index 14568e121..5d9a56c96 100644 --- a/apps/red-ui/src/app/components/notifications/notifications.component.ts +++ b/apps/red-ui/src/app/components/notifications/notifications.component.ts @@ -4,7 +4,7 @@ import { NotificationsService } from '@services/notifications.service'; import { Notification } from '@red/domain'; import { distinctUntilChanged, map } from 'rxjs/operators'; import { Observable } from 'rxjs'; -import { isToday, shareLast } from '@iqser/common-ui'; +import { isToday, shareLast, trackByFactory } from '@iqser/common-ui'; import dayjs, { Dayjs } from 'dayjs'; import { TranslateService } from '@ngx-translate/core'; @@ -29,6 +29,7 @@ function chronologically(first: string, second: string) { export class NotificationsComponent { readonly hasUnreadNotifications$: Observable; readonly groupedNotifications$: Observable; + readonly trackBy = trackByFactory(); constructor( private readonly _notificationsService: NotificationsService, @@ -47,9 +48,7 @@ export class NotificationsComponent { ); } - async markRead($event, notifications: Notification[] = this._notificationsService.all, isRead = true): Promise { - $event.stopPropagation(); - + async markRead(notifications: Notification[] = this._notificationsService.all, isRead = true): Promise { if (!notifications.find(notification => !!notification.readDate !== isRead)) { // If no notification changes status after the request, abort return; @@ -71,11 +70,10 @@ export class NotificationsComponent { return n.creationDate.split('T')[0]; }); - const grouped = [...groupedMap.entries()]; - const sorted = grouped.sort(([aDate], [bDate]) => chronologically(aDate, bDate)); - return sorted.map(([date, _notifications]) => ({ + const sortedGroups = [...groupedMap.entries()].sort(([aDate], [bDate]) => chronologically(aDate, bDate)); + return sortedGroups.map(([date, _notifications]) => ({ date: isToday(date) ? todayTranslation : this._datePipe.transform(date, 'sophisticatedDate'), - notifications: _notifications.sort((a, b) => chronologically(a.creationDate, b.creationDate)), + notifications: [..._notifications].sort((a, b) => chronologically(a.creationDate, b.creationDate)), })); } } diff --git a/apps/red-ui/src/app/guards/can-deactivate.guard.ts b/apps/red-ui/src/app/guards/can-deactivate.guard.ts index df72bc9c3..f71225068 100644 --- a/apps/red-ui/src/app/guards/can-deactivate.guard.ts +++ b/apps/red-ui/src/app/guards/can-deactivate.guard.ts @@ -1,6 +1,6 @@ import { CanDeactivate } from '@angular/router'; import { Injectable } from '@angular/core'; -import { map, Observable } from 'rxjs'; +import { map } from 'rxjs'; import { ConfirmationDialogService, ConfirmOptions } from '@iqser/common-ui'; export interface ComponentCanDeactivate { @@ -15,23 +15,24 @@ export interface ComponentCanDeactivate { export class PendingChangesGuard implements CanDeactivate { constructor(private _dialogService: ConfirmationDialogService) {} - canDeactivate(component: ComponentCanDeactivate): boolean | Observable { - if (component.changed) { - component.isLeavingPage = true; - - const dialogRef = this._dialogService.openDialog({ disableConfirm: component.valid === false }); - return dialogRef.afterClosed().pipe( - map(result => { - if (result === ConfirmOptions.CONFIRM) { - component.save().then(); - } else { - component.discard?.().then(); - } - component.isLeavingPage = false; - return !!result; - }), - ); + canDeactivate(component: ComponentCanDeactivate) { + if (!component.changed) { + return true; } - return true; + + component.isLeavingPage = true; + + const dialogRef = this._dialogService.open({ disableConfirm: component.valid === false }); + return dialogRef.afterClosed().pipe( + map(result => { + if (result === ConfirmOptions.CONFIRM) { + component.save().then(); + } else { + component.discard?.().then(); + } + component.isLeavingPage = false; + return !!result; + }), + ); } } diff --git a/apps/red-ui/src/app/guards/dossier-files-guard.ts b/apps/red-ui/src/app/guards/dossier-files-guard.ts index 4dddfa180..fce03e70e 100644 --- a/apps/red-ui/src/app/guards/dossier-files-guard.ts +++ b/apps/red-ui/src/app/guards/dossier-files-guard.ts @@ -6,6 +6,7 @@ import { firstValueFrom } from 'rxjs'; import { DOSSIER_ID, DOSSIER_TEMPLATE_ID } from '@red/domain'; import { DossiersService } from '@services/dossiers/dossiers.service'; import { DictionaryService } from '@services/entity-services/dictionary.service'; +import { DossierDictionariesMapService } from '@services/entity-services/dossier-dictionaries-map.service'; @Injectable({ providedIn: 'root' }) export class DossierFilesGuard implements CanActivate { @@ -14,6 +15,7 @@ export class DossierFilesGuard implements CanActivate { private readonly _filesMapService: FilesMapService, private readonly _filesService: FilesService, private readonly _dictionaryService: DictionaryService, + private readonly _dictionaryMapService: DossierDictionariesMapService, private readonly _router: Router, ) {} @@ -41,8 +43,10 @@ export class DossierFilesGuard implements CanActivate { async loadDossierData(dossierId: string, dossierTemplateId: string) { const promises = []; - const dictionary$ = this._dictionaryService.loadDossierDictionary(dossierTemplateId, dossierId); - promises.push(firstValueFrom(dictionary$)); + if (!this._dictionaryMapService.has(dossierId)) { + const dictionary$ = this._dictionaryService.loadDossierDictionary(dossierTemplateId, dossierId); + promises.push(firstValueFrom(dictionary$)); + } if (!this._filesMapService.has(dossierId)) { promises.push(firstValueFrom(this._filesService.loadAll(dossierId))); diff --git a/apps/red-ui/src/app/modules/account/account.module.ts b/apps/red-ui/src/app/modules/account/account.module.ts index df77271c3..a8b2382fa 100644 --- a/apps/red-ui/src/app/modules/account/account.module.ts +++ b/apps/red-ui/src/app/modules/account/account.module.ts @@ -6,13 +6,20 @@ import { AccountSideNavComponent } from './account-side-nav/account-side-nav.com import { BaseAccountScreenComponent } from './base-account-screen/base-account-screen-component'; import { NotificationPreferencesService } from './services/notification-preferences.service'; import { TranslateModule } from '@ngx-translate/core'; -import { IqserSharedModule } from '@iqser/common-ui'; -import { IqserHelpModeModule } from '@iqser/common-ui'; +import { IconButtonComponent, IqserHelpModeModule, SideNavComponent } from '@iqser/common-ui'; import { PreferencesComponent } from './screens/preferences/preferences.component'; @NgModule({ declarations: [AccountSideNavComponent, BaseAccountScreenComponent, PreferencesComponent], - imports: [CommonModule, SharedModule, AccountRoutingModule, TranslateModule, IqserSharedModule, IqserHelpModeModule], + imports: [ + CommonModule, + SharedModule, + AccountRoutingModule, + TranslateModule, + IqserHelpModeModule, + IconButtonComponent, + SideNavComponent, + ], providers: [NotificationPreferencesService], }) export class AccountModule {} diff --git a/apps/red-ui/src/app/modules/account/screens/notifications/notifications.module.ts b/apps/red-ui/src/app/modules/account/screens/notifications/notifications.module.ts index 4621b45c0..5cdaf8a16 100644 --- a/apps/red-ui/src/app/modules/account/screens/notifications/notifications.module.ts +++ b/apps/red-ui/src/app/modules/account/screens/notifications/notifications.module.ts @@ -5,11 +5,12 @@ import { SharedModule } from '@shared/shared.module'; import { NotificationsScreenComponent } from './notifications-screen/notifications-screen.component'; import { PendingChangesGuard } from '@guards/can-deactivate.guard'; import { TranslateModule } from '@ngx-translate/core'; +import { IconButtonComponent } from '@iqser/common-ui'; const routes = [{ path: '', component: NotificationsScreenComponent, canDeactivate: [PendingChangesGuard] }]; @NgModule({ declarations: [NotificationsScreenComponent], - imports: [RouterModule.forChild(routes), CommonModule, SharedModule, TranslateModule], + imports: [RouterModule.forChild(routes), CommonModule, SharedModule, TranslateModule, IconButtonComponent], }) export class NotificationsModule {} diff --git a/apps/red-ui/src/app/modules/account/screens/user-profile/user-profile-screen/user-profile-screen.component.ts b/apps/red-ui/src/app/modules/account/screens/user-profile/user-profile-screen/user-profile-screen.component.ts index 8d4bd718e..3307196e9 100644 --- a/apps/red-ui/src/app/modules/account/screens/user-profile/user-profile-screen/user-profile-screen.component.ts +++ b/apps/red-ui/src/app/modules/account/screens/user-profile/user-profile-screen/user-profile-screen.component.ts @@ -86,7 +86,7 @@ export class UserProfileScreenComponent extends BaseFormComponent implements OnI const value = this.form.getRawValue() as IProfile; if (this.emailChanged) { - const dialogRef = this._dialogService.openDialog('confirmPassword', null, null); + const dialogRef = this._dialogService.openDialog('confirmPassword'); const password = await firstValueFrom(dialogRef.afterClosed()); if (!password) { return; diff --git a/apps/red-ui/src/app/modules/account/screens/user-profile/user-profile.module.ts b/apps/red-ui/src/app/modules/account/screens/user-profile/user-profile.module.ts index 6ef42fa66..72f352ea7 100644 --- a/apps/red-ui/src/app/modules/account/screens/user-profile/user-profile.module.ts +++ b/apps/red-ui/src/app/modules/account/screens/user-profile/user-profile.module.ts @@ -7,12 +7,13 @@ import { PendingChangesGuard } from '@guards/can-deactivate.guard'; import { TranslateModule } from '@ngx-translate/core'; import { ConfirmPasswordDialogComponent } from './confirm-password-dialog/confirm-password-dialog.component'; import { UserProfileDialogService } from './services/user-profile-dialog.service'; +import { CircleButtonComponent, IconButtonComponent } from '@iqser/common-ui'; const routes = [{ path: '', component: UserProfileScreenComponent, canDeactivate: [PendingChangesGuard] }]; @NgModule({ declarations: [UserProfileScreenComponent, ConfirmPasswordDialogComponent], - imports: [RouterModule.forChild(routes), CommonModule, SharedModule, TranslateModule], + imports: [RouterModule.forChild(routes), CommonModule, SharedModule, TranslateModule, IconButtonComponent, CircleButtonComponent], providers: [UserProfileDialogService], }) export class UserProfileModule {} diff --git a/apps/red-ui/src/app/modules/admin/admin-routing.module.ts b/apps/red-ui/src/app/modules/admin/admin-routing.module.ts index 23456dd05..7492cccfa 100644 --- a/apps/red-ui/src/app/modules/admin/admin-routing.module.ts +++ b/apps/red-ui/src/app/modules/admin/admin-routing.module.ts @@ -3,13 +3,11 @@ import { CompositeRouteGuard, IqserAuthGuard, IqserPermissionsGuard, IqserRoutes import { RedRoleGuard } from '@users/red-role.guard'; import { EntitiesListingScreenComponent } from './screens/entities-listing/entities-listing-screen.component'; import { PendingChangesGuard } from '@guards/can-deactivate.guard'; -import { FileAttributesListingScreenComponent } from './screens/file-attributes-listing/file-attributes-listing-screen.component'; import { DefaultColorsScreenComponent } from './screens/default-colors/default-colors-screen.component'; import { UserListingScreenComponent } from './screens/user-listing/user-listing-screen.component'; import { DigitalSignatureScreenComponent } from './screens/digital-signature/digital-signature-screen.component'; import { AuditScreenComponent } from './screens/audit/audit-screen.component'; import { RouterModule } from '@angular/router'; -import { DossierAttributesListingScreenComponent } from './screens/dossier-attributes-listing/dossier-attributes-listing-screen.component'; import { GeneralConfigScreenComponent } from './screens/general-config/general-config-screen.component'; import { BaseAdminScreenComponent } from './base-admin-screen/base-admin-screen.component'; import { BaseDossierTemplateScreenComponent } from './base-dossier-templates-screen/base-dossier-template-screen.component'; @@ -17,7 +15,6 @@ import { DossierTemplatesGuard } from '@guards/dossier-templates.guard'; import { DOSSIER_TEMPLATE_ID, ENTITY_TYPE } from '@red/domain'; import { DossierTemplateExistsGuard } from '@guards/dossier-template-exists.guard'; import { EntityExistsGuard } from '@guards/entity-exists-guard.service'; -import { DossierStatesListingScreenComponent } from './screens/dossier-states-listing/dossier-states-listing-screen.component'; import { BaseEntityScreenComponent } from './base-entity-screen/base-entity-screen.component'; import { PermissionsGuard } from '@guards/permissions-guard'; import { ROLES } from '@users/roles'; @@ -65,7 +62,9 @@ const dossierTemplateIdRoutes: IqserRoutes = [ }, { path: 'file-attributes', - component: FileAttributesListingScreenComponent, + component: BaseDossierTemplateScreenComponent, + loadChildren: () => + import('./screens/file-attributes-listing/file-attributes-listing.module').then(m => m.FileAttributesListingModule), canActivate: [CompositeRouteGuard], data: { routeGuards: [IqserAuthGuard, RedRoleGuard], @@ -90,7 +89,9 @@ const dossierTemplateIdRoutes: IqserRoutes = [ }, { path: 'dossier-attributes', - component: DossierAttributesListingScreenComponent, + component: BaseDossierTemplateScreenComponent, + loadChildren: () => + import('./screens/dossier-attributes-listing/dossier-attributes-listing.module').then(m => m.DossierAttributesListingModule), canActivate: [CompositeRouteGuard], data: { routeGuards: [IqserAuthGuard, RedRoleGuard], @@ -98,7 +99,9 @@ const dossierTemplateIdRoutes: IqserRoutes = [ }, { path: 'dossier-states', - component: DossierStatesListingScreenComponent, + component: BaseDossierTemplateScreenComponent, + loadChildren: () => + import('./screens/dossier-states-listing/dossier-states-listing.module').then(m => m.DossierStatesListingModule), canActivate: [CompositeRouteGuard], data: { routeGuards: [IqserAuthGuard, RedRoleGuard], diff --git a/apps/red-ui/src/app/modules/admin/admin.module.ts b/apps/red-ui/src/app/modules/admin/admin.module.ts index 92aa673d4..881b9ddc6 100644 --- a/apps/red-ui/src/app/modules/admin/admin.module.ts +++ b/apps/red-ui/src/app/modules/admin/admin.module.ts @@ -6,10 +6,8 @@ import { AuditScreenComponent } from './screens/audit/audit-screen.component'; import { DefaultColorsScreenComponent } from './screens/default-colors/default-colors-screen.component'; import { EntitiesListingScreenComponent } from './screens/entities-listing/entities-listing-screen.component'; import { DigitalSignatureScreenComponent } from './screens/digital-signature/digital-signature-screen.component'; -import { FileAttributesListingScreenComponent } from './screens/file-attributes-listing/file-attributes-listing-screen.component'; import { UserListingScreenComponent } from './screens/user-listing/user-listing-screen.component'; -import { DossierTemplateBreadcrumbsComponent } from './components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component'; -import { AddEditFileAttributeDialogComponent } from './dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component'; +import { DossierTemplateBreadcrumbsComponent } from './shared/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component'; import { AddEditCloneDossierTemplateDialogComponent } from './dialogs/add-edit-dossier-template-dialog/add-edit-clone-dossier-template-dialog.component'; import { AddEntityDialogComponent } from './dialogs/add-entity-dialog/add-entity-dialog.component'; import { EditColorDialogComponent } from './dialogs/edit-color-dialog/edit-color-dialog.component'; @@ -18,12 +16,8 @@ import { GeneralConfigScreenComponent } from './screens/general-config/general-c import { SmtpAuthDialogComponent } from './dialogs/smtp-auth-dialog/smtp-auth-dialog.component'; import { AddEditUserDialogComponent } from './dialogs/add-edit-user-dialog/add-edit-user-dialog.component'; import { UsersStatsComponent } from './components/users-stats/users-stats.component'; -import { FileAttributesCsvImportDialogComponent } from './dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component'; -import { ActiveFieldsListingComponent } from './dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component'; import { ResetPasswordComponent } from './dialogs/add-edit-user-dialog/reset-password/reset-password.component'; import { UserDetailsComponent } from './dialogs/add-edit-user-dialog/user-details/user-details.component'; -import { AddEditDossierAttributeDialogComponent } from './dialogs/add-edit-dossier-attribute-dialog/add-edit-dossier-attribute-dialog.component'; -import { DossierAttributesListingScreenComponent } from './screens/dossier-attributes-listing/dossier-attributes-listing-screen.component'; import { AuditService } from './services/audit.service'; import { DigitalSignatureService } from './services/digital-signature.service'; import { BaseAdminScreenComponent } from './base-admin-screen/base-admin-screen.component'; @@ -32,48 +26,43 @@ import { SmtpConfigService } from './services/smtp-config.service'; import { UploadDictionaryDialogComponent } from './dialogs/upload-dictionary-dialog/upload-dictionary-dialog.component'; import { GeneralConfigFormComponent } from './screens/general-config/general-config-form/general-config-form.component'; import { SmtpFormComponent } from './screens/general-config/smtp-form/smtp-form.component'; -import { FileAttributesConfigurationsDialogComponent } from './dialogs/file-attributes-configurations-dialog/file-attributes-configurations-dialog.component'; -import { SharedAdminModule } from './shared/shared-admin.module'; import { BaseDossierTemplateScreenComponent } from './base-dossier-templates-screen/base-dossier-template-screen.component'; -import { DossierStatesListingScreenComponent } from './screens/dossier-states-listing/dossier-states-listing-screen.component'; -import { AddEditDossierStateDialogComponent } from './dialogs/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component'; import { A11yModule } from '@angular/cdk/a11y'; -import { ConfirmDeleteDossierStateDialogComponent } from './dialogs/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component'; import { BaseEntityScreenComponent } from './base-entity-screen/base-entity-screen.component'; -import { AdminSideNavComponent } from './admin-side-nav/admin-side-nav.component'; +import { AdminSideNavComponent } from './shared/components/admin-side-nav/admin-side-nav.component'; import { SystemPreferencesFormComponent } from './screens/general-config/system-preferences-form/system-preferences-form.component'; import { ConfigureCertificateDialogComponent } from './dialogs/configure-digital-signature-dialog/configure-certificate-dialog.component'; import { PkcsSignatureConfigurationComponent } from './dialogs/configure-digital-signature-dialog/form/pkcs-signature-configuration/pkcs-signature-configuration.component'; import { KmsSignatureConfigurationComponent } from './dialogs/configure-digital-signature-dialog/form/kms-signature-configuration/kms-signature-configuration.component'; import { + ChevronButtonComponent, + CircleButtonComponent, + DetailsRadioComponent, + EditableInputComponent, + EmptyStateComponent, + HasScrollbarDirective, HumanizePipe, - IqserButtonsModule, - IqserEmptyStatesModule, + IconButtonComponent, + InputWithActionComponent, + IqserAllowDirective, + IqserDenyDirective, IqserHelpModeModule, - IqserInputsModule, IqserListingModule, - IqserPermissionsModule, - IqserScrollbarModule, - IqserSharedModule, IqserUploadFileModule, IqserUsersModule, + RoundCheckboxComponent, } from '@iqser/common-ui'; import { TranslateModule } from '@ngx-translate/core'; import { AuditInfoDialogComponent } from './dialogs/audit-info-dialog/audit-info-dialog.component'; +import { DossierTemplateActionsComponent } from './shared/components/dossier-template-actions/dossier-template-actions.component'; const dialogs = [ AddEditCloneDossierTemplateDialogComponent, AddEntityDialogComponent, - AddEditFileAttributeDialogComponent, EditColorDialogComponent, SmtpAuthDialogComponent, AddEditUserDialogComponent, - FileAttributesConfigurationsDialogComponent, - FileAttributesCsvImportDialogComponent, - AddEditDossierAttributeDialogComponent, UploadDictionaryDialogComponent, - AddEditDossierStateDialogComponent, - ConfirmDeleteDossierStateDialogComponent, ConfigureCertificateDialogComponent, AuditInfoDialogComponent, ]; @@ -83,18 +72,12 @@ const screens = [ DefaultColorsScreenComponent, EntitiesListingScreenComponent, DigitalSignatureScreenComponent, - FileAttributesListingScreenComponent, UserListingScreenComponent, GeneralConfigScreenComponent, - DossierAttributesListingScreenComponent, - DossierStatesListingScreenComponent, ]; const components = [ - DossierTemplateBreadcrumbsComponent, UsersStatsComponent, - AdminSideNavComponent, - ActiveFieldsListingComponent, ResetPasswordComponent, UserDetailsComponent, BaseAdminScreenComponent, @@ -117,20 +100,27 @@ const components = [ CommonModule, SharedModule, AdminRoutingModule, - SharedAdminModule, A11yModule, IqserUsersModule, TranslateModule, HumanizePipe, - IqserButtonsModule, IqserListingModule, - IqserScrollbarModule, - IqserInputsModule, IqserUploadFileModule, - IqserEmptyStatesModule, - IqserSharedModule, IqserHelpModeModule, - IqserPermissionsModule, + AdminSideNavComponent, + DossierTemplateActionsComponent, + DossierTemplateBreadcrumbsComponent, + IconButtonComponent, + CircleButtonComponent, + ChevronButtonComponent, + EmptyStateComponent, + HasScrollbarDirective, + RoundCheckboxComponent, + InputWithActionComponent, + EditableInputComponent, + DetailsRadioComponent, + IqserAllowDirective, + IqserDenyDirective, ], }) export class AdminModule {} diff --git a/apps/red-ui/src/app/modules/admin/base-dossier-templates-screen/base-dossier-template-screen.component.html b/apps/red-ui/src/app/modules/admin/base-dossier-templates-screen/base-dossier-template-screen.component.html index ee08d19cc..84f9dc06e 100644 --- a/apps/red-ui/src/app/modules/admin/base-dossier-templates-screen/base-dossier-template-screen.component.html +++ b/apps/red-ui/src/app/modules/admin/base-dossier-templates-screen/base-dossier-template-screen.component.html @@ -10,6 +10,7 @@ diff --git a/apps/red-ui/src/app/modules/admin/base-entity-screen/base-entity-screen.component.ts b/apps/red-ui/src/app/modules/admin/base-entity-screen/base-entity-screen.component.ts index adcd8e171..a2b3a7af4 100644 --- a/apps/red-ui/src/app/modules/admin/base-entity-screen/base-entity-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/base-entity-screen/base-entity-screen.component.ts @@ -17,8 +17,8 @@ import { PermissionsService } from '@services/permissions.service'; export class BaseEntityScreenComponent { readonly disabledItems$: Observable; readonly canDeleteEntity$: Observable; - readonly #dossierTemplateId: string = getParam(DOSSIER_TEMPLATE_ID); - readonly #entityType: string = getParam(ENTITY_TYPE); + readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID); + readonly #entityType = getParam(ENTITY_TYPE); constructor( private readonly _router: Router, @@ -37,7 +37,7 @@ export class BaseEntityScreenComponent { } openDeleteDictionariesDialog() { - this._dialogService.openDialog('confirm', null, null, async () => { + this._dialogService.openDialog('confirm', null, async () => { this._loadingService.start(); const dossierTemplate = this._dossierTemplatesService.find(this.#dossierTemplateId); await firstValueFrom(this._dictionaryService.deleteDictionaries([this.#entityType], this.#dossierTemplateId)); diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/user-details/user-details.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/user-details/user-details.component.ts index b57e13cdf..8950b2780 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/user-details/user-details.component.ts +++ b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/user-details/user-details.component.ts @@ -103,7 +103,7 @@ export class UserDetailsComponent extends BaseFormComponent implements OnChanges } delete() { - this._dialogService.deleteUsers([this.user.id], null, () => this.closeDialog.emit(true)); + this._dialogService.deleteUsers([this.user.id], () => this.closeDialog.emit(true)); } setRolesRequirements(checked: boolean, role: string): void { diff --git a/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.html index fd39ef1e3..2c24e476d 100644 --- a/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.html @@ -27,8 +27,12 @@
- - + + {{ (translations[category] | translate) || category }} @@ -37,7 +41,7 @@
- +
- +
@@ -60,7 +64,13 @@
ยท
- + @@ -70,7 +80,13 @@
- + @@ -99,7 +115,7 @@
implements OnI } } + openAuditDetails(log: Audit) { + this._dialogService.openDialog('auditInfo', { auditEntry: log }); + } + private _getForm(): UntypedFormGroup { return this._formBuilder.group({ category: [this.ALL_CATEGORIES], @@ -136,8 +140,4 @@ export class AuditScreenComponent extends ListingComponent implements OnI } this._loadingService.stop(); } - - openAuditDetails($event: MouseEvent, log: Audit) { - this._dialogService.openDialog('auditInfo', $event, { auditEntry: log }); - } } diff --git a/apps/red-ui/src/app/modules/admin/screens/default-colors/default-colors-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/default-colors/default-colors-screen.component.html index da6885c67..533f1ec38 100644 --- a/apps/red-ui/src/app/modules/admin/screens/default-colors/default-colors-screen.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/default-colors/default-colors-screen.component.html @@ -38,7 +38,7 @@
{ ); } - openEditColorDialog($event: MouseEvent, color: ListItem) { - this._dialogService.openDialog('editColor', $event, { colorKey: color.key, dossierTemplateId: this.#dossierTemplateId }); + openEditColorDialog(color: ListItem) { + this._dialogService.openDialog('editColor', { colorKey: color.key, dossierTemplateId: this.#dossierTemplateId }); } } diff --git a/apps/red-ui/src/app/modules/admin/screens/digital-signature/digital-signature-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/digital-signature/digital-signature-screen.component.ts index 98d10be79..f53c497d4 100644 --- a/apps/red-ui/src/app/modules/admin/screens/digital-signature/digital-signature-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/digital-signature/digital-signature-screen.component.ts @@ -81,7 +81,7 @@ export class DigitalSignatureScreenComponent implements OnInit { } openConfigureCertificate(): void { - const dialogRef = this._dialogService.openDialog('configureCertificate', null, null); + const dialogRef = this._dialogService.openDialog('configureCertificate'); firstValueFrom(dialogRef.afterClosed()).then(async res => { if (res) { await this.loadDigitalSignature(); diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dossier-attribute-dialog/add-edit-dossier-attribute-dialog.component.html b/apps/red-ui/src/app/modules/admin/screens/dossier-attributes-listing/add-edit-dossier-attribute-dialog/add-edit-dossier-attribute-dialog.component.html similarity index 99% rename from apps/red-ui/src/app/modules/admin/dialogs/add-edit-dossier-attribute-dialog/add-edit-dossier-attribute-dialog.component.html rename to apps/red-ui/src/app/modules/admin/screens/dossier-attributes-listing/add-edit-dossier-attribute-dialog/add-edit-dossier-attribute-dialog.component.html index 42c9d30b1..c793c3bab 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dossier-attribute-dialog/add-edit-dossier-attribute-dialog.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/dossier-attributes-listing/add-edit-dossier-attribute-dialog/add-edit-dossier-attribute-dialog.component.html @@ -29,6 +29,7 @@
+
- - -
-
- - - -
- -
-
- +
+ +
@@ -73,6 +50,7 @@ +
@@ -91,7 +69,7 @@
{ - await this._loadData(); + await this.#loadData(); } async openConfirmDeleteAttributeDialog($event: MouseEvent, attributes: DossierAttributeConfig[] = this.listingService.selected) { - await this._dialogService.deleteAttributes( - attributes, - this.#dossierTemplateId, - this.impactedTemplatesRef, - 'dossier', - $event, - async () => { - this._loadingService.start(); - const ids = attributes.map(a => a.id); - await firstValueFrom(this._dossierAttributesService.delete(ids, this.#dossierTemplateId)); - await this._loadData(); - }, - ); + await this._dialogService.deleteAttributes(attributes, this.#dossierTemplateId, this.impactedTemplatesRef, 'dossier', async () => { + this._loadingService.start(); + const ids = attributes.map(a => a.id); + await firstValueFrom(this._dossierAttributesService.delete(ids, this.#dossierTemplateId)); + await this.#loadData(); + }); } - openAddEditAttributeDialog($event: MouseEvent, dossierAttribute?: IDossierAttributeConfig) { + async openAddEditAttributeDialog(dossierAttribute?: IDossierAttributeConfig) { const dossierTemplateId = this.#dossierTemplateId; - this._dialogService.openDialog('addEditDossierAttribute', $event, { dossierAttribute, dossierTemplateId }, async () => - this._loadData(), + const ref = this._dialog.open( + AddEditDossierAttributeDialogComponent, + { + ...defaultDialogConfig, + autoFocus: true, + data: { dossierAttribute, dossierTemplateId }, + }, ); + + const result = await firstValueFrom(ref.afterClosed()); + + if (result) { + await this.#loadData(); + } } - private async _loadData() { + async #loadData() { this._loadingService.start(); await firstValueFrom(this._dossierAttributesService.loadAll(this.#dossierTemplateId)); this._loadingService.stop(); diff --git a/apps/red-ui/src/app/modules/admin/screens/dossier-attributes-listing/dossier-attributes-listing.module.ts b/apps/red-ui/src/app/modules/admin/screens/dossier-attributes-listing/dossier-attributes-listing.module.ts new file mode 100644 index 000000000..5729c4cd7 --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/dossier-attributes-listing/dossier-attributes-listing.module.ts @@ -0,0 +1,37 @@ +import { NgModule } from '@angular/core'; +import { DossierAttributesListingScreenComponent } from './dossier-attributes-listing-screen.component'; +import { CommonModule } from '@angular/common'; +import { + CircleButtonComponent, + IconButtonComponent, + InputWithActionComponent, + IqserHelpModeModule, + IqserListingModule, + IqserRoutes, +} from '@iqser/common-ui'; +import { RouterModule } from '@angular/router'; +import { TranslateModule } from '@ngx-translate/core'; +import { MatTooltipModule } from '@angular/material/tooltip'; + +const routes: IqserRoutes = [ + { + path: '', + component: DossierAttributesListingScreenComponent, + }, +]; + +@NgModule({ + declarations: [DossierAttributesListingScreenComponent], + imports: [ + CommonModule, + RouterModule.forChild(routes), + TranslateModule, + CircleButtonComponent, + InputWithActionComponent, + IconButtonComponent, + IqserHelpModeModule, + MatTooltipModule, + IqserListingModule, + ], +}) +export class DossierAttributesListingModule {} diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component.html b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component.html similarity index 98% rename from apps/red-ui/src/app/modules/admin/dialogs/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component.html rename to apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component.html index 88f644372..41c82eed6 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component.html @@ -51,8 +51,7 @@ [label]="'add-edit-dossier-state.save' | translate" [submit]="true" [type]="iconButtonTypes.primary" - > - + >
diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component.scss b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component.scss similarity index 100% rename from apps/red-ui/src/app/modules/admin/dialogs/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component.scss rename to apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component.scss diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component.ts b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component.ts similarity index 92% rename from apps/red-ui/src/app/modules/admin/dialogs/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component.ts rename to apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component.ts index 280674e8f..70f623695 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component.ts @@ -6,8 +6,8 @@ import { DossierStatesService } from '@services/entity-services/dossier-states.s import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -interface DialogData { - readonly dossierState: IDossierState; +export interface AddEditDossierStateDialogData { + readonly dossierState?: IDossierState; readonly dossierTemplateId: string; } @@ -25,7 +25,7 @@ export class AddEditDossierStateDialogComponent extends BaseDialogComponent { constructor( private readonly _dossierStatesService: DossierStatesService, protected readonly _dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) readonly data: DialogData, + @Inject(MAT_DIALOG_DATA) readonly data: AddEditDossierStateDialogData, ) { super(_dialogRef, !!data.dossierState); this.form = this.#getForm(); diff --git a/apps/red-ui/src/app/modules/admin/dialogs/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component.html b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component.html similarity index 93% rename from apps/red-ui/src/app/modules/admin/dialogs/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component.html rename to apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component.html index 32e3c9315..0311a09eb 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component.html @@ -4,7 +4,7 @@
-
{{ 'confirm-delete-dossier-state.warning' | translate : translateArgs }}
+
diff --git a/apps/red-ui/src/app/modules/admin/dialogs/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component.scss b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component.scss similarity index 100% rename from apps/red-ui/src/app/modules/admin/dialogs/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component.scss rename to apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component.scss diff --git a/apps/red-ui/src/app/modules/admin/dialogs/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component.ts b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component.ts similarity index 61% rename from apps/red-ui/src/app/modules/admin/dialogs/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component.ts rename to apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component.ts index 50d35b6ec..bf8db1b8b 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component.ts @@ -1,74 +1,66 @@ -import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'; +import { Component, Inject } from '@angular/core'; import { DossierState } from '@red/domain'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { FormBuilder } from '@angular/forms'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; -import { firstValueFrom, forkJoin } from 'rxjs'; +import { combineLatest, firstValueFrom } from 'rxjs'; import { DossierStatesService } from '@services/entity-services/dossier-states.service'; import { IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui'; import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service'; import { ArchivedDossiersService } from '@services/dossiers/archived-dossiers.service'; -import { take } from 'rxjs/operators'; -interface DialogData { +export interface ConfirmDeleteDossierStateDialogData { readonly toBeDeletedState: DossierState; readonly otherStates: DossierState[]; readonly dossierCount: number; } @Component({ - selector: 'redaction-confirm-delete-dossier-state-dialog', templateUrl: './confirm-delete-dossier-state-dialog.component.html', styleUrls: ['./confirm-delete-dossier-state-dialog.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, }) export class ConfirmDeleteDossierStateDialogComponent { readonly iconButtonTypes = IconButtonTypes; - readonly form: UntypedFormGroup; + readonly form = this.#form; + readonly translateArgs = { + name: this.data.toBeDeletedState.name, + count: this.data.dossierCount, + }; constructor( - private readonly _formBuilder: UntypedFormBuilder, + private readonly _formBuilder: FormBuilder, private readonly _loadingService: LoadingService, private readonly _toaster: Toaster, private readonly _dossierStatesService: DossierStatesService, private readonly _dialogRef: MatDialogRef, private readonly _activeDossiersService: ActiveDossiersService, private readonly _archivedDossiersService: ArchivedDossiersService, - @Inject(MAT_DIALOG_DATA) readonly data: DialogData, - ) { - this.form = this.#getForm(); - } - - get translateArgs() { - return { - name: this.data.toBeDeletedState.name, - count: this.data.dossierCount, - }; - } + @Inject(MAT_DIALOG_DATA) readonly data: ConfirmDeleteDossierStateDialogData, + ) {} get label(): string { return this.#replaceDossierStatusId ? _('confirm-delete-dossier-state.delete-replace') : _('confirm-delete-dossier-state.delete'); } get #replaceDossierStatusId(): string { - return this.form.get('replace').value ? this.form.get('replaceDossierStatusId').value : undefined; + return this.form.controls.replace.value ? this.form.controls.replaceDossierStatusId.value : undefined; } - async save(): Promise { - this._loadingService.start(); - await firstValueFrom(this._dossierStatesService.deleteState(this.data.toBeDeletedState, this.#replaceDossierStatusId)); - await firstValueFrom( - forkJoin([this._activeDossiersService.loadAll().pipe(take(1)), this._archivedDossiersService.loadAll().pipe(take(1))]), - ); - this._toaster.success(_('confirm-delete-dossier-state.success')); - this._dialogRef.close(); - this._loadingService.stop(); - } - - #getForm(): UntypedFormGroup { + get #form() { return this._formBuilder.group({ replace: [false], replaceDossierStatusId: [null], }); } + + async save() { + this._loadingService.start(); + + await this._dossierStatesService.deleteState(this.data.toBeDeletedState, this.#replaceDossierStatusId); + await firstValueFrom(combineLatest([this._activeDossiersService.loadAll(), this._archivedDossiersService.loadAll()])); + + this._toaster.success(_('confirm-delete-dossier-state.success')); + this._dialogRef.close(); + this._loadingService.stop(); + } } diff --git a/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing-screen.component.html deleted file mode 100644 index 46106d68b..000000000 --- a/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing-screen.component.html +++ /dev/null @@ -1,101 +0,0 @@ -
- - -
-
- - - -
- -
- -
- -
-
-
- - -
- - - -
-
- - -
-
-
-
-
{{ state.name }}
-
-
- -
- {{ state.rank }} -
- -
- {{ state.dossierCount }} -
- -
-
-
- - -
-
-
-
-
diff --git a/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing-screen/dossier-states-listing-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing-screen/dossier-states-listing-screen.component.html new file mode 100644 index 000000000..f87e63f8b --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing-screen/dossier-states-listing-screen.component.html @@ -0,0 +1,44 @@ +
+ +
+ +
+ +
+ + +
+ + + +
+
+ + + + diff --git a/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing-screen/dossier-states-listing-screen.component.scss b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing-screen/dossier-states-listing-screen.component.scss new file mode 100644 index 000000000..a1aa8713e --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing-screen/dossier-states-listing-screen.component.scss @@ -0,0 +1,8 @@ +:host { + flex-direction: row !important; +} + +.right-container { + padding: 50px 26px 0; + width: 250px; +} diff --git a/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing-screen/dossier-states-listing-screen.component.ts similarity index 67% rename from apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing-screen.component.ts rename to apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing-screen/dossier-states-listing-screen.component.ts index e144ba6dc..b830e8473 100644 --- a/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing-screen/dossier-states-listing-screen.component.ts @@ -1,6 +1,6 @@ -import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit } from '@angular/core'; import { - CircleButtonTypes, + defaultDialogConfig, getParam, IconButtonTypes, ListingComponent, @@ -8,35 +8,37 @@ import { SortingOrders, TableColumnConfig, } from '@iqser/common-ui'; -import { DonutChartConfig, DossierState, IDossierState } from '@red/domain'; +import { type DonutChartConfig, DOSSIER_TEMPLATE_ID, type DossierState } from '@red/domain'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { firstValueFrom, Observable } from 'rxjs'; -import { AdminDialogService } from '../../services/admin-dialog.service'; import { DossierStatesMapService } from '@services/entity-services/dossier-states-map.service'; import { map, tap } from 'rxjs/operators'; import { DossierStatesService } from '@services/entity-services/dossier-states.service'; import { PermissionsService } from '@services/permissions.service'; +import { MatDialog } from '@angular/material/dialog'; +import { + AddEditDossierStateDialogComponent, + AddEditDossierStateDialogData, +} from '../add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component'; @Component({ templateUrl: './dossier-states-listing-screen.component.html', styleUrls: ['./dossier-states-listing-screen.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, providers: listingProvidersFactory(DossierStatesListingScreenComponent), }) export class DossierStatesListingScreenComponent extends ListingComponent implements OnInit, OnDestroy { readonly iconButtonTypes = IconButtonTypes; - readonly circleButtonTypes = CircleButtonTypes; readonly tableHeaderLabel = _('dossier-states-listing.table-header.title'); readonly tableColumnConfigs: TableColumnConfig[] = [ { label: _('dossier-states-listing.table-col-names.name'), sortByKey: 'name', width: '3fr' }, { label: _('dossier-states-listing.table-col-names.rank'), sortByKey: 'rank', class: 'flex-center' }, { label: _('dossier-states-listing.table-col-names.dossiers-count'), class: 'flex-center' }, ]; - chartConfig$: Observable; - readonly #dossierTemplateId = getParam('dossierTemplateId'); + readonly chartConfig$: Observable; + readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID); constructor( - private readonly _dialogService: AdminDialogService, + private readonly _dialog: MatDialog, private readonly _dossierStatesMapService: DossierStatesMapService, private readonly _dossierStatesService: DossierStatesService, readonly permissionsService: PermissionsService, @@ -56,21 +58,13 @@ export class DossierStatesListingScreenComponent extends ListingComponent state.id !== dossierState.id), - dossierCount: dossierState.dossierCount, - }; - this._dialogService.openDialog('deleteDossierState', $event, data); + openAddStateDialog() { + this._dialog.open(AddEditDossierStateDialogComponent, { + ...defaultDialogConfig, + data: { + dossierTemplateId: this.#dossierTemplateId, + }, + }); } #chartConfig(states: DossierState[]): DonutChartConfig[] { diff --git a/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing.module.ts b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing.module.ts new file mode 100644 index 000000000..6b34c2cf5 --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing.module.ts @@ -0,0 +1,62 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { DossierStatesListingScreenComponent } from './dossier-states-listing-screen/dossier-states-listing-screen.component'; +import { DossierStatesTableItemComponent } from './dossier-states-table-item/dossier-states-table-item.component'; +import { RouterModule } from '@angular/router'; +import { + CircleButtonComponent, + IconButtonComponent, + InputWithActionComponent, + IqserHelpModeModule, + IqserListingModule, +} from '@iqser/common-ui'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { TranslateModule } from '@ngx-translate/core'; +import { DonutChartComponent } from '@shared/components/donut-chart/donut-chart.component'; +import { AdminSideNavComponent } from '../../shared/components/admin-side-nav/admin-side-nav.component'; +import { DossierTemplateActionsComponent } from '../../shared/components/dossier-template-actions/dossier-template-actions.component'; +import { DossierTemplateBreadcrumbsComponent } from '../../shared/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component'; +import { AddEditDossierStateDialogComponent } from './add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component'; +import { ReactiveFormsModule } from '@angular/forms'; +import { ColorPickerModule } from 'ngx-color-picker'; +import { MatIconModule } from '@angular/material/icon'; +import { ConfirmDeleteDossierStateDialogComponent } from './confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component'; +import { MatLegacyCheckboxModule } from '@angular/material/legacy-checkbox'; +import { MatSelectModule } from '@angular/material/select'; +import { MatDialogModule } from '@angular/material/dialog'; + +@NgModule({ + declarations: [ + DossierStatesListingScreenComponent, + DossierStatesTableItemComponent, + AddEditDossierStateDialogComponent, + ConfirmDeleteDossierStateDialogComponent, + ], + imports: [ + CommonModule, + RouterModule.forChild([ + { + path: '', + component: DossierStatesListingScreenComponent, + }, + ]), + MatDialogModule, + CircleButtonComponent, + IqserHelpModeModule, + MatTooltipModule, + IconButtonComponent, + TranslateModule, + IqserListingModule, + DonutChartComponent, + AdminSideNavComponent, + DossierTemplateActionsComponent, + InputWithActionComponent, + DossierTemplateBreadcrumbsComponent, + ReactiveFormsModule, + ColorPickerModule, + MatIconModule, + MatLegacyCheckboxModule, + MatSelectModule, + ], +}) +export class DossierStatesListingModule {} diff --git a/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-table-item/dossier-states-table-item.component.html b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-table-item/dossier-states-table-item.component.html new file mode 100644 index 000000000..570097587 --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-table-item/dossier-states-table-item.component.html @@ -0,0 +1,34 @@ +
+
+
+
{{ state.name }}
+
+
+ +
+ {{ state.rank }} +
+ +
+ {{ state.dossierCount }} +
+ +
+
+
+ + + +
+
+
diff --git a/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing-screen.component.scss b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-table-item/dossier-states-table-item.component.scss similarity index 80% rename from apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing-screen.component.scss rename to apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-table-item/dossier-states-table-item.component.scss index bce23ef20..76adc2722 100644 --- a/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-listing-screen.component.scss +++ b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-table-item/dossier-states-table-item.component.scss @@ -12,11 +12,6 @@ color: var(--iqser-text); } -.right-container { - padding: 50px 26px 0; - width: 250px; -} - .action-buttons > div { display: flex; } diff --git a/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-table-item/dossier-states-table-item.component.ts b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-table-item/dossier-states-table-item.component.ts new file mode 100644 index 000000000..a9236d212 --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/dossier-states-listing/dossier-states-table-item/dossier-states-table-item.component.ts @@ -0,0 +1,53 @@ +import { Component, inject, Input } from '@angular/core'; +import { CircleButtonTypes, defaultDialogConfig, EntitiesService } from '@iqser/common-ui'; +import { DossierState, IDossierState } from '@red/domain'; +import { PermissionsService } from '@services/permissions.service'; +import { + AddEditDossierStateDialogComponent, + AddEditDossierStateDialogData, +} from '../add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component'; +import { MatDialog } from '@angular/material/dialog'; +import { + ConfirmDeleteDossierStateDialogComponent, + ConfirmDeleteDossierStateDialogData, +} from '../confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component'; + +@Component({ + selector: 'redaction-dossier-states-table-item', + templateUrl: './dossier-states-table-item.component.html', + styleUrls: ['./dossier-states-table-item.component.scss'], +}) +export class DossierStatesTableItemComponent { + @Input() state: DossierState; + + readonly circleButtonTypes = CircleButtonTypes; + readonly permissionsService = inject(PermissionsService); + readonly #dialog = inject(MatDialog); + readonly #entitiesService = inject(EntitiesService); + + openConfirmDeleteStateDialog(dossierState: DossierState) { + const data: ConfirmDeleteDossierStateDialogData = { + toBeDeletedState: dossierState, + otherStates: this.#entitiesService.all.filter(s => s.id !== dossierState.id), + dossierCount: dossierState.dossierCount, + }; + + this.#dialog.open( + ConfirmDeleteDossierStateDialogComponent, + { + ...defaultDialogConfig, + data, + }, + ); + } + + openEditStateDialog(dossierState: IDossierState) { + this.#dialog.open(AddEditDossierStateDialogComponent, { + ...defaultDialogConfig, + data: { + dossierState, + dossierTemplateId: dossierState.dossierTemplateId, + }, + }); + } +} diff --git a/apps/red-ui/src/app/modules/admin/screens/dossier-templates-listing/dossier-templates-listing-screen/dossier-templates-listing-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/dossier-templates-listing/dossier-templates-listing-screen/dossier-templates-listing-screen.component.html index dfb60f821..b0339acaf 100644 --- a/apps/red-ui/src/app/modules/admin/screens/dossier-templates-listing/dossier-templates-listing-screen/dossier-templates-listing-screen.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/dossier-templates-listing/dossier-templates-listing-screen/dossier-templates-listing-screen.component.html @@ -18,21 +18,21 @@
{ + openBulkDeleteTemplatesDialog() { + return this._dialogService.openDialog('confirm', null, () => { this._loadingService.loadWhile(this._deleteTemplates()); }); } openAddDossierTemplateDialog() { - this._dialogService.openDialog('addEditCloneDossierTemplate', null, null); + this._dialogService.openDialog('addEditCloneDossierTemplate'); } private async _deleteTemplates(templateIds = this.listingService.selected.map(d => d.dossierTemplateId)) { diff --git a/apps/red-ui/src/app/modules/admin/screens/dossier-templates-listing/dossier-templates-listing.module.ts b/apps/red-ui/src/app/modules/admin/screens/dossier-templates-listing/dossier-templates-listing.module.ts index c311876e6..5334b90e3 100644 --- a/apps/red-ui/src/app/modules/admin/screens/dossier-templates-listing/dossier-templates-listing.module.ts +++ b/apps/red-ui/src/app/modules/admin/screens/dossier-templates-listing/dossier-templates-listing.module.ts @@ -4,18 +4,20 @@ import { RouterModule } from '@angular/router'; import { SharedModule } from '@shared/shared.module'; import { TableItemComponent } from './table-item/table-item.component'; import { DossierTemplatesListingScreenComponent } from './dossier-templates-listing-screen/dossier-templates-listing-screen.component'; -import { SharedAdminModule } from '../../shared/shared-admin.module'; import { - IqserButtonsModule, + CircleButtonComponent, + IconButtonComponent, + InputWithActionComponent, + IqserAllowDirective, IqserHelpModeModule, - IqserInputsModule, IqserListingModule, - IqserPermissionsModule, + IqserRoutes, IqserUsersModule, } from '@iqser/common-ui'; import { TranslateModule } from '@ngx-translate/core'; +import { DossierTemplateActionsComponent } from '../../shared/components/dossier-template-actions/dossier-template-actions.component'; -const routes = [{ path: '', component: DossierTemplatesListingScreenComponent }]; +const routes: IqserRoutes = [{ path: '', component: DossierTemplatesListingScreenComponent }]; @NgModule({ declarations: [TableItemComponent, DossierTemplatesListingScreenComponent], @@ -23,14 +25,15 @@ const routes = [{ path: '', component: DossierTemplatesListingScreenComponent }] RouterModule.forChild(routes), CommonModule, SharedModule, - SharedAdminModule, IqserUsersModule, TranslateModule, - IqserInputsModule, - IqserButtonsModule, IqserListingModule, IqserHelpModeModule, - IqserPermissionsModule, + DossierTemplateActionsComponent, + CircleButtonComponent, + IconButtonComponent, + InputWithActionComponent, + IqserAllowDirective, ], }) export class DossierTemplatesListingModule {} diff --git a/apps/red-ui/src/app/modules/admin/screens/dossier-templates-listing/table-item/table-item.component.html b/apps/red-ui/src/app/modules/admin/screens/dossier-templates-listing/table-item/table-item.component.html index 0df08d19a..8d9d1a787 100644 --- a/apps/red-ui/src/app/modules/admin/screens/dossier-templates-listing/table-item/table-item.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/dossier-templates-listing/table-item/table-item.component.html @@ -6,7 +6,7 @@
- {{ 'dossier-templates-listing.entities' | translate: { length: stats.numberOfDictionaries } }} + {{ 'dossier-templates-listing.entities' | translate : { length: stats.numberOfDictionaries } }}
@@ -22,25 +22,25 @@
- {{ dossierTemplate.dateAdded | date: 'd MMM yyyy' }} + {{ dossierTemplate.dateAdded | date : 'd MMM yyyy' }}
- {{ dossierTemplate.dateModified | date: 'd MMM yyyy' }} + {{ dossierTemplate.dateModified | date : 'd MMM yyyy' }}
- {{ dossierTemplate.validFrom | date: 'd MMM yyyy' }} + {{ dossierTemplate.validFrom | date : 'd MMM yyyy' }}
- {{ dossierTemplate.validTo | date: 'd MMM yyyy' }} + {{ dossierTemplate.validTo | date : 'd MMM yyyy' }}
@@ -48,6 +48,7 @@
{{ translations[dossierTemplate.dossierTemplateStatus] | translate }}
+ @@ -101,7 +101,7 @@
this.templateStats$ = this._dossierTemplateStatsService.watch$(this.#dossierTemplateId).pipe(tap(() => this._loadDictionaryData())); } - openDeleteEntitiesDialog($event?: MouseEvent, types = this.listingService.selected) { - this._dialogService.openDialog('confirm', $event, null, async () => { + openDeleteEntitiesDialog(types = this.listingService.selected) { + this._dialogService.openDialog('confirm', null, async () => { this._loadingService.start(); await firstValueFrom( this._dictionaryService.deleteDictionaries( @@ -62,8 +62,8 @@ export class EntitiesListingScreenComponent extends ListingComponent }); } - openAddEntityDialog($event?: MouseEvent) { - this._dialogService.openDialog('addEntity', $event, { dossierTemplateId: this.#dossierTemplateId }); + openAddEntityDialog() { + this._dialogService.openDialog('addEntity', { dossierTemplateId: this.#dossierTemplateId }); } private _loadDictionaryData(): void { diff --git a/apps/red-ui/src/app/modules/admin/screens/entities/entities.module.ts b/apps/red-ui/src/app/modules/admin/screens/entities/entities.module.ts index 7f874e8cc..5eeddef83 100644 --- a/apps/red-ui/src/app/modules/admin/screens/entities/entities.module.ts +++ b/apps/red-ui/src/app/modules/admin/screens/entities/entities.module.ts @@ -6,9 +6,8 @@ import { DictionaryScreenComponent } from './screens/dictionary/dictionary-scree import { PendingChangesGuard } from '@guards/can-deactivate.guard'; import { EntityInfoComponent } from './screens/entity-info/entity-info.component'; import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor'; -import { SharedAdminModule } from '../../shared/shared-admin.module'; import { TranslateModule } from '@ngx-translate/core'; -import { IqserHelpModeModule, IqserPermissionsModule, IqserScrollbarModule } from '@iqser/common-ui'; +import { HasScrollbarDirective, IconButtonComponent, IqserDenyDirective, IqserHelpModeModule } from '@iqser/common-ui'; const routes: Routes = [ { path: '', redirectTo: 'info', pathMatch: 'full' }, @@ -38,14 +37,14 @@ const routes: Routes = [ declarations: [DictionaryScreenComponent, EntityInfoComponent], imports: [ RouterModule.forChild(routes), - SharedAdminModule, CommonModule, SharedModule, MonacoEditorModule, TranslateModule, - IqserScrollbarModule, IqserHelpModeModule, - IqserPermissionsModule, + IconButtonComponent, + HasScrollbarDirective, + IqserDenyDirective, ], }) export class EntitiesModule {} diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component.html b/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component.html similarity index 100% rename from apps/red-ui/src/app/modules/admin/dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component.html rename to apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component.html diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component.scss b/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component.scss similarity index 63% rename from apps/red-ui/src/app/modules/admin/dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component.scss rename to apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component.scss index 21b66161f..711304813 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component.scss +++ b/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component.scss @@ -1,3 +1,5 @@ +@use 'common-mixins'; + .options-wrapper { display: flex; align-items: center; @@ -7,3 +9,7 @@ margin-right: 32px; } } + +.dialog-header { + @include common-mixins.line-clamp(1); +} diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component.ts b/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component.ts similarity index 74% rename from apps/red-ui/src/app/modules/admin/dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component.ts rename to apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component.ts index 4c83ffc7c..0e61bd806 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component.ts @@ -1,9 +1,16 @@ import { Component, Inject } from '@angular/core'; -import { Validators } from '@angular/forms'; +import { ReactiveFormsModule, Validators } from '@angular/forms'; import { FileAttributeConfigTypes, IFileAttributeConfig } from '@red/domain'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { fileAttributeTypesTranslations } from '@translations/file-attribute-types-translations'; -import { BaseDialogComponent } from '@iqser/common-ui'; +import { BaseDialogComponent, CircleButtonComponent, IconButtonComponent } from '@iqser/common-ui'; +import { TranslateModule } from '@ngx-translate/core'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatSelectModule } from '@angular/material/select'; +import { NgForOf } from '@angular/common'; +import { MatLegacySlideToggleModule } from '@angular/material/legacy-slide-toggle'; +import { MatLegacyCheckboxModule as MatCheckboxModule } from '@angular/material/legacy-checkbox'; +import { MatTooltipModule } from '@angular/material/tooltip'; export interface AddEditFileAttributeDialogData { readonly fileAttribute: IFileAttributeConfig; @@ -15,6 +22,19 @@ export interface AddEditFileAttributeDialogData { @Component({ templateUrl: './add-edit-file-attribute-dialog.component.html', styleUrls: ['./add-edit-file-attribute-dialog.component.scss'], + standalone: true, + imports: [ + ReactiveFormsModule, + TranslateModule, + MatFormFieldModule, + MatSelectModule, + NgForOf, + MatLegacySlideToggleModule, + MatCheckboxModule, + MatTooltipModule, + IconButtonComponent, + CircleButtonComponent, + ], }) export class AddEditFileAttributeDialogComponent extends BaseDialogComponent { readonly DISPLAYED_FILTERABLE_LIMIT = 3; @@ -30,7 +50,7 @@ export class AddEditFileAttributeDialogComponent extends BaseDialogComponent { }; constructor( - protected readonly _dialogRef: MatDialogRef, + protected readonly _dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) readonly data: AddEditFileAttributeDialogData, ) { super(_dialogRef, !!data.fileAttribute); diff --git a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-configurations-dialog/file-attributes-configurations-dialog.component.html b/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-configurations-dialog/file-attributes-configurations-dialog.component.html similarity index 100% rename from apps/red-ui/src/app/modules/admin/dialogs/file-attributes-configurations-dialog/file-attributes-configurations-dialog.component.html rename to apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-configurations-dialog/file-attributes-configurations-dialog.component.html diff --git a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-configurations-dialog/file-attributes-configurations-dialog.component.ts b/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-configurations-dialog/file-attributes-configurations-dialog.component.ts similarity index 65% rename from apps/red-ui/src/app/modules/admin/dialogs/file-attributes-configurations-dialog/file-attributes-configurations-dialog.component.ts rename to apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-configurations-dialog/file-attributes-configurations-dialog.component.ts index 1d2d34bcb..41db9883c 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-configurations-dialog/file-attributes-configurations-dialog.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-configurations-dialog/file-attributes-configurations-dialog.component.ts @@ -1,23 +1,37 @@ -import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { UntypedFormGroup, Validators } from '@angular/forms'; +import { ReactiveFormsModule, Validators } from '@angular/forms'; import { FileAttributeEncodingTypes, IFileAttributesConfig } from '@red/domain'; import { fileAttributeEncodingTypesTranslations } from '@translations/file-attribute-encoding-types-translations'; -import { BaseDialogComponent } from '@iqser/common-ui'; +import { BaseDialogComponent, CircleButtonComponent, IconButtonComponent } from '@iqser/common-ui'; +import { MatLegacySlideToggleModule } from '@angular/material/legacy-slide-toggle'; +import { NgForOf, NgIf } from '@angular/common'; +import { TranslateModule } from '@ngx-translate/core'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatSelectModule } from '@angular/material/select'; @Component({ templateUrl: './file-attributes-configurations-dialog.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + standalone: true, + imports: [ + ReactiveFormsModule, + MatLegacySlideToggleModule, + NgIf, + TranslateModule, + MatFormFieldModule, + MatSelectModule, + NgForOf, + IconButtonComponent, + CircleButtonComponent, + ], }) export class FileAttributesConfigurationsDialogComponent extends BaseDialogComponent { readonly encodingTypeOptions = Object.keys(FileAttributeEncodingTypes); readonly translations = fileAttributeEncodingTypesTranslations; - readonly #configuration = this._data.config; + readonly #configuration = inject(MAT_DIALOG_DATA); - constructor( - protected readonly _dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) private readonly _data: { config: IFileAttributesConfig; dossierTemplateId: string }, - ) { + constructor(protected readonly _dialogRef: MatDialogRef) { super(_dialogRef, true); this.form = this.#getForm(); this.initialFormValue = this.form.getRawValue(); @@ -39,7 +53,7 @@ export class FileAttributesConfigurationsDialogComponent extends BaseDialogCompo this._dialogRef.close(this.#getConfiguration()); } - #getConfiguration() { + #getConfiguration(): IFileAttributesConfig { const supportCsvMapping = this.form.get('supportCsvMapping').value; if (supportCsvMapping) { return { @@ -56,7 +70,7 @@ export class FileAttributesConfigurationsDialogComponent extends BaseDialogCompo }; } - #getForm(): UntypedFormGroup { + #getForm() { return this._formBuilder.group({ supportCsvMapping: [!!this.#configuration.filenameMappingColumnHeaderName], keyColumn: [this.#configuration.filenameMappingColumnHeaderName || '', [Validators.required]], diff --git a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.html b/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.html similarity index 100% rename from apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.html rename to apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.html diff --git a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.scss b/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.scss similarity index 100% rename from apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.scss rename to apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.scss diff --git a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.ts b/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.ts similarity index 100% rename from apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.ts rename to apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.ts diff --git a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.html b/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.html similarity index 100% rename from apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.html rename to apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.html diff --git a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.scss b/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.scss similarity index 100% rename from apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.scss rename to apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.scss diff --git a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.ts b/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.ts similarity index 99% rename from apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.ts rename to apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.ts index f84139d14..010bdaa8a 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.ts @@ -10,7 +10,7 @@ import { FileAttributeConfig, FileAttributeConfigTypes, FileAttributeEncodingTyp import { FileAttributesService } from '@services/entity-services/file-attributes.service'; import { fileAttributeEncodingTypesTranslations } from '@translations/file-attribute-encoding-types-translations'; -interface IFileAttributesCSVImportData { +export interface IFileAttributesCSVImportData { readonly csv: File; readonly dossierTemplateId: string; readonly existingConfiguration: IFileAttributesConfig; @@ -43,7 +43,7 @@ export class FileAttributesCsvImportDialogComponent extends ListingComponent, + readonly dialogRef: MatDialogRef, private readonly _fileAttributesService: FileAttributesService, @Inject(MAT_DIALOG_DATA) readonly data: IFileAttributesCSVImportData, ) { diff --git a/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.module.ts b/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.module.ts new file mode 100644 index 000000000..15dcd8708 --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.module.ts @@ -0,0 +1,47 @@ +import { NgModule } from '@angular/core'; +import { FileAttributesCsvImportDialogComponent } from './file-attributes-csv-import-dialog.component'; +import { ActiveFieldsListingComponent } from './active-fields-listing/active-fields-listing.component'; +import { + ChevronButtonComponent, + CircleButtonComponent, + EditableInputComponent, + IconButtonComponent, + InputWithActionComponent, + IqserListingModule, + RoundCheckboxComponent, +} from '@iqser/common-ui'; +import { CommonModule } from '@angular/common'; +import { TranslateModule } from '@ngx-translate/core'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatSelectModule } from '@angular/material/select'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatAutocompleteModule } from '@angular/material/autocomplete'; +import { MatInputModule } from '@angular/material/input'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatLegacySlideToggleModule } from '@angular/material/legacy-slide-toggle'; + +@NgModule({ + declarations: [FileAttributesCsvImportDialogComponent, ActiveFieldsListingComponent], + imports: [ + CommonModule, + CircleButtonComponent, + TranslateModule, + MatDialogModule, + IconButtonComponent, + InputWithActionComponent, + MatFormFieldModule, + MatSelectModule, + ReactiveFormsModule, + MatAutocompleteModule, + MatInputModule, + IqserListingModule, + MatMenuModule, + ChevronButtonComponent, + EditableInputComponent, + FormsModule, + MatLegacySlideToggleModule, + RoundCheckboxComponent, + ], +}) +export class FileAttributesCsvImportDialogModule {} diff --git a/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-listing-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-listing-screen.component.html index 5b12e3b78..aa3fc3f99 100644 --- a/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-listing-screen.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-listing-screen.component.html @@ -1,41 +1,18 @@ -
- - -
-
- - - -
- -
- -
-
-
+
    @@ -45,7 +22,7 @@ +
    +
    @@ -134,13 +113,14 @@
    + implements OnInit, OnDestroy { @@ -58,6 +69,7 @@ export class FileAttributesListingScreenComponent extends ListingComponent { - this._loadingService.loadWhile(this.#createNewFileAttributeAndRefreshView(newValue)); + + const ref = this._dialog.open( + AddEditFileAttributeDialogComponent, + { + ...defaultDialogConfig, + autoFocus: true, + data, + }, + ); + + const result = await firstValueFrom(ref.afterClosed()); + this._loadingService.loadWhile(this.#createNewFileAttributeAndRefreshView(result)); + } + + async openConfirmDeleteAttributeDialog(attributes: FileAttributeConfig[] = this.listingService.selected): Promise { + await this._dialogService.deleteAttributes(attributes, this.#dossierTemplateId, this._impactedTemplatesRef, 'file', async () => { + this._loadingService.start(); + const ids = attributes.map(a => a.id); + await firstValueFrom(this._fileAttributesService.deleteFileAttributes(ids, this.#dossierTemplateId)); + await firstValueFrom(this._dossierTemplatesService.refreshDossierTemplate(this.#dossierTemplateId)); + await this.#loadData(); }); } - async openConfirmDeleteAttributeDialog( - $event: MouseEvent, - attributes: FileAttributeConfig[] = this.listingService.selected, - ): Promise { - await this._dialogService.deleteAttributes( - attributes, - this.#dossierTemplateId, - this._impactedTemplatesRef, - 'file', - $event, - async () => { - this._loadingService.start(); - const ids = attributes.map(a => a.id); - await firstValueFrom(this._fileAttributesService.deleteFileAttributes(ids, this.#dossierTemplateId)); - await firstValueFrom(this._dossierTemplatesService.refreshDossierTemplate(this.#dossierTemplateId)); - await this.#loadData(); - }, - ); - } - - importCSV(files: FileList | File[]) { + async importCSV(files: FileList | File[]) { const csv = files[0]; this._fileInput.nativeElement.value = null; - this._dialogService.openDialog( - 'importFileAttributes', - null, + const ref = this._dialog.open( + FileAttributesCsvImportDialogComponent, { - csv, - dossierTemplateId: this.#dossierTemplateId, - existingConfiguration: this.#existingConfiguration, + ...largeDialogConfig, + disableClose: false, + data: { + csv, + dossierTemplateId: this.#dossierTemplateId, + existingConfiguration: this.#existingConfiguration, + }, }, - async () => this.#loadData(), ); + + const result = await firstValueFrom(ref.afterClosed()); + if (result) { + await this.#loadData(); + } } - openConfigurationsDialog($event: MouseEvent) { - const ref = this._dialogService.openDialog('fileAttributesConfigurations', $event, { - config: this.#existingConfiguration, - dossierTemplateId: this.#dossierTemplateId, - }); + async openConfigurationsDialog() { + const ref = this._dialog.open( + FileAttributesConfigurationsDialogComponent, + { + ...defaultDialogConfig, + data: this.#existingConfiguration, + }, + ); - ref.afterClosed().subscribe(async (configuration: IFileAttributesConfig) => { - if (configuration) { - await this.#setConfigAndLoadData(configuration); - } - }); + const configuration = await firstValueFrom(ref.afterClosed()); + + if (configuration) { + await this.#setConfigAndLoadData(configuration); + } } async #setConfigAndLoadData(configuration: IFileAttributesConfig) { diff --git a/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-listing.module.ts b/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-listing.module.ts new file mode 100644 index 000000000..07b2937e3 --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-listing.module.ts @@ -0,0 +1,43 @@ +import { NgModule } from '@angular/core'; +import { FileAttributesListingScreenComponent } from './file-attributes-listing-screen.component'; +import { RouterModule } from '@angular/router'; +import { + CircleButtonComponent, + IconButtonComponent, + InputWithActionComponent, + IqserAllowDirective, + IqserHelpModeModule, + IqserListingModule, + IqserRoutes, + RoundCheckboxComponent, +} from '@iqser/common-ui'; +import { CommonModule } from '@angular/common'; +import { TranslateModule } from '@ngx-translate/core'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTooltipModule } from '@angular/material/tooltip'; + +const routes: IqserRoutes = [ + { + path: '', + component: FileAttributesListingScreenComponent, + }, +]; + +@NgModule({ + declarations: [FileAttributesListingScreenComponent], + imports: [ + CommonModule, + RouterModule.forChild(routes), + IqserListingModule, + TranslateModule, + CircleButtonComponent, + InputWithActionComponent, + IqserHelpModeModule, + IqserAllowDirective, + IconButtonComponent, + MatIconModule, + MatTooltipModule, + RoundCheckboxComponent, + ], +}) +export class FileAttributesListingModule {} diff --git a/apps/red-ui/src/app/modules/admin/screens/general-config/smtp-form/smtp-form.component.ts b/apps/red-ui/src/app/modules/admin/screens/general-config/smtp-form/smtp-form.component.ts index e6194bd90..e1830a2bf 100644 --- a/apps/red-ui/src/app/modules/admin/screens/general-config/smtp-form/smtp-form.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/general-config/smtp-form/smtp-form.component.ts @@ -42,7 +42,7 @@ export class SmtpFormComponent extends BaseFormComponent implements OnInit { } openAuthConfigDialog(skipDisableOnCancel?: boolean) { - this._dialogService.openDialog('smtpAuthConfig', null, this.form.getRawValue(), null, authConfig => { + this._dialogService.openDialog('smtpAuthConfig', this.form.getRawValue(), null, authConfig => { if (authConfig) { this.form.patchValue(authConfig); } else if (!skipDisableOnCancel) { diff --git a/apps/red-ui/src/app/modules/admin/screens/info/dossier-template-info.module.ts b/apps/red-ui/src/app/modules/admin/screens/info/dossier-template-info.module.ts index bd74d6021..f4ad7c25d 100644 --- a/apps/red-ui/src/app/modules/admin/screens/info/dossier-template-info.module.ts +++ b/apps/red-ui/src/app/modules/admin/screens/info/dossier-template-info.module.ts @@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common'; import { DossierTemplateInfoScreenComponent } from './info-screen/dossier-template-info-screen.component'; import { RouterModule } from '@angular/router'; import { SharedModule } from '@shared/shared.module'; -import { IqserButtonsModule, IqserHelpModeModule, IqserScrollbarModule, IqserUsersModule } from '@iqser/common-ui'; +import { HasScrollbarDirective, IqserHelpModeModule, IqserUsersModule } from '@iqser/common-ui'; import { TranslateModule } from '@ngx-translate/core'; const routes = [{ path: '', component: DossierTemplateInfoScreenComponent }]; @@ -16,9 +16,8 @@ const routes = [{ path: '', component: DossierTemplateInfoScreenComponent }]; SharedModule, IqserUsersModule, TranslateModule, - IqserButtonsModule, - IqserScrollbarModule, IqserHelpModeModule, + HasScrollbarDirective, ], }) export class DossierTemplateInfoModule {} diff --git a/apps/red-ui/src/app/modules/admin/screens/justifications/add-edit-justification-dialog/add-edit-justification-dialog.component.html b/apps/red-ui/src/app/modules/admin/screens/justifications/add-edit-justification-dialog/add-edit-justification-dialog.component.html index e5bb07198..a236f7090 100644 --- a/apps/red-ui/src/app/modules/admin/screens/justifications/add-edit-justification-dialog/add-edit-justification-dialog.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/justifications/add-edit-justification-dialog/add-edit-justification-dialog.component.html @@ -48,8 +48,7 @@ [label]="'add-edit-justification.actions.save' | translate" [submit]="true" [type]="iconButtonTypes.primary" - > - + >
    diff --git a/apps/red-ui/src/app/modules/admin/screens/justifications/justifications-dialog.service.ts b/apps/red-ui/src/app/modules/admin/screens/justifications/justifications-dialog.service.ts index 668de1d80..b774bbef6 100644 --- a/apps/red-ui/src/app/modules/admin/screens/justifications/justifications-dialog.service.ts +++ b/apps/red-ui/src/app/modules/admin/screens/justifications/justifications-dialog.service.ts @@ -2,9 +2,9 @@ import { Injectable } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { ConfirmationDialogComponent, - ConfirmationDialogInput, DialogConfig, DialogService, + IConfirmationDialogData, LoadingService, TitleColors, } from '@iqser/common-ui'; @@ -39,7 +39,7 @@ export class JustificationsDialogService extends DialogService { } confirmDelete(justifications: Justification[], dossierTemplateId: string) { - const data = new ConfirmationDialogInput({ + const data: IConfirmationDialogData = { title: _('confirmation-dialog.delete-justification.title'), titleColor: TitleColors.WARN, question: _('confirmation-dialog.delete-justification.question'), @@ -47,8 +47,9 @@ export class JustificationsDialogService extends DialogService { count: justifications.length, justificationName: justifications[0].name, }, - }); - this.openDialog('confirm', null, data, async () => { + }; + + this.openDialog('confirm', data, async () => { this._loadingService.start(); const justificationIds = justifications.map(j => j.id); await firstValueFrom(this._justificationService.delete(justificationIds, dossierTemplateId)); diff --git a/apps/red-ui/src/app/modules/admin/screens/justifications/justifications-screen/justifications-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/justifications/justifications-screen/justifications-screen.component.ts index 7a31b9ff1..59e11f857 100644 --- a/apps/red-ui/src/app/modules/admin/screens/justifications/justifications-screen/justifications-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/justifications/justifications-screen/justifications-screen.component.ts @@ -60,7 +60,7 @@ export class JustificationsScreenComponent extends ListingComponent{{ licenseService.totalLicensedNumberOfPages }}
    +
    +
    -
    -
    {{ licenseService.currentInfo.numberOfAnalyzedPages }}
    +
    +
    + {{ licenseService.analyzedPagesInCurrentLicensingPeriod }} + ({{ analysisPercentageOfLicense$ | async | number : '1.0-2' }}%) +
    @@ -71,27 +76,26 @@
    {{ licenseService.currentInfo.numberOfOcrPages }}
    -
    +
    +
    +
    {{ licenseService.unlicensedPages }}
    +
    -
    {{ licenseService.totalInfo.numberOfAnalyzedPages }}
    + +
    {{ licenseService.annualInfo.numberOfAnalyzedPages }}
    -
    -
    - {{ licenseService.currentInfo.numberOfAnalyzedPages }} - ({{ analysisPercentageOfLicense$ | async | number : '1.0-2' }}%) -
    -
    - -
    -
    -
    {{ licenseService.unlicensedInfo.numberOfAnalyzedPages }}
    +
    +
    {{ licenseService.annualInfo.numberOfOcrPages }}
    diff --git a/apps/red-ui/src/app/modules/admin/screens/license/license-screen/license-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/license/license-screen/license-screen.component.ts index 0b9cbcd6c..b38bf630e 100644 --- a/apps/red-ui/src/app/modules/admin/screens/license/license-screen/license-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/license/license-screen/license-screen.component.ts @@ -50,7 +50,7 @@ export class LicenseScreenComponent { getAnalysisPercentageOfLicense() { const totalLicensedNumberOfPages = this.licenseService.totalLicensedNumberOfPages; - const numberOfAnalyzedPages = this.licenseService.currentInfo.numberOfAnalyzedPages; + const numberOfAnalyzedPages = this.licenseService.analyzedPagesInCurrentLicensingPeriod; return totalLicensedNumberOfPages > 0 ? (numberOfAnalyzedPages / totalLicensedNumberOfPages) * 100 : 100; } diff --git a/apps/red-ui/src/app/modules/admin/screens/permissions/permissions-screen/permissions-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/permissions/permissions-screen/permissions-screen.component.html index f1a935495..53a8ea3f6 100644 --- a/apps/red-ui/src/app/modules/admin/screens/permissions/permissions-screen/permissions-screen.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/permissions/permissions-screen/permissions-screen.component.html @@ -12,7 +12,8 @@
    { + this._dialogService.openDialog('confirm', null, () => { this._loadingService.loadWhile(this._deleteTemplate(template)); }); } @@ -80,7 +80,7 @@ export class ReportsScreenComponent implements OnInit { return; } - const data = new ConfirmationDialogInput({ + const data: IConfirmationDialogData = { title: _('confirmation-dialog.upload-report-template.title'), question: _('confirmation-dialog.upload-report-template.question'), confirmationText: _('confirmation-dialog.upload-report-template.confirmation-text'), @@ -89,8 +89,9 @@ export class ReportsScreenComponent implements OnInit { translateParams: { fileName: file.name, }, - }); - this._dialogService.openDialog('confirm', null, data, null, async result => { + }; + + this._dialogService.openDialog('confirm', data, null, async result => { if (result) { const multiFileReport = result > 1; if ( @@ -119,7 +120,7 @@ export class ReportsScreenComponent implements OnInit { } private async _openOverwriteConfirmationDialog(file: File, multiFileReport: boolean): Promise { - const data = new ConfirmationDialogInput({ + const data: IConfirmationDialogData = { title: _('confirmation-dialog.report-template-same-name.title'), question: _('confirmation-dialog.report-template-same-name.question'), confirmationText: _('confirmation-dialog.report-template-same-name.confirmation-text'), @@ -127,9 +128,9 @@ export class ReportsScreenComponent implements OnInit { translateParams: { fileName: file.name, }, - }); + }; - this._dialogService.openDialog('confirm', null, data, null, async result => { + this._dialogService.openDialog('confirm', data, null, async result => { if (result) { await this._uploadTemplateForm(file, multiFileReport); } diff --git a/apps/red-ui/src/app/modules/admin/screens/reports/reports.module.ts b/apps/red-ui/src/app/modules/admin/screens/reports/reports.module.ts index 6be448070..9175eae4b 100644 --- a/apps/red-ui/src/app/modules/admin/screens/reports/reports.module.ts +++ b/apps/red-ui/src/app/modules/admin/screens/reports/reports.module.ts @@ -1,24 +1,22 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RouterModule } from '@angular/router'; -import { SharedModule } from '@shared/shared.module'; import { ReportsScreenComponent } from './reports-screen/reports-screen.component'; import { TranslateModule } from '@ngx-translate/core'; -import { IqserButtonsModule, IqserHelpModeModule, IqserPermissionsModule, IqserScrollbarModule } from '@iqser/common-ui'; +import { CircleButtonComponent, HasScrollbarDirective, IqserAllowDirective, IqserHelpModeModule, IqserRoutes } from '@iqser/common-ui'; -const routes = [{ path: '', component: ReportsScreenComponent }]; +const routes: IqserRoutes = [{ path: '', component: ReportsScreenComponent }]; @NgModule({ declarations: [ReportsScreenComponent], imports: [ RouterModule.forChild(routes), CommonModule, - SharedModule, TranslateModule, - IqserButtonsModule, - IqserScrollbarModule, IqserHelpModeModule, - IqserPermissionsModule, + CircleButtonComponent, + HasScrollbarDirective, + IqserAllowDirective, ], }) export class ReportsModule {} diff --git a/apps/red-ui/src/app/modules/admin/screens/rules/rules-screen/rules-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/rules/rules-screen/rules-screen.component.ts index 2e41aafe2..c819bdd82 100644 --- a/apps/red-ui/src/app/modules/admin/screens/rules/rules-screen/rules-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/rules/rules-screen/rules-screen.component.ts @@ -13,7 +13,6 @@ import IModelDeltaDecoration = monaco.editor.IModelDeltaDecoration; import IStandaloneEditorConstructionOptions = monaco.editor.IStandaloneEditorConstructionOptions; @Component({ - selector: 'redaction-rules-screen', templateUrl: './rules-screen.component.html', styleUrls: ['./rules-screen.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/apps/red-ui/src/app/modules/admin/screens/rules/rules.module.ts b/apps/red-ui/src/app/modules/admin/screens/rules/rules.module.ts index 7b8d87b2f..bd26cc278 100644 --- a/apps/red-ui/src/app/modules/admin/screens/rules/rules.module.ts +++ b/apps/red-ui/src/app/modules/admin/screens/rules/rules.module.ts @@ -1,17 +1,17 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RouterModule } from '@angular/router'; -import { SharedModule } from '@shared/shared.module'; import { RulesScreenComponent } from './rules-screen/rules-screen.component'; import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor'; import { PendingChangesGuard } from '@guards/can-deactivate.guard'; import { TranslateModule } from '@ngx-translate/core'; -import { IqserButtonsModule } from '@iqser/common-ui'; +import { IconButtonComponent } from '@iqser/common-ui'; +import { FormsModule } from '@angular/forms'; const routes = [{ path: '', component: RulesScreenComponent, canDeactivate: [PendingChangesGuard] }]; @NgModule({ declarations: [RulesScreenComponent], - imports: [RouterModule.forChild(routes), CommonModule, SharedModule, MonacoEditorModule, TranslateModule, IqserButtonsModule], + imports: [RouterModule.forChild(routes), CommonModule, MonacoEditorModule, TranslateModule, IconButtonComponent, FormsModule], }) export class RulesModule {} diff --git a/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.html index 62caae326..f6cd676c9 100644 --- a/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.html @@ -1,5 +1,4 @@
    @@ -28,7 +28,6 @@ @@ -53,10 +53,10 @@
    @@ -71,14 +71,14 @@
    implement await this.#loadData(); } - openAddEditUserDialog(event?: MouseEvent, user?: User) { - this._dialogService.openDialog('addEditUser', event, user, async () => { + openAddEditUserDialog(user?: User) { + this._dialogService.openDialog('addEditUser', user, async () => { await this.#loadData(); }); } - openDeleteUsersDialog(userIds: string[], $event?: MouseEvent) { - this._dialogService.deleteUsers(userIds, $event, async () => { + openDeleteUsersDialog(userIds: string[]) { + this._dialogService.deleteUsers(userIds, async () => { await this.#loadData(); }); } diff --git a/apps/red-ui/src/app/modules/admin/screens/watermark/paginator/paginator.component.html b/apps/red-ui/src/app/modules/admin/screens/watermark/paginator/paginator.component.html new file mode 100644 index 000000000..7c177e1ea --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/watermark/paginator/paginator.component.html @@ -0,0 +1,13 @@ + diff --git a/apps/red-ui/src/app/modules/admin/screens/watermark/paginator/paginator.component.scss b/apps/red-ui/src/app/modules/admin/screens/watermark/paginator/paginator.component.scss new file mode 100644 index 000000000..7293a732d --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/watermark/paginator/paginator.component.scss @@ -0,0 +1,42 @@ +.pagination { + z-index: 1; + position: absolute; + bottom: 20px; + right: calc(50% - (var(--viewer-width) / 2)); + transform: translate(-50%); + background: var(--iqser-background); + color: var(--iqser-grey-7); + border: 1px solid var(--iqser-grey-7); + border-radius: 8px; + padding: 6px 2px; + display: flex; + justify-content: center; + align-items: center; + + > div { + height: 16px; + cursor: default; + } + + .separator { + padding-left: 2px; + padding-right: 2px; + } + + .page-button { + cursor: pointer; + align-items: center; + display: inline-flex; + + &:hover { + color: var(--iqser-text); + } + } + + .chevron-icon { + height: 16px; + transform: rotate(-90deg); + padding-left: 4px; + padding-right: 4px; + } +} diff --git a/apps/red-ui/src/app/modules/admin/screens/watermark/paginator/paginator.component.ts b/apps/red-ui/src/app/modules/admin/screens/watermark/paginator/paginator.component.ts new file mode 100644 index 000000000..02ba06c3b --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/watermark/paginator/paginator.component.ts @@ -0,0 +1,10 @@ +import { Component, EventEmitter, Output } from '@angular/core'; + +@Component({ + selector: 'redaction-paginator', + templateUrl: './paginator.component.html', + styleUrls: ['./paginator.component.scss'], +}) +export class PaginatorComponent { + @Output() readonly changePage = new EventEmitter(); +} diff --git a/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen/watermark-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen/watermark-screen.component.html index 373bbf0a8..859bf9fe7 100644 --- a/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen/watermark-screen.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen/watermark-screen.component.html @@ -1,7 +1,9 @@
    -
    +
    -
    + + +
    +
    diff --git a/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen/watermark-screen.component.scss b/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen/watermark-screen.component.scss index 08b29cd8a..7ec4b7bc0 100644 --- a/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen/watermark-screen.component.scss +++ b/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen/watermark-screen.component.scss @@ -5,6 +5,7 @@ flex-direction: row !important; flex-grow: 1; overflow: hidden; + --viewer-width: 380px; } .content-container { @@ -12,6 +13,7 @@ .viewer { height: 100%; + width: 100%; } } diff --git a/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen/watermark-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen/watermark-screen.component.ts index 473b6ad96..889b8dc20 100644 --- a/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen/watermark-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen/watermark-screen.component.ts @@ -1,4 +1,4 @@ -import { Component, Inject } from '@angular/core'; +import { Component, ElementRef, Inject, OnInit, ViewChild } from '@angular/core'; import WebViewer, { WebViewerInstance } from '@pdftron/webviewer'; import { HttpClient } from '@angular/common/http'; import { FormBuilder, FormGroup } from '@angular/forms'; @@ -18,14 +18,14 @@ import { DOSSIER_TEMPLATE_ID, type IWatermark, type User, WATERMARK_ID, Watermar import { stampPDFPage } from '@utils/page-stamper'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { WatermarkService } from '@services/entity-services/watermark.service'; -import { firstValueFrom, Observable, of } from 'rxjs'; -import { tap } from 'rxjs/operators'; +import { BehaviorSubject, firstValueFrom, Observable, of } from 'rxjs'; import { LicenseService } from '@services/license.service'; import { UserPreferenceService } from '@users/user-preference.service'; import { Router } from '@angular/router'; import { WatermarksMapService } from '@services/entity-services/watermarks-map.service'; import { ROLES } from '@users/roles'; import { environment } from '@environments/environment'; +import { tap } from 'rxjs/operators'; export const DEFAULT_WATERMARK: Partial = { text: 'Watermark', @@ -51,10 +51,10 @@ interface WatermarkForm { templateUrl: './watermark-screen.component.html', styleUrls: ['./watermark-screen.component.scss'], }) -export class WatermarkScreenComponent { +export class WatermarkScreenComponent implements OnInit { readonly iconButtonTypes = IconButtonTypes; readonly currentUser = getCurrentUser(); - readonly form = this._getForm(); + readonly form = this.#form; readonly watermark$: Observable>; readonly fontOptions = [ { value: 'times-new-roman', display: 'Times' }, @@ -62,10 +62,12 @@ export class WatermarkScreenComponent { { value: 'courier', display: 'Courier' }, ]; readonly orientationOptions = ['DIAGONAL', 'HORIZONTAL', 'VERTICAL']; + instance: WebViewerInstance; + readonly loaded$ = new BehaviorSubject(false); + @ViewChild('viewer', { static: true }) private readonly _viewer: ElementRef; readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID); readonly #watermarkId = Number(getParam(WATERMARK_ID)); - private _instance: WebViewerInstance; - private _watermark: Partial = {}; + #watermark: Partial = {}; constructor( private readonly _http: HttpClient, @@ -78,17 +80,16 @@ export class WatermarkScreenComponent { private readonly _watermarkService: WatermarkService, private readonly _userPreferenceService: UserPreferenceService, private readonly _router: Router, - private readonly _watermarksMapService: WatermarksMapService, + watermarksMapService: WatermarksMapService, ) { - const obs$: Observable> = this.#watermarkId - ? _watermarksMapService.watch$(this.#dossierTemplateId, this.#watermarkId) - : of(DEFAULT_WATERMARK); - this.watermark$ = obs$.pipe(tap(wm => this._initForm(wm))); + const watermark$ = watermarksMapService.watch$(this.#dossierTemplateId, this.#watermarkId); + const obs$: Observable> = this.#watermarkId ? watermark$ : of(DEFAULT_WATERMARK); + this.watermark$ = obs$.pipe(tap(watermark => this.#initForm(watermark))); } get changed(): boolean { for (const key of Object.keys(this.form.getRawValue())) { - if (this._watermark[key] !== this.form.get(key)?.value) { + if (this.#watermark[key] !== this.form.get(key)?.value) { return true; } } @@ -102,113 +103,7 @@ export class WatermarkScreenComponent { return this.form.valid; } - @Debounce() - async configChanged() { - await this._drawWatermark(); - } - - async save(): Promise { - const watermark: IWatermark = { - id: this._watermark.id, - enabled: this._watermark.id ? this._watermark.enabled : true, - dossierTemplateId: this.#dossierTemplateId, - ...this.form.getRawValue(), - }; - this._loadingService.start(); - try { - const updatedWatermark = await this._watermarkService.saveWatermark(watermark); - this._toaster.success( - watermark.id ? _('watermark-screen.action.change-success') : _('watermark-screen.action.created-success'), - ); - if (!watermark.id) { - await this._router.navigate([`/main/admin/dossier-templates/${this.#dossierTemplateId}/watermarks/${updatedWatermark.id}`]); - } - } catch (error) { - this._toaster.error(_('watermark-screen.action.error')); - } - this._loadingService.stop(); - } - - async revert() { - this.form.patchValue({ ...this._watermark }); - await this.configChanged(); - } - - async setValue(type: 'fontType' | 'orientation' | 'hexColor', value: any) { - if (!this.form.get(type).disabled) { - this.form.get(type).setValue(value); - await this.configChanged(); - } - } - - private async _initForm(watermark: Partial) { - this._watermark = { ...watermark, dossierTemplateId: this.#dossierTemplateId }; - this.form.patchValue({ ...watermark }); - await this._loadViewer(); - } - - private async _loadViewer() { - if (this._instance) { - return; - } - - this._instance = await WebViewer( - { - licenseKey: this._licenseService.activeLicenseKey, - path: this._convertPath('/assets/wv-resources'), - css: this._convertPath('/assets/pdftron/stylesheet.css'), - fullAPI: true, - isReadOnly: true, - backendType: 'ems', - }, - document.getElementById('viewer'), - ); - - this._instance.UI.setTheme(this._userPreferenceService.getTheme()); - - this._instance.Core.documentViewer.addEventListener('documentLoaded', async () => { - this._loadingService.stop(); - await this._drawWatermark(); - }); - - if (environment.production) { - this._instance.Core.setCustomFontURL('https://' + window.location.host + this._convertPath('/assets/pdftron')); - } - - this._disableElements(); - - const request = this._http.get('/assets/pdftron/blank.pdf', { - responseType: 'blob', - }); - const blobData = await firstValueFrom(request); - this._instance.UI.loadDocument(blobData, { filename: 'blank.pdf' }); - } - - private _disableElements() { - this._instance.UI.disableElements(['header', 'toolsHeader', 'pageNavOverlay', 'textPopup']); - } - - private async _drawWatermark() { - const pdfNet = this._instance.Core.PDFNet; - const document = await this._instance.Core.documentViewer.getDocument().getPDFDoc(); - - await stampPDFPage( - document, - pdfNet, - this.form.controls.text.value || '', - this.form.controls.fontSize.value, - this.form.controls.fontType.value, - this.form.controls.orientation.value, - this.form.controls.opacity.value, - this.form.controls.hexColor.value, - [1], - this._licenseService.activeLicenseKey, - ); - this._instance.Core.documentViewer.refreshAll(); - this._instance.Core.documentViewer.updateView([0], 0); - } - - private _getForm() { + get #form() { const form: FormGroup> = this._formBuilder.group({ name: [null], text: [null], @@ -225,4 +120,122 @@ export class WatermarkScreenComponent { return form; } + + async ngOnInit() { + await this.#loadViewer(); + } + + @Debounce() + async configChanged() { + await this.#drawWatermark(); + } + + async save(): Promise { + const watermark: IWatermark = { + id: this.#watermark.id, + enabled: this.#watermark.id ? this.#watermark.enabled : true, + dossierTemplateId: this.#dossierTemplateId, + ...this.form.getRawValue(), + }; + + this._loadingService.start(); + + try { + const updatedWatermark = await this._watermarkService.saveWatermark(watermark); + this._toaster.success( + watermark.id ? _('watermark-screen.action.change-success') : _('watermark-screen.action.created-success'), + ); + if (!watermark.id) { + await this._router.navigate([`/main/admin/dossier-templates/${this.#dossierTemplateId}/watermarks/${updatedWatermark.id}`]); + } + } catch (error) { + this._toaster.error(_('watermark-screen.action.error')); + } + + this._loadingService.stop(); + } + + async revert() { + this.form.patchValue({ ...this.#watermark }); + await this.configChanged(); + } + + async setValue(type: 'fontType' | 'orientation' | 'hexColor', value: any) { + if (!this.form.get(type).disabled) { + this.form.get(type).setValue(value); + await this.configChanged(); + } + } + + navigateTo($event: number) { + this.instance.Core.documentViewer.displayPageLocation($event, 0, 0); + } + + async #initForm(watermark: Partial) { + this.#watermark = { ...watermark, dossierTemplateId: this.#dossierTemplateId }; + this.form.patchValue({ ...watermark }); + } + + async #loadViewer() { + this.instance = await WebViewer( + { + licenseKey: this._licenseService.activeLicenseKey, + path: this._convertPath('/assets/wv-resources'), + css: this._convertPath('/assets/pdftron/stylesheet.css'), + fullAPI: true, + isReadOnly: true, + backendType: 'ems', + }, + // use nativeElement instead of document.getElementById('viwer') + // because WebViewer works better with this approach + this._viewer.nativeElement, + ); + + this.instance.UI.setTheme(this._userPreferenceService.getTheme()); + + this.instance.Core.documentViewer.addEventListener('documentLoaded', async () => { + this.loaded$.next(true); + this._loadingService.stop(); + await this.#drawWatermark(); + }); + + if (environment.production) { + this.instance.Core.setCustomFontURL('https://' + window.location.host + this._convertPath('/assets/pdftron')); + } + + this.#disableElements(); + await this.#loadDocument(); + } + + async #loadDocument() { + const request = this._http.get('/assets/pdftron/blank.pdf', { + responseType: 'blob', + }); + const blobData = await firstValueFrom(request); + this.instance.UI.loadDocument(blobData, { filename: 'blank.pdf' }); + } + + #disableElements() { + this.instance.UI.disableElements(['header', 'toolsHeader', 'pageNavOverlay', 'textPopup']); + } + + async #drawWatermark() { + const pdfNet = this.instance.Core.PDFNet; + const document = await this.instance.Core.documentViewer.getDocument().getPDFDoc(); + + await stampPDFPage( + document, + pdfNet, + this.form.controls.text.value || '', + this.form.controls.fontSize.value, + this.form.controls.fontType.value, + this.form.controls.orientation.value, + this.form.controls.opacity.value, + this.form.controls.hexColor.value, + [1, 2], + this._licenseService.activeLicenseKey, + ); + this.instance.Core.documentViewer.refreshAll(); + this.instance.Core.documentViewer.updateView([0, 1], 0); + } } diff --git a/apps/red-ui/src/app/modules/admin/screens/watermark/watermark.module.ts b/apps/red-ui/src/app/modules/admin/screens/watermark/watermark.module.ts index b33918c7f..9bd69ecab 100644 --- a/apps/red-ui/src/app/modules/admin/screens/watermark/watermark.module.ts +++ b/apps/red-ui/src/app/modules/admin/screens/watermark/watermark.module.ts @@ -1,25 +1,32 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RouterModule } from '@angular/router'; -import { SharedModule } from '@shared/shared.module'; import { WatermarkScreenComponent } from './watermark-screen/watermark-screen.component'; import { WatermarksListingScreenComponent } from './watermarks-listing/watermarks-listing-screen.component'; import { + CircleButtonComponent, CompositeRouteGuard, + HasScrollbarDirective, + IconButtonComponent, + IqserAllowDirective, IqserAuthGuard, - IqserButtonsModule, IqserHelpModeModule, IqserListingModule, - IqserPermissionsModule, - IqserScrollbarModule, + IqserRoutes, IqserUsersModule, } from '@iqser/common-ui'; import { RedRoleGuard } from '@users/red-role.guard'; import { WATERMARK_ID } from '@red/domain'; import { WatermarkExistsGuard } from '@guards/watermark-exists.guard'; import { TranslateModule } from '@ngx-translate/core'; +import { PaginatorComponent } from './paginator/paginator.component'; +import { MatIconModule } from '@angular/material/icon'; +import { ReactiveFormsModule } from '@angular/forms'; +import { MatLegacySliderModule } from '@angular/material/legacy-slider'; +import { ColorPickerModule } from 'ngx-color-picker'; +import { MatLegacySlideToggleModule } from '@angular/material/legacy-slide-toggle'; -const routes = [ +const routes: IqserRoutes = [ { path: '', component: WatermarksListingScreenComponent, @@ -47,18 +54,23 @@ const routes = [ ]; @NgModule({ - declarations: [WatermarkScreenComponent, WatermarksListingScreenComponent], + declarations: [WatermarkScreenComponent, WatermarksListingScreenComponent, PaginatorComponent], imports: [ RouterModule.forChild(routes), CommonModule, - SharedModule, IqserUsersModule, TranslateModule, - IqserButtonsModule, IqserListingModule, - IqserScrollbarModule, IqserHelpModeModule, - IqserPermissionsModule, + MatIconModule, + IconButtonComponent, + ReactiveFormsModule, + MatLegacySliderModule, + ColorPickerModule, + MatLegacySlideToggleModule, + CircleButtonComponent, + HasScrollbarDirective, + IqserAllowDirective, ], }) export class WatermarkModule {} diff --git a/apps/red-ui/src/app/modules/admin/screens/watermark/watermarks-listing/watermarks-listing-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/watermark/watermarks-listing/watermarks-listing-screen.component.html index 92e1acfd9..a6083442b 100644 --- a/apps/red-ui/src/app/modules/admin/screens/watermark/watermarks-listing/watermarks-listing-screen.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/watermark/watermarks-listing/watermarks-listing-screen.component.html @@ -14,8 +14,8 @@ { + }; + + this._dialogService.openDialog('confirm', data, async () => { await this._deleteWatermark(watermark); }); } diff --git a/apps/red-ui/src/app/modules/admin/services/admin-dialog.service.ts b/apps/red-ui/src/app/modules/admin/services/admin-dialog.service.ts index 95e85faa4..1700faaa7 100644 --- a/apps/red-ui/src/app/modules/admin/services/admin-dialog.service.ts +++ b/apps/red-ui/src/app/modules/admin/services/admin-dialog.service.ts @@ -1,27 +1,20 @@ import { Injectable, TemplateRef } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; -import { AddEditFileAttributeDialogComponent } from '../dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component'; import { AddEntityDialogComponent } from '../dialogs/add-entity-dialog/add-entity-dialog.component'; import { AddEditCloneDossierTemplateDialogComponent } from '../dialogs/add-edit-dossier-template-dialog/add-edit-clone-dossier-template-dialog.component'; import { EditColorDialogComponent } from '../dialogs/edit-color-dialog/edit-color-dialog.component'; import { SmtpAuthDialogComponent } from '../dialogs/smtp-auth-dialog/smtp-auth-dialog.component'; import { AddEditUserDialogComponent } from '../dialogs/add-edit-user-dialog/add-edit-user-dialog.component'; -import { FileAttributesCsvImportDialogComponent } from '../dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component'; -import { AddEditDossierAttributeDialogComponent } from '../dialogs/add-edit-dossier-attribute-dialog/add-edit-dossier-attribute-dialog.component'; import { ConfirmationDialogComponent, - ConfirmationDialogInput, ConfirmOptions, DialogConfig, DialogService, - largeDialogConfig, + IConfirmationDialogData, LoadingService, TitleColors, } from '@iqser/common-ui'; import { UploadDictionaryDialogComponent } from '../dialogs/upload-dictionary-dialog/upload-dictionary-dialog.component'; -import { FileAttributesConfigurationsDialogComponent } from '../dialogs/file-attributes-configurations-dialog/file-attributes-configurations-dialog.component'; -import { AddEditDossierStateDialogComponent } from '../dialogs/add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component'; -import { ConfirmDeleteDossierStateDialogComponent } from '../dialogs/confirm-delete-dossier-state-dialog/confirm-delete-dossier-state-dialog.component'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { firstValueFrom, forkJoin } from 'rxjs'; import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service'; @@ -35,17 +28,11 @@ type DialogType = | 'confirm' | 'addEntity' | 'editColor' - | 'addEditFileAttribute' - | 'importFileAttributes' - | 'fileAttributesConfigurations' | 'addEditUser' | 'smtpAuthConfig' | 'addEditCloneDossierTemplate' | 'auditInfo' - | 'addEditDossierAttribute' | 'uploadDictionary' - | 'addEditDossierState' - | 'deleteDossierState' | 'configureCertificate'; @Injectable() @@ -63,17 +50,6 @@ export class AdminDialogService extends DialogService { component: EditColorDialogComponent, dialogConfig: { autoFocus: true }, }, - addEditFileAttribute: { - component: AddEditFileAttributeDialogComponent, - dialogConfig: { autoFocus: true }, - }, - fileAttributesConfigurations: { - component: FileAttributesConfigurationsDialogComponent, - }, - importFileAttributes: { - component: FileAttributesCsvImportDialogComponent, - dialogConfig: { ...largeDialogConfig, ...{ disableClose: false } }, - }, addEditUser: { component: AddEditUserDialogComponent, dialogConfig: { autoFocus: true }, @@ -86,19 +62,9 @@ export class AdminDialogService extends DialogService { component: AddEditCloneDossierTemplateDialogComponent, dialogConfig: { width: '900px', autoFocus: true }, }, - addEditDossierAttribute: { - component: AddEditDossierAttributeDialogComponent, - dialogConfig: { autoFocus: true }, - }, uploadDictionary: { component: UploadDictionaryDialogComponent, }, - addEditDossierState: { - component: AddEditDossierStateDialogComponent, - }, - deleteDossierState: { - component: ConfirmDeleteDossierStateDialogComponent, - }, configureCertificate: { component: ConfigureCertificateDialogComponent, dialogConfig: { disableClose: false, maxHeight: '100vh' }, @@ -118,8 +84,8 @@ export class AdminDialogService extends DialogService { super(_dialog); } - deleteUsers(userIds: string[], $event?: MouseEvent, cb?: () => Promise | void): void { - const data = new ConfirmationDialogInput({ + deleteUsers(userIds: string[], cb?: () => Promise | void): void { + const data: IConfirmationDialogData = { title: _('confirm-delete-users.title'), question: _('confirm-delete-users.warning'), confirmationText: _('confirm-delete-users.delete'), @@ -131,9 +97,9 @@ export class AdminDialogService extends DialogService { { value: false, label: _('confirm-delete-users.impacted-documents') }, ], toastMessage: _('confirm-delete-users.toast-error'), - }); + }; - this.openDialog('confirm', $event, data, async result => { + this.openDialog('confirm', data, async result => { if (result === ConfirmOptions.CONFIRM) { this._loadingService.start(); await firstValueFrom(this._userService.delete(userIds)); @@ -148,7 +114,6 @@ export class AdminDialogService extends DialogService { dossierTemplateId: string, impactedTemplatesRef: TemplateRef, type: 'dossier' | 'file', - $event: MouseEvent, cb: () => Promise | void, ): Promise { this._loadingService.start(); @@ -160,7 +125,7 @@ export class AdminDialogService extends DialogService { const uniqueTemplates = Array.from(templateIds).map(id => templates.find(t => t.templateId === id)); this._loadingService.stop(); - const data = new ConfirmationDialogInput({ + const data: IConfirmationDialogData = { title: _('confirm-delete-attribute.title'), question: _('confirm-delete-attribute.warning'), confirmationText: _('confirm-delete-attribute.delete'), @@ -184,7 +149,8 @@ export class AdminDialogService extends DialogService { ], toastMessage: _('confirm-delete-attribute.toast-error'), translateParams: { reportsCount: uniqueTemplates.length, count: attributes.length, name: attributes[0].label }, - }); + }; + if (templates.length) { data.checkboxes.push({ value: false, @@ -193,7 +159,8 @@ export class AdminDialogService extends DialogService { extraContentData: { templates: uniqueTemplates }, }); } - this.openDialog('confirm', $event, data, async () => { + + this.openDialog('confirm', data, async () => { await cb(); }); } diff --git a/apps/red-ui/src/app/modules/admin/admin-side-nav/admin-side-nav.component.html b/apps/red-ui/src/app/modules/admin/shared/components/admin-side-nav/admin-side-nav.component.html similarity index 100% rename from apps/red-ui/src/app/modules/admin/admin-side-nav/admin-side-nav.component.html rename to apps/red-ui/src/app/modules/admin/shared/components/admin-side-nav/admin-side-nav.component.html diff --git a/apps/red-ui/src/app/modules/admin/admin-side-nav/admin-side-nav.component.scss b/apps/red-ui/src/app/modules/admin/shared/components/admin-side-nav/admin-side-nav.component.scss similarity index 100% rename from apps/red-ui/src/app/modules/admin/admin-side-nav/admin-side-nav.component.scss rename to apps/red-ui/src/app/modules/admin/shared/components/admin-side-nav/admin-side-nav.component.scss diff --git a/apps/red-ui/src/app/modules/admin/admin-side-nav/admin-side-nav.component.ts b/apps/red-ui/src/app/modules/admin/shared/components/admin-side-nav/admin-side-nav.component.ts similarity index 93% rename from apps/red-ui/src/app/modules/admin/admin-side-nav/admin-side-nav.component.ts rename to apps/red-ui/src/app/modules/admin/shared/components/admin-side-nav/admin-side-nav.component.ts index 5fe8c41b8..41731fbe6 100644 --- a/apps/red-ui/src/app/modules/admin/admin-side-nav/admin-side-nav.component.ts +++ b/apps/red-ui/src/app/modules/admin/shared/components/admin-side-nav/admin-side-nav.component.ts @@ -2,10 +2,12 @@ import { Component, HostBinding, Input, OnInit } from '@angular/core'; import { UserPreferenceService } from '@users/user-preference.service'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { adminSideNavTranslations } from '@translations/admin-side-nav-translations'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, RouterLink, RouterLinkActive } from '@angular/router'; import { AdminSideNavType, AdminSideNavTypes, ENTITY_TYPE, User } from '@red/domain'; import { ROLES } from '@users/roles'; -import { getCurrentUser, IqserPermissionsService } from '@iqser/common-ui'; +import { getCurrentUser, IqserHelpModeModule, IqserPermissionsService, SideNavComponent } from '@iqser/common-ui'; +import { TranslateModule } from '@ngx-translate/core'; +import { NgForOf, NgIf } from '@angular/common'; interface NavItem { readonly label: string; @@ -18,6 +20,8 @@ interface NavItem { selector: 'redaction-admin-side-nav [type]', templateUrl: './admin-side-nav.component.html', styleUrls: ['./admin-side-nav.component.scss'], + standalone: true, + imports: [TranslateModule, NgIf, IqserHelpModeModule, RouterLink, RouterLinkActive, NgForOf, SideNavComponent], }) export class AdminSideNavComponent implements OnInit { @Input() type: AdminSideNavType; diff --git a/apps/red-ui/src/app/modules/admin/shared/components/dossier-template-actions/dossier-template-actions.component.html b/apps/red-ui/src/app/modules/admin/shared/components/dossier-template-actions/dossier-template-actions.component.html index a707dd9ed..4b8099c03 100644 --- a/apps/red-ui/src/app/modules/admin/shared/components/dossier-template-actions/dossier-template-actions.component.html +++ b/apps/red-ui/src/app/modules/admin/shared/components/dossier-template-actions/dossier-template-actions.component.html @@ -1,27 +1,27 @@ -
    +
    diff --git a/apps/red-ui/src/app/modules/admin/shared/components/dossier-template-actions/dossier-template-actions.component.ts b/apps/red-ui/src/app/modules/admin/shared/components/dossier-template-actions/dossier-template-actions.component.ts index 37ab5ac25..2d2232f3d 100644 --- a/apps/red-ui/src/app/modules/admin/shared/components/dossier-template-actions/dossier-template-actions.component.ts +++ b/apps/red-ui/src/app/modules/admin/shared/components/dossier-template-actions/dossier-template-actions.component.ts @@ -1,20 +1,23 @@ import { Component, Input, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { AdminDialogService } from '../../../services/admin-dialog.service'; -import { CircleButtonTypes, LoadingService } from '@iqser/common-ui'; -import { getCurrentUser } from '@users/user.service'; +import { CircleButtonComponent, CircleButtonTypes, getCurrentUser, IqserHelpModeModule, LoadingService } from '@iqser/common-ui'; import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service'; import { firstValueFrom } from 'rxjs'; -import { DOSSIER_TEMPLATE_ID } from '@red/domain'; +import { DOSSIER_TEMPLATE_ID, type User } from '@red/domain'; +import { NgIf } from '@angular/common'; +import { TranslateModule } from '@ngx-translate/core'; @Component({ selector: 'redaction-dossier-template-actions', templateUrl: './dossier-template-actions.component.html', styleUrls: ['./dossier-template-actions.component.scss'], + standalone: true, + imports: [NgIf, IqserHelpModeModule, CircleButtonComponent, TranslateModule], }) export class DossierTemplateActionsComponent implements OnInit { readonly circleButtonTypes = CircleButtonTypes; - readonly currentUser = getCurrentUser(); + readonly currentUser = getCurrentUser(); @Input() dossierTemplateId: string; @@ -30,12 +33,12 @@ export class DossierTemplateActionsComponent implements OnInit { this.dossierTemplateId ??= this._route.snapshot.paramMap.get(DOSSIER_TEMPLATE_ID); } - openEditCloneDossierTemplateDialog($event: MouseEvent, clone: boolean = false) { - this._dialogService.openDialog('addEditCloneDossierTemplate', $event, { dossierTemplateId: this.dossierTemplateId, clone }); + openEditCloneDossierTemplateDialog(clone: boolean = false) { + this._dialogService.openDialog('addEditCloneDossierTemplate', { dossierTemplateId: this.dossierTemplateId, clone }); } - openDeleteDossierTemplateDialog($event?: MouseEvent) { - this._dialogService.openDialog('confirm', $event, null, async () => { + openDeleteDossierTemplateDialog() { + this._dialogService.openDialog('confirm', null, async () => { this._loadingService.start(); const success = await firstValueFrom(this._dossierTemplatesService.delete([this.dossierTemplateId])); diff --git a/apps/red-ui/src/app/modules/admin/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component.html b/apps/red-ui/src/app/modules/admin/shared/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component.html similarity index 100% rename from apps/red-ui/src/app/modules/admin/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component.html rename to apps/red-ui/src/app/modules/admin/shared/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component.html diff --git a/apps/red-ui/src/app/modules/admin/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component.scss b/apps/red-ui/src/app/modules/admin/shared/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component.scss similarity index 100% rename from apps/red-ui/src/app/modules/admin/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component.scss rename to apps/red-ui/src/app/modules/admin/shared/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component.scss diff --git a/apps/red-ui/src/app/modules/admin/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component.ts b/apps/red-ui/src/app/modules/admin/shared/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component.ts similarity index 81% rename from apps/red-ui/src/app/modules/admin/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component.ts rename to apps/red-ui/src/app/modules/admin/shared/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component.ts index 44e499a80..9c0c5a773 100644 --- a/apps/red-ui/src/app/modules/admin/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component.ts +++ b/apps/red-ui/src/app/modules/admin/shared/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component.ts @@ -2,14 +2,19 @@ import { Component, Input } from '@angular/core'; import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service'; import { Observable, of } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, RouterLink, RouterLinkActive } from '@angular/router'; import { Dictionary, DOSSIER_TEMPLATE_ID, DossierTemplate, ENTITY_TYPE } from '@red/domain'; import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service'; +import { AsyncPipe, NgIf } from '@angular/common'; +import { MatIconModule } from '@angular/material/icon'; +import { TranslateModule } from '@ngx-translate/core'; @Component({ selector: 'redaction-dossier-template-breadcrumbs', templateUrl: './dossier-template-breadcrumbs.component.html', styleUrls: ['./dossier-template-breadcrumbs.component.scss'], + standalone: true, + imports: [NgIf, AsyncPipe, RouterLink, MatIconModule, TranslateModule, RouterLinkActive], }) export class DossierTemplateBreadcrumbsComponent { @Input() root = false; diff --git a/apps/red-ui/src/app/modules/admin/shared/shared-admin.module.ts b/apps/red-ui/src/app/modules/admin/shared/shared-admin.module.ts deleted file mode 100644 index 743fb0e9b..000000000 --- a/apps/red-ui/src/app/modules/admin/shared/shared-admin.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { SharedModule } from '@shared/shared.module'; -import { DossierTemplateActionsComponent } from './components/dossier-template-actions/dossier-template-actions.component'; -import { RouterModule } from '@angular/router'; -import { TranslateModule } from '@ngx-translate/core'; -import { IqserButtonsModule, IqserHelpModeModule } from '@iqser/common-ui'; - -const components = [DossierTemplateActionsComponent]; - -const modules = [CommonModule, SharedModule]; - -@NgModule({ - declarations: [...components], - exports: [...components, ...modules], - imports: [...modules, RouterModule, TranslateModule, IqserButtonsModule, IqserHelpModeModule], -}) -export class SharedAdminModule {} diff --git a/apps/red-ui/src/app/modules/archive/archive.module.ts b/apps/red-ui/src/app/modules/archive/archive.module.ts index b0d95d24c..32f4c7725 100644 --- a/apps/red-ui/src/app/modules/archive/archive.module.ts +++ b/apps/red-ui/src/app/modules/archive/archive.module.ts @@ -3,28 +3,23 @@ import { CommonModule } from '@angular/common'; import { ArchivedDossiersScreenComponent } from './screens/archived-dossiers-screen/archived-dossiers-screen.component'; import { ArchiveRoutingModule } from './archive-routing.module'; import { TableItemComponent } from './components/table-item/table-item.component'; -import { SharedModule } from '@shared/shared.module'; import { ConfigService } from './services/config.service'; -import { SharedDossiersModule } from '../shared-dossiers/shared-dossiers.module'; -import { IqserButtonsModule, IqserHelpModeModule, IqserListingModule, IqserPermissionsModule, IqserUsersModule } from '@iqser/common-ui'; +import { IqserHelpModeModule, IqserListingModule, IqserUsersModule } from '@iqser/common-ui'; import { TranslateModule } from '@ngx-translate/core'; - -const components = [TableItemComponent]; -const screens = [ArchivedDossiersScreenComponent]; +import { SharedModule } from '@shared/shared.module'; +import { SharedDossiersModule } from '../shared-dossiers/shared-dossiers.module'; @NgModule({ - declarations: [...components, ...screens], + declarations: [TableItemComponent, ArchivedDossiersScreenComponent], imports: [ CommonModule, ArchiveRoutingModule, - SharedModule, - SharedDossiersModule, IqserHelpModeModule, IqserUsersModule, TranslateModule, IqserListingModule, - IqserButtonsModule, - IqserPermissionsModule, + SharedModule, + SharedDossiersModule, ], providers: [ConfigService], }) diff --git a/apps/red-ui/src/app/modules/dashboard/components/template-stats/template-stats.component.html b/apps/red-ui/src/app/modules/dashboard/components/template-stats/template-stats.component.html index cb5500ab6..5e87bf2ac 100644 --- a/apps/red-ui/src/app/modules/dashboard/components/template-stats/template-stats.component.html +++ b/apps/red-ui/src/app/modules/dashboard/components/template-stats/template-stats.component.html @@ -13,7 +13,7 @@
    @@ -21,18 +21,20 @@
    - +
    @@ -42,7 +44,7 @@ [config]="translateChartService.translateDossierStates(dossierTemplate.dossiersChartConfig, dossierTemplate.id)" [radius]="63" [strokeWidth]="15" - [subtitles]="['dossier-template-stats.active-dossiers' | translate: { count: dossierTemplate.numberOfActiveDossiers }]" + [subtitles]="['dossier-template-stats.active-dossiers' | translate : { count: dossierTemplate.numberOfActiveDossiers }]" direction="row" totalType="sum" > diff --git a/apps/red-ui/src/app/modules/dashboard/components/template-stats/template-stats.component.ts b/apps/red-ui/src/app/modules/dashboard/components/template-stats/template-stats.component.ts index b111418ab..bb83e1846 100644 --- a/apps/red-ui/src/app/modules/dashboard/components/template-stats/template-stats.component.ts +++ b/apps/red-ui/src/app/modules/dashboard/components/template-stats/template-stats.component.ts @@ -24,6 +24,6 @@ export class TemplateStatsComponent { ) {} newDossier(): void { - this._dialogService.openDialog('addDossier', null, { dossierTemplateId: this.stats.dossierTemplateId }); + this._dialogService.openDialog('addDossier', { dossierTemplateId: this.stats.dossierTemplateId }); } } diff --git a/apps/red-ui/src/app/modules/dashboard/dashboard.module.ts b/apps/red-ui/src/app/modules/dashboard/dashboard.module.ts index 6197c84c7..c89c1929b 100644 --- a/apps/red-ui/src/app/modules/dashboard/dashboard.module.ts +++ b/apps/red-ui/src/app/modules/dashboard/dashboard.module.ts @@ -6,9 +6,9 @@ import { SharedModule } from '@shared/shared.module'; import { TemplateStatsComponent } from './components/template-stats/template-stats.component'; import { BreadcrumbTypes } from '@red/domain'; import { TranslateModule } from '@ngx-translate/core'; -import { IqserButtonsModule, IqserHelpModeModule, IqserPermissionsModule } from '@iqser/common-ui'; +import { IconButtonComponent, IqserHelpModeModule, IqserRoutes } from '@iqser/common-ui'; -const routes = [ +const routes: IqserRoutes = [ { path: '', component: DashboardScreenComponent, @@ -20,14 +20,6 @@ const routes = [ @NgModule({ declarations: [DashboardScreenComponent, TemplateStatsComponent], - imports: [ - RouterModule.forChild(routes), - CommonModule, - SharedModule, - TranslateModule, - IqserButtonsModule, - IqserHelpModeModule, - IqserPermissionsModule, - ], + imports: [RouterModule.forChild(routes), CommonModule, SharedModule, TranslateModule, IqserHelpModeModule, IconButtonComponent], }) export class DashboardModule {} diff --git a/apps/red-ui/src/app/modules/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.ts b/apps/red-ui/src/app/modules/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.ts index 443ed5c06..4245fb641 100644 --- a/apps/red-ui/src/app/modules/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.ts +++ b/apps/red-ui/src/app/modules/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.ts @@ -48,7 +48,7 @@ export class DossierOverviewBulkActionsComponent implements OnChanges { ) {} private get _buttons(): Action[] { - return [ + const actions: Action[] = [ { id: 'delete-files-btn', type: ActionTypes.circleBtn, @@ -161,7 +161,9 @@ export class DossierOverviewBulkActionsComponent implements OnChanges { checked: !this.#allFilesAreExcluded, show: this.#canToggleAnalysis, }, - ].filter(btn => btn.show); + ]; + + return actions.filter(btn => btn.show); } ngOnChanges() { diff --git a/apps/red-ui/src/app/modules/dossier-overview/components/dossier-details-stats/dossier-details-stats.component.ts b/apps/red-ui/src/app/modules/dossier-overview/components/dossier-details-stats/dossier-details-stats.component.ts index 767368052..cde04f782 100644 --- a/apps/red-ui/src/app/modules/dossier-overview/components/dossier-details-stats/dossier-details-stats.component.ts +++ b/apps/red-ui/src/app/modules/dossier-overview/components/dossier-details-stats/dossier-details-stats.component.ts @@ -33,7 +33,7 @@ export class DossierDetailsStatsComponent implements OnInit { openEditDossierDialog(section: string): void { const data = { dossierId: this.dossier.id, section }; - this._dialogService.openDialog('editDossier', null, data, async () => { + this._dialogService.openDialog('editDossier', data, async () => { await firstValueFrom(this._filesService.loadAll(this.dossier.id)); }); } diff --git a/apps/red-ui/src/app/modules/dossier-overview/components/table-item/file-attribute/file-attribute.component.html b/apps/red-ui/src/app/modules/dossier-overview/components/table-item/file-attribute/file-attribute.component.html index 69898c796..602e751e4 100644 --- a/apps/red-ui/src/app/modules/dossier-overview/components/table-item/file-attribute/file-attribute.component.html +++ b/apps/red-ui/src/app/modules/dossier-overview/components/table-item/file-attribute/file-attribute.component.html @@ -4,41 +4,34 @@ {{ fileAttributeValue ? (fileAttributeValue | date : 'd MMM yyyy') : '-' }} - -
    + +
    + -
    +
    - + > - - + + +
    - - - diff --git a/apps/red-ui/src/app/modules/dossier-overview/components/table-item/file-attribute/file-attribute.component.ts b/apps/red-ui/src/app/modules/dossier-overview/components/table-item/file-attribute/file-attribute.component.ts index c22ab777c..863ee9353 100644 --- a/apps/red-ui/src/app/modules/dossier-overview/components/table-item/file-attribute/file-attribute.component.ts +++ b/apps/red-ui/src/app/modules/dossier-overview/components/table-item/file-attribute/file-attribute.component.ts @@ -1,10 +1,10 @@ -import { ChangeDetectionStrategy, Component, HostListener, Input, OnInit } from '@angular/core'; +import { Component, HostListener, Input, OnDestroy, OnInit } from '@angular/core'; import { Dossier, File, FileAttributeConfigTypes, IFileAttributeConfig } from '@red/domain'; import { BaseFormComponent, ListingService, Toaster } from '@iqser/common-ui'; import { PermissionsService } from '@services/permissions.service'; import { FormBuilder, UntypedFormGroup } from '@angular/forms'; import { FileAttributesService } from '@services/entity-services/file-attributes.service'; -import { BehaviorSubject, firstValueFrom, Observable } from 'rxjs'; +import { firstValueFrom, Subscription } from 'rxjs'; import { FilesService } from '@services/files/files.service'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import dayjs from 'dayjs'; @@ -15,48 +15,34 @@ import { filter, map, tap } from 'rxjs/operators'; selector: 'redaction-file-attribute [fileAttribute] [file] [dossier]', templateUrl: './file-attribute.component.html', styleUrls: ['./file-attribute.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, }) -export class FileAttributeComponent extends BaseFormComponent implements OnInit { +export class FileAttributeComponent extends BaseFormComponent implements OnInit, OnDestroy { @Input() fileAttribute!: IFileAttributeConfig; - @Input() file!: File; - @Input() dossier!: Dossier; + isInEditMode = false; closedDatepicker = true; - readonly isEditingFileAttribute$: BehaviorSubject; - readonly selectedFile$: Observable; + readonly #subscriptions = new Subscription(); constructor( - private readonly _fileAttributesService: FileAttributesService, + router: Router, private readonly _toaster: Toaster, private readonly _formBuilder: FormBuilder, private readonly _filesService: FilesService, - private readonly _router: Router, - private readonly _listingService: ListingService, readonly permissionsService: PermissionsService, + private readonly _listingService: ListingService, + readonly fileAttributesService: FileAttributesService, ) { super(); - this.isEditingFileAttribute$ = this._fileAttributesService.isEditingFileAttribute$; + const sub = router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe(() => this.close()); + this.#subscriptions.add(sub); - _router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe(() => { - this.close(); - }); - - this.selectedFile$ = this._listingService.selectedLength$.pipe( + const sub2 = this._listingService.selectedLength$.pipe( map(selectedLength => !!selectedLength), tap(() => this.close()), ); - } - - ngOnInit(): void { - if (this.#noFileAttributes) { - this.#initFileAttributes(); - } - - this.form = this.#getForm(); - this.initialFormValue = this.form.getRawValue(); + this.#subscriptions.add(sub2.subscribe()); } get isDate(): boolean { @@ -71,16 +57,63 @@ export class FileAttributeComponent extends BaseFormComponent implements OnInit return JSON.stringify(this.file.fileAttributes.attributeIdToValue) === '{}'; } - #initFileAttributes() { - const configs = this._fileAttributesService.getFileAttributeConfig(this.file.dossierTemplateId).fileAttributeConfigs; - configs.forEach(config => (this.file.fileAttributes.attributeIdToValue[config.id] = null)); + ngOnDestroy() { + this.#subscriptions.unsubscribe(); } - async editFileAttribute($event: MouseEvent): Promise { - $event?.stopPropagation(); + ngOnInit(): void { + if (this.#noFileAttributes) { + this.#initFileAttributes(); + } + + this.form = this.#getForm(); + this.initialFormValue = this.form.getRawValue(); + } + + async editFileAttribute(): Promise { this.#toggleEdit(); } + async save() { + const rawFormValue = this.form.getRawValue(); + const fileAttrValue = rawFormValue[this.fileAttribute.id]; + const attributeIdToValue = { + ...rawFormValue, + [this.fileAttribute.id]: this.#formatAttributeValue(fileAttrValue), + }; + try { + await firstValueFrom( + this.fileAttributesService.setFileAttributes({ attributeIdToValue }, this.file.dossierId, this.file.fileId), + ); + await firstValueFrom(this._filesService.reload(this.file.dossierId, this.file)); + this.initialFormValue = rawFormValue; + this._toaster.success(_('file-attribute.update.success')); + } catch (e) { + this._toaster.error(_('file-attribute.update.error')); + } + + this.#toggleEdit(); + } + + close(): void { + if (this.isInEditMode) { + this.form = this.#getForm(); + this.#toggleEdit(); + } + } + + @HostListener('document:click') + clickOutside() { + if (this.isInEditMode && this.closedDatepicker) { + this.close(); + } + } + + #initFileAttributes() { + const configs = this.fileAttributesService.getFileAttributeConfig(this.file.dossierTemplateId).fileAttributeConfigs; + configs.forEach(config => (this.file.fileAttributes.attributeIdToValue[config.id] = null)); + } + #getForm(): UntypedFormGroup { const config = {}; const fileAttributes = this.file.fileAttributes.attributeIdToValue; @@ -91,43 +124,13 @@ export class FileAttributeComponent extends BaseFormComponent implements OnInit return this._formBuilder.group(config); } - async save($event?: MouseEvent): Promise { - $event?.stopPropagation(); - const rawFormValue = this.form.getRawValue(); - const fileAttrValue = rawFormValue[this.fileAttribute.id]; - const attributeIdToValue = { - ...rawFormValue, - [this.fileAttribute.id]: this.#formatAttributeValue(fileAttrValue), - }; - try { - await firstValueFrom( - this._fileAttributesService.setFileAttributes({ attributeIdToValue }, this.file.dossierId, this.file.fileId), - ); - await firstValueFrom(this._filesService.reload(this.file.dossierId, this.file)); - this.initialFormValue = rawFormValue; - this._toaster.success(_('file-attribute.update.success')); - } catch (e) { - this._toaster.error(_('file-attribute.update.error')); - } - - this.#toggleEdit(); - } - - close($event?: MouseEvent): void { - $event?.stopPropagation(); - if (this.isInEditMode) { - this.form = this.#getForm(); - this.#toggleEdit(); - } - } - #formatAttributeValue(attrValue) { return this.isDate ? attrValue && dayjs(attrValue).format('YYYY-MM-DD') : attrValue; } #toggleEdit(): void { this.isInEditMode = !this.isInEditMode; - this.isEditingFileAttribute$.next(this.isInEditMode); + this.fileAttributesService.isEditingFileAttribute$.next(this.isInEditMode); if (this.isInEditMode) { this.#focusOnEditInput(); @@ -140,11 +143,4 @@ export class FileAttributeComponent extends BaseFormComponent implements OnInit input.focus(); }, 100); } - - @HostListener('document:click') - clickOutside() { - if (this.isInEditMode && this.closedDatepicker) { - this.close(); - } - } } diff --git a/apps/red-ui/src/app/modules/dossier-overview/components/table-item/table-item.component.ts b/apps/red-ui/src/app/modules/dossier-overview/components/table-item/table-item.component.ts index 657dead78..3cce7aa73 100644 --- a/apps/red-ui/src/app/modules/dossier-overview/components/table-item/table-item.component.ts +++ b/apps/red-ui/src/app/modules/dossier-overview/components/table-item/table-item.component.ts @@ -1,11 +1,10 @@ -import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { Component, Input } from '@angular/core'; import { Dossier, File, IFileAttributeConfig } from '@red/domain'; @Component({ selector: 'redaction-table-item [file] [dossier] [displayedAttributes] [dossierTemplateId]', templateUrl: './table-item.component.html', styleUrls: ['./table-item.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, }) export class TableItemComponent { @Input() file: File; diff --git a/apps/red-ui/src/app/modules/dossier-overview/components/workflow-item/workflow-item.component.html b/apps/red-ui/src/app/modules/dossier-overview/components/workflow-item/workflow-item.component.html index 9b743922e..27720cc5e 100644 --- a/apps/red-ui/src/app/modules/dossier-overview/components/workflow-item/workflow-item.component.html +++ b/apps/red-ui/src/app/modules/dossier-overview/components/workflow-item/workflow-item.component.html @@ -13,8 +13,9 @@
    -
    - {{ file.fileAttributes.attributeIdToValue[config.id] || '-' }} +
    + {{ config.label }} + {{ file.fileAttributes.attributeIdToValue[config.id] || '-' }}
    diff --git a/apps/red-ui/src/app/modules/dossier-overview/components/workflow-item/workflow-item.component.scss b/apps/red-ui/src/app/modules/dossier-overview/components/workflow-item/workflow-item.component.scss index 3f2e7cc60..80fd48faa 100644 --- a/apps/red-ui/src/app/modules/dossier-overview/components/workflow-item/workflow-item.component.scss +++ b/apps/red-ui/src/app/modules/dossier-overview/components/workflow-item/workflow-item.component.scss @@ -10,6 +10,22 @@ &:hover redaction-file-actions { display: initial; } + + .attribute { + display: flex; + flex-direction: column; + + b { + text-transform: uppercase; + } + + b, + span { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + } + } } .details-wrapper { diff --git a/apps/red-ui/src/app/modules/dossier-overview/components/workflow-item/workflow-item.component.ts b/apps/red-ui/src/app/modules/dossier-overview/components/workflow-item/workflow-item.component.ts index 35da040ca..f2a7488d8 100644 --- a/apps/red-ui/src/app/modules/dossier-overview/components/workflow-item/workflow-item.component.ts +++ b/apps/red-ui/src/app/modules/dossier-overview/components/workflow-item/workflow-item.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core'; +import { ChangeDetectorRef, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core'; import { Dossier, File, IFileAttributeConfig } from '@red/domain'; import { Debounce } from '@iqser/common-ui'; @@ -6,7 +6,6 @@ import { Debounce } from '@iqser/common-ui'; selector: 'redaction-workflow-item [file] [dossier] [displayedAttributes]', templateUrl: './workflow-item.component.html', styleUrls: ['./workflow-item.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, }) export class WorkflowItemComponent implements OnInit { @Input() file: File; diff --git a/apps/red-ui/src/app/modules/dossier-overview/config.service.ts b/apps/red-ui/src/app/modules/dossier-overview/config.service.ts index 94ccb7e61..7bd2317da 100644 --- a/apps/red-ui/src/app/modules/dossier-overview/config.service.ts +++ b/apps/red-ui/src/app/modules/dossier-overview/config.service.ts @@ -1,6 +1,7 @@ import { Injectable, TemplateRef } from '@angular/core'; import { ActionConfig, + getConfig, getCurrentUser, getParam, IFilterGroup, @@ -19,6 +20,7 @@ import { import { annotationDefaultColorConfig, AnnotationShapeMap, + AppConfig, DOSSIER_ID, File, IFileAttributeConfig, @@ -36,7 +38,6 @@ import { UserService } from '@users/user.service'; import { DossiersDialogService } from '../shared-dossiers/services/dossiers-dialog.service'; import { annotationFilterChecker, RedactionFilterSorter } from '../../utils'; import { workloadTranslations } from '@translations/workload-translations'; -import { ConfigService as AppConfigService } from '@services/config.service'; import { BehaviorSubject, Observable } from 'rxjs'; import { BulkActionsService } from './services/bulk-actions.service'; import dayjs from 'dayjs'; @@ -50,16 +51,16 @@ import { ROLES } from '@users/roles'; @Injectable() export class ConfigService { readonly listingMode$: Observable; - readonly dossierId = getParam(DOSSIER_ID); - readonly currentUser = getCurrentUser(); - private readonly _listingMode$ = new BehaviorSubject(ListingModes.table); + readonly #dossierId = getParam(DOSSIER_ID); + readonly #currentUser = getCurrentUser(); + readonly #config = getConfig(); + readonly #listingMode$: BehaviorSubject; constructor( private readonly _permissionsService: PermissionsService, private readonly _translateService: TranslateService, private readonly _userService: UserService, private readonly _dialogService: DossiersDialogService, - private readonly _appConfigService: AppConfigService, private readonly _bulkActionsService: BulkActionsService, private readonly _defaultColorsService: DefaultColorsService, private readonly _dictionariesMapService: DictionariesMapService, @@ -67,23 +68,23 @@ export class ConfigService { private readonly _dossiersService: DossiersService, private readonly _iqserPermissionsService: IqserPermissionsService, ) { - this.listingMode$ = this._listingMode$.asObservable(); - const previousListingMode = this._userPreferenceService.getFilesListingMode(); - this._listingMode$.next(previousListingMode ? previousListingMode : ListingModes.table); + const listingMode = previousListingMode ? previousListingMode : ListingModes.table; + this.#listingMode$ = new BehaviorSubject(listingMode); + this.listingMode$ = this.#listingMode$.asObservable(); } get listingMode(): ListingMode { - return this._listingMode$.value; + return this.#listingMode$.value; } set listingMode(listingMode: ListingMode) { - this._listingMode$.next(listingMode); + this.#listingMode$.next(listingMode); this._userPreferenceService.saveFilesListingMode(listingMode).then(); } get #dossier() { - return this._dossiersService.find(this.dossierId); + return this._dossiersService.find(this.#dossierId); } workflowConfig(): WorkflowConfig { @@ -157,9 +158,9 @@ export class ConfigService { { id: 'editDossier', label: this._translateService.instant('dossier-overview.header-actions.edit'), - action: $event => this._openEditDossierDialog($event, dossierId), + action: () => this._openEditDossierDialog(dossierId), icon: 'iqser:edit', - hide: !this.currentUser.isManager && !this._iqserPermissionsService.has(ROLES.dossiers.edit), + hide: !this.#currentUser.isManager && !this._iqserPermissionsService.has(ROLES.dossiers.edit), helpModeKey: 'edit_dossier_in_dossier', overlappingElements: [OverlappingElements.USER_MENU], disabled$, @@ -403,8 +404,7 @@ export class ConfigService { return filterGroups; } - _recentlyModifiedChecker = (file: File) => - dayjs(file.lastUpdated).add(this._appConfigService.values.RECENT_PERIOD_IN_HOURS, 'hours').isAfter(dayjs()); + _recentlyModifiedChecker = (file: File) => dayjs(file.lastUpdated).add(this.#config.RECENT_PERIOD_IN_HOURS, 'hours').isAfter(dayjs()); _assignedToMeChecker = (file: File) => file.assignee === this._userService.currentUser.id; @@ -413,7 +413,7 @@ export class ConfigService { _assignedToOthersChecker = (file: File) => file.assignee && file.assignee !== this._userService.currentUser.id; private _quickFilters(entities: File[]): NestedFilter[] { - const recentPeriod = this._appConfigService.values.RECENT_PERIOD_IN_HOURS; + const recentPeriod = this.#config.RECENT_PERIOD_IN_HOURS; return [ { id: 'recent', @@ -449,8 +449,8 @@ export class ConfigService { ].map(filter => new NestedFilter(filter)); } - private _openEditDossierDialog($event: MouseEvent, dossierId: string) { - this._dialogService.openDialog('editDossier', $event, { dossierId }); + private _openEditDossierDialog(dossierId: string) { + this._dialogService.openDialog('editDossier', { dossierId }); } private _sortByName(ids: string[]) { diff --git a/apps/red-ui/src/app/modules/dossier-overview/dossier-overview.module.ts b/apps/red-ui/src/app/modules/dossier-overview/dossier-overview.module.ts index 3e2a64bfb..8f23b68c9 100644 --- a/apps/red-ui/src/app/modules/dossier-overview/dossier-overview.module.ts +++ b/apps/red-ui/src/app/modules/dossier-overview/dossier-overview.module.ts @@ -1,18 +1,18 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { RouterModule, Routes } from '@angular/router'; -import { SharedModule } from '@shared/shared.module'; +import { RouterModule } from '@angular/router'; import { - IqserButtonsModule, + CircleButtonComponent, + DynamicInputComponent, + HasScrollbarDirective, + IqserAllowDirective, IqserHelpModeModule, - IqserIconsModule, - IqserInputsModule, IqserListingModule, IqserLoadingModule, - IqserPermissionsModule, - IqserScrollbarModule, - IqserSharedModule, + IqserRoutes, IqserUsersModule, + StatusBarComponent, + StopPropagationDirective, } from '@iqser/common-ui'; import { TranslateModule } from '@ngx-translate/core'; import { DossierOverviewScreenComponent } from './screen/dossier-overview-screen.component'; @@ -26,8 +26,9 @@ import { WorkflowItemComponent } from './components/workflow-item/workflow-item. import { DossierOverviewScreenHeaderComponent } from './components/screen-header/dossier-overview-screen-header.component'; import { ViewModeSelectionComponent } from './components/view-mode-selection/view-mode-selection.component'; import { FileAttributeComponent } from './components/table-item/file-attribute/file-attribute.component'; +import { SharedModule } from '@shared/shared.module'; -const routes: Routes = [ +const routes: IqserRoutes = [ { path: '', component: DossierOverviewScreenComponent, @@ -54,19 +55,19 @@ const routes: Routes = [ imports: [ RouterModule.forChild(routes), CommonModule, - SharedModule, SharedDossiersModule, - IqserIconsModule, TranslateModule, IqserHelpModeModule, IqserUsersModule, IqserListingModule, - IqserButtonsModule, IqserLoadingModule, - IqserSharedModule, - IqserScrollbarModule, - IqserPermissionsModule, - IqserInputsModule, + SharedModule, + CircleButtonComponent, + StatusBarComponent, + StopPropagationDirective, + HasScrollbarDirective, + DynamicInputComponent, + IqserAllowDirective, ], }) export class DossierOverviewModule {} diff --git a/apps/red-ui/src/app/modules/dossier-overview/screen/dossier-overview-screen.component.html b/apps/red-ui/src/app/modules/dossier-overview/screen/dossier-overview-screen.component.html index e951e7522..7c87c717c 100644 --- a/apps/red-ui/src/app/modules/dossier-overview/screen/dossier-overview-screen.component.html +++ b/apps/red-ui/src/app/modules/dossier-overview/screen/dossier-overview-screen.component.html @@ -53,7 +53,7 @@
    -
    +
    - - - - + + + + implements OnInit, OnAttach { +export class DossierOverviewScreenComponent extends ListingComponent implements OnInit, OnAttach, OnDetach, OnDestroy { readonly listingModes = ListingModes; readonly circleButtonTypes = CircleButtonTypes; readonly tableHeaderLabel = _('dossier-overview.table-header.title'); @@ -70,10 +68,9 @@ export class DossierOverviewScreenComponent extends ListingComponent imple readonly dossier$: Observable; readonly files$: Observable; readonly dossierId = getParam(DOSSIER_ID); - readonly dossierTemplateId: string; readonly workflowConfig: WorkflowConfig; readonly dossierAttributes$: Observable; - #currentDossier: Dossier; + #dossier: Dossier; @ViewChild('needsWorkFilterTemplate', { read: TemplateRef, static: true }) private readonly _needsWorkFilterTemplate: TemplateRef; @ViewChild('fileInput', { static: true }) private readonly _fileInput: ElementRef; @@ -81,8 +78,6 @@ export class DossierOverviewScreenComponent extends ListingComponent imple private _fileAttributeConfigs: IFileAttributeConfig[]; constructor( - private readonly _router: Router, - private readonly _logger: NGXLogger, readonly configService: ConfigService, private readonly _errorService: ErrorService, private readonly _filesService: FilesService, @@ -99,16 +94,12 @@ export class DossierOverviewScreenComponent extends ListingComponent imple private readonly _dossierAttributesService: DossierAttributesService, ) { super(); - this.dossier$ = _dossiersService.getEntityChanged$(this.dossierId).pipe(tap(dossier => (this.#currentDossier = dossier))); + this.dossier$ = _dossiersService.getEntityChanged$(this.dossierId).pipe(tap(dossier => (this.#dossier = dossier))); this.dossierAttributes$ = this._dossierAttributesService.all$.pipe(tap(() => this.#updateDossierAttributes())); - this.#currentDossier = _dossiersService.find(this.dossierId); + this.#dossier = _dossiersService.find(this.dossierId); this.workflowConfig = configService.workflowConfig(); - this.dossierTemplateId = this.#currentDossier.dossierTemplateId; - this.files$ = merge(this.#files$, this.#dossierFilesChange$).pipe(log('Files'), shareLast()); - this._updateFileAttributes(); - _router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe(() => { - this._fileDropOverlayService.cleanupFileDropHandling(); - }); + this.files$ = merge(this.#files$, this.#dossierFilesChange$).pipe(shareLast()); + this.#updateFileAttributes(); } get checkedRequiredFilters(): NestedFilter[] { @@ -142,30 +133,30 @@ export class DossierOverviewScreenComponent extends ListingComponent imple this._setRemovableSubscriptions(); this.#initFileDropHandling(); - this.addSubscription = this._dossierTemplatesService - .getEntityChanged$(this.dossierTemplateId) - .pipe( - skip(1), - tap(() => { - this._updateFileAttributes(); - }), - ) - .subscribe(); - this._loadingService.stop(); } ngOnAttach() { this.#initFileDropHandling(); this._setRemovableSubscriptions(); - this._updateFileAttributes(); + this.#updateFileAttributes(); this._tableComponent?.scrollToLastIndex(); } + override ngOnDetach() { + this.#cleanupFileDropHandling(); + super.ngOnDetach(); + } + + override ngOnDestroy() { + this.#cleanupFileDropHandling(); + super.ngOnDestroy(); + } + @HostListener('drop', ['$event']) onDrop(event: DragEvent): void { - if (this.permissionsService.canUploadFiles(this.#currentDossier)) { - handleFileDrop(event, this.#currentDossier, this._uploadFiles.bind(this)); + if (this.permissionsService.canUploadFiles(this.#dossier)) { + handleFileDrop(event, this.#dossier, this._uploadFiles.bind(this)); } } @@ -176,21 +167,27 @@ export class DossierOverviewScreenComponent extends ListingComponent imple } async uploadFiles(files: Files): Promise { - await this._uploadFiles(convertFiles(files, this.#currentDossier)); + await this._uploadFiles(convertFiles(files, this.#dossier)); (this._fileInput as any).nativeElement.value = null; } + #cleanupFileDropHandling() { + if (this.permissionsService.canUploadFiles(this.#dossier)) { + this._fileDropOverlayService.cleanupFileDropHandling(); + } + } + #initFileDropHandling(): void { - if (this.permissionsService.canUploadFiles(this.#currentDossier)) { + if (this.permissionsService.canUploadFiles(this.#dossier)) { this._fileDropOverlayService.initFileDropHandling(this.dossierId); } } async #updateDossierAttributes(): Promise { try { - this.dossierAttributes = await this._dossierAttributesService.getWithValues(this.#currentDossier); + this.dossierAttributes = await this._dossierAttributesService.getWithValues(this.#dossier); } catch (e) { - this._logger.error('[DOSSIER ATTRIBUTES] Error: ', e); + console.error('[DOSSIER ATTRIBUTES] Error: ', e); } } @@ -199,6 +196,14 @@ export class DossierOverviewScreenComponent extends ListingComponent imple .getEntityDeleted$(this.dossierId) .pipe(tap(() => this._handleDeletedDossier())) .subscribe(); + + this.addActiveScreenSubscription = this._dossierTemplatesService + .getEntityChanged$(this.#dossier.dossierTemplateId) + .pipe( + skip(1), + tap(() => this.#updateFileAttributes()), + ) + .subscribe(); } private _handleDeletedDossier(): void { @@ -207,8 +212,9 @@ export class DossierOverviewScreenComponent extends ListingComponent imple ); } - private _updateFileAttributes(): void { - this._fileAttributeConfigs = this._fileAttributesService.getFileAttributeConfig(this.dossierTemplateId)?.fileAttributeConfigs || []; + #updateFileAttributes() { + const attributes = this._fileAttributesService.getFileAttributeConfig(this.#dossier.dossierTemplateId); + this._fileAttributeConfigs = attributes?.fileAttributeConfigs || []; this.displayedInFileListAttributes = this._fileAttributeConfigs.filter(config => config.displayedInFileList); this.displayedAttributes = this.displayedInFileListAttributes.filter(c => c.displayedInFileList); this.displayedWorkflowAttributes = this.#getDisplayedWorkflowAttributes(this.displayedAttributes); @@ -236,7 +242,7 @@ export class DossierOverviewScreenComponent extends ListingComponent imple const filterGroups = this.configService.filterGroups( this.entitiesService.all, this._fileAttributeConfigs, - this.dossierTemplateId, + this.#dossier.dossierTemplateId, this._needsWorkFilterTemplate, () => this.checkedRequiredFilters, () => this.checkedNotRequiredFilters, diff --git a/apps/red-ui/src/app/modules/dossier-overview/services/bulk-actions.service.ts b/apps/red-ui/src/app/modules/dossier-overview/services/bulk-actions.service.ts index 343e61cd6..c092aad30 100644 --- a/apps/red-ui/src/app/modules/dossier-overview/services/bulk-actions.service.ts +++ b/apps/red-ui/src/app/modules/dossier-overview/services/bulk-actions.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { Dossier, File, WorkflowFileStatus, WorkflowFileStatuses } from '@red/domain'; import { DossiersDialogService } from '../../shared-dossiers/services/dossiers-dialog.service'; -import { ConfirmationDialogInput, LoadingService } from '@iqser/common-ui'; +import { IConfirmationDialogData, LoadingService } from '@iqser/common-ui'; import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service'; import { FilesService } from '@services/files/files.service'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; @@ -29,7 +29,7 @@ export class BulkActionsService { this._assignFiles(files, WorkflowFileStatuses.UNDER_APPROVAL, true); } else { this._loadingService.start(); - await firstValueFrom(this._filesService.setUnderApprovalFor(files, dossier.id, dossier.approverIds[0])); + await this._filesService.setUnderApproval(files, dossier.approverIds[0]); this._loadingService.stop(); } } @@ -47,11 +47,10 @@ export class BulkActionsService { delete(files: File[]) { this._dialogService.openDialog( 'confirm', - null, - new ConfirmationDialogInput({ + { title: _('confirmation-dialog.delete-file.title'), question: _('confirmation-dialog.delete-file.question'), - }), + } as IConfirmationDialogData, async () => { this._loadingService.start(); await firstValueFrom(this._fileManagementService.delete(files, files[0].dossierId)); @@ -82,13 +81,13 @@ export class BulkActionsService { async backToUnderReview(files: File[]): Promise { this._loadingService.start(); - await firstValueFrom(this._filesService.setUnderReviewFor(files, files[0].dossierId)); + await this._filesService.setUnderReviewFor(files); this._loadingService.stop(); } async setToNew(files: File[]): Promise { this._loadingService.start(); - await firstValueFrom(this._filesService.setToNewFor(files, files[0].dossierId)); + await this._filesService.setToNew(files); this._loadingService.stop(); } @@ -98,8 +97,7 @@ export class BulkActionsService { if (foundAnalysisRequiredFile || foundUpdatedFile) { this._dialogService.openDialog( 'confirm', - null, - new ConfirmationDialogInput({ + { title: foundAnalysisRequiredFile ? _('confirmation-dialog.approve-multiple-files-without-analysis.title') : _('confirmation-dialog.approve-multiple-files.title'), @@ -110,16 +108,16 @@ export class BulkActionsService { ? _('confirmation-dialog.approve-multiple-files-without-analysis.confirmationText') : null, denyText: foundAnalysisRequiredFile ? _('confirmation-dialog.approve-multiple-files-without-analysis.denyText') : null, - }), + } as IConfirmationDialogData, async () => { this._loadingService.start(); - await firstValueFrom(this._filesService.setApprovedFor(files, files[0].dossierId)); + await this._filesService.setApproved(files); this._loadingService.stop(); }, ); } else { this._loadingService.start(); - await firstValueFrom(this._filesService.setApprovedFor(files, files[0].dossierId)); + await this._filesService.setApproved(files); this._loadingService.stop(); } } @@ -133,6 +131,6 @@ export class BulkActionsService { } private _assignFiles(files: File[], targetStatus: WorkflowFileStatus, ignoreChanged = false, withUnassignedOption = false): void { - this._dialogService.openDialog('assignFile', null, { targetStatus, files, ignoreChanged, withUnassignedOption }); + this._dialogService.openDialog('assignFile', { targetStatus, files, ignoreChanged, withUnassignedOption }); } } diff --git a/apps/red-ui/src/app/modules/dossiers-listing/config.service.ts b/apps/red-ui/src/app/modules/dossiers-listing/config.service.ts index e50a6e8ef..c6a87dbc2 100644 --- a/apps/red-ui/src/app/modules/dossiers-listing/config.service.ts +++ b/apps/red-ui/src/app/modules/dossiers-listing/config.service.ts @@ -153,7 +153,7 @@ export class ConfigService { checker: (dossier: Dossier, filter: INestedFilter) => this._dossierStatusChecker(dossier, filter), }); - const peopleFilters = [...allDistinctPeople].map( + const peopleFilters = this._sortByName([...allDistinctPeople]).map( userId => new NestedFilter({ id: userId, @@ -217,7 +217,7 @@ export class ConfigService { } private _openAddDossierDialog(dossierTemplateId: string): void { - this._dialogService.openDialog('addDossier', null, { dossierTemplateId }); + this._dialogService.openDialog('addDossier', { dossierTemplateId }); } private _quickFilters(entities: Dossier[]): NestedFilter[] { @@ -265,4 +265,8 @@ export class ConfigService { } } }; + + private _sortByName(ids: string[]) { + return ids.sort((a, b) => this._userService.getName(a).localeCompare(this._userService.getName(b))); + } } diff --git a/apps/red-ui/src/app/modules/dossiers-listing/dossiers-listing.module.ts b/apps/red-ui/src/app/modules/dossiers-listing/dossiers-listing.module.ts index 7bfb2c734..5033d8fe1 100644 --- a/apps/red-ui/src/app/modules/dossiers-listing/dossiers-listing.module.ts +++ b/apps/red-ui/src/app/modules/dossiers-listing/dossiers-listing.module.ts @@ -2,19 +2,16 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { CompositeRouteGuard, - IqserButtonsModule, + HasScrollbarDirective, IqserHelpModeModule, - IqserIconsModule, IqserListingModule, - IqserPermissionsModule, - IqserScrollbarModule, - IqserSharedModule, + IqserRoutes, IqserUsersModule, - LogPipe, + StatusBarComponent, } from '@iqser/common-ui'; import { TranslateModule } from '@ngx-translate/core'; import { DossiersListingScreenComponent } from './screen/dossiers-listing-screen.component'; -import { RouterModule, Routes } from '@angular/router'; +import { RouterModule } from '@angular/router'; import { SharedModule } from '@shared/shared.module'; import { DossiersListingDetailsComponent } from './components/dossiers-listing-details/dossiers-listing-details.component'; import { ConfigService } from './config.service'; @@ -25,7 +22,7 @@ import { DossierDocumentsStatusComponent } from './components/dossier-documents- import { DossierFilesGuard } from '@guards/dossier-files-guard'; import { ACTIVE_DOSSIERS_SERVICE } from '../../tokens'; -const routes: Routes = [ +const routes: IqserRoutes = [ { path: '', component: DossiersListingScreenComponent, @@ -49,16 +46,12 @@ const routes: Routes = [ CommonModule, SharedModule, SharedDossiersModule, - IqserIconsModule, TranslateModule, IqserHelpModeModule, IqserUsersModule, - IqserButtonsModule, IqserListingModule, - IqserScrollbarModule, - IqserSharedModule, - IqserPermissionsModule, - LogPipe, + StatusBarComponent, + HasScrollbarDirective, ], }) export class DossiersListingModule {} diff --git a/apps/red-ui/src/app/modules/dossiers-listing/screen/dossiers-listing-screen.component.ts b/apps/red-ui/src/app/modules/dossiers-listing/screen/dossiers-listing-screen.component.ts index 9ab4b00e5..1ca7f7ddf 100644 --- a/apps/red-ui/src/app/modules/dossiers-listing/screen/dossiers-listing-screen.component.ts +++ b/apps/red-ui/src/app/modules/dossiers-listing/screen/dossiers-listing-screen.component.ts @@ -47,7 +47,7 @@ export class DossiersListingScreenComponent extends ListingComponent im } openAddDossierDialog(): void { - this._dialogService.openDialog('addDossier', null, { dossierTemplateId: this.dossierTemplate.id }); + this._dialogService.openDialog('addDossier', { dossierTemplateId: this.dossierTemplate.id }); } async ngOnInit(): Promise { diff --git a/apps/red-ui/src/app/modules/file-preview/components/annotation-actions/annotation-actions.component.html b/apps/red-ui/src/app/modules/file-preview/components/annotation-actions/annotation-actions.component.html index 7ac04139c..7702ff635 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/annotation-actions/annotation-actions.component.html +++ b/apps/red-ui/src/app/modules/file-preview/components/annotation-actions/annotation-actions.component.html @@ -6,7 +6,7 @@ this._annotations.reduce((acc, annotation) => !hidden.has(annotation.id) && acc, true)), - shareLast(), - ); - constructor( readonly viewModeService: ViewModeService, readonly helpModeService: HelpModeService, @@ -53,6 +48,11 @@ export class AnnotationActionsComponent implements OnChanges { private _annotations: AnnotationWrapper[]; + readonly isVisible$ = this._annotationManager.hidden$.pipe( + map(hidden => this._annotations.reduce((acc, annotation) => !hidden.has(annotation.id) && acc, true)), + shareLast(), + ); + get annotations(): AnnotationWrapper[] { return this._annotations; } @@ -75,43 +75,40 @@ export class AnnotationActionsComponent implements OnChanges { this._setPermissions(); } - removeOrSuggestRemoveAnnotation($event: MouseEvent, removeFromDict: boolean) { - $event.stopPropagation(); - this.annotationActionsService.removeOrSuggestRemoveAnnotation($event, this.annotations, removeFromDict); + removeOrSuggestRemoveAnnotation(removeFromDict: boolean) { + this.annotationActionsService.removeOrSuggestRemoveAnnotation(this.annotations, removeFromDict); } - markAsFalsePositive($event: MouseEvent) { - this.annotationActionsService.markAsFalsePositive($event, this.annotations); + markAsFalsePositive() { + this.annotationActionsService.markAsFalsePositive(this.annotations); } - acceptRecommendation($event: MouseEvent) { - this.annotationActionsService.convertRecommendationToAnnotation($event, this.annotations); + acceptRecommendation() { + this.annotationActionsService.convertRecommendationToAnnotation(this.annotations); } - hideAnnotation($event: MouseEvent) { - $event.stopPropagation(); + hideAnnotation() { this._annotationManager.hide(this.viewerAnnotations); this._annotationManager.deselect(); this._annotationManager.addToHidden(this.viewerAnnotations[0].Id); } - showAnnotation($event: MouseEvent) { - $event.stopPropagation(); + showAnnotation() { this._annotationManager.show(this.viewerAnnotations); this._annotationManager.deselect(); this._annotationManager.removeFromHidden(this.viewerAnnotations[0].Id); } - resize($event: MouseEvent) { - return this.annotationActionsService.resize($event, this.annotations[0]); + resize() { + return this.annotationActionsService.resize(this.annotations[0]); } - acceptResize($event: MouseEvent) { - return this.annotationActionsService.acceptResize($event, this.annotations[0]); + acceptResize() { + return this.annotationActionsService.acceptResize(this.annotations[0]); } - cancelResize($event: MouseEvent) { - return this.annotationActionsService.cancelResize($event, this.annotations[0]); + cancelResize() { + return this.annotationActionsService.cancelResize(this.annotations[0]); } private _setPermissions() { diff --git a/apps/red-ui/src/app/modules/file-preview/components/annotation-wrapper/annotation-wrapper.component.html b/apps/red-ui/src/app/modules/file-preview/components/annotation-wrapper/annotation-wrapper.component.html index 51e8453d4..b4c5a02da 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/annotation-wrapper/annotation-wrapper.component.html +++ b/apps/red-ui/src/app/modules/file-preview/components/annotation-wrapper/annotation-wrapper.component.html @@ -10,10 +10,11 @@
    {{ annotation.comments.length }} diff --git a/apps/red-ui/src/app/modules/file-preview/components/comments/comments.component.html b/apps/red-ui/src/app/modules/file-preview/components/comments/comments.component.html index 7d566fc69..2bec686eb 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/comments/comments.component.html +++ b/apps/red-ui/src/app/modules/file-preview/components/comments/comments.component.html @@ -8,7 +8,7 @@
    -
    +
    diff --git a/apps/red-ui/src/app/modules/file-preview/components/comments/comments.component.ts b/apps/red-ui/src/app/modules/file-preview/components/comments/comments.component.ts index 22c1e3e67..2af8ee215 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/comments/comments.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/components/comments/comments.component.ts @@ -71,13 +71,11 @@ export class CommentsComponent extends ContextComponent impleme this._loadingService.stop(); } - toggleExpandComments($event?: MouseEvent): void { - $event?.stopPropagation(); + toggleExpandComments(): void { this._commentingService.toggle(this.annotation.id); } - async deleteComment($event: MouseEvent, comment: IComment): Promise { - $event.stopPropagation(); + async deleteComment(comment: IComment): Promise { this._loadingService.start(); const { dossierId, fileId } = this._stateService; await this._manualRedactionService.deleteComment(comment.id, this.annotation.id, dossierId, fileId); diff --git a/apps/red-ui/src/app/modules/file-preview/components/document-info/document-info.component.ts b/apps/red-ui/src/app/modules/file-preview/components/document-info/document-info.component.ts index 9ff04473f..9328d3a55 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/document-info/document-info.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/components/document-info/document-info.component.ts @@ -56,7 +56,7 @@ export class DocumentInfoComponent extends ContextComponent { } edit(file: File) { - this._dialogService.openDialog('documentInfo', null, file); + this._dialogService.openDialog('documentInfo', file); } isDate(attribute: FileAttribute) { diff --git a/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.html b/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.html index 992bf5aa0..eb3cbb197 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.html +++ b/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.html @@ -214,9 +214,10 @@ diff --git a/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.ts b/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.ts index 6e8fba035..595a6cbc2 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectorRef, Component, ElementRef, HostListener, Input, OnDestroy, ViewChild } from '@angular/core'; import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { AnnotationProcessingService } from '../../services/annotation-processing.service'; -import { MatDialogState } from '@angular/material/dialog'; +import { MatDialog } from '@angular/material/dialog'; import scrollIntoView from 'scroll-into-view-if-needed'; import { AutoUnsubscribe, @@ -60,6 +60,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy constructor( readonly filterService: FilterService, readonly skippedService: SkippedService, + private readonly _dialog: MatDialog, readonly state: FilePreviewStateService, readonly pdf: PdfViewer, readonly fileDataService: FileDataService, @@ -186,7 +187,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy handleKeyEvent($event: KeyboardEvent): void { if ( !ALL_HOTKEY_ARRAY.includes($event.key) || - this.state.dialogRef?.getState() === MatDialogState.OPEN || + this._dialog.openDialogs.length || ($event.target as IqserEventTarget).localName === 'input' ) { return; diff --git a/apps/red-ui/src/app/modules/file-preview/components/highlights-separator/highlights-separator.component.ts b/apps/red-ui/src/app/modules/file-preview/components/highlights-separator/highlights-separator.component.ts index 0ace5b714..531e7ec32 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/highlights-separator/highlights-separator.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/components/highlights-separator/highlights-separator.component.ts @@ -31,12 +31,12 @@ export class HighlightsSeparatorComponent { convertHighlights(highlightGroup: EarmarkGroup): void { const data = this._getActionData(highlightGroup, EarmarkOperation.CONVERT); - this._dialogService.openDialog('highlightAction', null, data); + this._dialogService.openDialog('highlightAction', data); } removeHighlights(highlightGroup: EarmarkGroup): void { const data = this._getActionData(highlightGroup, EarmarkOperation.REMOVE); - this._dialogService.openDialog('highlightAction', null, data); + this._dialogService.openDialog('highlightAction', data); } private _getActionData(highlightGroup: EarmarkGroup, operation: EarmarkOperation) { diff --git a/apps/red-ui/src/app/modules/file-preview/components/page-indicator/page-indicator.component.ts b/apps/red-ui/src/app/modules/file-preview/components/page-indicator/page-indicator.component.ts index 674188379..505ccaaa9 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/page-indicator/page-indicator.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/components/page-indicator/page-indicator.component.ts @@ -4,12 +4,14 @@ import { ViewedPagesService } from '@services/files/viewed-pages.service'; import { FilePreviewStateService } from '../../services/file-preview-state.service'; import { PageRotationService } from '../../../pdf-viewer/services/page-rotation.service'; import { ContextComponent, getConfig } from '@iqser/common-ui'; -import { tap } from 'rxjs/operators'; +import { map, tap } from 'rxjs/operators'; import { AppConfig, ViewedPage } from '@red/domain'; import { ViewedPagesMapService } from '@services/files/viewed-pages-map.service'; +import { pairwise } from 'rxjs'; interface PageIndicatorContext { isRotated: boolean; + assigneeChanged: boolean; } @Component({ @@ -47,7 +49,12 @@ export class PageIndicatorComponent extends ContextComponent prevFile.assignee !== currFile.assignee), + tap(assigneeChanged => assigneeChanged && this.handlePageRead()), + ); + super._initContext({ isRotated: isRotated$, assigneeChanged: assigneeChanged$ }); } ngOnChanges() { diff --git a/apps/red-ui/src/app/modules/file-preview/components/user-management/user-management.component.html b/apps/red-ui/src/app/modules/file-preview/components/user-management/user-management.component.html index 8be9ccc97..6684b0f1b 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/user-management/user-management.component.html +++ b/apps/red-ui/src/app/modules/file-preview/components/user-management/user-management.component.html @@ -27,7 +27,7 @@ (save)="assignReviewer(file, $event)" *ngIf="editingReviewer" [options]="usersOptions$ | async" - [value]="file.assignee" + [value]="file.assignee === null ? undefined : file.assignee" >
    diff --git a/apps/red-ui/src/app/modules/file-preview/components/user-management/user-management.component.ts b/apps/red-ui/src/app/modules/file-preview/components/user-management/user-management.component.ts index e8ee23ee7..9d086d657 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/user-management/user-management.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/components/user-management/user-management.component.ts @@ -1,13 +1,13 @@ import { Component } from '@angular/core'; import { Dossier, File, StatusBarConfigs, User } from '@red/domain'; -import { List, LoadingService, Toaster } from '@iqser/common-ui'; +import { getCurrentUser, List, LoadingService, Toaster } from '@iqser/common-ui'; import { PermissionsService } from '@services/permissions.service'; import { workflowFileStatusTranslations } from '@translations/file-status-translations'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { UserService } from '@users/user.service'; import { FilesService } from '@services/files/files.service'; import { TranslateService } from '@ngx-translate/core'; -import { combineLatest, combineLatestWith, firstValueFrom, Observable, switchMap } from 'rxjs'; +import { combineLatest, combineLatestWith, Observable, switchMap } from 'rxjs'; import { FilePreviewStateService } from '../../services/file-preview-state.service'; import { distinctUntilChanged, map } from 'rxjs/operators'; import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service'; @@ -32,6 +32,7 @@ export class UserManagementComponent { private readonly _dossier$: Observable; private readonly _canAssignUser$: Observable; private readonly _canUnassignUser$: Observable; + readonly currentUserId = getCurrentUser().id; constructor( readonly fileAssignService: FileAssignService, @@ -89,41 +90,41 @@ export class UserManagementComponent { this.usersOptions$ = combineLatest([this._canUnassignUser$, this.stateService.file$, this._dossier$]).pipe( map(([canUnassignUser, file, dossier]) => { - const unassignUser = canUnassignUser ? [undefined] : []; + const unassignUser = canUnassignUser && file.assignee ? [undefined] : []; + console.log(unassignUser); return file.isUnderApproval - ? this._customSort([...dossier.approverIds, ...unassignUser], file) - : this._customSort([...dossier.memberIds, ...unassignUser], file); + ? this.#customSort([...dossier.approverIds, ...unassignUser]) + : this.#customSort([...dossier.memberIds, ...unassignUser]); }), ); } async assignReviewer(file: File, user: User | string) { const assigneeId = typeof user === 'string' ? user : user?.id; - const reviewerName = this.userService.getName(assigneeId); - const { dossierId, filename } = file; this.loadingService.start(); if (!assigneeId || file.isApproved) { - await firstValueFrom(this.filesService.setAssignee([file], dossierId, assigneeId)); + await this.filesService.setAssignee(file, assigneeId); } else if (file.isNew || file.isUnderReview) { - await firstValueFrom(this.filesService.setReviewerFor([file], dossierId, assigneeId)); + await this.filesService.setReviewer(file, assigneeId); } else { - await firstValueFrom(this.filesService.setUnderApprovalFor([file], dossierId, assigneeId)); + await this.filesService.setUnderApproval(file, assigneeId); } this.loadingService.stop(); - this.toaster.success(_('assignment.reviewer'), { params: { reviewerName, filename } }); + const translateParams = { reviewerName: this.userService.getName(assigneeId), filename: file.filename }; + this.toaster.success(_('assignment.reviewer'), { params: translateParams }); this.editingReviewer = false; } - private _customSort(ids: string[], file: File) { + #customSort(ids: string[]) { let sorted = [...ids].sort((a, b) => this.userService.getName(a).localeCompare(this.userService.getName(b))); - if (file.assignee) { - sorted = moveElementInArray(sorted, file.assignee, 0); + sorted = moveElementInArray(sorted, this.currentUserId, 0); + if (sorted.includes(undefined)) { + sorted = moveElementInArray(sorted, undefined, 1); } - sorted = moveElementInArray(sorted, undefined, file.assignee ? 1 : 0); return sorted; } } diff --git a/apps/red-ui/src/app/modules/file-preview/components/view-switch/view-switch.component.ts b/apps/red-ui/src/app/modules/file-preview/components/view-switch/view-switch.component.ts index 95c8c4f8a..ba927b32e 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/view-switch/view-switch.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/components/view-switch/view-switch.component.ts @@ -5,7 +5,7 @@ import { FilePreviewStateService } from '../../services/file-preview-state.servi import { combineLatest, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { FileDataService } from '../../services/file-data.service'; -import { BASE_HREF, ConfirmationDialogInput, ConfirmOptions, Toaster } from '@iqser/common-ui'; +import { BASE_HREF, ConfirmOptions, IConfirmationDialogData, Toaster } from '@iqser/common-ui'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { UserPreferenceService } from '@users/user-preference.service'; import { FilePreviewDialogService } from '../../services/file-preview-dialog.service'; @@ -66,7 +66,7 @@ export class ViewSwitchComponent { const question = displaySuggestionsInPreview ? _('unapproved-suggestions.confirmation-dialog.displayed-question') : _('unapproved-suggestions.confirmation-dialog.not-displayed-question'); - const data = new ConfirmationDialogInput({ + const data: IConfirmationDialogData = { title: _('unapproved-suggestions.confirmation-dialog.title'), question: question, confirmationText: _('unapproved-suggestions.confirmation-dialog.confirmation-text'), @@ -78,9 +78,9 @@ export class ViewSwitchComponent { }, ], checkboxesValidation: false, - }); + }; - return this._dialogService.openDialog('confirm', null, data, result => { + return this._dialogService.openDialog('confirm', data, result => { if (!result) { return; } diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/document-info-dialog/document-info-dialog.component.html b/apps/red-ui/src/app/modules/file-preview/dialogs/document-info-dialog/document-info-dialog.component.html index 51a1f6d89..9848ee289 100644 --- a/apps/red-ui/src/app/modules/file-preview/dialogs/document-info-dialog/document-info-dialog.component.html +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/document-info-dialog/document-info-dialog.component.html @@ -5,29 +5,28 @@
    - + >
    diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/rss-dialog/rss-dialog.component.ts b/apps/red-ui/src/app/modules/file-preview/dialogs/rss-dialog/rss-dialog.component.ts index d5ffaae8b..1f9ab5cc7 100644 --- a/apps/red-ui/src/app/modules/file-preview/dialogs/rss-dialog/rss-dialog.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/rss-dialog/rss-dialog.component.ts @@ -2,11 +2,10 @@ import { Component, Inject, OnInit } from '@angular/core'; import { BaseDialogComponent, CircleButtonTypes } from '@iqser/common-ui'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { RssService } from '@services/files/rss.service'; -import { IFile, RssEntry, RssResult } from '@red/domain'; +import { IFile, RssEntry } from '@red/domain'; import { BehaviorSubject, firstValueFrom } from 'rxjs'; import { FilesMapService } from '@services/files/files-map.service'; import { UserPreferenceService } from '@users/user-preference.service'; -import { KeyValue } from '@angular/common'; interface RssData { file: IFile; @@ -35,14 +34,14 @@ export class RssDialogComponent extends BaseDialogComponent implements OnInit { await this.#loadData(); } - originalOrder = (a: KeyValue, b: KeyValue): number => 0; + originalOrder = (): number => 0; exportJSON() { - this._rssService.exportJSON(this.data.file.dossierId, this.data.file.fileId, this.data.file.filename).subscribe(); + return firstValueFrom(this._rssService.exportJSON(this.data.file.dossierId, this.data.file.fileId, this.data.file.filename)); } exportXML() { - this._rssService.exportXML(this.data.file.dossierId, this.data.file.fileId, this.data.file.filename).subscribe(); + return firstValueFrom(this._rssService.exportXML(this.data.file.dossierId, this.data.file.fileId, this.data.file.filename)); } async exportAllInDossier() { @@ -53,8 +52,8 @@ export class RssDialogComponent extends BaseDialogComponent implements OnInit { } } - save(): void { - this.exportJSON(); + save() { + return this.exportJSON(); } async undo(originalKey: string) { diff --git a/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts b/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts index e3fc9b7ec..ff1d8a794 100644 --- a/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts @@ -13,31 +13,34 @@ import { import { ActivatedRoute, ActivatedRouteSnapshot, NavigationExtras, Router } from '@angular/router'; import { AutoUnsubscribe, + Bind, bool, CircleButtonTypes, - ConfirmationDialogInput, + ConfirmOption, ConfirmOptions, CustomError, Debounce, ErrorService, FilterService, HelpModeService, + IConfirmationDialogData, List, LoadingService, NestedFilter, OnAttach, OnDetach, processFilters, + shareDistinctLast, Toaster, } from '@iqser/common-ui'; -import { MatDialogState } from '@angular/material/dialog'; +import { MatDialog } from '@angular/material/dialog'; import { ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry.wrapper'; import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { AnnotationDrawService } from '../pdf-viewer/services/annotation-draw.service'; import { AnnotationProcessingService } from './services/annotation-processing.service'; import { Dictionary, File, ViewModes } from '@red/domain'; import { PermissionsService } from '@services/permissions.service'; -import { combineLatest, firstValueFrom, from, Observable, of, pairwise } from 'rxjs'; +import { combineLatest, firstValueFrom, Observable, of, pairwise } from 'rxjs'; import { PreferencesKeys, UserPreferenceService } from '@users/user-preference.service'; import { byId, byPage, download, handleFilterDelta, hasChanges } from '../../utils'; import { FilesService } from '@services/files/files.service'; @@ -87,7 +90,10 @@ export class FilePreviewScreenComponent fullScreen = false; readonly fileId = this.state.fileId; readonly dossierId = this.state.dossierId; - readonly file$ = this.state.file$.pipe(tap(file => this._fileDataService.loadAnnotations(file))); + readonly file$ = this.state.file$.pipe( + tap(file => this._fileDataService.loadAnnotations(file)), + shareDistinctLast(), + ); width: number; @ViewChild('annotationFilterTemplate', { read: TemplateRef, @@ -133,13 +139,9 @@ export class FilePreviewScreenComponent private readonly _readableRedactionsService: ReadableRedactionsService, private readonly _helpModeService: HelpModeService, private readonly _suggestionsService: SuggestionsService, + private readonly _dialog: MatDialog, ) { super(); - document.documentElement.addEventListener('fullscreenchange', () => { - if (!document.fullscreenElement) { - this.fullScreen = false; - } - }); } get changed() { @@ -264,11 +266,24 @@ export class FilePreviewScreenComponent ngOnDetach() { this._viewerHeaderService.resetCompareButtons(); - this._viewerHeaderService.enableLoadAllAnnotations(); + this._viewerHeaderService.enableLoadAllAnnotations(); // Reset the button state (since the viewer is reused between files) super.ngOnDetach(); + document.documentElement.removeEventListener('fullscreenchange', this.fullscreenListener); this._changeRef.markForCheck(); } + ngOnDestroy() { + document.documentElement.removeEventListener('fullscreenchange', this.fullscreenListener); + super.ngOnDestroy(); + } + + @Bind() + fullscreenListener() { + if (!document.fullscreenElement) { + this.fullScreen = false; + } + } + async ngOnAttach(previousRoute: ActivatedRouteSnapshot) { if (!this.state.file.canBeOpened) { return this._navigateToDossier(); @@ -277,7 +292,6 @@ export class FilePreviewScreenComponent this._viewModeService.switchToStandard(); await this.ngOnInit(); - await this._fileDataService.loadRedactionLog(); this._viewerHeaderService.updateElements(); const page = previousRoute.queryParams.page ?? '1'; await this.#updateQueryParamsPage(Number(page)); @@ -301,6 +315,7 @@ export class FilePreviewScreenComponent } this.pdfProxyService.configureElements(); + document.documentElement.addEventListener('fullscreenchange', this.fullscreenListener); } ngAfterViewInit() { @@ -314,9 +329,8 @@ export class FilePreviewScreenComponent return this._ngZone.run(() => { const file = this.state.file; - this.state.dialogRef = this._dialogService.openDialog( + this._dialogService.openDialog( 'manualAnnotation', - null, { manualRedactionEntryWrapper, dossierId: this.dossierId, file }, (result: { annotations: ManualRedactionEntryWrapper[]; dictionary?: Dictionary }) => { const selectedAnnotations = this._annotationManager.selected; @@ -353,7 +367,7 @@ export class FilePreviewScreenComponent return; } - if (!ALL_HOTKEYS.includes($event.key) || this.state.dialogRef?.getState() === MatDialogState.OPEN) { + if (!ALL_HOTKEYS.includes($event.key) || this._dialog.openDialogs.length) { return; } @@ -362,7 +376,6 @@ export class FilePreviewScreenComponent this.closeFullScreen(); this.pdf.deactivateSearch(); this._changeRef.markForCheck(); - window.focus(); } if (['f', 'F'].includes($event.key)) { @@ -370,12 +383,6 @@ export class FilePreviewScreenComponent if ($event.target instanceof HTMLInputElement || $event.target instanceof HTMLTextAreaElement) { return; } - if ($event.ctrlKey) { - this.pdf.focusSearch(); - this.pdf.activateSearch(); - return; - } - this.toggleFullScreen(); return; } @@ -425,7 +432,7 @@ export class FilePreviewScreenComponent } openRSSView(file: File) { - this._dialogService.openDialog('rss', null, { file }); + this._dialogService.openDialog('rss', { file }); } loadAnnotations() { @@ -553,24 +560,10 @@ export class FilePreviewScreenComponent return selectToDrawIfDoesNotExist(newAnnotation); } - if (this.userPreferenceService.areDevFeaturesEnabled) { - this.#logDiff(oldAnnotation, newAnnotation); - } - return true; }); } - #logDiff(oldAnnotation: AnnotationWrapper, newAnnotation: AnnotationWrapper) { - import('@iqser/common-ui').then(commonUi => { - this._logger.info('[ANNOTATIONS] Changed annotation: ', { - value: oldAnnotation.value, - before: commonUi.deepDiffObj(newAnnotation, oldAnnotation), - after: commonUi.deepDiffObj(oldAnnotation, newAnnotation), - }); - }); - } - private _setExcludedPageStyles() { const file = this._filesMapService.get(this.dossierId, this.fileId); setTimeout(() => { @@ -622,12 +615,12 @@ export class FilePreviewScreenComponent this.addActiveScreenSubscription = this.deleteEarmarksOnViewChange$().subscribe(); this.addActiveScreenSubscription = this.state.dossierFileChange$.subscribe(); + this.addActiveScreenSubscription = this.state.dossierDictionary$.subscribe(); this.addActiveScreenSubscription = this.state.blob$ .pipe( - switchMap(blob => from(this._documentViewer.lock()).pipe(map(() => blob))), tap(() => this._errorService.clear()), - tap(blob => this.pdf.loadDocument(blob, this.state.file, () => this.state.reloadBlob())), + switchMap(blob => this.pdf.loadDocument(blob, this.state.file, () => this.state.reloadBlob())), ) .subscribe(); @@ -648,11 +641,12 @@ export class FilePreviewScreenComponent filter(event => event.type === ViewerEvents.LOAD_ALL_ANNOTATIONS), switchMap(() => this._fileDataService.annotations), switchMap>(annotations => { + // TODO: this switchMap is ugly, to be refactored const showWarning = !this.userPreferenceService.getBool(PreferencesKeys.loadAllAnnotationsWarning); const annotationsExceedThreshold = annotations.length >= this.configService.values.ANNOTATIONS_THRESHOLD; if (annotationsExceedThreshold && showWarning) { - const data = new ConfirmationDialogInput({ + const data = { question: _('load-all-annotations-threshold-exceeded'), checkboxes: [ { @@ -664,17 +658,18 @@ export class FilePreviewScreenComponent translateParams: { threshold: this.configService.values.ANNOTATIONS_THRESHOLD, }, - }); + } as IConfirmationDialogData; - const ref = this._dialogService.openDialog('confirm', null, data); + const ref = this._dialogService.openDialog('confirm', data); return ref.afterClosed().pipe( - switchMap(async (result: ConfirmOptions) => { + switchMap(async (result: ConfirmOption) => { const doNotShowWarningAgain = result === ConfirmOptions.SECOND_CONFIRM; if (doNotShowWarningAgain) { await this.userPreferenceService.save(PreferencesKeys.loadAllAnnotationsWarning, 'true'); await this.userPreferenceService.reload(); } - const shouldLoad = [ConfirmOptions.CONFIRM, ConfirmOptions.SECOND_CONFIRM].includes(result); + const validOptions: number[] = [ConfirmOptions.CONFIRM, ConfirmOptions.SECOND_CONFIRM]; + const shouldLoad = validOptions.includes(result); return [shouldLoad, annotations] as const; }), ); @@ -693,12 +688,14 @@ export class FilePreviewScreenComponent .subscribe(); this.addActiveScreenSubscription = this._readableRedactionsService.active$.pipe(switchMap(() => this.updateViewMode())).subscribe(); - this.addActiveScreenSubscription = this._viewModeService.viewMode$ + + this.addActiveScreenSubscription = combineLatest([this._viewModeService.viewMode$, this.state.file$, this._documentViewer.loaded$]) .pipe( - tap(viewMode => - viewMode === 'STANDARD' || viewMode === 'TEXT_HIGHLIGHTS' - ? this._viewerHeaderService.enableRotationButtons() - : this._viewerHeaderService.disableRotationButtons(), + map( + ([viewMode, file]) => ['STANDARD', 'TEXT_HIGHLIGHTS'].includes(viewMode) && this.permissionsService.canRotatePage(file), + ), + tap(canRotate => + canRotate ? this._viewerHeaderService.enableRotationButtons() : this._viewerHeaderService.disableRotationButtons(), ), tap(viewMode => viewMode === 'REDACTED' && !this._readableRedactionsService.active diff --git a/apps/red-ui/src/app/modules/file-preview/file-preview.module.ts b/apps/red-ui/src/app/modules/file-preview/file-preview.module.ts index 578db0e0e..ab604c7af 100644 --- a/apps/red-ui/src/app/modules/file-preview/file-preview.module.ts +++ b/apps/red-ui/src/app/modules/file-preview/file-preview.module.ts @@ -2,22 +2,28 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { CapitalizePipe, - IqserButtonsModule, - IqserEmptyStatesModule, + CircleButtonComponent, + DetailsRadioComponent, + DynamicInputComponent, + EditableInputComponent, + EmptyStateComponent, + HasScrollbarDirective, + IconButtonComponent, + InputWithActionComponent, + IqserAllowDirective, + IqserDenyDirective, IqserFiltersModule, IqserHelpModeModule, - IqserIconsModule, - IqserInputsModule, - IqserPermissionsModule, - IqserScrollbarModule, - IqserSharedModule, + IqserRoutes, IqserUploadFileModule, IqserUsersModule, + PreventDefaultDirective, + RoundCheckboxComponent, + StatusBarComponent, + StopPropagationDirective, } from '@iqser/common-ui'; import { TranslateModule } from '@ngx-translate/core'; -import { RouterModule, Routes } from '@angular/router'; -import { SharedModule } from '@shared/shared.module'; -import { SharedDossiersModule } from '../shared-dossiers/shared-dossiers.module'; +import { RouterModule } from '@angular/router'; import { FilePreviewScreenComponent } from './file-preview-screen.component'; import { FileWorkloadComponent } from './components/file-workload/file-workload.component'; import { AnnotationDetailsComponent } from './components/annotation-details/annotation-details.component'; @@ -55,8 +61,10 @@ import { RssDialogComponent } from './dialogs/rss-dialog/rss-dialog.component'; import { ReadonlyBannerComponent } from './components/readonly-banner/readonly-banner.component'; import { SuggestionsService } from './services/suggestions.service'; import { PagesComponent } from './components/pages/pages.component'; +import { SharedModule } from '@shared/shared.module'; +import { SharedDossiersModule } from '../shared-dossiers/shared-dossiers.module'; -const routes: Routes = [ +const routes: IqserRoutes = [ { path: '', component: FilePreviewScreenComponent, @@ -108,22 +116,29 @@ const components = [ imports: [ RouterModule.forChild(routes), CommonModule, - SharedModule, - SharedDossiersModule, - IqserIconsModule, TranslateModule, OverlayModule, IqserHelpModeModule, IqserUsersModule, CapitalizePipe, - IqserButtonsModule, - IqserInputsModule, - IqserSharedModule, IqserUploadFileModule, - IqserEmptyStatesModule, IqserFiltersModule, - IqserScrollbarModule, - IqserPermissionsModule, + SharedModule, + CircleButtonComponent, + SharedDossiersModule, + IconButtonComponent, + EmptyStateComponent, + StatusBarComponent, + StopPropagationDirective, + PreventDefaultDirective, + HasScrollbarDirective, + EditableInputComponent, + DetailsRadioComponent, + DynamicInputComponent, + InputWithActionComponent, + RoundCheckboxComponent, + IqserAllowDirective, + IqserDenyDirective, ], providers: [FilePreviewDialogService, ManualRedactionService, DocumentUnloadedGuard, SuggestionsService], }) diff --git a/apps/red-ui/src/app/modules/file-preview/services/annotation-actions.service.ts b/apps/red-ui/src/app/modules/file-preview/services/annotation-actions.service.ts index 134622ea8..1d4c5b12a 100644 --- a/apps/red-ui/src/app/modules/file-preview/services/annotation-actions.service.ts +++ b/apps/red-ui/src/app/modules/file-preview/services/annotation-actions.service.ts @@ -6,7 +6,6 @@ import { getFirstRelevantTextPart } from '../../../utils'; import { Core } from '@pdftron/webviewer'; import { DictionaryEntryTypes, - Dossier, EarmarkOperation, IAddRedactionRequest, ILegalBasisChangeRequest, @@ -16,7 +15,6 @@ import { } from '@red/domain'; import { toPosition } from '../utils/pdf-calculation.utils'; import { AnnotationDrawService } from '../../pdf-viewer/services/annotation-draw.service'; -import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service'; import { AcceptRecommendationData, AcceptRecommendationDialogComponent, @@ -43,18 +41,12 @@ export class AnnotationActionsService { private readonly _documentViewer: REDDocumentViewer, private readonly _annotationManager: REDAnnotationManager, private readonly _annotationDrawService: AnnotationDrawService, - private readonly _activeDossiersService: ActiveDossiersService, private readonly _state: FilePreviewStateService, private readonly _fileDataService: FileDataService, private readonly _skippedService: SkippedService, ) {} - private get _dossier(): Dossier { - return this._activeDossiersService.find(this._state.dossierId); - } - - acceptSuggestion($event: MouseEvent, annotations: AnnotationWrapper[]) { - $event?.stopPropagation(); + acceptSuggestion(annotations: AnnotationWrapper[]) { const { dossierId, fileId } = this._state; const ids = annotations.map(a => a.id); const request = this._manualRedactionService.approve(ids, dossierId, fileId); @@ -63,16 +55,15 @@ export class AnnotationActionsService { removeHighlights(highlights: AnnotationWrapper[]): void { const data = this.#getHighlightOperationData(EarmarkOperation.REMOVE, highlights); - this._dialogService.openDialog('highlightAction', null, data); + this._dialogService.openDialog('highlightAction', data); } convertHighlights(highlights: AnnotationWrapper[]): void { const data = this.#getHighlightOperationData(EarmarkOperation.CONVERT, highlights); - this._dialogService.openDialog('highlightAction', null, data); + this._dialogService.openDialog('highlightAction', data); } - rejectSuggestion($event: MouseEvent, annotations: AnnotationWrapper[]) { - $event?.stopPropagation(); + rejectSuggestion(annotations: AnnotationWrapper[]) { const { dossierId, fileId } = this._state; this.#processObsAndEmit( this._manualRedactionService.declineOrRemove( @@ -84,10 +75,10 @@ export class AnnotationActionsService { ); } - forceAnnotation($event: MouseEvent, annotations: AnnotationWrapper[], hint: boolean = false) { + forceAnnotation(annotations: AnnotationWrapper[], hint: boolean = false) { const { dossierId, fileId } = this._state; - const data = { dossier: this._dossier, annotations, hint }; - this._dialogService.openDialog('forceAnnotation', $event, data, (request: ILegalBasisChangeRequest) => { + const data = { dossier: this._state.dossier, annotations, hint }; + this._dialogService.openDialog('forceAnnotation', data, (request: ILegalBasisChangeRequest) => { this.#processObsAndEmit( this._manualRedactionService.bulkForce( annotations.map(a => ({ ...request, annotationId: a.id })), @@ -99,12 +90,11 @@ export class AnnotationActionsService { }); } - changeLegalBasis($event: MouseEvent, annotations: AnnotationWrapper[]) { + changeLegalBasis(annotations: AnnotationWrapper[]) { const { dossierId, fileId } = this._state; this._dialogService.openDialog( 'changeLegalBasis', - $event, - { annotations, dossier: this._dossier }, + { annotations, dossier: this._state.dossier }, (data: { comment: string; legalBasis: string; section: string; value: string }) => { const body = annotations.map(annotation => ({ annotationId: annotation.id, @@ -119,15 +109,15 @@ export class AnnotationActionsService { ); } - removeOrSuggestRemoveAnnotation($event: MouseEvent, annotations: AnnotationWrapper[], removeFromDictionary: boolean) { + removeOrSuggestRemoveAnnotation(annotations: AnnotationWrapper[], removeFromDictionary: boolean) { const data = { annotationsToRemove: annotations, removeFromDictionary, - dossier: this._dossier, + dossier: this._state.dossier, hint: annotations[0].hintDictionary, }; const { dossierId, fileId } = this._state; - this._dialogService.openDialog('removeAnnotations', $event, data, (result: { comment: string }) => { + this._dialogService.openDialog('removeAnnotations', data, (result: { comment: string }) => { const body = annotations.map(annotation => ({ annotationId: annotation.id, removeFromDictionary, @@ -139,10 +129,10 @@ export class AnnotationActionsService { }); } - recategorizeImages($event: MouseEvent, annotations: AnnotationWrapper[]) { - const data = { annotations, dossier: this._dossier }; + recategorizeImages(annotations: AnnotationWrapper[]) { + const data = { annotations, dossier: this._state.dossier }; const { dossierId, fileId } = this._state; - this._dialogService.openDialog('recategorizeImage', $event, data, ({ comment, type }: { type: string; comment: string }) => { + this._dialogService.openDialog('recategorizeImage', data, ({ comment, type }: { type: string; comment: string }) => { const body: List = annotations.map(({ annotationId }) => ({ annotationId, type, @@ -152,9 +142,7 @@ export class AnnotationActionsService { }); } - undoDirectAction($event: MouseEvent, annotations: AnnotationWrapper[]) { - $event?.stopPropagation(); - + undoDirectAction(annotations: AnnotationWrapper[]) { const { dossierId, fileId } = this._state; const modifyDictionary = annotations[0].isModifyDictionary; this.#processObsAndEmit( @@ -167,9 +155,7 @@ export class AnnotationActionsService { ); } - convertRecommendationToAnnotation($event: any, recommendations: AnnotationWrapper[]) { - $event?.stopPropagation(); - + convertRecommendationToAnnotation(recommendations: AnnotationWrapper[]) { const { dossierId, fileId } = this._state; const dialogRef = this._dialog.open( AcceptRecommendationDialogComponent, @@ -179,19 +165,16 @@ export class AnnotationActionsService { const dialogClosed = dialogRef.afterClosed().pipe(filter(value => !!value && !!value.annotations)); dialogClosed.subscribe(({ annotations, comment: commentText }) => { if (isJustOne(annotations) && this._annotationManager.resizingAnnotationId === annotations[0].id) { - this.cancelResize(null, annotations[0]).then(); + this.cancelResize(annotations[0]).then(); } const comment = commentText ? { text: commentText } : undefined; this.#processObsAndEmit(this._manualRedactionService.addRecommendation(annotations, dossierId, fileId, comment)); }); } - async resize($event: MouseEvent, annotationWrapper: AnnotationWrapper) { - $event?.stopPropagation(); - + async resize(annotationWrapper: AnnotationWrapper) { this._annotationManager.resizingAnnotationId = annotationWrapper.id; - const viewerAnnotation = this._annotationManager.get(annotationWrapper); if (annotationWrapper.rectangle || annotationWrapper.imported || annotationWrapper.isImage) { this._annotationManager.delete(annotationWrapper); const rectangleAnnotation = this.#generateRectangle(annotationWrapper); @@ -199,6 +182,7 @@ export class AnnotationActionsService { return; } + const viewerAnnotation = this._annotationManager.get(annotationWrapper); viewerAnnotation.ReadOnly = false; viewerAnnotation.Hidden = false; viewerAnnotation.disableRotationControl(); @@ -206,7 +190,7 @@ export class AnnotationActionsService { this._annotationManager.select(viewerAnnotation); } - async acceptResize($event: MouseEvent, annotation: AnnotationWrapper): Promise { + async acceptResize(annotation: AnnotationWrapper): Promise { const textAndPositions = await this.#extractTextAndPositions(annotation.id); if (annotation.isRecommendation) { const recommendation = { @@ -214,14 +198,14 @@ export class AnnotationActionsService { value: textAndPositions.text, positions: textAndPositions.positions, } as AnnotationWrapper; - return this.convertRecommendationToAnnotation($event, [recommendation]); + return this.convertRecommendationToAnnotation([recommendation]); } const isImageText = annotation.isImage ? 'Image' : textAndPositions.text; const text = annotation.rectangle ? annotation.value : isImageText; const data = { annotation, text }; - this._dialogService.openDialog('resizeAnnotation', $event, data, (result: { comment: string; updateDictionary: boolean }) => { + this._dialogService.openDialog('resizeAnnotation', data, (result: { comment: string; updateDictionary: boolean }) => { const resizeRequest: IResizeRequest = { annotationId: annotation.id, comment: result.comment, @@ -230,7 +214,7 @@ export class AnnotationActionsService { updateDictionary: result.updateDictionary, }; - this.cancelResize(null, annotation).then(); + this.cancelResize(annotation).then(); const { fileId, dossierId } = this._state; const request = this._manualRedactionService.resizeOrSuggestResize([resizeRequest], dossierId, fileId); @@ -238,20 +222,15 @@ export class AnnotationActionsService { }); } - async cancelResize($event: MouseEvent, annotationWrapper: AnnotationWrapper) { - $event?.stopPropagation(); - + async cancelResize(annotationWrapper: AnnotationWrapper) { this._annotationManager.resizingAnnotationId = undefined; - this._annotationManager.delete(annotationWrapper); await this._annotationDrawService.draw([annotationWrapper], this._skippedService.hideSkipped, this._state.dossierTemplateId); this._annotationManager.deselect(); await this._fileDataService.annotationsChanged(); } - markAsFalsePositive($event: MouseEvent, annotations: AnnotationWrapper[]) { - $event?.stopPropagation(); - + markAsFalsePositive(annotations: AnnotationWrapper[]) { const requests: List = annotations.map(annotation => ({ sourceId: annotation.id, value: this._getFalsePositiveText(annotation), @@ -301,6 +280,7 @@ export class AnnotationActionsService { } #processObsAndEmit(obs: Observable) { + // TODO: remove observables and use promises instead obs.subscribe({ next: () => this._fileDataService.annotationsChanged(), error: () => this._fileDataService.annotationsChanged(), diff --git a/apps/red-ui/src/app/modules/file-preview/services/annotations-listing.service.ts b/apps/red-ui/src/app/modules/file-preview/services/annotations-listing.service.ts index c44540b9a..60118d95c 100644 --- a/apps/red-ui/src/app/modules/file-preview/services/annotations-listing.service.ts +++ b/apps/red-ui/src/app/modules/file-preview/services/annotations-listing.service.ts @@ -1,13 +1,16 @@ import { AnnotationWrapper } from '@models/file/annotation.wrapper'; -import { Injectable } from '@angular/core'; +import { Injectable, OnDestroy } from '@angular/core'; import { EntitiesService, FilterService, ListingService, SearchService, SortingService } from '@iqser/common-ui'; import { filter, tap } from 'rxjs/operators'; import { MultiSelectService } from './multi-select.service'; import { PdfViewer } from '../../pdf-viewer/services/pdf-viewer.service'; import { REDAnnotationManager } from '../../pdf-viewer/services/annotation-manager.service'; +import { Subscription } from 'rxjs'; @Injectable() -export class AnnotationsListingService extends ListingService { +export class AnnotationsListingService extends ListingService implements OnDestroy { + readonly #subscriptions: Subscription; + constructor( protected readonly _filterService: FilterService, protected readonly _searchService: SearchService, @@ -19,7 +22,7 @@ export class AnnotationsListingService extends ListingService ) { super(_filterService, _searchService, _entitiesService, _sortingService); - this.selectedLength$ + this.#subscriptions = this.selectedLength$ .pipe( filter(length => length > 1), tap(() => this._multiSelectService.activate()), @@ -27,6 +30,10 @@ export class AnnotationsListingService extends ListingService .subscribe(); } + ngOnDestroy() { + this.#subscriptions.unsubscribe(); + } + selectAnnotations(annotations: AnnotationWrapper[] | AnnotationWrapper) { annotations = Array.isArray(annotations) ? annotations : [annotations]; const pageNumber = annotations[annotations.length - 1].pageNumber; diff --git a/apps/red-ui/src/app/modules/file-preview/services/file-data.service.ts b/apps/red-ui/src/app/modules/file-preview/services/file-data.service.ts index 0a7402161..0374026eb 100644 --- a/apps/red-ui/src/app/modules/file-preview/services/file-data.service.ts +++ b/apps/red-ui/src/app/modules/file-preview/services/file-data.service.ts @@ -10,9 +10,9 @@ import { ViewModes, } from '@red/domain'; import { AnnotationWrapper } from '@models/file/annotation.wrapper'; -import { BehaviorSubject, firstValueFrom, iif, Observable, Subject } from 'rxjs'; +import { BehaviorSubject, firstValueFrom, iif, Observable, Subject, Subscription } from 'rxjs'; import { RedactionLogEntry } from '@models/file/redaction-log.entry'; -import { Injectable } from '@angular/core'; +import { Injectable, OnDestroy } from '@angular/core'; import { FilePreviewStateService } from './file-preview-state.service'; import { ViewedPagesService } from '@services/files/viewed-pages.service'; import { UserPreferenceService } from '@users/user-preference.service'; @@ -43,7 +43,7 @@ function chronologicallyBy(property: (x: T) => string) { } @Injectable() -export class FileDataService extends EntitiesService { +export class FileDataService extends EntitiesService implements OnDestroy { missingTypes = new Set(); readonly annotations$: Observable; readonly earmarks$: Observable>; @@ -51,6 +51,7 @@ export class FileDataService extends EntitiesService(); readonly #earmarks$ = new BehaviorSubject>(new Map()); #originalViewedPages: ViewedPage[] = []; + readonly #subscription: Subscription; constructor( private readonly _state: FilePreviewStateService, @@ -72,7 +73,7 @@ export class FileDataService extends EntitiesService iif( @@ -103,6 +104,10 @@ export class FileDataService extends EntitiesService; readonly isWritable$: Observable; - readonly dossierId: string = getParam(DOSSIER_ID); - readonly dossierTemplateId: string = getParam(DOSSIER_TEMPLATE_ID); - readonly fileId: string = getParam(FILE_ID); + readonly dossierId = getParam(DOSSIER_ID); + readonly dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID); + readonly fileId = getParam(FILE_ID); dossier: Dossier; - dialogRef: MatDialogRef; file: File; + readonly dossierDictionary$: Observable; #dossierDictionary: Dictionary; readonly #reloadBlob$ = new Subject(); + // readonly #routeKey = getReusableRouteKey(inject(ActivatedRoute).snapshot); + // readonly isAttached = inject(CustomRouteReuseStrategy).attached$.pipe( + // map(route => getReusableRouteKey(route) === this.#routeKey), + // startWith(true), + // ); constructor( router: Router, - filesMapService: FilesMapService, + private readonly _filesMapService: FilesMapService, private readonly _injector: Injector, - permissionsService: PermissionsService, + private readonly _permissionsService: PermissionsService, private readonly _filesService: FilesService, private readonly _dossiersService: DossiersService, private readonly _fileManagementService: FileManagementService, @@ -62,26 +66,28 @@ export class FilePreviewStateService { ) { const dossiersService = dossiersServiceResolver(_injector, router); this.dossier$ = dossiersService.getEntityChanged$(this.dossierId).pipe(tap(dossier => (this.dossier = dossier))); - this.file$ = filesMapService.watch$(this.dossierId, this.fileId).pipe(tap(file => (this.file = file))); + this.file$ = _filesMapService.watch$(this.dossierId, this.fileId).pipe(tap(file => (this.file = file))); + // this.file$ = combineLatest([this.isAttached, file$]).pipe( + // filter(([isAttached]) => isAttached), + // map(([, file]) => file), + // log('file$'), + // shareDistinctLast(), + // ); [this.isReadonly$, this.isWritable$] = boolFactory( combineLatest([this.file$, this.dossier$]), - ([file, dossier]) => !permissionsService.canPerformAnnotationActions(file, dossier), + ([file, dossier]) => !_permissionsService.canPerformAnnotationActions(file, dossier), ); this.blob$ = this.#blob$; this.dossierFileChange$ = this.#dossierFilesChange$(); - this._dossierDictionariesMapService + this.dossierDictionary$ = this._dossierDictionariesMapService .watch$(this.dossierId, 'dossier_redaction') - .subscribe(dictionary => (this.#dossierDictionary = dictionary)); - } - - get dossierDictionary(): Dictionary { - return this.#dossierDictionary; + .pipe(tap(dictionary => (this.#dossierDictionary = dictionary))); } get dictionaries(): Dictionary[] { - return this._dictionariesMapService.get(this.dossierTemplateId).concat([this.dossierDictionary]); + return this._dictionariesMapService.get(this.dossierTemplateId).concat([this.#dossierDictionary]); } get blob(): Promise { diff --git a/apps/red-ui/src/app/modules/file-preview/services/pdf-annotation-actions.service.ts b/apps/red-ui/src/app/modules/file-preview/services/pdf-annotation-actions.service.ts index f1bc826ef..9feac270f 100644 --- a/apps/red-ui/src/app/modules/file-preview/services/pdf-annotation-actions.service.ts +++ b/apps/red-ui/src/app/modules/file-preview/services/pdf-annotation-actions.service.ts @@ -1,7 +1,7 @@ import { inject, Injectable, NgZone } from '@angular/core'; -import { AnnotationWrapper } from '../../../models/file/annotation.wrapper'; -import { AnnotationPermissions } from '../../../models/file/annotation.permissions'; -import { PermissionsService } from '../../../services/permissions.service'; +import { AnnotationWrapper } from '@models/file/annotation.wrapper'; +import { AnnotationPermissions } from '@models/file/annotation.permissions'; +import { PermissionsService } from '@services/permissions.service'; import { FilePreviewStateService } from './file-preview-state.service'; import { TranslateService } from '@ngx-translate/core'; import { AnnotationActionsService } from './annotation-actions.service'; @@ -32,17 +32,17 @@ export class PdfAnnotationActionsService { // if we already entered resize-mode previously if (firstAnnotation.id === this.#annotationManager.resizingAnnotationId) { const acceptResizeButton = this.#getButton('check', _('annotation-actions.resize-accept.label'), () => - this.#annotationActionsService.acceptResize(null, firstAnnotation), + this.#annotationActionsService.acceptResize(firstAnnotation), ); const cancelResizeButton = this.#getButton('close', _('annotation-actions.resize-cancel.label'), () => - this.#annotationActionsService.cancelResize(null, firstAnnotation), + this.#annotationActionsService.cancelResize(firstAnnotation), ); return [acceptResizeButton, cancelResizeButton]; } const resizeButton = this.#getButton('resize', _('annotation-actions.resize.label'), () => - this.#annotationActionsService.resize(null, firstAnnotation), + this.#annotationActionsService.resize(firstAnnotation), ); availableActions.push(resizeButton); @@ -50,14 +50,14 @@ export class PdfAnnotationActionsService { if (permissions.canChangeLegalBasis) { const editButton = this.#getButton('edit', _('annotation-actions.edit-reason.label'), () => - this.#annotationActionsService.changeLegalBasis(null, annotations), + this.#annotationActionsService.changeLegalBasis(annotations), ); availableActions.push(editButton); } if (permissions.canRecategorizeImage) { const recategorizeButton = this.#getButton('thumb-down', _('annotation-actions.recategorize-image'), () => - this.#annotationActionsService.recategorizeImages(null, annotations), + this.#annotationActionsService.recategorizeImages(annotations), ); availableActions.push(recategorizeButton); } @@ -66,56 +66,56 @@ export class PdfAnnotationActionsService { const removeFromDictButton = this.#getButton( 'remove-from-dict', _('annotation-actions.remove-annotation.remove-from-dict'), - () => this.#annotationActionsService.removeOrSuggestRemoveAnnotation(null, annotations, true), + () => this.#annotationActionsService.removeOrSuggestRemoveAnnotation(annotations, true), ); availableActions.push(removeFromDictButton); } if (permissions.canAcceptRecommendation) { const acceptRecommendationButton = this.#getButton('check', _('annotation-actions.accept-recommendation.label'), () => - this.#annotationActionsService.convertRecommendationToAnnotation(null, annotations), + this.#annotationActionsService.convertRecommendationToAnnotation(annotations), ); availableActions.push(acceptRecommendationButton); } if (permissions.canAcceptSuggestion) { const acceptSuggestionButton = this.#getButton('check', _('annotation-actions.accept-suggestion.label'), () => - this.#annotationActionsService.acceptSuggestion(null, annotations), + this.#annotationActionsService.acceptSuggestion(annotations), ); availableActions.push(acceptSuggestionButton); } if (permissions.canUndo) { const undoButton = this.#getButton('undo', _('annotation-actions.undo'), () => - this.#annotationActionsService.undoDirectAction(null, annotations), + this.#annotationActionsService.undoDirectAction(annotations), ); availableActions.push(undoButton); } if (permissions.canMarkAsFalsePositive) { const markAsFalsePositiveButton = this.#getButton('thumb-down', _('annotation-actions.remove-annotation.false-positive'), () => - this.#annotationActionsService.markAsFalsePositive(null, annotations), + this.#annotationActionsService.markAsFalsePositive(annotations), ); availableActions.push(markAsFalsePositiveButton); } if (permissions.canForceRedaction) { const forceRedactionButton = this.#getButton('thumb-up', _('annotation-actions.force-redaction.label'), () => - this.#annotationActionsService.forceAnnotation(null, annotations), + this.#annotationActionsService.forceAnnotation(annotations), ); availableActions.push(forceRedactionButton); } if (permissions.canForceHint) { const forceHintButton = this.#getButton('thumb-up', _('annotation-actions.force-hint.label'), () => - this.#annotationActionsService.forceAnnotation(null, annotations, true), + this.#annotationActionsService.forceAnnotation(annotations, true), ); availableActions.push(forceHintButton); } if (permissions.canRejectSuggestion) { const rejectSuggestionButton = this.#getButton('close', _('annotation-actions.reject-suggestion'), () => - this.#annotationActionsService.rejectSuggestion(null, annotations), + this.#annotationActionsService.rejectSuggestion(annotations), ); availableActions.push(rejectSuggestionButton); } @@ -124,7 +124,7 @@ export class PdfAnnotationActionsService { const removeOrSuggestToRemoveOnlyHereButton = this.#getButton( 'trash', _('annotation-actions.remove-annotation.only-here'), - () => this.#annotationActionsService.removeOrSuggestRemoveAnnotation(null, annotations, false), + () => this.#annotationActionsService.removeOrSuggestRemoveAnnotation(annotations, false), ); availableActions.push(removeOrSuggestToRemoveOnlyHereButton); } diff --git a/apps/red-ui/src/app/modules/file-preview/services/pdf-proxy.service.ts b/apps/red-ui/src/app/modules/file-preview/services/pdf-proxy.service.ts index b283a4e07..e3c3c2045 100644 --- a/apps/red-ui/src/app/modules/file-preview/services/pdf-proxy.service.ts +++ b/apps/red-ui/src/app/modules/file-preview/services/pdf-proxy.service.ts @@ -191,7 +191,7 @@ export class PdfProxyService { return; } - this._annotationsActionsService.cancelResize(null, wrapper).then(); + this._annotationsActionsService.cancelResize(wrapper).then(); } #configureAnnotationSpecificActions(viewerAnnotations: Annotation[]) { diff --git a/apps/red-ui/src/app/modules/file-preview/services/skipped.service.ts b/apps/red-ui/src/app/modules/file-preview/services/skipped.service.ts index fa6e81fde..a59e962c9 100644 --- a/apps/red-ui/src/app/modules/file-preview/services/skipped.service.ts +++ b/apps/red-ui/src/app/modules/file-preview/services/skipped.service.ts @@ -21,9 +21,7 @@ export class SkippedService { return this.#hideSkipped$.value; } - toggleSkipped($event): void { - $event.stopPropagation(); - $event.preventDefault(); + toggleSkipped(): void { this.#hideSkipped$.next(!this.hideSkipped); } diff --git a/apps/red-ui/src/app/modules/pdf-viewer/components/compare-file-input/compare-file-input.component.ts b/apps/red-ui/src/app/modules/pdf-viewer/components/compare-file-input/compare-file-input.component.ts index aaa5c0ad2..c11b5eed4 100644 --- a/apps/red-ui/src/app/modules/pdf-viewer/components/compare-file-input/compare-file-input.component.ts +++ b/apps/red-ui/src/app/modules/pdf-viewer/components/compare-file-input/compare-file-input.component.ts @@ -1,6 +1,6 @@ import { Component, ElementRef, ViewChild } from '@angular/core'; import { HeaderElements } from '../../../file-preview/utils/constants'; -import { ConfirmationDialogInput, ConfirmOptions, LoadingService } from '@iqser/common-ui'; +import { ConfirmOption, ConfirmOptions, IConfirmationDialogData, LoadingService } from '@iqser/common-ui'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { FilesMapService } from '@services/files/files-map.service'; import { SharedDialogService } from '@shared/services/dialog.service'; @@ -81,19 +81,15 @@ export class CompareFileInputComponent { } #askForConfirmation(fileName: string, currentDocumentPageCount: number, compareDocumentPageCount: number) { - const ref: MatDialogRef = this._dialogService.openDialog( - 'confirm', - null, - new ConfirmationDialogInput({ - title: _('confirmation-dialog.compare-file.title'), - question: _('confirmation-dialog.compare-file.question'), - translateParams: { - fileName, - currentDocumentPageCount, - compareDocumentPageCount, - }, - }), - ); + const ref: MatDialogRef = this._dialogService.openDialog('confirm', { + title: _('confirmation-dialog.compare-file.title'), + question: _('confirmation-dialog.compare-file.question'), + translateParams: { + fileName, + currentDocumentPageCount, + compareDocumentPageCount, + }, + } as IConfirmationDialogData); return firstValueFrom(ref.afterClosed()); } diff --git a/apps/red-ui/src/app/modules/pdf-viewer/services/annotation-draw.service.ts b/apps/red-ui/src/app/modules/pdf-viewer/services/annotation-draw.service.ts index 17dc374e7..ccc32de6c 100644 --- a/apps/red-ui/src/app/modules/pdf-viewer/services/annotation-draw.service.ts +++ b/apps/red-ui/src/app/modules/pdf-viewer/services/annotation-draw.service.ts @@ -35,7 +35,7 @@ export class AnnotationDrawService { async draw(annotations: List, hideSkipped: boolean, dossierTemplateId: string) { try { - await this._draw(annotations, hideSkipped, dossierTemplateId); + await this._pdf.runWithCleanup(async () => await this._draw(annotations, hideSkipped, dossierTemplateId)); } catch (e) { console.error(e); } @@ -145,10 +145,13 @@ export class AnnotationDrawService { annotation.Id = annotationWrapper.id; annotation.ReadOnly = true; + const isOCR = annotationWrapper.isOCR && !annotationWrapper.isSuggestionResize; + if (isOCR && !this._annotationManager.isHidden(annotationWrapper.annotationId)) { + this._annotationManager.addToHidden(annotationWrapper.annotationId); + } annotation.Hidden = annotationWrapper.isChangeLogRemoved || (hideSkipped && annotationWrapper.isSkipped) || - (annotationWrapper.isOCR && !annotationWrapper.isSuggestionResize) || this._annotationManager.isHidden(annotationWrapper.annotationId); annotation.setCustomData('redact-manager', 'true'); annotation.setCustomData('redaction', String(annotationWrapper.previewAnnotation)); diff --git a/apps/red-ui/src/app/modules/pdf-viewer/services/annotation-manager.service.ts b/apps/red-ui/src/app/modules/pdf-viewer/services/annotation-manager.service.ts index da2f81069..88aca43ff 100644 --- a/apps/red-ui/src/app/modules/pdf-viewer/services/annotation-manager.service.ts +++ b/apps/red-ui/src/app/modules/pdf-viewer/services/annotation-manager.service.ts @@ -32,6 +32,11 @@ export class REDAnnotationManager { return this.#hidden$.value; } + get #annotationSelected$() { + const onSelect$ = fromEvent<[Annotation[], string]>(this.#manager, 'annotationSelected'); + return onSelect$.pipe(tap(value => console.log('Annotation selected: ', value))); + } + addToHidden(value: string) { this.#hidden$.next(new Set([...this.hidden, value])); } @@ -40,11 +45,6 @@ export class REDAnnotationManager { this.#hidden$.next(new Set([...this.hidden].filter(v => v !== value))); } - get #annotationSelected$() { - const onSelect$ = fromEvent<[Annotation[], string]>(this.#manager, 'annotationSelected'); - return onSelect$.pipe(tap(value => console.log('Annotation selected: ', value))); - } - init(annotationManager: AnnotationManager) { this.#manager = annotationManager; this.annotationSelected$ = this.#annotationSelected$; diff --git a/apps/red-ui/src/app/modules/pdf-viewer/services/document-viewer.service.ts b/apps/red-ui/src/app/modules/pdf-viewer/services/document-viewer.service.ts index cca642fef..3d50465dc 100644 --- a/apps/red-ui/src/app/modules/pdf-viewer/services/document-viewer.service.ts +++ b/apps/red-ui/src/app/modules/pdf-viewer/services/document-viewer.service.ts @@ -1,12 +1,12 @@ import { inject, Injectable } from '@angular/core'; import { Core } from '@pdftron/webviewer'; import { NGXLogger } from 'ngx-logger'; -import { fromEvent, merge, Observable } from 'rxjs'; +import { fromEvent, merge, Observable, Subject } from 'rxjs'; import { debounceTime, filter, map, tap } from 'rxjs/operators'; import { ActivatedRoute } from '@angular/router'; import { PdfViewer } from './pdf-viewer.service'; import { UserPreferenceService } from '@users/user-preference.service'; -import { log, shareLast } from '@iqser/common-ui'; +import { log, shareDistinctLast, shareLast } from '@iqser/common-ui'; import { stopAndPrevent, stopAndPreventIfNotAllowed } from '../utils/functions'; import { RotationType, RotationTypes } from '@red/domain'; import { AnnotationToolNames } from '../utils/constants'; @@ -23,6 +23,8 @@ export class REDDocumentViewer { selectedText = ''; #document: DocumentViewer; + readonly #documentClosed$ = new Subject(); + readonly #logger = inject(NGXLogger); readonly #userPreferenceService = inject(UserPreferenceService); readonly #pdf = inject(PdfViewer); @@ -37,7 +39,7 @@ export class REDDocumentViewer { } get #documentUnloaded$() { - const event$ = fromEvent(this.#document, 'documentUnloaded'); + const event$ = merge(fromEvent(this.#document, 'documentUnloaded'), this.#documentClosed$); const toBool$ = event$.pipe(map(() => false)); return toBool$.pipe(tap(() => this.#logger.info('[PDF] Document unloaded'))); @@ -48,10 +50,14 @@ export class REDDocumentViewer { const toBool$ = event$.pipe(map(() => true)); return toBool$.pipe( - tap(() => this.#flattenAnnotations()), - tap(() => this.#setCurrentPage()), - tap(() => this.#setInitialDisplayMode()), - tap(() => this.updateTooltipsVisibility()), + tap(() => + this.#pdf.runWithCleanup(async () => { + await this.#flattenAnnotations(); + this.#setCurrentPage(); + this.#setInitialDisplayMode(); + this.updateTooltipsVisibility(); + }), + ), tap(() => this.#logger.info('[PDF] Document loaded')), ); } @@ -67,13 +73,7 @@ export class REDDocumentViewer { } return ($event.target as HTMLElement)?.tagName?.toLowerCase() !== 'input'; }), - filter( - $event => - $event.key.startsWith('Arrow') || - $event.key === 'f' || - ['h', 'H'].includes($event.key) || - ['Escape'].includes($event.key), - ), + filter($event => $event.key.startsWith('Arrow') || ['f', 'h', 'H', 'Escape'].includes($event.key)), tap(stopAndPrevent), log('[PDF] Keyboard shortcut'), ); @@ -92,7 +92,7 @@ export class REDDocumentViewer { } get #loaded$() { - return merge(this.#documentUnloaded$, this.#documentLoaded$).pipe(shareLast()); + return merge(this.#documentUnloaded$, this.#documentLoaded$).pipe(shareDistinctLast()); } clearSelection() { @@ -101,9 +101,16 @@ export class REDDocumentViewer { } close() { - this.#logger.info('[PDF] Closing document'); - this.#document.closeDocument(); - this.#pdf.closeCompareMode(); + this.#documentClosed$.next(undefined); + + const closeAction = async () => { + this.#logger.info('[PDF] Closing document'); + this.#document.closeDocument(); + this.#pdf.closeCompareMode(); + await this.#pdf.instance.UI.closeDocument(); + }; + + this.#pdf.runWithCleanup(closeAction).then(); } updateTooltipsVisibility(): void { @@ -119,17 +126,6 @@ export class REDDocumentViewer { this.textSelected$ = this.#textSelected$; } - async lock() { - const document = await this.PDFDoc; - if (!document) { - return false; - } - - await document.lock(); - this.#logger.info('[PDF] Locked'); - return true; - } - async blob() { const data = await this.document.getFileData(); return new Blob([new Uint8Array(data)], { type: 'application/pdf' }); diff --git a/apps/red-ui/src/app/modules/pdf-viewer/services/page-rotation.service.ts b/apps/red-ui/src/app/modules/pdf-viewer/services/page-rotation.service.ts index ada27f747..e56d9afd1 100644 --- a/apps/red-ui/src/app/modules/pdf-viewer/services/page-rotation.service.ts +++ b/apps/red-ui/src/app/modules/pdf-viewer/services/page-rotation.service.ts @@ -5,9 +5,10 @@ import { FileManagementService } from '@services/files/file-management.service'; import { distinctUntilChanged, map, switchMap, tap } from 'rxjs/operators'; import { ConfirmationDialogComponent, - ConfirmationDialogInput, + ConfirmOption, ConfirmOptions, defaultDialogConfig, + IConfirmationDialogData, LoadingService, } from '@iqser/common-ui'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; @@ -93,15 +94,15 @@ export class PageRotationService { #showConfirmationDialog() { const ref = this._injector.get(MatDialog).open(ConfirmationDialogComponent, { ...defaultDialogConfig, - data: new ConfirmationDialogInput({ + data: { title: _('page-rotation.confirmation-dialog.title'), question: _('page-rotation.confirmation-dialog.question'), confirmationText: _('page-rotation.apply'), discardChangesText: _('page-rotation.discard'), - }), + } as IConfirmationDialogData, }); - const closed$ = ref.afterClosed().pipe(map((option: ConfirmOptions) => option === ConfirmOptions.CONFIRM)); + const closed$ = ref.afterClosed().pipe(map((option: ConfirmOption) => option === ConfirmOptions.CONFIRM)); return closed$.pipe(tap(apply => (apply ? this.applyRotation() : this.discardRotation()))); } diff --git a/apps/red-ui/src/app/modules/pdf-viewer/services/pdf-viewer.service.ts b/apps/red-ui/src/app/modules/pdf-viewer/services/pdf-viewer.service.ts index cf68ffbf3..aaf4f88d2 100644 --- a/apps/red-ui/src/app/modules/pdf-viewer/services/pdf-viewer.service.ts +++ b/apps/red-ui/src/app/modules/pdf-viewer/services/pdf-viewer.service.ts @@ -1,11 +1,10 @@ -import { Inject, Injectable, Injector } from '@angular/core'; +import { inject, Inject, Injectable, Injector } from '@angular/core'; import WebViewer, { Core, WebViewerInstance, WebViewerOptions } from '@pdftron/webviewer'; -import { BASE_HREF_FN, BaseHrefFn, ErrorService, shareDistinctLast } from '@iqser/common-ui'; -import { File, IHeaderElement } from '@red/domain'; +import { BASE_HREF_FN, BaseHrefFn, ErrorService, getConfig, shareDistinctLast } from '@iqser/common-ui'; +import { AppConfig, File, IHeaderElement } from '@red/domain'; import { ActivatedRoute } from '@angular/router'; import { map, startWith } from 'rxjs/operators'; import { BehaviorSubject, combineLatest, fromEvent, Observable, switchMap } from 'rxjs'; -import { ConfigService } from '@services/config.service'; import { NGXLogger } from 'ngx-logger'; import { DISABLED_HOTKEYS, DOCUMENT_LOADING_ERROR, SEARCH_OPTIONS, USELESS_ELEMENTS } from '../utils/constants'; import { Rgb } from '../utils/types'; @@ -22,7 +21,7 @@ import Quad = Core.Math.Quad; @Injectable() export class PdfViewer { - readonly currentPage$ = this._activatedRoute.queryParamMap.pipe( + readonly currentPage$ = inject(ActivatedRoute).queryParamMap.pipe( map(params => Number(params.get('page') ?? '1')), shareDistinctLast(), ); @@ -37,11 +36,13 @@ export class PdfViewer { totalPages$: Observable; #instance: WebViewerInstance; + readonly #licenseKey = inject(LicenseService).activeLicenseKey; + readonly #config = getConfig(); readonly #compareMode$ = new BehaviorSubject(false); readonly #searchButton: IHeaderElement = { type: 'actionButton', img: this._convertPath('/assets/icons/general/pdftron-action-search.svg'), - title: this._translateService.instant('pdf-viewer.text-popup.actions.search'), + title: inject(TranslateService).instant('pdf-viewer.text-popup.actions.search'), onClick: () => { this.#instance.UI.openElements(['searchPanel']); setTimeout(() => this.#searchForSelectedText(), 250); @@ -51,10 +52,7 @@ export class PdfViewer { constructor( private readonly _logger: NGXLogger, private readonly _injector: Injector, - private readonly _activatedRoute: ActivatedRoute, - private readonly _licenseService: LicenseService, private readonly _errorService: ErrorService, - private readonly _translateService: TranslateService, private readonly _userPreferenceService: UserPreferenceService, @Inject(BASE_HREF_FN) private readonly _convertPath: BaseHrefFn, ) {} @@ -154,25 +152,28 @@ export class PdfViewer { this.#instance.Core.setCustomFontURL('https://' + window.location.host + this._convertPath('/assets/pdftron')); } - try { - await this.PDFNet.initialize(this._licenseService.activeLicenseKey); - } catch (e) { - this._errorService.set(e); - throw e; - } + await this.runWithCleanup(async () => { + try { + await this.PDFNet.initialize(this.#licenseKey); + } catch (e) { + this._errorService.set(e); + throw e; + } - this.#instance.UI.setTheme(this._userPreferenceService.getTheme()); - this._logger.info('[PDF] Initialized'); + this.#instance.UI.setTheme(this._userPreferenceService.getTheme()); + this._logger.info('[PDF] Initialized'); - this.documentViewer = this.#instance.Core.documentViewer; + this.documentViewer = this.#instance.Core.documentViewer; - this.compareMode$ = this.#compareMode$.asObservable(); - this.pageChanged$ = this.#pageChanged$.pipe(shareDistinctLast()); - this.totalPages$ = this.#totalPages$.pipe(shareDistinctLast()); - this.#setSelectionMode(); - this.#configureElements(); - this.#disableHotkeys(); - this.#clearSearchResultsWhenVisibilityChanged(); + this.compareMode$ = this.#compareMode$.asObservable(); + this.pageChanged$ = this.#pageChanged$.pipe(shareDistinctLast()); + this.totalPages$ = this.#totalPages$.pipe(shareDistinctLast()); + this.#setSelectionMode(); + this.#configureElements(); + this.#disableHotkeys(); + this.#clearSearchResultsWhenVisibilityChanged(); + this.#listenForCommandF(); + }); return this.#instance; } @@ -195,6 +196,10 @@ export class PdfViewer { this.#compareMode$.next(false); } + runWithCleanup(action: () => Promise | void) { + return this.PDFNet.runWithCleanup(action, this.#licenseKey); + } + async loadDocument(blob: Blob, file: File, actionOnError?: () => void) { const onError = () => { this._injector.get(ErrorService).set(DOCUMENT_LOADING_ERROR); @@ -207,7 +212,11 @@ export class PdfViewer { this._logger.info('[PDF] Loading document...'); - this.#instance.UI.loadDocument(blob, { documentId: file.fileId, filename: file?.filename ?? 'document.pdf', onError }); + await this.runWithCleanup(async () => { + const document = await this.documentViewer.getDocument()?.getPDFDoc(); + await document?.lock(); + this.#instance.UI.loadDocument(blob, { documentId: file.fileId, filename: file?.filename ?? 'document.pdf', onError }); + }); } quad(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number) { @@ -249,6 +258,14 @@ export class PdfViewer { this.#instance.UI.textPopup.update([...popups, this.#searchButton, { dataElement: 'copyTextButton' }]); } + #listenForCommandF() { + this.#instance.UI.hotkeys.on('command+f, ctrl+f', e => { + e.preventDefault(); + this.focusSearch(); + this.activateSearch(); + }); + } + #adjustPage(page: number) { if (this.isCompare) { if (page % 2 === 1) { @@ -298,13 +315,12 @@ export class PdfViewer { #setSelectionMode(): void { const textTool = this.#instance.Core.Tools.TextTool as unknown as TextTool; - const configService = this._injector.get(ConfigService); - textTool.SELECTION_MODE = configService.values.SELECTION_MODE; + textTool.SELECTION_MODE = this.#config.SELECTION_MODE; } #getInstance(htmlElement: HTMLElement) { const options: WebViewerOptions = { - licenseKey: this._licenseService.activeLicenseKey, + licenseKey: this.#licenseKey, fullAPI: true, path: this._convertPath('/assets/wv-resources'), css: this._convertPath('/assets/pdftron/stylesheet.css'), diff --git a/apps/red-ui/src/app/modules/pdf-viewer/services/viewer-header.service.ts b/apps/red-ui/src/app/modules/pdf-viewer/services/viewer-header.service.ts index f4b0088be..226bae523 100644 --- a/apps/red-ui/src/app/modules/pdf-viewer/services/viewer-header.service.ts +++ b/apps/red-ui/src/app/modules/pdf-viewer/services/viewer-header.service.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@angular/core'; import { IHeaderElement, RotationTypes } from '@red/domain'; import { HeaderElements, HeaderElementType } from '../../file-preview/utils/constants'; import { TranslateService } from '@ngx-translate/core'; -import { BASE_HREF_FN, BaseHrefFn, IqserPermissionsService } from '@iqser/common-ui'; +import { BASE_HREF_FN, BaseHrefFn } from '@iqser/common-ui'; import { TooltipsService } from './tooltips.service'; import { PageRotationService } from './page-rotation.service'; import { PdfViewer } from './pdf-viewer.service'; @@ -13,7 +13,6 @@ import { UserPreferenceService } from '@users/user-preference.service'; import { fromEvent, Observable, Subject } from 'rxjs'; import { ViewerEvent, VisibilityChangedEvent } from '../utils/types'; import { ReadableRedactionsService } from './readable-redactions.service'; -import { ROLES } from '@users/roles'; import { filter, map, tap } from 'rxjs/operators'; const divider: IHeaderElement = { @@ -23,6 +22,7 @@ const divider: IHeaderElement = { @Injectable() export class ViewerHeaderService { readonly events$: Observable; + toggleLoadAnnotations$: Observable; #buttons: Map; readonly #config = new Map([ [HeaderElements.SHAPE_TOOL_GROUP_BUTTON, true], @@ -38,7 +38,6 @@ export class ViewerHeaderService { ]); #docBeforeCompare: Blob; readonly #events$ = new Subject(); - toggleLoadAnnotations$: Observable; constructor( @Inject(BASE_HREF_FN) private readonly _convertPath: BaseHrefFn, @@ -50,7 +49,6 @@ export class ViewerHeaderService { private readonly _tooltipsService: TooltipsService, private readonly _readableRedactionsService: ReadableRedactionsService, private readonly _userPreferenceService: UserPreferenceService, - private readonly _iqserPermissionsService: IqserPermissionsService, ) { this.events$ = this.#events$.asObservable(); } @@ -169,11 +167,6 @@ export class ViewerHeaderService { }; } - #discardRotation(): void { - this._rotationService.discardRotation(); - this.disable(ROTATION_ACTION_BUTTONS); - } - private get _rotateRight(): IHeaderElement { return { type: 'actionButton', @@ -305,9 +298,7 @@ export class ViewerHeaderService { } enableRotationButtons(): void { - if (this._iqserPermissionsService.has(ROLES.files.rotatePage)) { - this.enable(ROTATION_BUTTONS); - } + this.enable(ROTATION_BUTTONS); } disableRotationButtons(): void { @@ -320,6 +311,11 @@ export class ViewerHeaderService { this.enable([HeaderElements.COMPARE_BUTTON]); } + #discardRotation(): void { + this._rotationService.discardRotation(); + this.disable(ROTATION_ACTION_BUTTONS); + } + #toggleRotationActionButtons() { if (this._rotationService.hasRotations) { this.enable(ROTATION_ACTION_BUTTONS); diff --git a/apps/red-ui/src/app/modules/pdf-viewer/utils/constants.ts b/apps/red-ui/src/app/modules/pdf-viewer/utils/constants.ts index 51396e3c6..e40fd8b43 100644 --- a/apps/red-ui/src/app/modules/pdf-viewer/utils/constants.ts +++ b/apps/red-ui/src/app/modules/pdf-viewer/utils/constants.ts @@ -68,6 +68,7 @@ export const DISABLED_HOTKEYS = [ 'CTRL+P', 'COMMAND+P', 'CTRL+F', + 'COMMAND+F', 'SPACE', 'UP', 'DOWN', diff --git a/apps/red-ui/src/app/modules/pdf-viewer/utils/functions.ts b/apps/red-ui/src/app/modules/pdf-viewer/utils/functions.ts index 55273e8db..12091decb 100644 --- a/apps/red-ui/src/app/modules/pdf-viewer/utils/functions.ts +++ b/apps/red-ui/src/app/modules/pdf-viewer/utils/functions.ts @@ -1,5 +1,5 @@ import { List } from '@iqser/common-ui'; -import { AnnotationWrapper } from '../../../models/file/annotation.wrapper'; +import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { ALLOWED_KEYBOARD_SHORTCUTS } from './constants'; export function stopAndPrevent($event: T) { diff --git a/apps/red-ui/src/app/modules/search/search-item-template/search-item-template.component.html b/apps/red-ui/src/app/modules/search/search-item-template/search-item-template.component.html index d4e9ea51f..5901dc0b4 100644 --- a/apps/red-ui/src/app/modules/search/search-item-template/search-item-template.component.html +++ b/apps/red-ui/src/app/modules/search/search-item-template/search-item-template.component.html @@ -22,7 +22,7 @@ {{ 'search-screen.missing' | translate }}: {{ term }}. {{ 'search-screen.must-contain' | translate }}: -  {{ term }} @@ -47,10 +47,10 @@ >
    -
    +
    - {{ item.dossierName }} + {{ item.dossierName }}
    diff --git a/apps/red-ui/src/app/modules/search/search-item-template/search-item-template.component.ts b/apps/red-ui/src/app/modules/search/search-item-template/search-item-template.component.ts index ea3a27016..5ba409269 100644 --- a/apps/red-ui/src/app/modules/search/search-item-template/search-item-template.component.ts +++ b/apps/red-ui/src/app/modules/search/search-item-template/search-item-template.component.ts @@ -1,21 +1,32 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; import { workflowFileStatusTranslations } from '@translations/file-status-translations'; import { ISearchListItem } from '@red/domain'; import { escapeHtml } from '@iqser/common-ui'; +import { getDossierRouterLink } from '@utils/router-links'; @Component({ selector: 'redaction-search-item-template', templateUrl: './search-item-template.component.html', styleUrls: ['./search-item-template.component.scss'], }) -export class SearchItemTemplateComponent { +export class SearchItemTemplateComponent implements OnChanges { @Input() item: ISearchListItem; @Output() readonly mustContain = new EventEmitter(); readonly fileStatusTranslations = workflowFileStatusTranslations; + routerLink: string; + sanitize(value: string) { return escapeHtml(value, { ignoreTags: ['em'], }); } + + ngOnChanges(changes: SimpleChanges): void { + if (changes.item) { + this.routerLink = getDossierRouterLink(this.item.dossierTemplateId, this.item.dossierId, this.item.archived); + } else { + this.routerLink = undefined; + } + } } diff --git a/apps/red-ui/src/app/modules/search/search-screen/search-screen.component.ts b/apps/red-ui/src/app/modules/search/search-screen/search-screen.component.ts index 5ec6e2f09..5c360fbef 100644 --- a/apps/red-ui/src/app/modules/search/search-screen/search-screen.component.ts +++ b/apps/red-ui/src/app/modules/search/search-screen/search-screen.component.ts @@ -283,6 +283,10 @@ export class SearchScreenComponent extends ListingComponent imp } const dossier = this._dossiersCacheService.get(dossierId); + if (!dossier) { + console.error('Missing dossier'); + return undefined; + } return { id: fileId, diff --git a/apps/red-ui/src/app/modules/search/search.module.ts b/apps/red-ui/src/app/modules/search/search.module.ts index 7a3f551fa..160d2a5bb 100644 --- a/apps/red-ui/src/app/modules/search/search.module.ts +++ b/apps/red-ui/src/app/modules/search/search.module.ts @@ -2,7 +2,7 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { SearchScreenComponent } from './search-screen/search-screen.component'; import { RouterModule } from '@angular/router'; -import { IqserListingModule, IqserSharedModule, IqserUsersModule } from '@iqser/common-ui'; +import { IqserListingModule, IqserUsersModule, StatusBarComponent, StopPropagationDirective } from '@iqser/common-ui'; import { SharedModule } from '@shared/shared.module'; import { TranslateModule } from '@ngx-translate/core'; import { SearchItemTemplateComponent } from './search-item-template/search-item-template.component'; @@ -18,7 +18,8 @@ const routes = [{ path: '', component: SearchScreenComponent }]; IqserUsersModule, TranslateModule, IqserListingModule, - IqserSharedModule, + StatusBarComponent, + StopPropagationDirective, ], }) export class SearchModule {} diff --git a/apps/red-ui/src/app/modules/shared-dossiers/components/dossiers-listing-actions/dossiers-listing-actions.component.html b/apps/red-ui/src/app/modules/shared-dossiers/components/dossiers-listing-actions/dossiers-listing-actions.component.html index 6661fb648..dd634a0c5 100644 --- a/apps/red-ui/src/app/modules/shared-dossiers/components/dossiers-listing-actions/dossiers-listing-actions.component.html +++ b/apps/red-ui/src/app/modules/shared-dossiers/components/dossiers-listing-actions/dossiers-listing-actions.component.html @@ -1,6 +1,6 @@
    { - $event.stopPropagation(); + async reanalyseDossier(dossier: Dossier): Promise { await this._reanalysisService.reanalyzeDossier(dossier); } } diff --git a/apps/red-ui/src/app/modules/shared-dossiers/components/file-actions/file-actions.component.html b/apps/red-ui/src/app/modules/shared-dossiers/components/file-actions/file-actions.component.html index 9541fae3e..a31bbe1b3 100644 --- a/apps/red-ui/src/app/modules/shared-dossiers/components/file-actions/file-actions.component.html +++ b/apps/red-ui/src/app/modules/shared-dossiers/components/file-actions/file-actions.component.html @@ -1,4 +1,4 @@ -
    +
    @@ -20,6 +20,7 @@ this._openDeleteFileDialog($event), + action: () => this._openDeleteFileDialog(), tooltip: _('dossier-overview.delete.action'), icon: 'iqser:trash', show: this.showDelete, @@ -132,7 +132,7 @@ export class FileActionsComponent implements OnChanges { { id: 'assign-btn', type: ActionTypes.circleBtn, - action: ($event: MouseEvent) => this._assign($event), + action: () => this._assign(), tooltip: this.assignTooltip, icon: 'red:assign', show: this.showAssign, @@ -140,7 +140,7 @@ export class FileActionsComponent implements OnChanges { { id: 'assign-to-me-btn', type: ActionTypes.circleBtn, - action: ($event: MouseEvent) => this._assignToMe($event), + action: () => this._assignToMe(), tooltip: _('dossier-overview.assign-me'), icon: 'red:assign-me', show: this.showAssignToSelf, @@ -148,7 +148,7 @@ export class FileActionsComponent implements OnChanges { { id: 'open-import-redactions-dialog-btn', type: ActionTypes.circleBtn, - action: ($event: MouseEvent) => this._openImportRedactionsDialog($event), + action: () => this._openImportRedactionsDialog(), tooltip: _('dossier-overview.import-redactions'), icon: 'red:import_redactions', show: this.showImportRedactions && !this._iqserPermissionsService.has(ROLES.getRss), @@ -187,7 +187,7 @@ export class FileActionsComponent implements OnChanges { { id: 'set-file-to-new-btn', type: ActionTypes.circleBtn, - action: ($event: MouseEvent) => this._setToNew($event), + action: () => this.#setToNew(), tooltip: _('dossier-overview.back-to-new'), icon: 'red:undo', show: this.showSetToNew, @@ -195,7 +195,7 @@ export class FileActionsComponent implements OnChanges { { id: 'set-file-under-approval-btn', type: ActionTypes.circleBtn, - action: ($event: MouseEvent) => this._setFileUnderApproval($event), + action: () => this._setFileUnderApproval(), tooltip: _('dossier-overview.under-approval'), icon: 'red:ready-for-approval', show: this.showUnderApproval, @@ -203,7 +203,7 @@ export class FileActionsComponent implements OnChanges { { id: 'set-file-under-review-btn', type: ActionTypes.circleBtn, - action: ($event: MouseEvent) => this._setFileUnderReview($event), + action: () => this._setFileUnderReview(), tooltip: _('dossier-overview.under-review'), icon: 'red:undo', show: this.showUnderReview, @@ -211,7 +211,7 @@ export class FileActionsComponent implements OnChanges { { id: 'set-file-approved-btn', type: ActionTypes.circleBtn, - action: ($event: MouseEvent) => this.setFileApproved($event), + action: () => this.setFileApproved(), tooltip: this.file.canBeApproved ? _('dossier-overview.approve') : _('dossier-overview.approve-disabled'), icon: 'red:approved', disabled: !this.file.canBeApproved, @@ -220,7 +220,7 @@ export class FileActionsComponent implements OnChanges { { id: 'toggle-automatic-analysis-btn', type: ActionTypes.circleBtn, - action: ($event: MouseEvent) => this._toggleAutomaticAnalysis($event), + action: () => this._toggleAutomaticAnalysis(), tooltip: _('dossier-overview.stop-auto-analysis'), icon: 'red:disable-analysis', show: this.canDisableAutoAnalysis, @@ -228,7 +228,7 @@ export class FileActionsComponent implements OnChanges { { id: 'reanalyse-file-preview-btn', type: ActionTypes.circleBtn, - action: ($event: MouseEvent) => this._reanalyseFile($event), + action: () => this._reanalyseFile(), tooltip: _('file-preview.reanalyse-notification'), tooltipClass: 'small', icon: 'iqser:refresh', @@ -238,7 +238,7 @@ export class FileActionsComponent implements OnChanges { { id: 'toggle-automatic-analysis-btn', type: ActionTypes.circleBtn, - action: ($event: MouseEvent) => this._toggleAutomaticAnalysis($event), + action: () => this._toggleAutomaticAnalysis(), tooltip: _('dossier-overview.start-auto-analysis'), buttonType: this.isFilePreview ? CircleButtonTypes.warn : CircleButtonTypes.default, icon: 'red:enable-analysis', @@ -247,7 +247,7 @@ export class FileActionsComponent implements OnChanges { { id: 'set-under-approval-btn', type: ActionTypes.circleBtn, - action: ($event: MouseEvent) => this._setFileUnderApproval($event), + action: () => this._setFileUnderApproval(), tooltip: _('dossier-overview.under-approval'), icon: 'red:undo', show: this.showUndoApproval, @@ -255,7 +255,7 @@ export class FileActionsComponent implements OnChanges { { id: 'ocr-file-btn', type: ActionTypes.circleBtn, - action: ($event: MouseEvent) => this._ocrFile($event), + action: () => this._ocrFile(), tooltip: _('dossier-overview.ocr-file'), icon: 'iqser:ocr', show: this.showOCR, @@ -263,7 +263,7 @@ export class FileActionsComponent implements OnChanges { { id: 'reanalyse-file-btn', type: ActionTypes.circleBtn, - action: ($event: MouseEvent) => this._reanalyseFile($event), + action: () => this._reanalyseFile(), tooltip: _('dossier-overview.reanalyse.action'), icon: 'iqser:refresh', show: this.showReanalyseDossierOverview, @@ -278,7 +278,9 @@ export class FileActionsComponent implements OnChanges { checked: !this.file.excluded, show: this.showToggleAnalysis, }, - ].filter(btn => btn.show); + ]; + + return actions.filter(btn => btn.show); } ngOnChanges(changes: SimpleChanges) { @@ -289,30 +291,24 @@ export class FileActionsComponent implements OnChanges { } } - async setFileApproved($event: MouseEvent) { - $event.stopPropagation(); + async setFileApproved() { if (!this.file.analysisRequired && !this.file.hasUpdates) { - await this._setFileApproved(); + await this.#setFileApproved(); return; } - this._dialogService.openDialog( - 'confirm', - $event, - new ConfirmationDialogInput({ - title: this.file.analysisRequired - ? _('confirmation-dialog.approve-file-without-analysis.title') - : _('confirmation-dialog.approve-file.title'), - question: this.file.analysisRequired - ? _('confirmation-dialog.approve-file-without-analysis.question') - : _('confirmation-dialog.approve-file.question'), - confirmationText: this.file.analysisRequired - ? _('confirmation-dialog.approve-file-without-analysis.confirmationText') - : null, - denyText: this.file.analysisRequired ? _('confirmation-dialog.approve-file-without-analysis.denyText') : null, - }), - () => this._setFileApproved(), - ); + const data: IConfirmationDialogData = { + title: this.file.analysisRequired + ? _('confirmation-dialog.approve-file-without-analysis.title') + : _('confirmation-dialog.approve-file.title'), + question: this.file.analysisRequired + ? _('confirmation-dialog.approve-file-without-analysis.question') + : _('confirmation-dialog.approve-file.question'), + confirmationText: this.file.analysisRequired ? _('confirmation-dialog.approve-file-without-analysis.confirmationText') : null, + denyText: this.file.analysisRequired ? _('confirmation-dialog.approve-file-without-analysis.denyText') : null, + }; + + this._dialogService.openDialog('confirm', data, () => this.#setFileApproved()); } forceReanalysisAction($event: LongPressEvent) { @@ -321,28 +317,27 @@ export class FileActionsComponent implements OnChanges { } #showOCRConfirmationDialog(): Observable { - const data = new ConfirmationDialogInput({ + const data: IConfirmationDialogData = { title: _('ocr.confirmation-dialog.title'), question: _('ocr.confirmation-dialog.question'), denyText: _('ocr.confirmation-dialog.cancel'), - }); + }; - const ref = this._dialogService.openDialog('confirm', null, data); + const ref = this._dialogService.openDialog('confirm', data); return ref.afterClosed(); } - private _openImportRedactionsDialog($event: MouseEvent) { - this._dialogService.openDialog('importRedactions', $event, { dossierId: this.file.dossierId, fileId: this.file.fileId }); + private _openImportRedactionsDialog() { + this._dialogService.openDialog('importRedactions', { dossierId: this.file.dossierId, fileId: this.file.fileId }); } - private _openDeleteFileDialog($event: MouseEvent) { + private _openDeleteFileDialog() { this._dialogService.openDialog( 'confirm', - $event, - new ConfirmationDialogInput({ + { title: _('confirmation-dialog.delete-file.title'), question: _('confirmation-dialog.delete-file.question'), - }), + } as IConfirmationDialogData, async () => { this._loadingService.start(); try { @@ -357,21 +352,19 @@ export class FileActionsComponent implements OnChanges { ); } - private _assign($event: MouseEvent) { + private _assign() { const files = [this.file]; const targetStatus = this.file.workflowStatus; const withCurrentUserAsDefault = true; const withUnassignedOption = true; - this._dialogService.openDialog('assignFile', $event, { targetStatus, files, withCurrentUserAsDefault, withUnassignedOption }); + this._dialogService.openDialog('assignFile', { targetStatus, files, withCurrentUserAsDefault, withUnassignedOption }); } - private async _assignToMe($event: MouseEvent) { - $event.stopPropagation(); + private async _assignToMe() { await this._fileAssignService.assignToMe([this.file]); } - private async _reanalyseFile($event?: MouseEvent) { - $event?.stopPropagation(); + private async _reanalyseFile() { const params: ReanalyzeQueryParams = { force: true, triggeredByUser: true, @@ -379,20 +372,17 @@ export class FileActionsComponent implements OnChanges { await firstValueFrom(this._reanalysisService.reanalyzeFilesForDossier([this.file], this.file.dossierId, params)); } - private async _toggleAutomaticAnalysis($event: MouseEvent) { - $event.stopPropagation(); + private async _toggleAutomaticAnalysis() { this._loadingService.start(); await firstValueFrom(this._reanalysisService.toggleAutomaticAnalysis(this.file.dossierId, [this.file])); this._loadingService.stop(); } - private async _setFileUnderApproval($event: MouseEvent) { - $event.stopPropagation(); - await this._fileAssignService.assignApprover($event, this.file, true); + private async _setFileUnderApproval() { + await this._fileAssignService.assignApprover(this.file, true); } - private async _ocrFile($event: MouseEvent) { - $event.stopPropagation(); + private async _ocrFile() { if (this.file.lastManualChangeDate) { const confirm = await firstValueFrom(this.#showOCRConfirmationDialog()); if (!confirm) { @@ -410,8 +400,8 @@ export class FileActionsComponent implements OnChanges { this._loadingService.stop(); } - private async _setFileUnderReview($event: MouseEvent) { - await this._fileAssignService.assignReviewer($event, this.file, true); + private async _setFileUnderReview() { + await this._fileAssignService.assignReviewer(this.file, true); } private async _toggleAnalysis() { @@ -466,16 +456,15 @@ export class FileActionsComponent implements OnChanges { this._changeRef.markForCheck(); } - private async _setFileApproved() { + async #setFileApproved() { this._loadingService.start(); - await firstValueFrom(this._filesService.setApprovedFor([this.file], this.file.dossierId)); + await this._filesService.setApproved(this.file); this._loadingService.stop(); } - private async _setToNew($event: MouseEvent) { - $event.stopPropagation(); + async #setToNew() { this._loadingService.start(); - await firstValueFrom(this._filesService.setToNewFor([this.file], this.file.dossierId)); + await this._filesService.setToNew(this.file); this._loadingService.stop(); } } diff --git a/apps/red-ui/src/app/modules/shared-dossiers/dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component.ts b/apps/red-ui/src/app/modules/shared-dossiers/dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component.ts index 9870066b4..1d2b8f2d8 100644 --- a/apps/red-ui/src/app/modules/shared-dossiers/dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component.ts +++ b/apps/red-ui/src/app/modules/shared-dossiers/dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component.ts @@ -1,14 +1,13 @@ import { Component, Inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { UserService } from '@users/user.service'; -import { IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui'; -import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; -import { Dossier, File, WorkflowFileStatus, WorkflowFileStatuses } from '@red/domain'; +import { getCurrentUser, IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui'; +import { FormBuilder, Validators } from '@angular/forms'; +import { File, User, WorkflowFileStatus, WorkflowFileStatuses } from '@red/domain'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { FilesService } from '@services/files/files.service'; import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service'; import { PermissionsService } from '@services/permissions.service'; -import { firstValueFrom } from 'rxjs'; import { moveElementInArray } from '@utils/functions'; class DialogData { @@ -24,59 +23,37 @@ class DialogData { }) export class AssignReviewerApproverDialogComponent { readonly iconButtonTypes = IconButtonTypes; - readonly form: UntypedFormGroup; - readonly mode: 'reviewer' | 'approver'; - dossier: Dossier; + readonly currentUser = getCurrentUser(); + readonly mode = this.#mode; + readonly dossier = this._activeDossiersService.find(this.data.files[0].dossierId); + readonly userOptions = this.#userOptions; + readonly form = this.#form; constructor( readonly userService: UserService, private readonly _toaster: Toaster, - private readonly _formBuilder: UntypedFormBuilder, + private readonly _formBuilder: FormBuilder, private readonly _activeDossiersService: ActiveDossiersService, private readonly _filesService: FilesService, private readonly _loadingService: LoadingService, readonly permissionsService: PermissionsService, private readonly _dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) readonly data: DialogData, - ) { - this.dossier = this._activeDossiersService.find(this.data.files[0].dossierId); - this.form = this._getForm(); - this.mode = - data.targetStatus === WorkflowFileStatuses.UNDER_APPROVAL || data.targetStatus === WorkflowFileStatuses.APPROVED - ? 'approver' - : 'reviewer'; - } + ) {} get selectedUser(): string { - const value = this.form.get('user').value; + const value = this.form.controls.user.value; return value === 'undefined' ? undefined : value; } - get userOptions() { - const unassignUser = this._canUnassignFiles && this.data.withUnassignedOption ? ['undefined'] : []; - const cannotAssignUser = !this.permissionsService.canAssignUser(this.data.files, this.dossier); - - if (this.mode === 'reviewer') { - if (this.dossier.hasReviewers && cannotAssignUser) { - return [...unassignUser]; - } - return this._customSort([...this.dossier.memberIds, ...unassignUser]); - } - - if (this.dossier.approverIds.length > 1 && cannotAssignUser) { - return [...unassignUser]; - } - - return this._customSort([...this.dossier.approverIds, ...unassignUser]); - } - get changed(): boolean { if (this.data.ignoreChanged) { return true; } + const selectedUser = this.selectedUser; for (const file of this.data.files) { - if (file.assignee !== this.selectedUser) { + if (file.assignee !== selectedUser) { return true; } } @@ -84,71 +61,79 @@ export class AssignReviewerApproverDialogComponent { return false; } - private get _canUnassignFiles() { + get #mode() { + const isUnderApproval = this.data.targetStatus === WorkflowFileStatuses.UNDER_APPROVAL; + const isApproved = this.data.targetStatus === WorkflowFileStatuses.APPROVED; + return isUnderApproval || isApproved ? 'approver' : 'reviewer'; + } + + get #userOptions() { + const unassignUser = this.#canUnassignUser && this.data.withUnassignedOption ? ['undefined'] : []; + const cannotAssignUser = !this.permissionsService.canAssignUser(this.data.files, this.dossier); + + if (this.mode === 'reviewer') { + if (this.dossier.hasReviewers && cannotAssignUser) { + return [...unassignUser]; + } + + return this.#customSort([...this.dossier.memberIds, ...unassignUser]); + } + + if (this.dossier.approverIds.length > 1 && cannotAssignUser) { + return [...unassignUser]; + } + + return this.#customSort([...this.dossier.approverIds, ...unassignUser]); + } + + get #canUnassignUser() { return this.permissionsService.canUnassignUser(this.data.files, this.dossier); } - /** Initialize the form with: - * the id of the current reviewer of the files list if there is only one reviewer for all of them; - * or the id of the current user - **/ - - private get _uniqueReviewers(): Set { - const uniqueReviewers = new Set(); - for (const file of this.data.files) { - if (file.assignee) { - uniqueReviewers.add(file.assignee); - } + get #user(): string { + if (this.data.files.every(file => !file.assignee)) { + return null; } - return uniqueReviewers; + return this.data.files.length === 1 ? this.data.files[0].assignee : this.currentUser.id; } - private get _user(): string { - const userOptions = this.userOptions; - - if (this.data.withCurrentUserAsDefault && userOptions.includes(this.userService.currentUser.id)) { - return this.userService.currentUser.id; - } - - const uniqueReviewers = [...this._uniqueReviewers.values()]; - const user = uniqueReviewers.length === 1 ? uniqueReviewers[0] : this.userService.currentUser.id; - - return userOptions.indexOf(user) >= 0 ? userOptions[userOptions.indexOf(user)] : user; + get #form() { + const user = this.#user; + return this._formBuilder.group({ + // Allow a null reviewer if a previous reviewer exists (= it's not the first assignment) & current user is allowed to unassign + user: [user, this.#canUnassignUser && !user ? Validators.required : null], + }); } async save() { this._loadingService.start(); + const selectedUser = this.selectedUser; + try { - if (!this.selectedUser || this.data.targetStatus === WorkflowFileStatuses.APPROVED) { - await firstValueFrom(this._filesService.setAssignee(this.data.files, this.dossier.id, this.selectedUser)); + if (!selectedUser || this.data.targetStatus === WorkflowFileStatuses.APPROVED) { + await this._filesService.setAssignee(this.data.files, selectedUser); } else if (this.mode === 'reviewer') { - await firstValueFrom(this._filesService.setReviewerFor(this.data.files, this.dossier.id, this.selectedUser)); + await this._filesService.setReviewer(this.data.files, selectedUser); } else { - await firstValueFrom(this._filesService.setUnderApprovalFor(this.data.files, this.dossier.id, this.selectedUser)); + await this._filesService.setUnderApproval(this.data.files, selectedUser); } } catch (error) { this._toaster.error(_('error.http.generic'), { params: error }); } - this._loadingService.stop(); + this._loadingService.stop(); this._dialogRef.close(true); } - private _getForm(): UntypedFormGroup { - return this._formBuilder.group({ - // Allow a null reviewer if a previous reviewer exists (= it's not the first assignment) & current user is allowed to unassign - user: [this._user, this._canUnassignFiles && !this._user ? Validators.required : null], - }); - } - - private _customSort(ids: string[]) { + #customSort(ids: string[]) { let sorted = ids.sort((a, b) => this.userService.getName(a).localeCompare(this.userService.getName(b))); - if (this.data.files.length === 1 && this.data.files[0].assignee) { - sorted = moveElementInArray(sorted, this.data.files[0].assignee, 0); - } - if (this.data.withUnassignedOption) { - sorted = moveElementInArray(sorted, 'undefined', this.data.files[0].assignee && this.data.files.length === 1 ? 1 : 0); + + sorted = moveElementInArray(sorted, this.currentUser.id, 0); + + if (sorted.includes('undefined')) { + sorted = moveElementInArray(sorted, 'undefined', 1); } + return sorted; } } diff --git a/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/attributes/edit-dossier-attributes.component.html b/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/attributes/edit-dossier-attributes.component.html index 5595207f9..9c3d18d86 100644 --- a/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/attributes/edit-dossier-attributes.component.html +++ b/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/attributes/edit-dossier-attributes.component.html @@ -12,13 +12,13 @@ - + [canEditInput]="!disabled" + >
    diff --git a/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/dictionary/edit-dossier-dictionary.component.ts b/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/dictionary/edit-dossier-dictionary.component.ts index 63a7e45e7..32660e26c 100644 --- a/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/dictionary/edit-dossier-dictionary.component.ts +++ b/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/dictionary/edit-dossier-dictionary.component.ts @@ -74,7 +74,7 @@ export class EditDossierDictionaryComponent implements EditDossierSectionInterfa openEditDetailsDialog(): void { const data = { dictionary: this.dossierDictionary, dossierId: this.dossier.id, readOnly: !this.canEdit }; - this._dialogService.openDialog('editDictionaryDetails', null, data, async () => { + this._dialogService.openDialog('editDictionaryDetails', data, async () => { await this._updateDossierDictionary(); }); } diff --git a/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/download-package/edit-dossier-download-package.component.html b/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/download-package/edit-dossier-download-package.component.html index e9e68b36f..f11b0df05 100644 --- a/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/download-package/edit-dossier-download-package.component.html +++ b/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/download-package/edit-dossier-download-package.component.html @@ -1,16 +1,18 @@
    diff --git a/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.ts b/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.ts index f1dc89ce3..5049f12e9 100644 --- a/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.ts +++ b/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.ts @@ -164,13 +164,14 @@ export class EditDossierDialogComponent extends BaseDialogComponent implements A key: 'dossierInfo', title: _('edit-dossier-dialog.nav-items.general-info'), sideNavTitle: _('edit-dossier-dialog.nav-items.dossier-info'), - readonly: !this._dossier.isActive, + readonly: !this._dossier.isActive || !this._permissionsService.canEditDossier(this._dossier), helpModeKey: 'edit_dossier_dossier_info', }, { key: 'downloadPackage', title: _('edit-dossier-dialog.nav-items.choose-download'), sideNavTitle: _('edit-dossier-dialog.nav-items.download-package'), + readonly: !this._permissionsService.canEditDossier(this._dossier), helpModeKey: 'edit_dossier_download_package', }, { diff --git a/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.html b/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.html index b4d769083..b3658280c 100644 --- a/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.html +++ b/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.html @@ -41,7 +41,7 @@
    {{ 'assign-dossier-owner.dialog.no-reviewers' | translate }}
    - +
    u.isManager).map(m => m.id); readonly selectedReviewers$ = new BehaviorSubject([]); - readonly #currentUser = getCurrentUser(); constructor( readonly userService: UserService, @@ -101,9 +100,7 @@ export class EditDossierTeamComponent implements EditDossierSectionInterface, On return this.selectedApproversList.indexOf(userId) !== -1; } - toggleApprover(userId: string, $event?: MouseEvent) { - $event?.stopPropagation(); - + toggleApprover(userId: string) { if (this.isApprover(userId)) { this.selectedApproversList.splice(this.selectedApproversList.indexOf(userId), 1); } else { @@ -142,9 +139,11 @@ export class EditDossierTeamComponent implements EditDossierSectionInterface, On this.membersSelectOptions = possibleMembers .filter(user => this.userService.getName(user.id).toLowerCase().includes(value.toLowerCase())) .filter(user => this.selectedOwnerId !== user.id) - .map(user => user.id); + .map(user => user.id) + .sort((a, b) => this.userService.getName(a).localeCompare(this.userService.getName(b))); } + @Debounce(0) onChangeOwner(ownerId: string) { if (this.hasOwner) { if (!this.isApprover(ownerId)) { @@ -157,7 +156,7 @@ export class EditDossierTeamComponent implements EditDossierSectionInterface, On private _setSelectedReviewersList() { const selectedReviewers = this.selectedMembersList.filter(m => this.selectedApproversList.indexOf(m) === -1); - this.selectedReviewers$.next(selectedReviewers); + this.selectedReviewers$.next(this._sortByName(selectedReviewers)); } private _loadData() { @@ -169,8 +168,8 @@ export class EditDossierTeamComponent implements EditDossierSectionInterface, On }, Validators.required, ], - approvers: [[...this.dossier.approverIds]], - members: [[...this.dossier.memberIds]], + approvers: [this._sortByName([...this.dossier.approverIds])], + members: [this._sortByName([...this.dossier.memberIds])], }); this._updateLists(); } @@ -179,4 +178,8 @@ export class EditDossierTeamComponent implements EditDossierSectionInterface, On this._setSelectedReviewersList(); this.setMembersSelectOptions(); } + + private _sortByName(ids: string[]) { + return ids.sort((a, b) => this.userService.getName(a).localeCompare(this.userService.getName(b))); + } } diff --git a/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.html b/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.html index 574c38578..716387c8b 100644 --- a/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.html +++ b/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.html @@ -80,7 +80,7 @@
    { + }; + + this._dialogService.openDialog('confirm', data, async () => { this._loadingService.start(); await firstValueFrom(this._trashService.deleteDossier(this.dossier)); this._editDossierDialogRef.close(); @@ -154,7 +155,7 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti } archiveDossier() { - const data = new ConfirmationDialogInput({ + const data: IConfirmationDialogData = { title: _('confirm-archive-dossier.title'), details: _('confirm-archive-dossier.details'), question: _('confirm-archive-dossier.warning'), @@ -164,8 +165,9 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti translateParams: { ...this.dossier }, checkboxes: [{ value: false, label: _('confirm-archive-dossier.checkbox.documents') }], toastMessage: _('confirm-archive-dossier.toast-error'), - }); - this._dialogService.openDialog('confirm', null, data, async result => { + }; + + this._dialogService.openDialog('confirm', data, async result => { if (result === ConfirmOptions.CONFIRM) { this._loadingService.start(); await firstValueFrom(this._archivedDossiersService.archive([this.dossier])); diff --git a/apps/red-ui/src/app/modules/shared-dossiers/services/file-assign.service.ts b/apps/red-ui/src/app/modules/shared-dossiers/services/file-assign.service.ts index 046268d4e..06a9f335b 100644 --- a/apps/red-ui/src/app/modules/shared-dossiers/services/file-assign.service.ts +++ b/apps/red-ui/src/app/modules/shared-dossiers/services/file-assign.service.ts @@ -1,10 +1,9 @@ import { Injectable } from '@angular/core'; -import { UserService } from '@users/user.service'; import { Dossier, File, User, WorkflowFileStatus, WorkflowFileStatuses } from '@red/domain'; import { DossiersDialogService } from './dossiers-dialog.service'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { FilesService } from '@services/files/files.service'; -import { ConfirmationDialogInput, LoadingService, Toaster } from '@iqser/common-ui'; +import { getCurrentUser, IConfirmationDialogData, LoadingService, Toaster } from '@iqser/common-ui'; import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service'; import { firstValueFrom } from 'rxjs'; @@ -12,18 +11,15 @@ const atLeastOneAssignee = (files: File[]) => files.reduce((acc, fs) => acc || ! @Injectable() export class FileAssignService { - readonly currentUser: User; + readonly currentUser = getCurrentUser(); constructor( - userService: UserService, private readonly _toaster: Toaster, private readonly _filesService: FilesService, private readonly _loadingService: LoadingService, private readonly _dialogService: DossiersDialogService, private readonly _activeDossiersService: ActiveDossiersService, - ) { - this.currentUser = userService.currentUser; - } + ) {} async assignToMe(files: File[]): Promise { const assignReq = async () => { @@ -31,36 +27,35 @@ export class FileAssignService { if (files[0].isNew) { await this._makeAssignFileRequest(this.currentUser.id, 'UNDER_REVIEW', files); } else { - await firstValueFrom(this._filesService.setAssignee(files, files[0].dossierId, this.currentUser.id)); + await this._filesService.setAssignee(files, this.currentUser.id); } this._loadingService.stop(); }; + if (atLeastOneAssignee(files)) { - const dialogInput = new ConfirmationDialogInput({ + const dialogInput: IConfirmationDialogData = { title: _('confirmation-dialog.assign-file-to-me.title'), question: files.length === 1 ? _('confirmation-dialog.assign-file-to-me.question.single') : _('confirmation-dialog.assign-file-to-me.question.multiple'), - }); - const ref = this._dialogService.openDialog('confirm', null, dialogInput, assignReq); + }; + const ref = this._dialogService.openDialog('confirm', dialogInput, assignReq); return firstValueFrom(ref.afterClosed()); } return assignReq(); } - async assignReviewer($event: MouseEvent, file: File, ignoreChanged = false): Promise { - await this._assignFile(WorkflowFileStatuses.UNDER_REVIEW, $event, file, ignoreChanged); + async assignReviewer(file: File, ignoreChanged = false): Promise { + await this._assignFile(WorkflowFileStatuses.UNDER_REVIEW, file, ignoreChanged); } - async assignApprover($event: MouseEvent, file: File, ignoreChanged = false): Promise { - await this._assignFile(WorkflowFileStatuses.UNDER_APPROVAL, $event, file, ignoreChanged); + async assignApprover(file: File, ignoreChanged = false): Promise { + await this._assignFile(WorkflowFileStatuses.UNDER_APPROVAL, file, ignoreChanged); } - private async _assignFile(targetStatus: WorkflowFileStatus, $event: MouseEvent, file: File, ignoreChanged = false): Promise { - $event?.stopPropagation(); - + private async _assignFile(targetStatus: WorkflowFileStatus, file: File, ignoreChanged = false): Promise { const currentUserId = this.currentUser.id; const currentDossier = this._activeDossiersService.find(file.dossierId); const eligibleUsersIds = this._getUserIds(targetStatus, currentDossier); @@ -74,31 +69,33 @@ export class FileAssignService { await this._makeAssignFileRequest(eligibleUsersIds[0], targetStatus, [file]); } else { const data = { targetStatus, files: [file], ignoreChanged }; - this._dialogService.openDialog('assignFile', null, data); + this._dialogService.openDialog('assignFile', data); } } else { if (eligibleUsersIds.length === 1) { await this._makeAssignFileRequest(eligibleUsersIds[0], targetStatus, [file]); } else { const data = { targetStatus, files: [file], ignoreChanged, withCurrentUserAsDefault: true }; - this._dialogService.openDialog('assignFile', null, data); + this._dialogService.openDialog('assignFile', data); } } } private async _makeAssignFileRequest(userId: string, targetStatus: WorkflowFileStatus, files: File[]) { this._loadingService.start(); + try { if (!userId || targetStatus === WorkflowFileStatuses.APPROVED) { - await firstValueFrom(this._filesService.setAssignee(files, files[0].dossierId, userId)); + await this._filesService.setAssignee(files, userId); } else if (targetStatus === WorkflowFileStatuses.UNDER_REVIEW) { - await firstValueFrom(this._filesService.setReviewerFor(files, files[0].dossierId, userId)); + await this._filesService.setReviewer(files, userId); } else { - await firstValueFrom(this._filesService.setUnderApprovalFor(files, files[0].dossierId, userId)); + await this._filesService.setUnderApproval(files, userId); } } catch (error) { this._toaster.error(_('error.http.generic'), { params: error }); } + this._loadingService.stop(); } diff --git a/apps/red-ui/src/app/modules/shared-dossiers/shared-dossiers.module.ts b/apps/red-ui/src/app/modules/shared-dossiers/shared-dossiers.module.ts index 0659a7b1b..6e1627e3b 100644 --- a/apps/red-ui/src/app/modules/shared-dossiers/shared-dossiers.module.ts +++ b/apps/red-ui/src/app/modules/shared-dossiers/shared-dossiers.module.ts @@ -13,14 +13,21 @@ import { DateColumnComponent } from './components/date-column/date-column.compon import { FileAssignService } from './services/file-assign.service'; import { DictionaryDetailsDialogComponent } from './dialogs/edit-dossier-dialog/dictionary/dictionary-details-dialog/dictionary-details-dialog.component'; import { - IqserButtonsModule, - IqserEmptyStatesModule, + CircleButtonComponent, + DynamicInputComponent, + EmptyStateComponent, + HasScrollbarDirective, + IconButtonComponent, + InputWithActionComponent, + IqserAllowDirective, + IqserDenyDirective, IqserHelpModeModule, - IqserInputsModule, - IqserPermissionsModule, - IqserScrollbarModule, - IqserSharedModule, IqserUsersModule, + RoundCheckboxComponent, + SideNavComponent, + SmallChipComponent, + StatusBarComponent, + StopPropagationDirective, } from '@iqser/common-ui'; import { TranslateModule } from '@ngx-translate/core'; import { DossiersListingActionsComponent } from './components/dossiers-listing-actions/dossiers-listing-actions.component'; @@ -37,24 +44,30 @@ const components = [ DossiersListingActionsComponent, ]; const dialogs = [EditDossierDialogComponent, AssignReviewerApproverDialogComponent, DictionaryDetailsDialogComponent]; -const services = [FileAssignService]; @NgModule({ declarations: [...components, ...dialogs], exports: [...components, ...dialogs], - providers: [...services], + providers: [FileAssignService], imports: [ CommonModule, SharedModule, IqserHelpModeModule, IqserUsersModule, TranslateModule, - IqserButtonsModule, - IqserEmptyStatesModule, - IqserSharedModule, - IqserInputsModule, - IqserScrollbarModule, - IqserPermissionsModule, + IconButtonComponent, + CircleButtonComponent, + EmptyStateComponent, + SmallChipComponent, + StatusBarComponent, + SideNavComponent, + StopPropagationDirective, + HasScrollbarDirective, + RoundCheckboxComponent, + InputWithActionComponent, + DynamicInputComponent, + IqserAllowDirective, + IqserDenyDirective, ], }) export class SharedDossiersModule {} diff --git a/apps/red-ui/src/app/modules/shared/components/buttons/file-download-btn/file-download-btn.component.html b/apps/red-ui/src/app/modules/shared/components/buttons/file-download-btn/file-download-btn.component.html index ffeba8738..2df56956c 100644 --- a/apps/red-ui/src/app/modules/shared/components/buttons/file-download-btn/file-download-btn.component.html +++ b/apps/red-ui/src/app/modules/shared/components/buttons/file-download-btn/file-download-btn.component.html @@ -1,5 +1,5 @@ (DownloadDialogComponent, { ...defaultDialogConfig, data: { dossier: this.dossier, files: this.files }, diff --git a/apps/red-ui/src/app/modules/shared/components/donut-chart/donut-chart.component.html b/apps/red-ui/src/app/modules/shared/components/donut-chart/donut-chart.component.html index ade5095d8..0ae3e26ed 100644 --- a/apps/red-ui/src/app/modules/shared/components/donut-chart/donut-chart.component.html +++ b/apps/red-ui/src/app/modules/shared/components/donut-chart/donut-chart.component.html @@ -20,6 +20,7 @@
    {{ displayedDataTotal }}
    {{ subtitles[0] }}
    + - + >
    diff --git a/apps/red-ui/src/app/modules/shared/components/donut-chart/donut-chart.component.ts b/apps/red-ui/src/app/modules/shared/components/donut-chart/donut-chart.component.ts index 97fe867bf..128b015ec 100644 --- a/apps/red-ui/src/app/modules/shared/components/donut-chart/donut-chart.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/donut-chart/donut-chart.component.ts @@ -1,13 +1,17 @@ import { Component, EventEmitter, Input, OnChanges, OnInit, Optional, Output } from '@angular/core'; import { DonutChartConfig } from '@red/domain'; -import { FilterService, get, INestedFilter, shareLast } from '@iqser/common-ui'; +import { FilterService, get, INestedFilter, IqserHelpModeModule, shareLast, StatusBarComponent } from '@iqser/common-ui'; import { Observable, of } from 'rxjs'; import { map } from 'rxjs/operators'; +import { AsyncPipe, NgForOf, NgIf } from '@angular/common'; +import { MatSelectModule } from '@angular/material/select'; @Component({ selector: 'redaction-donut-chart', templateUrl: './donut-chart.component.html', styleUrls: ['./donut-chart.component.scss'], + standalone: true, + imports: [NgForOf, NgIf, MatSelectModule, IqserHelpModeModule, StatusBarComponent, AsyncPipe], }) export class DonutChartComponent implements OnChanges, OnInit { @Input() subtitles: string[]; @@ -45,11 +49,12 @@ export class DonutChartComponent implements OnChanges, OnInit { } ngOnInit() { - this.filters$ = - this.filterService?.getFilterModels$(this.filterKey).pipe( - map(filters => filters ?? []), - shareLast(), - ) ?? of([]); + const filterModels$ = this.filterService?.getFilterModels$(this.filterKey).pipe( + map(filters => filters ?? []), + shareLast(), + ); + + this.filters$ = filterModels$ ?? of([]); } ngOnChanges(): void { diff --git a/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.html b/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.html index 39f50ccd1..78bc32d8b 100644 --- a/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.html +++ b/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.html @@ -29,7 +29,6 @@
    @@ -49,6 +49,7 @@ [icon]="'iqser:more-actions'" [matMenuTriggerFor]="hiddenButtonsMenu" [type]="buttonType" + buttonId="file-actions-menu-trigger-btn" > diff --git a/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.ts b/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.ts index 872e16569..abf7722c2 100644 --- a/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.ts @@ -30,7 +30,7 @@ export class ExpandableFileActionsComponent implements OnChanges { expanded = false; @ViewChild(MatMenuTrigger) matMenu: MatMenuTrigger; - trackBy = trackByFactory(); + readonly trackBy = trackByFactory(); constructor( private readonly _fileDownloadService: FileDownloadService, @@ -70,7 +70,7 @@ export class ExpandableFileActionsComponent implements OnChanges { // Patch download button const downloadBtn = this.actions.find(btn => btn.type === ActionTypes.downloadBtn); if (downloadBtn) { - downloadBtn.action = ($event: MouseEvent) => this._downloadFiles($event, downloadBtn.files, downloadBtn.dossier); + downloadBtn.action = () => this._downloadFiles(downloadBtn.files, downloadBtn.dossier); downloadBtn.disabled = !this._permissionsService.canDownloadFiles(downloadBtn.files, downloadBtn.dossier); } } @@ -81,8 +81,8 @@ export class ExpandableFileActionsComponent implements OnChanges { this.matMenu.closeMenu(); } - private async _downloadFiles($event: MouseEvent, files: File[], dossier: Dossier) { - $event.stopPropagation(); + // TODO: this doesn't look like its called + private async _downloadFiles(files: File[], dossier: Dossier) { const ref = this._dialog.open(DownloadDialogComponent, { ...defaultDialogConfig, data: { dossier, files }, diff --git a/apps/red-ui/src/app/modules/shared/components/pagination/pagination.component.html b/apps/red-ui/src/app/modules/shared/components/pagination/pagination.component.html index e1a7651a5..8a0db3f0d 100644 --- a/apps/red-ui/src/app/modules/shared/components/pagination/pagination.component.html +++ b/apps/red-ui/src/app/modules/shared/components/pagination/pagination.component.html @@ -1,4 +1,10 @@ -
    +
    |
    {{ displayValue(page) }}
    | -
    +
    diff --git a/apps/red-ui/src/app/modules/shared/components/pagination/pagination.component.ts b/apps/red-ui/src/app/modules/shared/components/pagination/pagination.component.ts index 0b465516b..0c1a5ff74 100644 --- a/apps/red-ui/src/app/modules/shared/components/pagination/pagination.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/pagination/pagination.component.ts @@ -40,6 +40,10 @@ export class PaginationComponent { return page === '...' ? page : (page as number) + 1; } + isNumber(page) { + return Number.isInteger(page); + } + private _updatePagesArray() { this.displayedPages = [0]; if (Math.max(1, this.currentPage - 1) > 1) { diff --git a/apps/red-ui/src/app/modules/shared/components/select/select.component.html b/apps/red-ui/src/app/modules/shared/components/select/select.component.html index fc01a781b..ebdd68eaf 100644 --- a/apps/red-ui/src/app/modules/shared/components/select/select.component.html +++ b/apps/red-ui/src/app/modules/shared/components/select/select.component.html @@ -1,11 +1,18 @@
    {{ label }}
    -
    +
    diff --git a/apps/red-ui/src/app/modules/shared/components/select/select.component.scss b/apps/red-ui/src/app/modules/shared/components/select/select.component.scss index c63444db5..a666eb725 100644 --- a/apps/red-ui/src/app/modules/shared/components/select/select.component.scss +++ b/apps/red-ui/src/app/modules/shared/components/select/select.component.scss @@ -35,3 +35,9 @@ mat-icon { width: 14px; } + +.disabled { + color: var(--iqser-grey-3); + cursor: default; + pointer-events: none; +} diff --git a/apps/red-ui/src/app/modules/shared/components/select/select.component.ts b/apps/red-ui/src/app/modules/shared/components/select/select.component.ts index 89a498516..58c32dd71 100644 --- a/apps/red-ui/src/app/modules/shared/components/select/select.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/select/select.component.ts @@ -85,8 +85,7 @@ export class SelectComponent implements AfterViewInit, ControlValueAccessor { this.disabled = isDisabled; } - selectAll($event) { - $event.stopPropagation(); + selectAll() { this.chipList._chips.forEach(chip => { if (!chip.selected) { chip.toggleSelected(true); @@ -94,8 +93,7 @@ export class SelectComponent implements AfterViewInit, ControlValueAccessor { }); } - deselectAll($event?) { - $event?.stopPropagation(); + deselectAll() { this.chipList._chips.forEach(chip => { if (chip.selected) { chip.toggleSelected(true); diff --git a/apps/red-ui/src/app/modules/shared/components/team-members/team-members.component.ts b/apps/red-ui/src/app/modules/shared/components/team-members/team-members.component.ts index f558da526..c416bdee0 100644 --- a/apps/red-ui/src/app/modules/shared/components/team-members/team-members.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/team-members/team-members.component.ts @@ -1,18 +1,20 @@ -import { Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core'; +import { Component, ElementRef, EventEmitter, inject, Input, OnChanges, Output, ViewChild } from '@angular/core'; import { CircleButtonTypes, getCurrentUser, List } from '@iqser/common-ui'; import { DossiersDialogService } from '../../../shared-dossiers/services/dossiers-dialog.service'; import { ROLES } from '@users/roles'; import { User } from '@red/domain'; +import { UserService } from '@users/user.service'; @Component({ selector: 'redaction-team-members', templateUrl: './team-members.component.html', styleUrls: ['./team-members.component.scss'], }) -export class TeamMembersComponent { +export class TeamMembersComponent implements OnChanges { readonly circleButtonTypes = CircleButtonTypes; readonly roles = ROLES; readonly currentUser = getCurrentUser(); + readonly userService = inject(UserService); @Input() memberIds: List; @Input() perLine: number; @@ -29,6 +31,10 @@ export class TeamMembersComponent { constructor(private readonly _dialogService: DossiersDialogService) {} + ngOnChanges() { + this.memberIds = [...this.memberIds].sort((a, b) => this.userService.getName(a).localeCompare(this.userService.getName(b))); + } + get maxTeamMembersBeforeExpand(): number { return this.perLine - (this.canAdd ? 1 : 0); } @@ -51,6 +57,6 @@ export class TeamMembersComponent { openEditDossierDialog(): void { const data = { dossierId: this.dossierId, section: 'members' }; - this._dialogService.openDialog('editDossier', null, data); + this._dialogService.openDialog('editDossier', data); } } diff --git a/apps/red-ui/src/app/modules/shared/dialogs/add-dossier-dialog/add-dossier-dialog.component.ts b/apps/red-ui/src/app/modules/shared/dialogs/add-dossier-dialog/add-dossier-dialog.component.ts index 9ef2c57f5..fb048bf13 100644 --- a/apps/red-ui/src/app/modules/shared/dialogs/add-dossier-dialog/add-dossier-dialog.component.ts +++ b/apps/red-ui/src/app/modules/shared/dialogs/add-dossier-dialog/add-dossier-dialog.component.ts @@ -84,7 +84,7 @@ export class AddDossierDialogComponent extends BaseDialogComponent implements On if (savedDossier) { await this._router.navigate([savedDossier.routerLink]); if (options?.addMembers) { - this._dialogService.openDialog('editDossier', null, { + this._dialogService.openDialog('editDossier', { dossierId: savedDossier.id, section: 'members', }); diff --git a/apps/red-ui/src/app/modules/shared/dialogs/download-dialog/download-dialog.component.ts b/apps/red-ui/src/app/modules/shared/dialogs/download-dialog/download-dialog.component.ts index e1826bdc0..d7c98e035 100644 --- a/apps/red-ui/src/app/modules/shared/dialogs/download-dialog/download-dialog.component.ts +++ b/apps/red-ui/src/app/modules/shared/dialogs/download-dialog/download-dialog.component.ts @@ -2,10 +2,10 @@ import { Component, Inject } from '@angular/core'; import { Dossier, DownloadFileType, DownloadFileTypes, IReportTemplate, File, WorkflowFileStatuses } from '@red/domain'; import { downloadTypesForDownloadTranslations } from '@translations/download-types-translations'; import { ReportTemplateService } from '@services/report-template.service'; -import { AbstractControl, FormBuilder } from '@angular/forms'; +import { AbstractControl } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { DefaultColorsService } from '@services/entity-services/default-colors.service'; -import { IconButtonTypes, List } from '@iqser/common-ui'; +import { BaseDialogComponent, IconButtonTypes, List } from '@iqser/common-ui'; export interface DownloadDialogData { readonly dossier: Dossier; @@ -23,19 +23,21 @@ export interface DownloadDialogResult { templateUrl: './download-dialog.component.html', styleUrls: ['./download-dialog.component.scss'], }) -export class DownloadDialogComponent { +export class DownloadDialogComponent extends BaseDialogComponent { readonly iconButtonTypes = IconButtonTypes; readonly downloadTypes: { key: DownloadFileType; label: string }[] = this._formDownloadTypes; readonly availableReportTypes = this._availableReportTypes; - readonly form = this._getForm(); constructor( private readonly _defaultColorsService: DefaultColorsService, private readonly _reportTemplateController: ReportTemplateService, - private readonly _formBuilder: FormBuilder, - private readonly _dialogRef: MatDialogRef, + protected readonly _dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) readonly data: DownloadDialogData, - ) {} + ) { + super(_dialogRef); + + this.form = this._getForm(); + } get reportTypesLength() { return this.form.controls.reportTemplateIds?.value?.length || 0; diff --git a/apps/red-ui/src/app/modules/shared/shared.module.ts b/apps/red-ui/src/app/modules/shared/shared.module.ts index 4589f81f2..4009a8470 100644 --- a/apps/red-ui/src/app/modules/shared/shared.module.ts +++ b/apps/red-ui/src/app/modules/shared/shared.module.ts @@ -9,12 +9,15 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { AnnotationIconComponent } from './components/annotation-icon/annotation-icon.component'; import { DonutChartComponent } from './components/donut-chart/donut-chart.component'; import { - IqserButtonsModule, + CircleButtonComponent, + HasScrollbarDirective, + IconButtonComponent, + IqserAllowDirective, + IqserDenyDirective, IqserHelpModeModule, - IqserPermissionsModule, - IqserScrollbarModule, - IqserSharedModule, IqserUsersModule, + SmallChipComponent, + StopPropagationDirective, } from '@iqser/common-ui'; import { SelectComponent } from './components/select/select.component'; import { NavigateLastDossiersScreenDirective } from './directives/navigate-last-dossiers-screen.directive'; @@ -50,7 +53,6 @@ const buttons = [FileDownloadBtnComponent]; const components = [ PaginationComponent, AnnotationIconComponent, - DonutChartComponent, SelectComponent, DictionaryManagerComponent, AssignUserDropdownComponent, @@ -75,7 +77,9 @@ const utils = [DatePipe, NavigateLastDossiersScreenDirective, LongPressDirective const services = [SharedDialogService]; -const modules = [MatConfigModule, ScrollingModule, IconsModule, FormsModule, IqserButtonsModule, ReactiveFormsModule, ColorPickerModule]; +const modules = [MatConfigModule, ScrollingModule, IconsModule, FormsModule, ReactiveFormsModule, ColorPickerModule]; + +const deleteThisWhenAllComponentsAreStandalone = [DonutChartComponent]; @NgModule({ declarations: [...components, ...utils, EditorComponent, DownloadDialogComponent], @@ -87,11 +91,16 @@ const modules = [MatConfigModule, ScrollingModule, IconsModule, FormsModule, Iqs RouterModule, IqserHelpModeModule, IqserUsersModule, - IqserSharedModule, - IqserScrollbarModule, - IqserPermissionsModule, + ...deleteThisWhenAllComponentsAreStandalone, + CircleButtonComponent, + IconButtonComponent, + SmallChipComponent, + StopPropagationDirective, + HasScrollbarDirective, + IqserAllowDirective, + IqserDenyDirective, ], - exports: [...modules, ...components, ...utils], + exports: [...modules, ...components, ...utils, ...deleteThisWhenAllComponentsAreStandalone], providers: [ ...services, { diff --git a/apps/red-ui/src/app/modules/trash/services/trash-dialog.service.ts b/apps/red-ui/src/app/modules/trash/services/trash-dialog.service.ts index 9ef73d9d6..41daa010a 100644 --- a/apps/red-ui/src/app/modules/trash/services/trash-dialog.service.ts +++ b/apps/red-ui/src/app/modules/trash/services/trash-dialog.service.ts @@ -2,9 +2,9 @@ import { Injectable } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { ConfirmationDialogComponent, - ConfirmationDialogInput, DialogConfig, DialogService, + IConfirmationDialogData, LoadingService, TitleColors, } from '@iqser/common-ui'; @@ -32,7 +32,7 @@ export class TrashDialogService extends DialogService { } confirmHardDelete(items: TrashItem[]): void { - const data = new ConfirmationDialogInput({ + const data: IConfirmationDialogData = { title: _('confirmation-dialog.delete-items.title'), titleColor: TitleColors.WARN, question: _('confirmation-dialog.delete-items.question'), @@ -40,8 +40,9 @@ export class TrashDialogService extends DialogService { name: items[0].name, itemsCount: items.length, }, - }); - this.openDialog('confirm', null, data, async () => { + }; + + this.openDialog('confirm', data, async () => { this._loadingService.start(); await firstValueFrom(this._trashService.hardDelete(items)); this._loadingService.stop(); diff --git a/apps/red-ui/src/app/modules/trash/trash-screen/trash-table-item/trash-table-item.component.html b/apps/red-ui/src/app/modules/trash/trash-screen/trash-table-item/trash-table-item.component.html index 6b289c9ac..c328c4ca3 100644 --- a/apps/red-ui/src/app/modules/trash/trash-screen/trash-table-item/trash-table-item.component.html +++ b/apps/red-ui/src/app/modules/trash/trash-screen/trash-table-item/trash-table-item.component.html @@ -29,13 +29,13 @@
    - {{ item.softDeletedTime | date: 'exactDate' }} + {{ item.softDeletedTime | date : 'exactDate' }}
    - {{ item.restoreDate | date: 'timeFromNow' }} + {{ item.restoreDate | date : 'timeFromNow' }}
    { protected readonly _defaultModelPath = 'async/download'; protected readonly _entityClass = DownloadStatus; + protected readonly _tenantContext = inject(TenantContext); constructor(private readonly _configService: ConfigService, private readonly _logger: NGXLogger) { super(); @@ -34,7 +36,9 @@ export class FileDownloadService extends EntitiesService templates)); } diff --git a/apps/red-ui/src/app/services/dossiers/dossier-changes.service.ts b/apps/red-ui/src/app/services/dossiers/dossier-changes.service.ts index efdf7c7fd..33b4950fe 100644 --- a/apps/red-ui/src/app/services/dossiers/dossier-changes.service.ts +++ b/apps/red-ui/src/app/services/dossiers/dossier-changes.service.ts @@ -1,4 +1,4 @@ -import { GenericService, List, QueryParam } from '@iqser/common-ui'; +import { GenericService, LAST_CHECKED_OFFSET, List, QueryParam, ROOT_CHANGES_KEY } from '@iqser/common-ui'; import { Dossier, DossierStats, IDossierChanges } from '@red/domain'; import { forkJoin, Observable, of, throwError, timer } from 'rxjs'; import { catchError, filter, map, switchMap, take, tap } from 'rxjs/operators'; @@ -13,7 +13,6 @@ import { CHANGED_CHECK_INTERVAL } from '@utils/constants'; @Injectable({ providedIn: 'root' }) export class DossiersChangesService extends GenericService { protected readonly _defaultModelPath = 'dossier'; - #initializedRefresh = false; readonly #activeDossiersService = inject(ActiveDossiersService); readonly #archivedDossiersService = inject(ArchivedDossiersService); @@ -22,8 +21,8 @@ export class DossiersChangesService extends GenericService { loadOnlyChanged(): Observable { const removeIfNotFound = (id: string) => - catchError((error: HttpErrorResponse) => { - if (error.status === HttpStatusCode.NotFound) { + catchError((error: unknown) => { + if (error instanceof HttpErrorResponse && error.status === HttpStatusCode.NotFound) { this.#activeDossiersService.remove(id); this.#archivedDossiersService.remove(id); return of([]); @@ -43,28 +42,22 @@ export class DossiersChangesService extends GenericService { } hasChangesDetails$(): Observable { - const body = { value: this._lastCheckedForChanges.get('root') }; - const dateBeforeRequest = new Date().toISOString(); + const body = { value: this._lastCheckedForChanges.get(ROOT_CHANGES_KEY) }; + const dateBeforeRequest = new Date(Date.now() - LAST_CHECKED_OFFSET).toISOString(); return this._post(body, `${this._defaultModelPath}/changes/details`).pipe( filter(changes => changes.length > 0), - tap(() => this._lastCheckedForChanges.set('root', dateBeforeRequest)), + tap(() => this._lastCheckedForChanges.set(ROOT_CHANGES_KEY, dateBeforeRequest)), ); } initializeRefresh() { - if (this.#initializedRefresh) { - return; - } - this.#initializedRefresh = true; - timer(CHANGED_CHECK_INTERVAL, CHANGED_CHECK_INTERVAL) - .pipe( - switchMap(() => this.loadOnlyChanged()), - tap(changes => { - this.#activeDossiersService.emitFileChanges(changes); - this.#archivedDossiersService.emitFileChanges(changes); - }), - ) - .subscribe(); + return timer(CHANGED_CHECK_INTERVAL, CHANGED_CHECK_INTERVAL).pipe( + switchMap(() => this.loadOnlyChanged()), + tap(changes => { + this.#activeDossiersService.emitFileChanges(changes); + this.#archivedDossiersService.emitFileChanges(changes); + }), + ); } private _load(id: string): Observable { diff --git a/apps/red-ui/src/app/services/dossiers/dossiers-cache.service.ts b/apps/red-ui/src/app/services/dossiers/dossiers-cache.service.ts index 76570444b..bf2f75ea0 100644 --- a/apps/red-ui/src/app/services/dossiers/dossiers-cache.service.ts +++ b/apps/red-ui/src/app/services/dossiers/dossiers-cache.service.ts @@ -1,25 +1,23 @@ -import { EventEmitter, Injectable } from '@angular/core'; +import { EventEmitter, Injectable, OnDestroy } from '@angular/core'; import { ActiveDossiersService } from './active-dossiers.service'; import { ArchivedDossiersService } from './archived-dossiers.service'; -import { firstValueFrom, forkJoin, merge } from 'rxjs'; -import { map, skip, take } from 'rxjs/operators'; +import { firstValueFrom, merge, Subscription } from 'rxjs'; +import { skip, tap } from 'rxjs/operators'; import { Dossier } from '@red/domain'; @Injectable({ providedIn: 'root', }) -export class DossiersCacheService { +export class DossiersCacheService implements OnDestroy { readonly changed$ = new EventEmitter(); private _dossiers: Dossier[] = JSON.parse(localStorage.getItem('dossiers')) || []; + readonly #subscriptions = new Subscription(); constructor( private readonly _activeDossiersService: ActiveDossiersService, private readonly _archivedDossiersService: ArchivedDossiersService, ) { - // Skip 1 to avoid clearing the cache when the dossier services are initialized - merge(_activeDossiersService.all$.pipe(skip(1)), _archivedDossiersService.all$.pipe(skip(1))).subscribe(() => { - this.set(); - }); + this.#subscriptions.add(this.#init().subscribe()); } get empty(): boolean { @@ -30,23 +28,31 @@ export class DossiersCacheService { return this._dossiers; } + ngOnDestroy() { + this.#subscriptions.unsubscribe(); + } + async load(): Promise { - await firstValueFrom( - forkJoin([this._activeDossiersService.loadAll().pipe(take(1)), this._archivedDossiersService.loadAll().pipe(take(1))]).pipe( - map(list => list.flat()), - ), - ); + const activeDossiers = firstValueFrom(this._activeDossiersService.loadAll()); + const archivedDossiers = firstValueFrom(this._archivedDossiersService.loadAll()); + await Promise.all([activeDossiers, archivedDossiers]); this.set(); } set(): void { - const dossiers = [this._activeDossiersService.all, this._archivedDossiersService.all].flat(); - this._dossiers = dossiers; - localStorage.setItem('dossiers', JSON.stringify(dossiers)); + this._dossiers = [this._activeDossiersService.all, this._archivedDossiersService.all].flat(); + localStorage.setItem('dossiers', JSON.stringify(this._dossiers)); this.changed$.emit(); } get(dossierId: string) { return this._dossiers.find(dossier => dossier.id === dossierId); } + + #init() { + // Skip 1 to avoid clearing the cache when the dossier services are initialized + const activeDossiers = this._activeDossiersService.all$.pipe(skip(1)); + const archivedDossiers = this._archivedDossiersService.all$.pipe(skip(1)); + return merge(activeDossiers, archivedDossiers).pipe(tap(() => this.set())); + } } diff --git a/apps/red-ui/src/app/services/dossiers/dossiers.service.ts b/apps/red-ui/src/app/services/dossiers/dossiers.service.ts index 9b27970db..11762ee9a 100644 --- a/apps/red-ui/src/app/services/dossiers/dossiers.service.ts +++ b/apps/red-ui/src/app/services/dossiers/dossiers.service.ts @@ -6,7 +6,6 @@ import { inject } from '@angular/core'; import { DossierStatsService } from './dossier-stats.service'; import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; -import { NGXLogger } from 'ngx-logger'; import { DashboardStatsService } from '../dossier-templates/dashboard-stats.service'; const CONFLICT_MSG = _('add-dossier-dialog.errors.dossier-already-exists'); @@ -18,7 +17,6 @@ export abstract class DossiersService extends EntitiesService protected readonly _dossierStatsService = inject(DossierStatsService); protected readonly _dashboardStatsService = inject(DashboardStatsService); protected readonly _toaster = inject(Toaster); - protected readonly _logger = inject(NGXLogger); protected abstract readonly _defaultModelPath: string; protected readonly _entityClass = Dossier; diff --git a/apps/red-ui/src/app/services/entity-services/dossier-states.service.ts b/apps/red-ui/src/app/services/entity-services/dossier-states.service.ts index ac986008d..25a3ee518 100644 --- a/apps/red-ui/src/app/services/entity-services/dossier-states.service.ts +++ b/apps/red-ui/src/app/services/entity-services/dossier-states.service.ts @@ -52,10 +52,9 @@ export class DossierStatesService extends EntitiesService { + async deleteState(dossierState: IDossierState, replaceDossierStatusId?: string) { const queryParams = replaceDossierStatusId ? [{ key: 'replaceDossierStatusId', value: replaceDossierStatusId }] : null; - return super - .delete(dossierState.dossierStatusId, this._defaultModelPath, queryParams) - .pipe(switchMap(() => this.loadAllForTemplate(dossierState.dossierTemplateId))); + await firstValueFrom(super.delete(dossierState.dossierStatusId, this._defaultModelPath, queryParams)); + return firstValueFrom(this.loadAllForTemplate(dossierState.dossierTemplateId)); } } diff --git a/apps/red-ui/src/app/services/entity-services/trash.service.ts b/apps/red-ui/src/app/services/entity-services/trash.service.ts index 5a00e378f..d089ca147 100644 --- a/apps/red-ui/src/app/services/entity-services/trash.service.ts +++ b/apps/red-ui/src/app/services/entity-services/trash.service.ts @@ -70,7 +70,7 @@ export class TrashService extends EntitiesService { dossier, this._systemPreferencesService.values.softDeleteCleanupTime, this._permissionsService.canRestoreDossier(dossier), - this._permissionsService.canHardDeleteDossier(dossier), + this._permissionsService.canDeleteDossier(dossier), this._userService.getName(dossier.ownerId), ), ), diff --git a/apps/red-ui/src/app/services/files/files.service.ts b/apps/red-ui/src/app/services/files/files.service.ts index 69ee46922..e4abe53db 100644 --- a/apps/red-ui/src/app/services/files/files.service.ts +++ b/apps/red-ui/src/app/services/files/files.service.ts @@ -1,13 +1,15 @@ import { Injectable } from '@angular/core'; -import { EntitiesService, List, mapEach, RequiredParam, Validate } from '@iqser/common-ui'; +import { EntitiesService, isArray, List, mapEach, QueryParam, RequiredParam, Validate } from '@iqser/common-ui'; import { File, IFile } from '@red/domain'; -import { Observable } from 'rxjs'; +import { firstValueFrom, Observable } from 'rxjs'; import { UserService } from '@users/user.service'; import { FilesMapService } from './files-map.service'; import { map, switchMap, tap } from 'rxjs/operators'; import { DossierStatsService } from '../dossiers/dossier-stats.service'; import { NGXLogger } from 'ngx-logger'; +const asList = (value: T | List): List => (isArray(value) ? value : [value]); + @Injectable({ providedIn: 'root', }) @@ -44,46 +46,47 @@ export class FilesService extends EntitiesService { } @Validate() - setAssignee(@RequiredParam() files: List, @RequiredParam() dossierId: string, assigneeId: string) { - const url = `${this._defaultModelPath}/set-assignee/${dossierId}/bulk`; - const fileIds = files.map(f => f.id); - return this._post(fileIds, url, [{ key: 'assigneeId', value: assigneeId }]).pipe(switchMap(() => this.loadAll(dossierId))); + async setAssignee(@RequiredParam() files: File | List, assigneeId: string) { + const _files = asList(files); + const url = `${this._defaultModelPath}/set-assignee/${_files[0].dossierId}/bulk`; + return this.#makePost(_files, url, [{ key: 'assigneeId', value: assigneeId }]); } @Validate() - setToNewFor(@RequiredParam() files: List, @RequiredParam() dossierId: string) { - const url = `${this._defaultModelPath}/new/${dossierId}/bulk`; - const fileIds = files.map(f => f.id); - return this._post(fileIds, url).pipe(switchMap(() => this.loadAll(dossierId))); + async setToNew(@RequiredParam() files: File | List) { + const _files = asList(files); + return this.#makePost(_files, `${this._defaultModelPath}/new/${_files[0].dossierId}/bulk`); } @Validate() - setUnderApprovalFor(@RequiredParam() files: List, @RequiredParam() dossierId: string, assigneeId: string) { - const url = `${this._defaultModelPath}/under-approval/${dossierId}/bulk`; - const fileIds = files.map(f => f.id); - return this._post(fileIds, url, [{ key: 'assigneeId', value: assigneeId }]).pipe(switchMap(() => this.loadAll(dossierId))); + async setUnderApproval(@RequiredParam() files: File | List, assigneeId: string) { + const _files = asList(files); + const url = `${this._defaultModelPath}/under-approval/${_files[0].dossierId}/bulk`; + return this.#makePost(_files, url, [{ key: 'assigneeId', value: assigneeId }]); } @Validate() - setReviewerFor(@RequiredParam() files: List, @RequiredParam() dossierId: string, assigneeId: string) { - const url = `${this._defaultModelPath}/under-review/${dossierId}/bulk`; - const fileIds = files.map(f => f.id); - return this._post(fileIds, url, [{ key: 'assigneeId', value: assigneeId }]).pipe(switchMap(() => this.loadAll(dossierId))); + async setReviewer(@RequiredParam() files: File | List, assigneeId: string) { + const _files = asList(files); + const url = `${this._defaultModelPath}/under-review/${_files[0].dossierId}/bulk`; + return this.#makePost(_files, url, [{ key: 'assigneeId', value: assigneeId }]); } @Validate() - setApprovedFor(@RequiredParam() files: List, @RequiredParam() dossierId: string) { - const fileIds = files.map(f => f.id); - return this._post(fileIds, `${this._defaultModelPath}/approved/${dossierId}/bulk`).pipe( - switchMap(() => this.loadAll(dossierId)), - ); + async setApproved(@RequiredParam() files: File | List) { + const _files = asList(files); + return this.#makePost(_files, `${this._defaultModelPath}/approved/${_files[0].dossierId}/bulk`); } @Validate() - setUnderReviewFor(@RequiredParam() files: List, @RequiredParam() dossierId: string) { + async setUnderReviewFor(@RequiredParam() files: File | List) { + const _files = asList(files); + return this.#makePost(_files, `${this._defaultModelPath}/under-review/${_files[0].dossierId}/bulk`); + } + + async #makePost(files: List, url: string, queryParams?: List) { const fileIds = files.map(f => f.id); - return this._post(fileIds, `${this._defaultModelPath}/under-review/${dossierId}/bulk`).pipe( - switchMap(() => this.loadAll(dossierId)), - ); + await firstValueFrom(this._post(fileIds, url, queryParams)); + return firstValueFrom(this.loadAll(files[0].dossierId)); } } diff --git a/apps/red-ui/src/app/services/license.service.ts b/apps/red-ui/src/app/services/license.service.ts index aa66c4dd9..05dfb64d9 100644 --- a/apps/red-ui/src/app/services/license.service.ts +++ b/apps/red-ui/src/app/services/license.service.ts @@ -51,20 +51,16 @@ export class LicenseService extends GenericService { activeLicenseId: string; totalLicensedNumberOfPages = 0; currentInfo: ILicenseReport = {}; - totalInfo: ILicenseReport = {}; - unlicensedInfo: ILicenseReport = {}; + annualInfo: ILicenseReport = {}; + unlicensedPages = 0; + analyzedPagesInCurrentLicensingPeriod = 0; protected readonly _defaultModelPath = 'report'; readonly #licenseData$ = new BehaviorSubject(undefined); readonly #selectedLicense$ = new BehaviorSubject(undefined); constructor(private readonly _logger: NGXLogger) { super(); - this.selectedLicense$ = this.#selectedLicense$.pipe( - filter(license => !!license), - tap(license => { - this.totalLicensedNumberOfPages = this.getTotalLicensedNumberOfPages(license); - }), - ); + this.selectedLicense$ = this.#selectedLicense$.pipe(filter(license => !!license)); this.licenseData$ = this.#licenseData$.pipe( filter(licenses => !!licenses), tap(data => (this.activeLicenseId = data.activeLicense)), @@ -95,6 +91,8 @@ export class LicenseService extends GenericService { } async loadLicenseData(license: ILicense = this.selectedLicense) { + this.totalLicensedNumberOfPages = this.getTotalLicensedNumberOfPages(license); + const startDate = dayjs(license.validFrom); const endDate = dayjs(license.validUntil); @@ -102,16 +100,23 @@ export class LicenseService extends GenericService { startDate: startDate.toDate(), endDate: endDate.toDate(), }; - const reports: Promise[] = [this.getReport(currentConfig), this.getReport({})]; - if (endDate.isBefore(dayjs())) { - const unlicensedConfig = { - startDate: endDate.toDate(), - }; - reports.push(this.getReport(unlicensedConfig)); + const thisYearConfig = { + startDate: `${startDate.year()}-01-01T00:00:00.000Z`, + endDate: `${startDate.year()}-12-31T00:00:00.000Z`, + }; + const configs = [currentConfig, thisYearConfig]; + const reports = configs.map(config => this.getReport(config)); + + [this.currentInfo, this.annualInfo] = await Promise.all(reports); + + if (this.currentInfo.numberOfAnalyzedPages > this.totalLicensedNumberOfPages) { + this.unlicensedPages = this.currentInfo.numberOfAnalyzedPages - this.totalLicensedNumberOfPages; + this.analyzedPagesInCurrentLicensingPeriod = this.totalLicensedNumberOfPages; + } else { + this.unlicensedPages = 0; + this.analyzedPagesInCurrentLicensingPeriod = this.currentInfo.numberOfAnalyzedPages; } - - [this.currentInfo, this.totalInfo, this.unlicensedInfo] = await Promise.all(reports); } getTotalLicensedNumberOfPages(license: ILicense) { diff --git a/apps/red-ui/src/app/services/notifications.service.ts b/apps/red-ui/src/app/services/notifications.service.ts index 399cc5734..4d353ed38 100644 --- a/apps/red-ui/src/app/services/notifications.service.ts +++ b/apps/red-ui/src/app/services/notifications.service.ts @@ -1,7 +1,7 @@ -import { Inject, Injectable } from '@angular/core'; +import { Inject, Injectable, OnDestroy } from '@angular/core'; import { BASE_HREF, EntitiesService, getConfig, List, mapEach, QueryParam, RequiredParam, Validate } from '@iqser/common-ui'; import { TranslateService } from '@ngx-translate/core'; -import { EMPTY, firstValueFrom, iif, Observable, of, timer } from 'rxjs'; +import { EMPTY, firstValueFrom, iif, merge, Observable, of, Subscription, timer } from 'rxjs'; import { AppConfig, Dossier, INotification, Notification, NotificationTypes } from '@red/domain'; import { map, switchMap, tap } from 'rxjs/operators'; import { notificationsTranslations } from '@translations/notifications-translations'; @@ -20,11 +20,12 @@ const NOTIFICATIONS_THRESHOLD = 1000; @Injectable({ providedIn: 'root', }) -export class NotificationsService extends EntitiesService { +export class NotificationsService extends EntitiesService implements OnDestroy { protected readonly _defaultModelPath = 'notification'; protected readonly _entityClass = Notification; readonly #config = getConfig(); + readonly #subscription = new Subscription(); constructor( @Inject(BASE_HREF) private readonly _baseHref: string, @@ -34,20 +35,15 @@ export class NotificationsService extends EntitiesService this.loadAll())).subscribe(); + if (!this._dossiersCacheService.empty) { + this._dossiersCacheService.load().then(async () => await firstValueFrom(this.loadAll())); + } - timer(0, CHANGED_CHECK_INTERVAL) - .pipe( - switchMap(() => (this._dossiersCacheService.empty ? this._dossiersCacheService.load() : of(null))), - switchMap(() => this.#loadNotificationsIfChanged()), - ) - .subscribe(); + this.#subscription.add(this.#initTimerAndChanges().subscribe()); + } - // Rebuild notifications when cached dossiers are updated - this._dossiersCacheService.changed$.subscribe(() => { - this.setEntities(this.all.map(e => this._new(e))); - }); + ngOnDestroy() { + this.#subscription.unsubscribe(); } @Validate() @@ -71,6 +67,17 @@ export class NotificationsService extends EntitiesService (this._dossiersCacheService.empty ? this._dossiersCacheService.load() : of(null))), + switchMap(() => this.#loadNotificationsIfChanged()), + ); + + // Rebuild notifications when cached dossiers are updated + const changed$ = this._dossiersCacheService.changed$.pipe(tap(() => this.setEntities(this.all.map(e => this._new(e))))); + return merge(timer$, changed$); + } + #filterNotifications(notifications: Notification[]): Notification[] { const todayDate = dayjs(new Date()); diff --git a/apps/red-ui/src/app/services/permissions.service.ts b/apps/red-ui/src/app/services/permissions.service.ts index 725837e62..2f915f76f 100644 --- a/apps/red-ui/src/app/services/permissions.service.ts +++ b/apps/red-ui/src/app/services/permissions.service.ts @@ -266,19 +266,12 @@ export class PermissionsService { return this.isApprover(dossier) && files.reduce((prev, file) => prev && !file.isInitialProcessing, true); } - canSoftDeleteDossier(dossier: IDossier): boolean { - return ( - this._iqserPermissionsService.has(ROLES.dossiers.delete) && - (this.isOwner(dossier) || (this.isManager() && this.isDossierMember(dossier))) - ); - } - - canHardDeleteDossier(dossier: IDossier): boolean { + canDeleteDossier(dossier: IDossier): boolean { return this._iqserPermissionsService.has(ROLES.dossiers.delete) && this.isOwner(dossier); } canRestoreDossier(dossier: IDossier): boolean { - return this._iqserPermissionsService.has(ROLES.dossiers.delete) && (this.isOwner(dossier) || this.isDossierMember(dossier)); + return this._iqserPermissionsService.has(ROLES.dossiers.delete) && this.isOwner(dossier); } canCreateDossier(dossierTemplate: DossierTemplate | DashboardStats): boolean { @@ -349,6 +342,10 @@ export class PermissionsService { ); } + canRotatePage(file: File) { + return this.isFileAssignee(file) && this._iqserPermissionsService.has(ROLES.files.rotatePage); + } + canDownloadRedactedFile() { return this._iqserPermissionsService.has(ROLES.files.processDownload); } diff --git a/apps/red-ui/src/app/users/user.service.ts b/apps/red-ui/src/app/users/user.service.ts index 337528063..55bbec07e 100644 --- a/apps/red-ui/src/app/users/user.service.ts +++ b/apps/red-ui/src/app/users/user.service.ts @@ -15,10 +15,7 @@ export class UserService extends IqserUserService { const currentUser = await super.loadCurrentUser(); this._permissionsService.add({ - [ROLES.any]: (permission, all) => { - console.log('all roles: ', Object.keys(all)); - return Object.keys(all).some(key => this._permissionsFilter(key)); - }, + [ROLES.any]: (permission, all) => Object.keys(all).some(key => this._permissionsFilter(key)), }); return currentUser; diff --git a/apps/red-ui/src/app/utils/index.ts b/apps/red-ui/src/app/utils/index.ts index 4dd4d4f26..6e79bdfaf 100644 --- a/apps/red-ui/src/app/utils/index.ts +++ b/apps/red-ui/src/app/utils/index.ts @@ -10,3 +10,4 @@ export * from './functions'; export * from './global-error-handler.service'; export * from './missing-translations-handler'; export * from './page-stamper'; +export * from './router-links'; diff --git a/apps/red-ui/src/app/utils/router-links.ts b/apps/red-ui/src/app/utils/router-links.ts new file mode 100644 index 000000000..5ca8f9dff --- /dev/null +++ b/apps/red-ui/src/app/utils/router-links.ts @@ -0,0 +1,7 @@ +import { ARCHIVE_ROUTE, DOSSIERS_ROUTE } from '@red/domain'; + +export function getDossierRouterLink(dossierTemplateId: string, dossierId: string, isArchived: boolean): string { + const routerPath = isArchived ? ARCHIVE_ROUTE : DOSSIERS_ROUTE; + const dossiersListRouterLink = `/main/${dossierTemplateId}/${routerPath}`; + return `${dossiersListRouterLink}/${dossierId}`; +} diff --git a/apps/red-ui/src/app/utils/tenant-context.ts b/apps/red-ui/src/app/utils/tenant-context.ts new file mode 100644 index 000000000..e4efbcb85 --- /dev/null +++ b/apps/red-ui/src/app/utils/tenant-context.ts @@ -0,0 +1,8 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ providedIn: 'root' }) +export class TenantContext { + getTenant(): string { + return 'redaction'; + } +} diff --git a/apps/red-ui/src/app/utils/tenant-id-interceptor.ts b/apps/red-ui/src/app/utils/tenant-id-interceptor.ts index a53ed2163..4deddeca1 100644 --- a/apps/red-ui/src/app/utils/tenant-id-interceptor.ts +++ b/apps/red-ui/src/app/utils/tenant-id-interceptor.ts @@ -1,12 +1,15 @@ import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; +import { TenantContext } from '@utils/tenant-context'; @Injectable() export class TenantIdInterceptor implements HttpInterceptor { + protected readonly _tenantContext = inject(TenantContext); + intercept(req: HttpRequest, next: HttpHandler): Observable> { const updatedRequest = req.clone({ - setHeaders: { 'X-TENANT-ID': 'redaction' }, + setHeaders: { 'X-TENANT-ID': this._tenantContext.getTenant() }, }); return next.handle(updatedRequest); } diff --git a/apps/red-ui/src/assets/i18n/redact/de.json b/apps/red-ui/src/assets/i18n/redact/de.json index c87a21c93..ba2c6f114 100644 --- a/apps/red-ui/src/assets/i18n/redact/de.json +++ b/apps/red-ui/src/assets/i18n/redact/de.json @@ -1633,7 +1633,6 @@ "table-header": "{length} {length, plural, one{Begrรผndung} other{Begrรผndung}}" }, "license-info-screen": { - "analyzed-pages": "Analysierte Seiten", "backend-version": "Backend-Version der Anwendung", "chart": { "cumulative": "Seiten insgesamt", @@ -1666,6 +1665,7 @@ "inactive": "" }, "total-analyzed": "Seit {date} insgesamt analysierte Seiten", + "total-ocr-analyzed": "", "unlicensed-analyzed": "รœber Lizenz hinaus analysierte Seiten", "usage-details": "Nutzungsdetails" }, diff --git a/apps/red-ui/src/assets/i18n/redact/en.json b/apps/red-ui/src/assets/i18n/redact/en.json index 8e39b3bd2..a6950c81d 100644 --- a/apps/red-ui/src/assets/i18n/redact/en.json +++ b/apps/red-ui/src/assets/i18n/redact/en.json @@ -1633,7 +1633,6 @@ "table-header": "{length} {length, plural, one{justification} other{justifications}}" }, "license-info-screen": { - "analyzed-pages": "Analyzed pages", "backend-version": "Backend Application Version", "chart": { "cumulative": "Cumulative Pages", @@ -1643,7 +1642,7 @@ }, "copyright-claim-text": "Copyright ยฉ 2020 - {currentYear} knecon AG (powered by IQSER)", "copyright-claim-title": "Copyright Claim", - "current-analyzed": "Analyzed Pages in Current Licensing Period", + "current-analyzed": "Analyzed Pages in Licensing Period", "custom-app-title": "Custom Application Title", "email-report": "Email Report", "email": { @@ -1656,16 +1655,17 @@ "end-user-license-text": "The use of this product is subject to the terms of the Redaction End User Agreement, unless otherwise specified therein.", "end-user-license-title": "End User License Agreement", "license-title": "License Title", - "licensed-page-count": "Number of licensed pages", + "licensed-page-count": "Licensed pages", "licensed-to": "Licensed to", "licensing-details": "Licensing Details", "licensing-period": "Licensing Period", - "ocr-analyzed-pages": "OCR Analyzed Pages", + "ocr-analyzed-pages": "OCR Processed Pages in Licensing Period", "status": { "active": "Active", "inactive": "Inactive" }, "total-analyzed": "Total Analyzed Pages Since {date}", + "total-ocr-analyzed": "Total OCR Processed Pages Since {date}", "unlicensed-analyzed": "Unlicensed Analyzed Pages", "usage-details": "Usage Details" }, diff --git a/apps/red-ui/src/assets/i18n/scm/de.json b/apps/red-ui/src/assets/i18n/scm/de.json index 29b71265d..d6c71b494 100644 --- a/apps/red-ui/src/assets/i18n/scm/de.json +++ b/apps/red-ui/src/assets/i18n/scm/de.json @@ -1633,7 +1633,6 @@ "table-header": "{length} {length, plural, one{Begrรผndung} other{Begrรผndung}}" }, "license-info-screen": { - "analyzed-pages": "Analysierte Seiten", "backend-version": "Backend-Version der Anwendung", "chart": { "cumulative": "Seiten insgesamt", @@ -1666,6 +1665,7 @@ "inactive": "" }, "total-analyzed": "Seit {date} insgesamt analysierte Seiten", + "total-ocr-analyzed": "", "unlicensed-analyzed": "รœber Lizenz hinaus analysierte Seiten", "usage-details": "Nutzungsdetails" }, diff --git a/apps/red-ui/src/assets/i18n/scm/en.json b/apps/red-ui/src/assets/i18n/scm/en.json index 4ce547ea5..59f92efd4 100644 --- a/apps/red-ui/src/assets/i18n/scm/en.json +++ b/apps/red-ui/src/assets/i18n/scm/en.json @@ -1633,7 +1633,6 @@ "table-header": "{length} {length, plural, one{justification} other{justifications}}" }, "license-info-screen": { - "analyzed-pages": "Analyzed pages", "backend-version": "Backend Application Version", "chart": { "cumulative": "Cumulative Pages", @@ -1643,7 +1642,7 @@ }, "copyright-claim-text": "Copyright ยฉ 2020 - {currentYear} knecon AG (powered by IQSER)", "copyright-claim-title": "Copyright Claim", - "current-analyzed": "Analyzed Pages in Current Licensing Period", + "current-analyzed": "Analyzed Pages in Licensing Period", "custom-app-title": "Custom Application Title", "email-report": "Email Report", "email": { @@ -1656,16 +1655,17 @@ "end-user-license-text": "The use of this product is subject to the terms of the Component End User Agreement, unless otherwise specified therein.", "end-user-license-title": "End User License Agreement", "license-title": "License Title", - "licensed-page-count": "Number of licensed pages", + "licensed-page-count": "Licensed pages", "licensed-to": "Licensed to", "licensing-details": "Licensing Details", "licensing-period": "Licensing Period", - "ocr-analyzed-pages": "OCR Analyzed Pages", + "ocr-analyzed-pages": "OCR Processed Pages in Licensing Period", "status": { "active": "Active", "inactive": "Inactive" }, "total-analyzed": "Total Analyzed Pages Since {date}", + "total-ocr-analyzed": "Total OCR Processed Pages Since {date}", "unlicensed-analyzed": "Unlicensed Analyzed Pages", "usage-details": "Usage Details" }, diff --git a/apps/red-ui/src/assets/pdftron/blank.pdf b/apps/red-ui/src/assets/pdftron/blank.pdf index 757bb1f36..03dc3db0a 100644 Binary files a/apps/red-ui/src/assets/pdftron/blank.pdf and b/apps/red-ui/src/assets/pdftron/blank.pdf differ diff --git a/jest.config.ts b/jest.config.ts new file mode 100644 index 000000000..9ec4b7265 --- /dev/null +++ b/jest.config.ts @@ -0,0 +1,5 @@ +import { getJestProjects } from '@nrwl/jest'; + +export default { + projects: getJestProjects(), +}; diff --git a/jest.preset.js b/jest.preset.js new file mode 100644 index 000000000..e6c8ebea0 --- /dev/null +++ b/jest.preset.js @@ -0,0 +1,3 @@ +const nxPreset = require('@nrwl/jest/preset').default; + +module.exports = { ...nxPreset }; diff --git a/libs/common-ui b/libs/common-ui index d09078e44..ba21886e2 160000 --- a/libs/common-ui +++ b/libs/common-ui @@ -1 +1 @@ -Subproject commit d09078e44c8c294c78c44080d0bb8403d0ec6c34 +Subproject commit ba21886e27187490c526816c4692c541384e1f5f diff --git a/libs/red-domain/src/lib/permissions/permissions-mapping.model.ts b/libs/red-domain/src/lib/permissions/permissions-mapping.model.ts index 2975be340..8ae4b31e3 100644 --- a/libs/red-domain/src/lib/permissions/permissions-mapping.model.ts +++ b/libs/red-domain/src/lib/permissions/permissions-mapping.model.ts @@ -8,14 +8,16 @@ export class PermissionsMapping extends Entity implements I readonly targetPermission: IPermission; readonly sort: number; - readonly #currentValues: string[] = []; + readonly #checked: string[]; + readonly #disabled: string[]; constructor(permissionsMapping: IPermissionsMapping) { super(permissionsMapping); this.mappedPermissions = permissionsMapping.mappedPermissions; this.targetPermission = permissionsMapping.targetPermission; this.sort = this.targetPermission.sort; - this.#currentValues = this.mappedPermissions.map(permission => permission.name); + this.#checked = this.mappedPermissions.map(permission => permission.name); + this.#disabled = this.mappedPermissions.filter(permission => !permission.changeable).map(permission => permission.name); } get id(): string { @@ -26,7 +28,11 @@ export class PermissionsMapping extends Entity implements I return this.targetPermission.name; } - getValue(permissionName: string): boolean { - return this.#currentValues.includes(permissionName); + isChecked(permissionName: string): boolean { + return this.#checked.includes(permissionName); + } + + isDisabled(permissionName: string): boolean { + return this.#disabled.includes(permissionName); } } diff --git a/libs/red-domain/src/lib/permissions/permissions.ts b/libs/red-domain/src/lib/permissions/permissions.ts index 8efa79e79..4757f7994 100644 --- a/libs/red-domain/src/lib/permissions/permissions.ts +++ b/libs/red-domain/src/lib/permissions/permissions.ts @@ -2,4 +2,5 @@ export interface IPermission { mask: number; name: string; sort: number; + changeable: boolean; } diff --git a/libs/red-domain/src/lib/reports/license-report.request.ts b/libs/red-domain/src/lib/reports/license-report.request.ts index f12577f42..796fb114a 100644 --- a/libs/red-domain/src/lib/reports/license-report.request.ts +++ b/libs/red-domain/src/lib/reports/license-report.request.ts @@ -2,7 +2,7 @@ import { List } from '@iqser/common-ui'; export interface ILicenseReportRequest { dossierIds?: List; - endDate?: Date; + endDate?: Date | string; requestId?: string; - startDate?: Date; + startDate?: Date | string; } diff --git a/nx.json b/nx.json index 1cc7bfd50..d70eb639c 100644 --- a/nx.json +++ b/nx.json @@ -14,7 +14,8 @@ }, "cli": { "analytics": false, - "packageManager": "yarn" + "packageManager": "yarn", + "schematicCollections": ["@angular-eslint/schematics"] }, "defaultProject": "red-ui", "generators": { @@ -33,6 +34,12 @@ "build": { "dependsOn": ["^build"], "inputs": ["production", "^production"] + }, + "lint": { + "inputs": ["default", "{workspaceRoot}/.eslintrc.json", "{workspaceRoot}/.eslintignore"] + }, + "test": { + "inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"] } }, "namedInputs": { diff --git a/package.json b/package.json index 9b2d412fb..ee27a6377 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redaction", - "version": "3.983.0", + "version": "4.30.0", "private": true, "license": "MIT", "scripts": { @@ -15,7 +15,8 @@ "migrate": "nx migrate --run-migrations", "workspace-generator": "nx workspace-generator", "analyze": "ng build --stats-json && webpack-bundle-analyzer dist/apps/red-ui/stats.json", - "prepare": "husky install" + "prepare": "husky install", + "lint": "ng lint" }, "lint-staged": { "*": "prettier --ignore-unknown --write", @@ -35,11 +36,11 @@ "@angular/service-worker": "15.1.2", "@biesbjerg/ngx-translate-extract-marker": "^1.0.0", "@materia-ui/ngx-monaco-editor": "^6.0.0", - "@messageformat/core": "^3.0.1", + "@messageformat/core": "^3.1.0", "@ngx-translate/core": "^14.0.0", "@ngx-translate/http-loader": "^7.0.0", "@nrwl/angular": "15.6.3", - "@pdftron/webviewer": "8.11.0", + "@pdftron/webviewer": "8.12.0", "@swimlane/ngx-charts": "^20.0.1", "dayjs": "^1.11.5", "file-saver": "^2.0.5", @@ -47,79 +48,67 @@ "keycloak-angular": "13.0.0", "keycloak-js": "20.0.3", "lodash-es": "^4.17.21", - "monaco-editor": "^0.34.0", - "ngx-color-picker": "^13.0.0", + "monaco-editor": "^0.36.1", + "ngx-color-picker": "^14.0.0", "ngx-logger": "^5.0.11", - "ngx-toastr": "^16.0.2", + "ngx-toastr": "^16.1.0", "ngx-translate-messageformat-compiler": "^6.2.0", "object-hash": "^3.0.0", - "papaparse": "^5.3.2", + "papaparse": "^5.4.0", "rxjs": "7.8.0", - "sass": "^1.58.0", - "scroll-into-view-if-needed": "^3.0.4", + "sass": "^1.59.2", + "scroll-into-view-if-needed": "^3.0.6", "streamsaver": "^2.0.5", "tslib": "^2.5.0", "zone.js": "0.12.0" }, "devDependencies": { "@angular-devkit/build-angular": "15.1.4", - "@angular-eslint/eslint-plugin": "15.2.0", - "@angular-eslint/eslint-plugin-template": "15.2.0", - "@angular-eslint/template-parser": "15.2.0", + "@angular-eslint/builder": "15.2.1", + "@angular-eslint/eslint-plugin": "15.2.1", + "@angular-eslint/eslint-plugin-template": "15.2.1", + "@angular-eslint/schematics": "15.2.1", + "@angular-eslint/template-parser": "15.2.1", "@angular/cli": "~15.1.0", "@angular/compiler-cli": "15.1.2", "@angular/language-service": "15.1.2", "@bartholomej/ngx-translate-extract": "^8.0.2", "@nrwl/cli": "15.6.3", "@nrwl/eslint-plugin-nx": "15.6.3", + "@nrwl/jest": "^15.8.7", "@nrwl/linter": "15.6.3", "@nrwl/workspace": "15.6.3", "@types/jest": "^29.4.0", "@types/lodash-es": "^4.17.6", - "@types/node": "18.11.18", - "@typescript-eslint/eslint-plugin": "5.50.0", - "@typescript-eslint/parser": "5.50.0", - "axios": "^1.3.1", + "@types/node": "18.15.1", + "@typescript-eslint/eslint-plugin": "5.48.2", + "@typescript-eslint/parser": "5.48.2", + "axios": "^1.3.4", "dotenv": "16.0.3", - "eslint": "8.33.0", - "eslint-config-prettier": "8.6.0", + "eslint": "^8.33.0", + "eslint-config-prettier": "8.7.0", "eslint-plugin-prettier": "^4.0.0", - "google-translate-api-browser": "^3.0.0", + "eslint-plugin-rxjs": "^5.0.2", + "google-translate-api-browser": "^4.0.6", "husky": "^8.0.3", - "jest": "^28.1.3", - "jest-environment-jsdom": "^29.4.1", - "jest-extended": "^3.2.3", - "jest-preset-angular": "12.2.6", - "lint-staged": "^13.1.0", + "jest": "^29.5.0", + "jest-environment-jsdom": "^29.5.0", + "jest-extended": "^3.2.4", + "jest-preset-angular": "13.0.0", + "lint-staged": "^13.2.0", "nx": "15.6.3", "postcss": "^8.4.21", "postcss-import": "15.1.0", "postcss-preset-env": "~8.0.1", "postcss-url": "10.1.3", - "prettier": "2.8.3", - "sonarqube-scanner": "^3.0.0", + "prettier": "2.8.4", + "sonarqube-scanner": "^3.0.1", "superagent": "^8.0.9", "superagent-promise": "^1.1.0", "ts-node": "10.9.1", "typescript": "4.9.5", - "webpack": "^5.75.0", - "webpack-bundle-analyzer": "^4.7.0", + "webpack": "^5.76.1", + "webpack-bundle-analyzer": "^4.8.0", "xliff": "^6.1.0" - }, - "jest": { - "preset": "jest-preset-angular", - "setupFilesAfterEnv": [ - "jest-preset-angular/setup-jest.js", - "jest-extended/all" - ], - "testPathIgnorePatterns": [ - "/node_modules/", - "/dist/" - ], - "globals": { - "ts-jest": { - "tsconfig": "/tsconfig.spec.json" - } - } } } diff --git a/paligo-theme.tar.gz b/paligo-theme.tar.gz index 8120c5543..8f56d3561 100644 Binary files a/paligo-theme.tar.gz and b/paligo-theme.tar.gz differ diff --git a/tsconfig.spec.json b/tsconfig.spec.json index d9599d486..79f2a84c5 100644 --- a/tsconfig.spec.json +++ b/tsconfig.spec.json @@ -2,8 +2,10 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "./dist/out-tsc/spec", + "module": "commonjs", "types": ["jest", "node"], "esModuleInterop": true }, - "include": ["./libs/**/*.spec.ts", "./libs/**/*.d.ts"] + "files": ["src/test-setup.ts"], + "include": ["jest.config.ts", "./libs/**/*.spec.ts", "./libs/**/*.d.ts"] } diff --git a/yarn.lock b/yarn.lock index 6feef5fb4..50076bc01 100644 --- a/yarn.lock +++ b/yarn.lock @@ -115,46 +115,62 @@ ora "5.4.1" rxjs "6.6.7" -"@angular-eslint/bundled-angular-compiler@15.2.0": - version "15.2.0" - resolved "https://registry.yarnpkg.com/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-15.2.0.tgz#506f94c96039346f33ba92222062edbf3943543e" - integrity sha512-a0bfXxYyGoWJHrVQ4QER0HdRgselcTtJeyqiFPAxID2ZxF0IBGKLNTtugUTXekEmiLev8yGLX9TqAtthN57fEg== +"@angular-eslint/builder@15.2.1": + version "15.2.1" + resolved "https://registry.yarnpkg.com/@angular-eslint/builder/-/builder-15.2.1.tgz#ce8c65e3b671897db75ad90b41ef4cd6efe626f0" + integrity sha512-7x2DANebLRl997Mj4DhZrnz5+vnSjavGGveJ0mBuU7CEsL0ZYLftdRqL0e0HtU3ksseS7xpchD6OM08nkNgySw== -"@angular-eslint/eslint-plugin-template@15.2.0": - version "15.2.0" - resolved "https://registry.yarnpkg.com/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-15.2.0.tgz#a7348bdddcdab51031439b9f9e09ea9bb9236484" - integrity sha512-aL3czf5Jpv29rKN3UG20tQepX1+V0d6xc0g+1l0zPHZJYjVd6Oy0nIxWiGfl4yanaXiVpmxiV4vUcLlqqaFwbw== +"@angular-eslint/bundled-angular-compiler@15.2.1": + version "15.2.1" + resolved "https://registry.yarnpkg.com/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-15.2.1.tgz#7c77a4a19942868d844372b5b3b562c0d630de1e" + integrity sha512-LO7Am8eVCr7oh6a0VmKSL7K03CnQEQhFO7Wt/YtbfYOxVjrbwmYLwJn+wZPOT7A02t/BttOD/WXuDrOWtSMQ/Q== + +"@angular-eslint/eslint-plugin-template@15.2.1": + version "15.2.1" + resolved "https://registry.yarnpkg.com/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-15.2.1.tgz#af39f66e9d971d7f7635add9cf1a396f1766fd64" + integrity sha512-IeiSLk6YxapFdH2z5o/O3R7VwtBd2T6fWmhLFPwDYMDknrwegnOjwswCdBplOccpUp0wqlCeGUx7LTsuzwaz7w== dependencies: - "@angular-eslint/bundled-angular-compiler" "15.2.0" - "@angular-eslint/utils" "15.2.0" - "@typescript-eslint/type-utils" "5.48.1" - "@typescript-eslint/utils" "5.48.1" + "@angular-eslint/bundled-angular-compiler" "15.2.1" + "@angular-eslint/utils" "15.2.1" + "@typescript-eslint/type-utils" "5.48.2" + "@typescript-eslint/utils" "5.48.2" aria-query "5.1.3" axobject-query "3.1.1" -"@angular-eslint/eslint-plugin@15.2.0": - version "15.2.0" - resolved "https://registry.yarnpkg.com/@angular-eslint/eslint-plugin/-/eslint-plugin-15.2.0.tgz#37771dfc5278ec8cbb01aa0d38d362ef52e60be4" - integrity sha512-yJGbmSUU0B0MFJ48ktpkqqEK+zv5k9iwlZSqEHtiQMKvDelfluovnEusihel7uPRo1c1iVlbSgXfGpxpUCfocA== +"@angular-eslint/eslint-plugin@15.2.1": + version "15.2.1" + resolved "https://registry.yarnpkg.com/@angular-eslint/eslint-plugin/-/eslint-plugin-15.2.1.tgz#616e7aa1f3aea6203e887c20749b87d08bb95d3e" + integrity sha512-OM7b1kS4E4CkXjkaWN+lEzawh4VxY6l7FO1Cuk4s7iv3/YpZG3rJxIZBqnFLTixwrBuqw8y4FNBzF3eDgmFAUw== dependencies: - "@angular-eslint/utils" "15.2.0" - "@typescript-eslint/utils" "5.48.1" + "@angular-eslint/utils" "15.2.1" + "@typescript-eslint/utils" "5.48.2" -"@angular-eslint/template-parser@15.2.0": - version "15.2.0" - resolved "https://registry.yarnpkg.com/@angular-eslint/template-parser/-/template-parser-15.2.0.tgz#2b9b794f2f763725cf4d107c0fcb6e07c460ac4e" - integrity sha512-xnnxPfV/G0Ll3B0HGrF1ucsc/DHmNE6UhhmWxYPTERq0McbZGRiATa66hCoOZ/Rdylun4ogBfsRKAG8XxEvlvw== +"@angular-eslint/schematics@15.2.1": + version "15.2.1" + resolved "https://registry.yarnpkg.com/@angular-eslint/schematics/-/schematics-15.2.1.tgz#f562e1b8b0824ade1cfdc5bbabab26c50510a7f1" + integrity sha512-0ZfBCejHWIcgy3J5kFs9sS/jqi8i5AptxggOwFySOlCLJ+CzNrktjD4jff1Zy8K/VLzY0Ci0BSZXvgWfP0k9Rg== dependencies: - "@angular-eslint/bundled-angular-compiler" "15.2.0" + "@angular-eslint/eslint-plugin" "15.2.1" + "@angular-eslint/eslint-plugin-template" "15.2.1" + ignore "5.2.4" + strip-json-comments "3.1.1" + tmp "0.2.1" + +"@angular-eslint/template-parser@15.2.1": + version "15.2.1" + resolved "https://registry.yarnpkg.com/@angular-eslint/template-parser/-/template-parser-15.2.1.tgz#dbe4978afdcea81b9d5cac3d672c20de5821dc54" + integrity sha512-ViCi79gC2aKJecmYLkOT+QlT5WMRNXeYz0Dr9Pr8qXzIbY0oAWE7nOT5jkXwQ9oUk+ybtGCWHma5JVJWVJsIog== + dependencies: + "@angular-eslint/bundled-angular-compiler" "15.2.1" eslint-scope "^7.0.0" -"@angular-eslint/utils@15.2.0": - version "15.2.0" - resolved "https://registry.yarnpkg.com/@angular-eslint/utils/-/utils-15.2.0.tgz#3a0ecc9d9d6877fbdaa9d24208071a5cdbbe5945" - integrity sha512-qfTOKQ+aef/YER679/xN1E+FkZKMd0I73P6txUZAb9k2G1ACVktG+wOUIBfgjIlUVq9Q01AV91LGOWcd+rdEEA== +"@angular-eslint/utils@15.2.1": + version "15.2.1" + resolved "https://registry.yarnpkg.com/@angular-eslint/utils/-/utils-15.2.1.tgz#b286fddb1b8b43d96b91009237da7f74d9e64dda" + integrity sha512-++FneAJHxJqcSu0igVN6uOkSoHxlzgLoMBswuovYJy3UKwm33/T6WFku8++753Ca/JucIoR1gdUfO7SoSspMDg== dependencies: - "@angular-eslint/bundled-angular-compiler" "15.2.0" - "@typescript-eslint/utils" "5.48.1" + "@angular-eslint/bundled-angular-compiler" "15.2.1" + "@typescript-eslint/utils" "5.48.2" "@angular/animations@15.1.2": version "15.1.2" @@ -446,6 +462,16 @@ "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" +"@babel/generator@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.3.tgz#232359d0874b392df04045d72ce2fd9bb5045fce" + integrity sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA== + dependencies: + "@babel/types" "^7.21.3" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@7.18.6", "@babel/helper-annotate-as-pure@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" @@ -549,6 +575,14 @@ "@babel/template" "^7.18.10" "@babel/types" "^7.19.0" +"@babel/helper-function-name@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" + integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== + dependencies: + "@babel/template" "^7.20.7" + "@babel/types" "^7.21.0" + "@babel/helper-hoist-variables@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" @@ -728,6 +762,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.5.tgz#7f3c7335fe417665d929f34ae5dceae4c04015e8" integrity sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA== +"@babel/parser@^7.10.3", "@babel/parser@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.3.tgz#1d285d67a19162ff9daa358d4cb41d50c06220b3" + integrity sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ== + "@babel/parser@^7.19.3", "@babel/parser@^7.20.13", "@babel/parser@^7.20.7": version "7.20.13" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.13.tgz#ddf1eb5a813588d2fb1692b70c6fce75b945c088" @@ -969,6 +1008,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-jsx@^7.7.2": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0" + integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" @@ -1452,6 +1498,22 @@ "@babel/parser" "^7.18.10" "@babel/types" "^7.18.10" +"@babel/traverse@^7.10.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.3.tgz#4747c5e7903d224be71f90788b06798331896f67" + integrity sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.21.3" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.21.3" + "@babel/types" "^7.21.3" + debug "^4.1.0" + globals "^11.1.0" + "@babel/traverse@^7.16.0", "@babel/traverse@^7.19.3", "@babel/traverse@^7.20.10", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.13", "@babel/traverse@^7.20.7": version "7.20.13" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.13.tgz#817c1ba13d11accca89478bd5481b2d168d07473" @@ -1493,6 +1555,15 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" +"@babel/types@^7.10.3", "@babel/types@^7.21.0", "@babel/types@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.3.tgz#4865a5357ce40f64e3400b0f3b737dc6d4f64d05" + integrity sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg== + dependencies: + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" + to-fast-properties "^2.0.0" + "@babel/types@^7.19.3", "@babel/types@^7.20.7": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f" @@ -1935,14 +2006,26 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.16.9.tgz#4595a29c2930c5157aa1be0963abbbac989647c9" integrity sha512-C4ZX+YFIp6+lPrru3tpH6Gaapy8IBRHw/e7l63fzGDhn/EaiGpQgbIlT5paByyy+oMvRFQoxxyvC4LE0AjJMqQ== -"@eslint/eslintrc@^1.4.1": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.4.1.tgz#af58772019a2d271b7e2d4c23ff4ddcba3ccfb3e" - integrity sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA== +"@eslint-community/eslint-utils@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.2.0.tgz#a831e6e468b4b2b5ae42bf658bea015bf10bc518" + integrity sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.4.0.tgz#3e61c564fcd6b921cb789838631c5ee44df09403" + integrity sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ== + +"@eslint/eslintrc@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.1.tgz#7888fe7ec8f21bc26d646dbd2c11cd776e21192d" + integrity sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.4.0" + espree "^9.5.0" globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" @@ -1950,6 +2033,11 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" +"@eslint/js@8.36.0": + version "8.36.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.36.0.tgz#9837f768c03a1e4a30bd304a64fb8844f0e72efe" + integrity sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg== + "@gar/promisify@^1.1.3": version "1.1.3" resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" @@ -2002,38 +2090,49 @@ jest-util "^28.1.3" slash "^3.0.0" -"@jest/core@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-28.1.3.tgz#0ebf2bd39840f1233cd5f2d1e6fc8b71bd5a1ac7" - integrity sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA== +"@jest/console@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.5.0.tgz#593a6c5c0d3f75689835f1b3b4688c4f8544cb57" + integrity sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ== dependencies: - "@jest/console" "^28.1.3" - "@jest/reporters" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/types" "^29.5.0" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^29.5.0" + jest-util "^29.5.0" + slash "^3.0.0" + +"@jest/core@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.5.0.tgz#76674b96904484e8214614d17261cc491e5f1f03" + integrity sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ== + dependencies: + "@jest/console" "^29.5.0" + "@jest/reporters" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" ci-info "^3.2.0" exit "^0.1.2" graceful-fs "^4.2.9" - jest-changed-files "^28.1.3" - jest-config "^28.1.3" - jest-haste-map "^28.1.3" - jest-message-util "^28.1.3" - jest-regex-util "^28.0.2" - jest-resolve "^28.1.3" - jest-resolve-dependencies "^28.1.3" - jest-runner "^28.1.3" - jest-runtime "^28.1.3" - jest-snapshot "^28.1.3" - jest-util "^28.1.3" - jest-validate "^28.1.3" - jest-watcher "^28.1.3" + jest-changed-files "^29.5.0" + jest-config "^29.5.0" + jest-haste-map "^29.5.0" + jest-message-util "^29.5.0" + jest-regex-util "^29.4.3" + jest-resolve "^29.5.0" + jest-resolve-dependencies "^29.5.0" + jest-runner "^29.5.0" + jest-runtime "^29.5.0" + jest-snapshot "^29.5.0" + jest-util "^29.5.0" + jest-validate "^29.5.0" + jest-watcher "^29.5.0" micromatch "^4.0.4" - pretty-format "^28.1.3" - rimraf "^3.0.0" + pretty-format "^29.5.0" slash "^3.0.0" strip-ansi "^6.0.0" @@ -2047,15 +2146,15 @@ "@types/node" "*" jest-mock "^28.1.3" -"@jest/environment@^29.4.1": - version "29.4.1" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.4.1.tgz#52d232a85cdc995b407a940c89c86568f5a88ffe" - integrity sha512-pJ14dHGSQke7Q3mkL/UZR9ZtTOxqskZaC91NzamEH4dlKRt42W+maRBXiw/LWkdJe+P0f/zDR37+SPMplMRlPg== +"@jest/environment@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.5.0.tgz#9152d56317c1fdb1af389c46640ba74ef0bb4c65" + integrity sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ== dependencies: - "@jest/fake-timers" "^29.4.1" - "@jest/types" "^29.4.1" + "@jest/fake-timers" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" - jest-mock "^29.4.1" + jest-mock "^29.5.0" "@jest/expect-utils@^28.1.3": version "28.1.3" @@ -2071,6 +2170,13 @@ dependencies: jest-get-type "^29.2.0" +"@jest/expect-utils@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.5.0.tgz#f74fad6b6e20f924582dc8ecbf2cb800fe43a036" + integrity sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg== + dependencies: + jest-get-type "^29.4.3" + "@jest/expect@^28.1.3": version "28.1.3" resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-28.1.3.tgz#9ac57e1d4491baca550f6bdbd232487177ad6a72" @@ -2079,6 +2185,14 @@ expect "^28.1.3" jest-snapshot "^28.1.3" +"@jest/expect@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.5.0.tgz#80952f5316b23c483fbca4363ce822af79c38fba" + integrity sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g== + dependencies: + expect "^29.5.0" + jest-snapshot "^29.5.0" + "@jest/fake-timers@^28.1.3": version "28.1.3" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-28.1.3.tgz#230255b3ad0a3d4978f1d06f70685baea91c640e" @@ -2091,17 +2205,17 @@ jest-mock "^28.1.3" jest-util "^28.1.3" -"@jest/fake-timers@^29.4.1": - version "29.4.1" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.4.1.tgz#7b673131e8ea2a2045858f08241cace5d518b42b" - integrity sha512-/1joI6rfHFmmm39JxNfmNAO3Nwm6Y0VoL5fJDy7H1AtWrD1CgRtqJbN9Ld6rhAkGO76qqp4cwhhxJ9o9kYjQMw== +"@jest/fake-timers@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.5.0.tgz#d4d09ec3286b3d90c60bdcd66ed28d35f1b4dc2c" + integrity sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg== dependencies: - "@jest/types" "^29.4.1" + "@jest/types" "^29.5.0" "@sinonjs/fake-timers" "^10.0.2" "@types/node" "*" - jest-message-util "^29.4.1" - jest-mock "^29.4.1" - jest-util "^29.4.1" + jest-message-util "^29.5.0" + jest-mock "^29.5.0" + jest-util "^29.5.0" "@jest/globals@^28.1.3": version "28.1.3" @@ -2112,6 +2226,16 @@ "@jest/expect" "^28.1.3" "@jest/types" "^28.1.3" +"@jest/globals@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.5.0.tgz#6166c0bfc374c58268677539d0c181f9c1833298" + integrity sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ== + dependencies: + "@jest/environment" "^29.5.0" + "@jest/expect" "^29.5.0" + "@jest/types" "^29.5.0" + jest-mock "^29.5.0" + "@jest/reporters@28.1.1": version "28.1.1" resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-28.1.1.tgz#9389f4bb3cce4d9b586f6195f83c79cd2a1c8662" @@ -2143,17 +2267,17 @@ terminal-link "^2.0.0" v8-to-istanbul "^9.0.0" -"@jest/reporters@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-28.1.3.tgz#9adf6d265edafc5fc4a434cfb31e2df5a67a369a" - integrity sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg== +"@jest/reporters@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.5.0.tgz#985dfd91290cd78ddae4914ba7921bcbabe8ac9b" + integrity sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" - "@jridgewell/trace-mapping" "^0.3.13" + "@jest/console" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" + "@jridgewell/trace-mapping" "^0.3.15" "@types/node" "*" chalk "^4.0.0" collect-v8-coverage "^1.0.0" @@ -2165,13 +2289,12 @@ istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.1.3" - jest-message-util "^28.1.3" - jest-util "^28.1.3" - jest-worker "^28.1.3" + jest-message-util "^29.5.0" + jest-util "^29.5.0" + jest-worker "^29.5.0" slash "^3.0.0" string-length "^4.0.1" strip-ansi "^6.0.0" - terminal-link "^2.0.0" v8-to-istanbul "^9.0.1" "@jest/schemas@^28.1.3": @@ -2188,10 +2311,10 @@ dependencies: "@sinclair/typebox" "^0.24.1" -"@jest/schemas@^29.4.0": - version "29.4.0" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.0.tgz#0d6ad358f295cc1deca0b643e6b4c86ebd539f17" - integrity sha512-0E01f/gOZeNTG76i5eWWSupvSHaIINrTie7vCyjiYFKgzNdyEGd12BUv4oNBFHOqlHDbtoJi3HrQ38KCC90NsQ== +"@jest/schemas@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.3.tgz#39cf1b8469afc40b6f5a2baaa146e332c4151788" + integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg== dependencies: "@sinclair/typebox" "^0.25.16" @@ -2204,6 +2327,15 @@ callsites "^3.0.0" graceful-fs "^4.2.9" +"@jest/source-map@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.4.3.tgz#ff8d05cbfff875d4a791ab679b4333df47951d20" + integrity sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w== + dependencies: + "@jridgewell/trace-mapping" "^0.3.15" + callsites "^3.0.0" + graceful-fs "^4.2.9" + "@jest/test-result@28.1.1": version "28.1.1" resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-28.1.1.tgz#c6f18d1bbb01aa88925dd687872a75f8414b317a" @@ -2224,7 +2356,17 @@ "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^28.1.1", "@jest/test-sequencer@^28.1.3": +"@jest/test-result@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.5.0.tgz#7c856a6ca84f45cc36926a4e9c6b57f1973f1408" + integrity sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ== + dependencies: + "@jest/console" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-sequencer@^28.1.1": version "28.1.3" resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz#9d0c283d906ac599c74bde464bc0d7e6a82886c3" integrity sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw== @@ -2234,6 +2376,16 @@ jest-haste-map "^28.1.3" slash "^3.0.0" +"@jest/test-sequencer@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz#34d7d82d3081abd523dbddc038a3ddcb9f6d3cc4" + integrity sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ== + dependencies: + "@jest/test-result" "^29.5.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.5.0" + slash "^3.0.0" + "@jest/transform@^28.1.1", "@jest/transform@^28.1.3": version "28.1.3" resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-28.1.3.tgz#59d8098e50ab07950e0f2fc0fc7ec462371281b0" @@ -2255,6 +2407,27 @@ slash "^3.0.0" write-file-atomic "^4.0.1" +"@jest/transform@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.5.0.tgz#cf9c872d0965f0cbd32f1458aa44a2b1988b00f9" + integrity sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw== + dependencies: + "@babel/core" "^7.11.6" + "@jest/types" "^29.5.0" + "@jridgewell/trace-mapping" "^0.3.15" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.5.0" + jest-regex-util "^29.4.3" + jest-util "^29.5.0" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + write-file-atomic "^4.0.2" + "@jest/types@^28.1.1", "@jest/types@^28.1.3": version "28.1.3" resolved "https://registry.yarnpkg.com/@jest/types/-/types-28.1.3.tgz#b05de80996ff12512bc5ceb1d208285a7d11748b" @@ -2279,12 +2452,12 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@jest/types@^29.4.1": - version "29.4.1" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.4.1.tgz#f9f83d0916f50696661da72766132729dcb82ecb" - integrity sha512-zbrAXDUOnpJ+FMST2rV7QZOgec8rskg2zv8g2ajeqitp4tvZiyqTCYXANrKsM+ryj5o+LI+ZN2EgU9drrkiwSA== +"@jest/types@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.5.0.tgz#f59ef9b031ced83047c67032700d8c807d6e1593" + integrity sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog== dependencies: - "@jest/schemas" "^29.4.0" + "@jest/schemas" "^29.4.3" "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" "@types/node" "*" @@ -2339,7 +2512,7 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.13", "@jridgewell/trace-mapping@^0.3.14", "@jridgewell/trace-mapping@^0.3.7", "@jridgewell/trace-mapping@^0.3.9": +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.13", "@jridgewell/trace-mapping@^0.3.14", "@jridgewell/trace-mapping@^0.3.15", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.7", "@jridgewell/trace-mapping@^0.3.9": version "0.3.17" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== @@ -3060,10 +3233,10 @@ "@material/theme" "15.0.0-canary.684e33d25.0" tslib "^2.1.0" -"@messageformat/core@^3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@messageformat/core/-/core-3.0.1.tgz#191e12cf9643704d1fd32e592a3fbdc194dd588e" - integrity sha512-yxj2+0e46hcZqJfNf0ZYbC2q6WlcGoh4g11mCyRtTueR0AD8F9z4JMYAS1aOiFG8Vl1LZg/h5hZHKmWTAyZq8g== +"@messageformat/core@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@messageformat/core/-/core-3.1.0.tgz#d4d2f5c3555228a6b5980b122a02b53dfc6458bd" + integrity sha512-UxAnjecnRG4u2iaggwIyylYPHmk5BTErJcKmWyAKTXqYgSW1bFLp4D7fIzuh6bk17Qfcmf3qtufdrstCB23nBA== dependencies: "@messageformat/date-skeleton" "^1.0.0" "@messageformat/number-skeleton" "^1.0.0" @@ -3239,6 +3412,13 @@ dependencies: nx "15.6.3" +"@nrwl/cli@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/cli/-/cli-15.8.7.tgz#1de7ba802de24edac64e8cb4cac1459a3f403505" + integrity sha512-G1NEy4jGuZJ/7KjhLQNOe11XmoTgwJS82FW8Tbo4iceq2ItSEbe7bkA8xTSK/AzUixZIMimztb9Oyxw/n1ajGQ== + dependencies: + nx "15.8.7" + "@nrwl/cypress@15.6.3": version "15.6.3" resolved "https://registry.yarnpkg.com/@nrwl/cypress/-/cypress-15.6.3.tgz#a9213555eb1581ad36f3df512ef5f32f77af9089" @@ -3262,6 +3442,18 @@ semver "7.3.4" tslib "^2.3.0" +"@nrwl/devkit@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/devkit/-/devkit-15.8.7.tgz#98e881e993c1314a20c050926df1466154782e58" + integrity sha512-A99nZrA5KN9wRn2uYX2vKByA+t2XEGoZBR5TU/bpXbPYrh92qAHkIJ8ke3ImGQOlzk4iIaZ5Me0k7k1p9Zx4wA== + dependencies: + "@phenomnomnominal/tsquery" "4.1.1" + ejs "^3.1.7" + ignore "^5.0.4" + semver "7.3.4" + tmp "~0.2.1" + tslib "^2.3.0" + "@nrwl/eslint-plugin-nx@15.6.3": version "15.6.3" resolved "https://registry.yarnpkg.com/@nrwl/eslint-plugin-nx/-/eslint-plugin-nx-15.6.3.tgz#a365de151783e90eccd8af8d49b13fd011f64943" @@ -3291,6 +3483,25 @@ resolve.exports "1.1.0" tslib "^2.3.0" +"@nrwl/jest@^15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/jest/-/jest-15.8.7.tgz#010adedc3e8852c53c1a87afca76eef8aec37792" + integrity sha512-olTRYD/m1qu/sAaw/z7XOp0amsTKDa6hJvQDK26/1BkJiuKaYLSW/Ec7f39Ilw/zrde+fNg3YUOGiXxuQ10k4g== + dependencies: + "@jest/reporters" "28.1.1" + "@jest/test-result" "28.1.1" + "@nrwl/devkit" "15.8.7" + "@nrwl/js" "15.8.7" + "@phenomnomnominal/tsquery" "4.1.1" + chalk "^4.1.0" + dotenv "~10.0.0" + identity-obj-proxy "3.0.0" + jest-config "28.1.1" + jest-resolve "28.1.1" + jest-util "28.1.1" + resolve.exports "1.1.0" + tslib "^2.3.0" + "@nrwl/js@15.6.3": version "15.6.3" resolved "https://registry.yarnpkg.com/@nrwl/js/-/js-15.6.3.tgz#82c831ab2bf620c3cd376515ff861c9041341d5c" @@ -3319,6 +3530,34 @@ tree-kill "1.2.2" tslib "^2.3.0" +"@nrwl/js@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/js/-/js-15.8.7.tgz#17e413b0cb15e90a2fc7250e42d03338dba1d3de" + integrity sha512-FPOtTSIVHXnQG2uPzgvgtjBlMaHnxPKwG+3Qv1gQN9uHxd9e59TryARS+Q7/Y/qSP6woQjSVEcxkp1zJ2DhFFA== + dependencies: + "@babel/core" "^7.15.0" + "@babel/plugin-proposal-class-properties" "^7.14.5" + "@babel/plugin-proposal-decorators" "^7.14.5" + "@babel/plugin-transform-runtime" "^7.15.0" + "@babel/preset-env" "^7.15.0" + "@babel/preset-typescript" "^7.15.0" + "@babel/runtime" "^7.14.8" + "@nrwl/devkit" "15.8.7" + "@nrwl/workspace" "15.8.7" + "@phenomnomnominal/tsquery" "4.1.1" + babel-plugin-const-enum "^1.0.1" + babel-plugin-macros "^2.8.0" + babel-plugin-transform-typescript-metadata "^0.3.1" + chalk "^4.1.0" + fast-glob "3.2.7" + fs-extra "^11.1.0" + ignore "^5.0.4" + js-tokens "^4.0.0" + minimatch "3.0.5" + source-map-support "0.5.19" + tree-kill "1.2.2" + tslib "^2.3.0" + "@nrwl/linter@15.6.3": version "15.6.3" resolved "https://registry.yarnpkg.com/@nrwl/linter/-/linter-15.6.3.tgz#9cffa150109c604827c06ce0ccd5c925d4cd7c01" @@ -3329,6 +3568,62 @@ tmp "~0.2.1" tslib "^2.3.0" +"@nrwl/linter@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/linter/-/linter-15.8.7.tgz#b4418c9bc7f1dcace14fe3ef951d1198a04e7083" + integrity sha512-s0RDjpGCkBZ83GuJfNGf3yTLb+KXzOz68BUEBPW2iw4ziMcMfQ5ep6zj7/5nzblaUMGslPBldqQ2N23JoiAo4w== + dependencies: + "@nrwl/devkit" "15.8.7" + "@nrwl/js" "15.8.7" + "@phenomnomnominal/tsquery" "4.1.1" + tmp "~0.2.1" + tslib "^2.3.0" + +"@nrwl/nx-darwin-arm64@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-darwin-arm64/-/nx-darwin-arm64-15.8.7.tgz#1fed566b5206afd710309079644782997ccb7895" + integrity sha512-+cu8J337gRxUHjz2TGwS/2Oh3yw8d3/T6SoBfvee1DY72VQaeYd8UTz0doOhDtmc/zowvRu7ZVsW0ytNB0jIXQ== + +"@nrwl/nx-darwin-x64@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-darwin-x64/-/nx-darwin-x64-15.8.7.tgz#7aaee9f56fa526e7049fa5a9829fa72044b35055" + integrity sha512-VqHJEP0wgFu1MU0Bo1vKZ5/s7ThRfYkX8SyGUxjVTzR02CrsjC4rNxFoKD8Cc4YkUn44U/F78toGf+i2gRcjSQ== + +"@nrwl/nx-linux-arm-gnueabihf@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-15.8.7.tgz#379a77ea46e0f741c487eeedd3389eafab26dcae" + integrity sha512-4F/8awwqPTt7zKQolvjBNrcR1wYicPjGchLOdaqnfMxn/iRRUdh0hD11mEP5zHNv9gZs/nOIvhdBUErNjFkplQ== + +"@nrwl/nx-linux-arm64-gnu@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-15.8.7.tgz#201a41c0c8531de94169faa48bd9a49bed04ec4b" + integrity sha512-3ZTSZx02Vv5emQOpaDROIcLtQucoXAe73zGKYDTXB95mxbOPSjjQJ8Rtx+BeqWq9JQoZZyRcD0qnBkTTy1aLRg== + +"@nrwl/nx-linux-arm64-musl@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-arm64-musl/-/nx-linux-arm64-musl-15.8.7.tgz#f6bbf2e7a1941952c25387a36be6cfa88079975d" + integrity sha512-SZxTomiHxAh8El+swbmGSGcaA0vGbHb/rmhFAixo19INu1wBJfD6hjkVJt17h6PyEO7BIYPOpRia6Poxnyv8hA== + +"@nrwl/nx-linux-x64-gnu@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-x64-gnu/-/nx-linux-x64-gnu-15.8.7.tgz#76a88784858224a720c5a28e40ad513704f45722" + integrity sha512-BlNC6Zz1/x6CFbBFTVrgRGMOPqb7zWh5cOjBVNpoBXYTEth1UXb2r1U+gpuQ4xdUqG+uXoWhy6BHJjqBIjzLJA== + +"@nrwl/nx-linux-x64-musl@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-linux-x64-musl/-/nx-linux-x64-musl-15.8.7.tgz#dd906423fa129d0c55633ebe80572bdd6be4d57f" + integrity sha512-FNYX/IKy8SUbw6bJpvwZrup2YQBYmSJwP6Rw76Vf7c32XHk7uA6AjiPWMIrZCSndXcry8fnwXvR+J2Dnyo82nQ== + +"@nrwl/nx-win32-arm64-msvc@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-15.8.7.tgz#145e415950d8ff507dcfbd7879f9c37477e7a620" + integrity sha512-sZALEzazjPAeLlw6IbFWsMidCZ4ZM3GKWZZ6rsAqG2y7I9t4nlUPH/y/Isl9MuLBvrBCBXbVnD20wh6EhtuwTw== + +"@nrwl/nx-win32-x64-msvc@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/nx-win32-x64-msvc/-/nx-win32-x64-msvc-15.8.7.tgz#66aa3cda4b9ae7b676d2282fbac129ce7a3c15d0" + integrity sha512-VMdDptI2rqkLQRCvertF29QeA/V/MnFtHbsmVzMCEv5EUfrkHbA5LLxV66LLfngmkDT1FHktffztlsMpbxvhRw== + "@nrwl/tao@15.6.3": version "15.6.3" resolved "https://registry.yarnpkg.com/@nrwl/tao/-/tao-15.6.3.tgz#b24e11345375dea96bc386c60b9b1102a7584932" @@ -3336,6 +3631,13 @@ dependencies: nx "15.6.3" +"@nrwl/tao@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/tao/-/tao-15.8.7.tgz#ea0bd4bc1784a2578dfb7cfb93f42d98504344cb" + integrity sha512-wA7QIEh0VwWcyo32Y/xSCTwnQTGcZupe933nResXv8mAb36W8MoR5SXRx+Wdd8fJ1eWlm2tuotIrslhN+lYx/Q== + dependencies: + nx "15.8.7" + "@nrwl/webpack@15.6.3": version "15.6.3" resolved "https://registry.yarnpkg.com/@nrwl/webpack/-/webpack-15.6.3.tgz#65b6d68a7a7c8580b8b97e8e4676dba5c5153090" @@ -3416,6 +3718,34 @@ yargs "^17.6.2" yargs-parser "21.1.1" +"@nrwl/workspace@15.8.7": + version "15.8.7" + resolved "https://registry.yarnpkg.com/@nrwl/workspace/-/workspace-15.8.7.tgz#c98b811214719a1fa2fe60bffdbc125b082bdb3f" + integrity sha512-ltJn5tLj8eCTNwJbKE9tWgiT/MKZ8f8jFld4YlG7H6i1mPc6d8rk+iNwoN8LZbHHss3x2g9lVnx2Sg5ZoDLNGA== + dependencies: + "@nrwl/devkit" "15.8.7" + "@nrwl/linter" "15.8.7" + "@parcel/watcher" "2.0.4" + chalk "^4.1.0" + chokidar "^3.5.1" + cli-cursor "3.1.0" + cli-spinners "2.6.1" + dotenv "~10.0.0" + figures "3.2.0" + flat "^5.0.2" + glob "7.1.4" + ignore "^5.0.4" + minimatch "3.0.5" + npm-run-path "^4.0.1" + nx "15.8.7" + open "^8.4.0" + rxjs "^6.5.4" + semver "7.3.4" + tmp "~0.2.1" + tslib "^2.3.0" + yargs "^17.6.2" + yargs-parser "21.1.1" + "@parcel/watcher@2.0.4": version "2.0.4" resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.0.4.tgz#f300fef4cc38008ff4b8c29d92588eced3ce014b" @@ -3424,10 +3754,10 @@ node-addon-api "^3.2.1" node-gyp-build "^4.3.0" -"@pdftron/webviewer@8.11.0": - version "8.11.0" - resolved "https://registry.yarnpkg.com/@pdftron/webviewer/-/webviewer-8.11.0.tgz#11f948f93bcb701c5d32357d650a17455bfa14b7" - integrity sha512-TWtvSDAvig/7IQq9gqn1SSYL0cpWSfWo5gEsqmNqRc/qUEEsuZUqpSDXi5zQFX0xr5wWnLSQvrnGKK0iJdKWPg== +"@pdftron/webviewer@8.12.0": + version "8.12.0" + resolved "https://registry.yarnpkg.com/@pdftron/webviewer/-/webviewer-8.12.0.tgz#00f297c6f6e9a0e0958321e49e3220a44d048a92" + integrity sha512-+CqzjLNio0vYTqBJm7JgwWak7OSIN4tWpU97dmrABG8qnKNIvOEzkuI+8d7P80u4cx9BKkQvNyBD6gEAPa1xeA== "@phenomnomnominal/tsquery@4.1.1": version "4.1.1" @@ -3693,15 +4023,6 @@ expect "^29.0.0" pretty-format "^29.0.0" -"@types/jsdom@^16.2.4": - version "16.2.15" - resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-16.2.15.tgz#6c09990ec43b054e49636cba4d11d54367fc90d6" - integrity sha512-nwF87yjBKuX/roqGYerZZM0Nv1pZDMAT5YhOHYeM/72Fic+VEqJh4nyoqoapzJnW3pUlfxPY5FhgsJtM+dRnQQ== - dependencies: - "@types/node" "*" - "@types/parse5" "^6.0.3" - "@types/tough-cookie" "*" - "@types/jsdom@^20.0.0": version "20.0.1" resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-20.0.1.tgz#07c14bc19bd2f918c1929541cdaacae894744808" @@ -3738,21 +4059,16 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.17.tgz#5c009e1d9c38f4a2a9d45c0b0c493fe6cdb4bcb5" integrity sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng== -"@types/node@18.11.18": - version "18.11.18" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.18.tgz#8dfb97f0da23c2293e554c5a50d61ef134d7697f" - integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA== +"@types/node@18.15.1": + version "18.15.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.1.tgz#41dc2bf78e8085a250d4670d95edb7fba621dd29" + integrity sha512-U2TWca8AeHSmbpi314QBESRk7oPjSZjDsR+c+H4ECC1l+kFgpZf8Ydhv3SJpPy51VyZHHqxlb6mTTqYNNRVAIw== "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== -"@types/parse5@^6.0.3": - version "6.0.3" - resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-6.0.3.tgz#705bb349e789efa06f43f128cef51240753424cb" - integrity sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g== - "@types/prettier@^2.1.5": version "2.7.1" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.1.tgz#dfd20e2dc35f027cdd6c1908e80a5ddc7499670e" @@ -3822,6 +4138,13 @@ resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== +"@types/yargs@^17.0.0": + version "17.0.23" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.23.tgz#a7db3a2062c95ca1a5e0d5d5ddb6521cbc649e35" + integrity sha512-yuogunc04OnzGQCrfHx+Kk883Q4X0aSwmYZhKjI21m+SVYzjIbrWl8dOOwSv5hf2Um2pdCOXWo9isteZTNXUZQ== + dependencies: + "@types/yargs-parser" "*" + "@types/yargs@^17.0.8": version "17.0.17" resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.17.tgz#5672e5621f8e0fca13f433a8017aae4b7a2a03e7" @@ -3829,30 +4152,36 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@5.50.0": - version "5.50.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.50.0.tgz#fb48c31cadc853ffc1dc35373f56b5e2a8908fe9" - integrity sha512-vwksQWSFZiUhgq3Kv7o1Jcj0DUNylwnIlGvKvLLYsq8pAWha6/WCnXUeaSoNNha/K7QSf2+jvmkxggC1u3pIwQ== +"@typescript-eslint/eslint-plugin@5.48.2": + version "5.48.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.2.tgz#112e6ae1e23a1dc8333ce82bb9c65c2608b4d8a3" + integrity sha512-sR0Gja9Ky1teIq4qJOl0nC+Tk64/uYdX+mi+5iB//MH8gwyx8e3SOyhEzeLZEFEEfCaLf8KJq+Bd/6je1t+CAg== dependencies: - "@typescript-eslint/scope-manager" "5.50.0" - "@typescript-eslint/type-utils" "5.50.0" - "@typescript-eslint/utils" "5.50.0" + "@typescript-eslint/scope-manager" "5.48.2" + "@typescript-eslint/type-utils" "5.48.2" + "@typescript-eslint/utils" "5.48.2" debug "^4.3.4" - grapheme-splitter "^1.0.4" ignore "^5.2.0" natural-compare-lite "^1.4.0" regexpp "^3.2.0" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/parser@5.50.0": - version "5.50.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.50.0.tgz#a33f44b2cc83d1b7176ec854fbecd55605b0b032" - integrity sha512-KCcSyNaogUDftK2G9RXfQyOCt51uB5yqC6pkUYqhYh8Kgt+DwR5M0EwEAxGPy/+DH6hnmKeGsNhiZRQxjH71uQ== +"@typescript-eslint/experimental-utils@^5.0.0": + version "5.56.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.56.0.tgz#2699b5eed7651cc361ac1e6ea2d31a0e51e989a5" + integrity sha512-sxWuj0eO5nItmKgZmsBbChVt90EhfkuncDCPbLAVeEJ+SCjXMcZN3AhhNbxed7IeGJ4XwsdL3/FMvD4r+FLqqA== dependencies: - "@typescript-eslint/scope-manager" "5.50.0" - "@typescript-eslint/types" "5.50.0" - "@typescript-eslint/typescript-estree" "5.50.0" + "@typescript-eslint/utils" "5.56.0" + +"@typescript-eslint/parser@5.48.2": + version "5.48.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.48.2.tgz#c9edef2a0922d26a37dba03be20c5fff378313b3" + integrity sha512-38zMsKsG2sIuM5Oi/olurGwYJXzmtdsHhn5mI/pQogP+BjYVkK5iRazCQ8RGS0V+YLk282uWElN70zAAUmaYHw== + dependencies: + "@typescript-eslint/scope-manager" "5.48.2" + "@typescript-eslint/types" "5.48.2" + "@typescript-eslint/typescript-estree" "5.48.2" debug "^4.3.4" "@typescript-eslint/scope-manager@5.46.1": @@ -3863,39 +4192,29 @@ "@typescript-eslint/types" "5.46.1" "@typescript-eslint/visitor-keys" "5.46.1" -"@typescript-eslint/scope-manager@5.48.1": - version "5.48.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.48.1.tgz#39c71e4de639f5fe08b988005beaaf6d79f9d64d" - integrity sha512-S035ueRrbxRMKvSTv9vJKIWgr86BD8s3RqoRZmsSh/s8HhIs90g6UlK8ZabUSjUZQkhVxt7nmZ63VJ9dcZhtDQ== +"@typescript-eslint/scope-manager@5.48.2": + version "5.48.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.48.2.tgz#bb7676cb78f1e94921eaab637a4b5d596f838abc" + integrity sha512-zEUFfonQid5KRDKoI3O+uP1GnrFd4tIHlvs+sTJXiWuypUWMuDaottkJuR612wQfOkjYbsaskSIURV9xo4f+Fw== dependencies: - "@typescript-eslint/types" "5.48.1" - "@typescript-eslint/visitor-keys" "5.48.1" + "@typescript-eslint/types" "5.48.2" + "@typescript-eslint/visitor-keys" "5.48.2" -"@typescript-eslint/scope-manager@5.50.0": - version "5.50.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.50.0.tgz#90b8a3b337ad2c52bbfe4eac38f9164614e40584" - integrity sha512-rt03kaX+iZrhssaT974BCmoUikYtZI24Vp/kwTSy841XhiYShlqoshRFDvN1FKKvU2S3gK+kcBW1EA7kNUrogg== +"@typescript-eslint/scope-manager@5.56.0": + version "5.56.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.56.0.tgz#62b4055088903b5254fa20403010e1c16d6ab725" + integrity sha512-jGYKyt+iBakD0SA5Ww8vFqGpoV2asSjwt60Gl6YcO8ksQ8s2HlUEyHBMSa38bdLopYqGf7EYQMUIGdT/Luw+sw== dependencies: - "@typescript-eslint/types" "5.50.0" - "@typescript-eslint/visitor-keys" "5.50.0" + "@typescript-eslint/types" "5.56.0" + "@typescript-eslint/visitor-keys" "5.56.0" -"@typescript-eslint/type-utils@5.48.1": - version "5.48.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.48.1.tgz#5d94ac0c269a81a91ad77c03407cea2caf481412" - integrity sha512-Hyr8HU8Alcuva1ppmqSYtM/Gp0q4JOp1F+/JH5D1IZm/bUBrV0edoewQZiEc1r6I8L4JL21broddxK8HAcZiqQ== +"@typescript-eslint/type-utils@5.48.2": + version "5.48.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.48.2.tgz#7d3aeca9fa37a7ab7e3d9056a99b42f342c48ad7" + integrity sha512-QVWx7J5sPMRiOMJp5dYshPxABRoZV1xbRirqSk8yuIIsu0nvMTZesKErEA3Oix1k+uvsk8Cs8TGJ6kQ0ndAcew== dependencies: - "@typescript-eslint/typescript-estree" "5.48.1" - "@typescript-eslint/utils" "5.48.1" - debug "^4.3.4" - tsutils "^3.21.0" - -"@typescript-eslint/type-utils@5.50.0": - version "5.50.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.50.0.tgz#509d5cc9728d520008f7157b116a42c5460e7341" - integrity sha512-dcnXfZ6OGrNCO7E5UY/i0ktHb7Yx1fV6fnQGGrlnfDhilcs6n19eIRcvLBqx6OQkrPaFlDPk3OJ0WlzQfrV0bQ== - dependencies: - "@typescript-eslint/typescript-estree" "5.50.0" - "@typescript-eslint/utils" "5.50.0" + "@typescript-eslint/typescript-estree" "5.48.2" + "@typescript-eslint/utils" "5.48.2" debug "^4.3.4" tsutils "^3.21.0" @@ -3904,15 +4223,15 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.46.1.tgz#4e9db2107b9a88441c4d5ecacde3bb7a5ebbd47e" integrity sha512-Z5pvlCaZgU+93ryiYUwGwLl9AQVB/PQ1TsJ9NZ/gHzZjN7g9IAn6RSDkpCV8hqTwAiaj6fmCcKSQeBPlIpW28w== -"@typescript-eslint/types@5.48.1": - version "5.48.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.48.1.tgz#efd1913a9aaf67caf8a6e6779fd53e14e8587e14" - integrity sha512-xHyDLU6MSuEEdIlzrrAerCGS3T7AA/L8Hggd0RCYBi0w3JMvGYxlLlXHeg50JI9Tfg5MrtsfuNxbS/3zF1/ATg== +"@typescript-eslint/types@5.48.2": + version "5.48.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.48.2.tgz#635706abb1ec164137f92148f06f794438c97b8e" + integrity sha512-hE7dA77xxu7ByBc6KCzikgfRyBCTst6dZQpwaTy25iMYOnbNljDT4hjhrGEJJ0QoMjrfqrx+j1l1B9/LtKeuqA== -"@typescript-eslint/types@5.50.0": - version "5.50.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.50.0.tgz#c461d3671a6bec6c2f41f38ed60bd87aa8a30093" - integrity sha512-atruOuJpir4OtyNdKahiHZobPKFvZnBnfDiyEaBf6d9vy9visE7gDjlmhl+y29uxZ2ZDgvXijcungGFjGGex7w== +"@typescript-eslint/types@5.56.0": + version "5.56.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.56.0.tgz#b03f0bfd6fa2afff4e67c5795930aff398cbd834" + integrity sha512-JyAzbTJcIyhuUhogmiu+t79AkdnqgPUEsxMTMc/dCZczGMJQh1MK2wgrju++yMN6AWroVAy2jxyPcPr3SWCq5w== "@typescript-eslint/typescript-estree@5.46.1": version "5.46.1" @@ -3927,58 +4246,58 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/typescript-estree@5.48.1": - version "5.48.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.1.tgz#9efa8ee2aa471c6ab62e649f6e64d8d121bc2056" - integrity sha512-Hut+Osk5FYr+sgFh8J/FHjqX6HFcDzTlWLrFqGoK5kVUN3VBHF/QzZmAsIXCQ8T/W9nQNBTqalxi1P3LSqWnRA== +"@typescript-eslint/typescript-estree@5.48.2": + version "5.48.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.2.tgz#6e206b462942b32383582a6c9251c05021cc21b0" + integrity sha512-bibvD3z6ilnoVxUBFEgkO0k0aFvUc4Cttt0dAreEr+nrAHhWzkO83PEVVuieK3DqcgL6VAK5dkzK8XUVja5Zcg== dependencies: - "@typescript-eslint/types" "5.48.1" - "@typescript-eslint/visitor-keys" "5.48.1" + "@typescript-eslint/types" "5.48.2" + "@typescript-eslint/visitor-keys" "5.48.2" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/typescript-estree@5.50.0": - version "5.50.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.50.0.tgz#0b9b82975bdfa40db9a81fdabc7f93396867ea97" - integrity sha512-Gq4zapso+OtIZlv8YNAStFtT6d05zyVCK7Fx3h5inlLBx2hWuc/0465C2mg/EQDDU2LKe52+/jN4f0g9bd+kow== +"@typescript-eslint/typescript-estree@5.56.0": + version "5.56.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.56.0.tgz#48342aa2344649a03321e74cab9ccecb9af086c3" + integrity sha512-41CH/GncsLXOJi0jb74SnC7jVPWeVJ0pxQj8bOjH1h2O26jXN3YHKDT1ejkVz5YeTEQPeLCCRY0U2r68tfNOcg== dependencies: - "@typescript-eslint/types" "5.50.0" - "@typescript-eslint/visitor-keys" "5.50.0" + "@typescript-eslint/types" "5.56.0" + "@typescript-eslint/visitor-keys" "5.56.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.48.1": - version "5.48.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.48.1.tgz#20f2f4e88e9e2a0961cbebcb47a1f0f7da7ba7f9" - integrity sha512-SmQuSrCGUOdmGMwivW14Z0Lj8dxG1mOFZ7soeJ0TQZEJcs3n5Ndgkg0A4bcMFzBELqLJ6GTHnEU+iIoaD6hFGA== +"@typescript-eslint/utils@5.48.2": + version "5.48.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.48.2.tgz#3777a91dcb22b8499a25519e06eef2e9569295a3" + integrity sha512-2h18c0d7jgkw6tdKTlNaM7wyopbLRBiit8oAxoP89YnuBOzCZ8g8aBCaCqq7h208qUTroL7Whgzam7UY3HVLow== dependencies: "@types/json-schema" "^7.0.9" "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.48.1" - "@typescript-eslint/types" "5.48.1" - "@typescript-eslint/typescript-estree" "5.48.1" + "@typescript-eslint/scope-manager" "5.48.2" + "@typescript-eslint/types" "5.48.2" + "@typescript-eslint/typescript-estree" "5.48.2" eslint-scope "^5.1.1" eslint-utils "^3.0.0" semver "^7.3.7" -"@typescript-eslint/utils@5.50.0": - version "5.50.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.50.0.tgz#807105f5ffb860644d30d201eefad7017b020816" - integrity sha512-v/AnUFImmh8G4PH0NDkf6wA8hujNNcrwtecqW4vtQ1UOSNBaZl49zP1SHoZ/06e+UiwzHpgb5zP5+hwlYYWYAw== +"@typescript-eslint/utils@5.56.0": + version "5.56.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.56.0.tgz#db64705409b9a15546053fb4deb2888b37df1f41" + integrity sha512-XhZDVdLnUJNtbzaJeDSCIYaM+Tgr59gZGbFuELgF7m0IY03PlciidS7UQNKLE0+WpUTn1GlycEr6Ivb/afjbhA== dependencies: + "@eslint-community/eslint-utils" "^4.2.0" "@types/json-schema" "^7.0.9" "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.50.0" - "@typescript-eslint/types" "5.50.0" - "@typescript-eslint/typescript-estree" "5.50.0" + "@typescript-eslint/scope-manager" "5.56.0" + "@typescript-eslint/types" "5.56.0" + "@typescript-eslint/typescript-estree" "5.56.0" eslint-scope "^5.1.1" - eslint-utils "^3.0.0" semver "^7.3.7" "@typescript-eslint/utils@^5.36.1": @@ -4003,20 +4322,20 @@ "@typescript-eslint/types" "5.46.1" eslint-visitor-keys "^3.3.0" -"@typescript-eslint/visitor-keys@5.48.1": - version "5.48.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.1.tgz#79fd4fb9996023ef86849bf6f904f33eb6c8fccb" - integrity sha512-Ns0XBwmfuX7ZknznfXozgnydyR8F6ev/KEGePP4i74uL3ArsKbEhJ7raeKr1JSa997DBDwol/4a0Y+At82c9dA== +"@typescript-eslint/visitor-keys@5.48.2": + version "5.48.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.2.tgz#c247582a0bcce467461d7b696513bf9455000060" + integrity sha512-z9njZLSkwmjFWUelGEwEbdf4NwKvfHxvGC0OcGN1Hp/XNDIcJ7D5DpPNPv6x6/mFvc1tQHsaWmpD/a4gOvvCJQ== dependencies: - "@typescript-eslint/types" "5.48.1" + "@typescript-eslint/types" "5.48.2" eslint-visitor-keys "^3.3.0" -"@typescript-eslint/visitor-keys@5.50.0": - version "5.50.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.50.0.tgz#b752ffc143841f3d7bc57d6dd01ac5c40f8c4903" - integrity sha512-cdMeD9HGu6EXIeGOh2yVW6oGf9wq8asBgZx7nsR/D36gTfQ0odE5kcRYe5M81vjEFAcPeugXrHg78Imu55F6gg== +"@typescript-eslint/visitor-keys@5.56.0": + version "5.56.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.56.0.tgz#f19eb297d972417eb13cb69b35b3213e13cc214f" + integrity sha512-1mFdED7u5bZpX6Xxf5N9U2c18sb+8EvU3tyOIj6LQZ5OOvnmj8BVeNNP603OFPm5KkS1a7IvCIcwrdHXaEMG/Q== dependencies: - "@typescript-eslint/types" "5.50.0" + "@typescript-eslint/types" "5.56.0" eslint-visitor-keys "^3.3.0" "@webassemblyjs/ast@1.11.1": @@ -4188,14 +4507,6 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" -acorn-globals@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" - integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== - dependencies: - acorn "^7.1.1" - acorn-walk "^7.1.1" - acorn-globals@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-7.0.1.tgz#0dbf05c44fa7c94332914c02066d5beff62c40c3" @@ -4214,21 +4525,11 @@ acorn-jsx@^5.3.2: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn-walk@^7.1.1: - version "7.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" - integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== - acorn-walk@^8.0.0, acorn-walk@^8.0.2, acorn-walk@^8.1.1: version "8.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== -acorn@^7.1.1: - version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== - acorn@^8.0.4, acorn@^8.1.0, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0, acorn@^8.8.1: version "8.8.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73" @@ -4503,10 +4804,10 @@ axios@^1.0.0: form-data "^4.0.0" proxy-from-env "^1.1.0" -axios@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.1.tgz#80bf6c8dbb46e6db1fa8fe9ab114c1ca7405c2ee" - integrity sha512-78pWJsQTceInlyaeBQeYZ/QgZeWS8hGeKiIJiDKQe3hEyBb7sEMq0K4gjx+Va6WHTYO4zI/RRl8qGRzn0YMadA== +axios@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.4.tgz#f5760cefd9cfb51fd2481acf88c05f67c4523024" + integrity sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ== dependencies: follow-redirects "^1.15.0" form-data "^4.0.0" @@ -4519,7 +4820,7 @@ axobject-query@3.1.1: dependencies: deep-equal "^2.0.5" -babel-jest@^28.1.1, babel-jest@^28.1.3: +babel-jest@^28.1.1: version "28.1.3" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-28.1.3.tgz#c1187258197c099072156a0a121c11ee1e3917d5" integrity sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q== @@ -4532,6 +4833,19 @@ babel-jest@^28.1.1, babel-jest@^28.1.3: graceful-fs "^4.2.9" slash "^3.0.0" +babel-jest@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.5.0.tgz#3fe3ddb109198e78b1c88f9ebdecd5e4fc2f50a5" + integrity sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q== + dependencies: + "@jest/transform" "^29.5.0" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^29.5.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + slash "^3.0.0" + babel-loader@9.1.2, babel-loader@^9.1.2: version "9.1.2" resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-9.1.2.tgz#a16a080de52d08854ee14570469905a5fc00d39c" @@ -4570,6 +4884,16 @@ babel-plugin-jest-hoist@^28.1.3: "@types/babel__core" "^7.1.14" "@types/babel__traverse" "^7.0.6" +babel-plugin-jest-hoist@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz#a97db437936f441ec196990c9738d4b88538618a" + integrity sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.1.14" + "@types/babel__traverse" "^7.0.6" + babel-plugin-macros@^2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138" @@ -4636,6 +4960,14 @@ babel-preset-jest@^28.1.3: babel-plugin-jest-hoist "^28.1.3" babel-preset-current-node-syntax "^1.0.0" +babel-preset-jest@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz#57bc8cc88097af7ff6a5ab59d1cd29d52a5916e2" + integrity sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg== + dependencies: + babel-plugin-jest-hoist "^29.5.0" + babel-preset-current-node-syntax "^1.0.0" + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -4658,6 +4990,15 @@ batch@0.6.1: resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== +bent@~7.3.6: + version "7.3.12" + resolved "https://registry.yarnpkg.com/bent/-/bent-7.3.12.tgz#e0a2775d4425e7674c64b78b242af4f49da6b035" + integrity sha512-T3yrKnVGB63zRuoco/7Ybl7BwwGZR0lceoVG5XmQyMIH9s19SV5m+a8qam4if0zQuAmOQTyPTPmsQBdAorGK3w== + dependencies: + bytesish "^0.4.1" + caseless "~0.12.0" + is-stream "^2.0.0" + big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -4754,11 +5095,6 @@ braces@^3.0.2, braces@~3.0.2: dependencies: fill-range "^7.0.1" -browser-process-hrtime@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" - integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== - browserslist@4.21.4, browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.6, browserslist@^4.21.3, browserslist@^4.21.4: version "4.21.4" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" @@ -4836,6 +5172,11 @@ bytes@3.1.2: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== +bytesish@^0.4.1: + version "0.4.4" + resolved "https://registry.yarnpkg.com/bytesish/-/bytesish-0.4.4.tgz#f3b535a0f1153747427aee27256748cff92347e6" + integrity sha512-i4uu6M4zuMUiyfZN4RU2+i9+peJh//pXhd9x1oSe1LBkZ3LEbCoygu8W0bXTukU1Jme2txKuotpCZRaC3FLxcQ== + cacache@17.0.4, cacache@^17.0.0: version "17.0.4" resolved "https://registry.yarnpkg.com/cacache/-/cacache-17.0.4.tgz#5023ed892ba8843e3b7361c26d0ada37e146290c" @@ -4917,6 +5258,16 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001400, caniuse-lite@^1.0.30001426: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz#ab7371faeb4adff4b74dad1718a6fd122e45d9cb" integrity sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A== +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== + +chalk@5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.2.0.tgz#249623b7d66869c673699fb66d65723e54dfcfb3" + integrity sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA== + chalk@^2.0.0: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -4926,7 +5277,7 @@ chalk@^2.0.0: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2, chalk@~4.1.0: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -5115,6 +5466,11 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" +commander@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.0.tgz#71797971162cd3cf65f0b9d24eb28f8d303acdf1" + integrity sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA== + commander@^2.20.0, commander@^2.8.1: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -5125,10 +5481,10 @@ commander@^7.2.0: resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== -commander@^9.4.1: - version "9.4.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-9.4.1.tgz#d1dd8f2ce6faf93147295c0df13c7c21141cfbdd" - integrity sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw== +common-tags@^1.8.0: + version "1.8.2" + resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" + integrity sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA== commondir@^1.0.1: version "1.0.1" @@ -5160,10 +5516,10 @@ compression@^1.7.4: safe-buffer "5.1.2" vary "~1.1.2" -compute-scroll-into-view@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-2.0.4.tgz#2b444b2b9e4724819d2531efacb7ac094155fdf6" - integrity sha512-y/ZA3BGnxoM/QHHQ2Uy49CLtnWPbt4tTPpEEZiEmmiWBFKjej7nEyH8Ryz54jH0MLXflUYA3Er2zUxPSJu5R+g== +compute-scroll-into-view@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-3.0.0.tgz#95d2f2f4653e7edda74dd1e38edaaa897918e0f0" + integrity sha512-Yk1An4qzo5++Cu6peT9PsmRKIU8tALpmdoE09n//AfGQFcPfx21/tMGMsmKYmLJWaBJrGOJ5Jz5hoU+7cZZUWQ== concat-map@0.0.1: version "0.0.1" @@ -5202,6 +5558,11 @@ convert-source-map@^1.4.0, convert-source-map@^1.5.1, convert-source-map@^1.6.0, resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" @@ -5607,7 +5968,7 @@ d3-transition@2, d3-transition@^2.0.0: d3-interpolate "1 - 2" d3-timer "1 - 2" -data-urls@^3.0.1, data-urls@^3.0.2: +data-urls@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143" integrity sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ== @@ -5649,7 +6010,12 @@ debug@~3.1.0: dependencies: ms "2.0.0" -decimal.js@^10.3.1, decimal.js@^10.4.2: +decamelize@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-5.0.1.tgz#db11a92e58c741ef339fb0a2868d8a06a9a7b1e9" + integrity sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA== + +decimal.js@^10.4.2: version "10.4.3" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== @@ -5833,6 +6199,11 @@ diff-sequences@^29.3.1: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.3.1.tgz#104b5b95fe725932421a9c6e5b4bef84c3f2249e" integrity sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ== +diff-sequences@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" + integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== + diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -5943,6 +6314,11 @@ emittery@^0.10.2: resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.2.tgz#902eec8aedb8c41938c46e9385e9db7e03182933" integrity sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw== +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -6148,10 +6524,19 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -eslint-config-prettier@8.6.0: - version "8.6.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz#dec1d29ab728f4fa63061774e1672ac4e363d207" - integrity sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA== +eslint-config-prettier@8.7.0: + version "8.7.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.7.0.tgz#f1cc58a8afebc50980bd53475451df146c13182d" + integrity sha512-HHVXLSlVUhMSmyW4ZzEuvjpwqamgmlfkutD53cYXLikh4pt/modINRcCIApJ84czDxM4GZInwUrromsDdTImTA== + +eslint-etc@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/eslint-etc/-/eslint-etc-5.2.0.tgz#c51c19a2ffb6447af76a1c5ffd13b9a212db859b" + integrity sha512-Gcm/NMa349FOXb1PEEfNMMyIANuorIc2/mI5Vfu1zENNsz+FBVhF62uY6gPUCigm/xDOc8JOnl+71WGnlzlDag== + dependencies: + "@typescript-eslint/experimental-utils" "^5.0.0" + tsutils "^3.17.1" + tsutils-etc "^1.4.1" eslint-plugin-prettier@^4.0.0: version "4.2.1" @@ -6160,6 +6545,21 @@ eslint-plugin-prettier@^4.0.0: dependencies: prettier-linter-helpers "^1.0.0" +eslint-plugin-rxjs@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-rxjs/-/eslint-plugin-rxjs-5.0.2.tgz#0f6f31d227f7d11f4596c3bbbded16e278629684" + integrity sha512-Q2wsEHWInhZ3uz5df+YbD4g/NPQqAeYHjJuEsxqgVS+XAsYCuVE2pj9kADdMFy4GsQy2jt7KP+TOrnq1i6bI5Q== + dependencies: + "@typescript-eslint/experimental-utils" "^5.0.0" + common-tags "^1.8.0" + decamelize "^5.0.0" + eslint-etc "^5.1.0" + requireindex "~1.2.0" + rxjs-report-usage "^1.0.4" + tslib "^2.0.0" + tsutils "^3.0.0" + tsutils-etc "^1.4.1" + eslint-scope@5.1.1, eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" @@ -6193,12 +6593,15 @@ eslint-visitor-keys@^3.3.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== -eslint@8.33.0: - version "8.33.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.33.0.tgz#02f110f32998cb598c6461f24f4d306e41ca33d7" - integrity sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA== +eslint@^8.33.0: + version "8.36.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.36.0.tgz#1bd72202200a5492f91803b113fb8a83b11285cf" + integrity sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw== dependencies: - "@eslint/eslintrc" "^1.4.1" + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.4.0" + "@eslint/eslintrc" "^2.0.1" + "@eslint/js" "8.36.0" "@humanwhocodes/config-array" "^0.11.8" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" @@ -6209,10 +6612,9 @@ eslint@8.33.0: doctrine "^3.0.0" escape-string-regexp "^4.0.0" eslint-scope "^7.1.1" - eslint-utils "^3.0.0" eslint-visitor-keys "^3.3.0" - espree "^9.4.0" - esquery "^1.4.0" + espree "^9.5.0" + esquery "^1.4.2" esutils "^2.0.2" fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" @@ -6233,15 +6635,14 @@ eslint@8.33.0: minimatch "^3.1.2" natural-compare "^1.4.0" optionator "^0.9.1" - regexpp "^3.2.0" strip-ansi "^6.0.1" strip-json-comments "^3.1.0" text-table "^0.2.0" -espree@^9.4.0: - version "9.4.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.1.tgz#51d6092615567a2c2cff7833445e37c28c0065bd" - integrity sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg== +espree@^9.5.0: + version "9.5.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.0.tgz#3646d4e3f58907464edba852fa047e6a27bdf113" + integrity sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw== dependencies: acorn "^8.8.0" acorn-jsx "^5.3.2" @@ -6252,13 +6653,20 @@ esprima@^4.0.0, esprima@^4.0.1: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.0.1, esquery@^1.4.0: +esquery@^1.0.1: version "1.4.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== dependencies: estraverse "^5.1.0" +esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + dependencies: + estraverse "^5.1.0" + esrecurse@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" @@ -6316,14 +6724,14 @@ execa@^5.0.0: signal-exit "^3.0.3" strip-final-newline "^2.0.0" -execa@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-6.1.0.tgz#cea16dee211ff011246556388effa0818394fb20" - integrity sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA== +execa@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-7.1.0.tgz#50c6f39438b7ce407e8c7a6829c72b074778238d" + integrity sha512-T6nIJO3LHxUZ6ahVRaxXz9WLEruXLqdcluA+UuTptXmLM7nDAn9lx9IfkxPyzEL21583qSt4RmL44pO71EHaJQ== dependencies: cross-spawn "^7.0.3" get-stream "^6.0.1" - human-signals "^3.0.1" + human-signals "^4.3.0" is-stream "^3.0.0" merge-stream "^2.0.0" npm-run-path "^5.1.0" @@ -6358,6 +6766,17 @@ expect@^29.0.0: jest-message-util "^29.3.1" jest-util "^29.3.1" +expect@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.5.0.tgz#68c0509156cb2a0adb8865d413b137eeaae682f7" + integrity sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg== + dependencies: + "@jest/expect-utils" "^29.5.0" + jest-get-type "^29.4.3" + jest-matcher-utils "^29.5.0" + jest-message-util "^29.5.0" + jest-util "^29.5.0" + express@^4.17.3: version "4.18.2" resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" @@ -6443,7 +6862,7 @@ fast-glob@^3.2.11, fast-glob@^3.2.12, fast-glob@^3.2.7, fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -6827,7 +7246,7 @@ glob@8.0.3, glob@^8.0.1: minimatch "^5.0.1" once "^1.3.0" -glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0: +glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0, glob@~7.2.0: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -6886,10 +7305,10 @@ globby@^13.1.1: merge2 "^1.4.1" slash "^4.0.0" -google-translate-api-browser@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/google-translate-api-browser/-/google-translate-api-browser-3.0.0.tgz#807fa31444e9c064e93b0aa3688526cc032df702" - integrity sha512-Nmd8Qi+QqXiWZTJf01cockJsQavd5TXlCwFW8eOmjE3lz9Dapy/dt0TzeUtIWev0ZBIf4BhVI/11i2hCVEI8Fg== +google-translate-api-browser@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/google-translate-api-browser/-/google-translate-api-browser-4.0.6.tgz#920af7d9d87dfda11bf40d0e2d3d5522831fec5c" + integrity sha512-pbQg+72GA/mtWQ1Wrwhb6X29xBxoq8V2kBJGFbo4cJhS7YSl5vGu81HjiPzTObbFxm0Y3TnfnF7uet+zMWcKQQ== gopd@^1.0.1: version "1.0.1" @@ -7126,10 +7545,10 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -human-signals@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-3.0.1.tgz#c740920859dafa50e5a3222da9d3bf4bb0e5eef5" - integrity sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ== +human-signals@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.0.tgz#2095c3cd5afae40049403d4b811235b03879db50" + integrity sha512-zyzVyMjpGBX2+6cDVZeFPCdtOtdsxOeseRhB9tkQ6xXmGUNrcnBzdEKPy3VPNYz+4gy1oukVOXcrJCunSyc6QQ== humanize-ms@^1.2.1: version "1.2.1" @@ -7181,6 +7600,11 @@ ignore-walk@^6.0.0: dependencies: minimatch "^5.0.1" +ignore@5.2.4: + version "5.2.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== + ignore@^5.0.4, ignore@^5.1.9, ignore@^5.2.0: version "5.2.2" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.2.tgz#7e5f30224584b67aeeefe383a24a61dce4cb370d" @@ -7586,15 +8010,15 @@ jake@^10.8.5: filelist "^1.0.1" minimatch "^3.0.4" -jest-changed-files@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-28.1.3.tgz#d9aeee6792be3686c47cb988a8eaf82ff4238831" - integrity sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA== +jest-changed-files@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.5.0.tgz#e88786dca8bf2aa899ec4af7644e16d9dcf9b23e" + integrity sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag== dependencies: execa "^5.0.0" p-limit "^3.1.0" -jest-circus@^28.1.1, jest-circus@^28.1.3: +jest-circus@^28.1.1: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-28.1.3.tgz#d14bd11cf8ee1a03d69902dc47b6bd4634ee00e4" integrity sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow== @@ -7619,21 +8043,47 @@ jest-circus@^28.1.1, jest-circus@^28.1.3: slash "^3.0.0" stack-utils "^2.0.3" -jest-cli@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-28.1.3.tgz#558b33c577d06de55087b8448d373b9f654e46b2" - integrity sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ== +jest-circus@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.5.0.tgz#b5926989449e75bff0d59944bae083c9d7fb7317" + integrity sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA== dependencies: - "@jest/core" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/environment" "^29.5.0" + "@jest/expect" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^0.7.0" + is-generator-fn "^2.0.0" + jest-each "^29.5.0" + jest-matcher-utils "^29.5.0" + jest-message-util "^29.5.0" + jest-runtime "^29.5.0" + jest-snapshot "^29.5.0" + jest-util "^29.5.0" + p-limit "^3.1.0" + pretty-format "^29.5.0" + pure-rand "^6.0.0" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-cli@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.5.0.tgz#b34c20a6d35968f3ee47a7437ff8e53e086b4a67" + integrity sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw== + dependencies: + "@jest/core" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/types" "^29.5.0" chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.9" import-local "^3.0.2" - jest-config "^28.1.3" - jest-util "^28.1.3" - jest-validate "^28.1.3" + jest-config "^29.5.0" + jest-util "^29.5.0" + jest-validate "^29.5.0" prompts "^2.0.1" yargs "^17.3.1" @@ -7665,31 +8115,31 @@ jest-config@28.1.1: slash "^3.0.0" strip-json-comments "^3.1.1" -jest-config@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-28.1.3.tgz#e315e1f73df3cac31447eed8b8740a477392ec60" - integrity sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ== +jest-config@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.5.0.tgz#3cc972faec8c8aaea9ae158c694541b79f3748da" + integrity sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA== dependencies: "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^28.1.3" - "@jest/types" "^28.1.3" - babel-jest "^28.1.3" + "@jest/test-sequencer" "^29.5.0" + "@jest/types" "^29.5.0" + babel-jest "^29.5.0" chalk "^4.0.0" ci-info "^3.2.0" deepmerge "^4.2.2" glob "^7.1.3" graceful-fs "^4.2.9" - jest-circus "^28.1.3" - jest-environment-node "^28.1.3" - jest-get-type "^28.0.2" - jest-regex-util "^28.0.2" - jest-resolve "^28.1.3" - jest-runner "^28.1.3" - jest-util "^28.1.3" - jest-validate "^28.1.3" + jest-circus "^29.5.0" + jest-environment-node "^29.5.0" + jest-get-type "^29.4.3" + jest-regex-util "^29.4.3" + jest-resolve "^29.5.0" + jest-runner "^29.5.0" + jest-util "^29.5.0" + jest-validate "^29.5.0" micromatch "^4.0.4" parse-json "^5.2.0" - pretty-format "^28.1.3" + pretty-format "^29.5.0" slash "^3.0.0" strip-json-comments "^3.1.1" @@ -7713,6 +8163,16 @@ jest-diff@^29.0.0, jest-diff@^29.3.1: jest-get-type "^29.2.0" pretty-format "^29.3.1" +jest-diff@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.5.0.tgz#e0d83a58eb5451dcc1fa61b1c3ee4e8f5a290d63" + integrity sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.4.3" + jest-get-type "^29.4.3" + pretty-format "^29.5.0" + jest-docblock@^28.1.1: version "28.1.1" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-28.1.1.tgz#6f515c3bf841516d82ecd57a62eed9204c2f42a8" @@ -7720,6 +8180,13 @@ jest-docblock@^28.1.1: dependencies: detect-newline "^3.0.0" +jest-docblock@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.4.3.tgz#90505aa89514a1c7dceeac1123df79e414636ea8" + integrity sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg== + dependencies: + detect-newline "^3.0.0" + jest-each@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-28.1.3.tgz#bdd1516edbe2b1f3569cfdad9acd543040028f81" @@ -7731,32 +8198,29 @@ jest-each@^28.1.3: jest-util "^28.1.3" pretty-format "^28.1.3" -jest-environment-jsdom@^28.0.0: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-28.1.3.tgz#2d4e5d61b7f1d94c3bddfbb21f0308ee506c09fb" - integrity sha512-HnlGUmZRdxfCByd3GM2F100DgQOajUBzEitjGqIREcb45kGjZvRrKUdlaF6escXBdcXNl0OBh+1ZrfeZT3GnAg== +jest-each@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.5.0.tgz#fc6e7014f83eac68e22b7195598de8554c2e5c06" + integrity sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA== dependencies: - "@jest/environment" "^28.1.3" - "@jest/fake-timers" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/jsdom" "^16.2.4" - "@types/node" "*" - jest-mock "^28.1.3" - jest-util "^28.1.3" - jsdom "^19.0.0" + "@jest/types" "^29.5.0" + chalk "^4.0.0" + jest-get-type "^29.4.3" + jest-util "^29.5.0" + pretty-format "^29.5.0" -jest-environment-jsdom@^29.4.1: - version "29.4.1" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-29.4.1.tgz#34d491244ddd6fe3d666da603b576bd0ae6aef78" - integrity sha512-+KfYmRTl5CBHQst9hIz77TiiriHYvuWoLjMT855gx2AMxhHxpk1vtKvag1DQfyWCPVTWV/AG7SIqVh5WI1O/uw== +jest-environment-jsdom@^29.0.0, jest-environment-jsdom@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-29.5.0.tgz#cfe86ebaf1453f3297b5ff3470fbe94739c960cb" + integrity sha512-/KG8yEK4aN8ak56yFVdqFDzKNHgF4BAymCx2LbPNPsUshUlfAl0eX402Xm1pt+eoG9SLZEUVifqXtX8SK74KCw== dependencies: - "@jest/environment" "^29.4.1" - "@jest/fake-timers" "^29.4.1" - "@jest/types" "^29.4.1" + "@jest/environment" "^29.5.0" + "@jest/fake-timers" "^29.5.0" + "@jest/types" "^29.5.0" "@types/jsdom" "^20.0.0" "@types/node" "*" - jest-mock "^29.4.1" - jest-util "^29.4.1" + jest-mock "^29.5.0" + jest-util "^29.5.0" jsdom "^20.0.0" jest-environment-node@^28.1.1, jest-environment-node@^28.1.3: @@ -7771,10 +8235,22 @@ jest-environment-node@^28.1.1, jest-environment-node@^28.1.3: jest-mock "^28.1.3" jest-util "^28.1.3" -jest-extended@^3.2.3: - version "3.2.3" - resolved "https://registry.yarnpkg.com/jest-extended/-/jest-extended-3.2.3.tgz#286c19b4622e2ab828e1bb28d3b2d4a1ed64f8b9" - integrity sha512-YcdjfFv3+N2AiWq4aG6gT/r1mfLtDKnbXs0hKXNlL/hf37TKQJTlh2zNwuMUYnvwKRRMtO/X9CfZU1EmOgUREA== +jest-environment-node@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.5.0.tgz#f17219d0f0cc0e68e0727c58b792c040e332c967" + integrity sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw== + dependencies: + "@jest/environment" "^29.5.0" + "@jest/fake-timers" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/node" "*" + jest-mock "^29.5.0" + jest-util "^29.5.0" + +jest-extended@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/jest-extended/-/jest-extended-3.2.4.tgz#4ee93c9e495f983ceb911170613ce305f3533b94" + integrity sha512-lSEYhSmvXZG/7YXI7KO3LpiUiQ90gi5giwCJNDMMsX5a+/NZhdbQF2G4ALOBN+KcXVT3H6FPVPohAuMXooaLTQ== dependencies: jest-diff "^29.0.0" jest-get-type "^29.0.0" @@ -7789,6 +8265,11 @@ jest-get-type@^29.0.0, jest-get-type@^29.2.0: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.2.0.tgz#726646f927ef61d583a3b3adb1ab13f3a5036408" integrity sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA== +jest-get-type@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" + integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== + jest-haste-map@^28.1.1, jest-haste-map@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-28.1.3.tgz#abd5451129a38d9841049644f34b034308944e2b" @@ -7808,6 +8289,25 @@ jest-haste-map@^28.1.1, jest-haste-map@^28.1.3: optionalDependencies: fsevents "^2.3.2" +jest-haste-map@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.5.0.tgz#69bd67dc9012d6e2723f20a945099e972b2e94de" + integrity sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA== + dependencies: + "@jest/types" "^29.5.0" + "@types/graceful-fs" "^4.1.3" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^29.4.3" + jest-util "^29.5.0" + jest-worker "^29.5.0" + micromatch "^4.0.4" + walker "^1.0.8" + optionalDependencies: + fsevents "^2.3.2" + jest-leak-detector@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz#a6685d9b074be99e3adee816ce84fd30795e654d" @@ -7816,6 +8316,14 @@ jest-leak-detector@^28.1.3: jest-get-type "^28.0.2" pretty-format "^28.1.3" +jest-leak-detector@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz#cf4bdea9615c72bac4a3a7ba7e7930f9c0610c8c" + integrity sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow== + dependencies: + jest-get-type "^29.4.3" + pretty-format "^29.5.0" + jest-matcher-utils@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz#5a77f1c129dd5ba3b4d7fc20728806c78893146e" @@ -7836,6 +8344,16 @@ jest-matcher-utils@^29.3.1: jest-get-type "^29.2.0" pretty-format "^29.3.1" +jest-matcher-utils@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz#d957af7f8c0692c5453666705621ad4abc2c59c5" + integrity sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw== + dependencies: + chalk "^4.0.0" + jest-diff "^29.5.0" + jest-get-type "^29.4.3" + pretty-format "^29.5.0" + jest-message-util@^28.1.1, jest-message-util@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-28.1.3.tgz#232def7f2e333f1eecc90649b5b94b0055e7c43d" @@ -7866,18 +8384,18 @@ jest-message-util@^29.3.1: slash "^3.0.0" stack-utils "^2.0.3" -jest-message-util@^29.4.1: - version "29.4.1" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.4.1.tgz#522623aa1df9a36ebfdffb06495c7d9d19e8a845" - integrity sha512-H4/I0cXUaLeCw6FM+i4AwCnOwHRgitdaUFOdm49022YD5nfyr8C/DrbXOBEyJaj+w/y0gGJ57klssOaUiLLQGQ== +jest-message-util@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.5.0.tgz#1f776cac3aca332ab8dd2e3b41625435085c900e" + integrity sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.4.1" + "@jest/types" "^29.5.0" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^29.4.1" + pretty-format "^29.5.0" slash "^3.0.0" stack-utils "^2.0.3" @@ -7889,30 +8407,31 @@ jest-mock@^28.1.3: "@jest/types" "^28.1.3" "@types/node" "*" -jest-mock@^29.4.1: - version "29.4.1" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.4.1.tgz#a218a2abf45c99c501d4665207748a6b9e29afbd" - integrity sha512-MwA4hQ7zBOcgVCVnsM8TzaFLVUD/pFWTfbkY953Y81L5ret3GFRZtmPmRFAjKQSdCKoJvvqOu6Bvfpqlwwb0dQ== +jest-mock@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.5.0.tgz#26e2172bcc71d8b0195081ff1f146ac7e1518aed" + integrity sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw== dependencies: - "@jest/types" "^29.4.1" + "@jest/types" "^29.5.0" "@types/node" "*" - jest-util "^29.4.1" + jest-util "^29.5.0" jest-pnp-resolver@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== -jest-preset-angular@12.2.6: - version "12.2.6" - resolved "https://registry.yarnpkg.com/jest-preset-angular/-/jest-preset-angular-12.2.6.tgz#61b4643c1da3852dee65832048ce87aead2e885f" - integrity sha512-h5xazwr6Q4ZgQ/Bpcko2bJ6EnvSn3ek4cujhjK4+EPoB/27g0UzEiyNdlqTFa4m4uNjrqWRTSX+rHmWyPMg5PQ== +jest-preset-angular@13.0.0: + version "13.0.0" + resolved "https://registry.yarnpkg.com/jest-preset-angular/-/jest-preset-angular-13.0.0.tgz#8427926052711a31b09aa20b67ec0d409e2c4f8a" + integrity sha512-FNaWL41PgW3yvleB423pf1Z7xqaDaFMJxi2TwUek9J1p3SHT1Se+A2jeOI27XcC16zj87blUuffh/Smse2Grpg== dependencies: bs-logger "^0.2.6" esbuild-wasm ">=0.13.8" - jest-environment-jsdom "^28.0.0" - pretty-format "^28.0.0" - ts-jest "^28.0.0" + jest-environment-jsdom "^29.0.0" + jest-util "^29.0.0" + pretty-format "^29.0.0" + ts-jest "^29.0.0" optionalDependencies: esbuild ">=0.13.8" @@ -7921,13 +8440,18 @@ jest-regex-util@^28.0.2: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-28.0.2.tgz#afdc377a3b25fb6e80825adcf76c854e5bf47ead" integrity sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw== -jest-resolve-dependencies@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz#8c65d7583460df7275c6ea2791901fa975c1fe66" - integrity sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA== +jest-regex-util@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.4.3.tgz#a42616141e0cae052cfa32c169945d00c0aa0bb8" + integrity sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg== + +jest-resolve-dependencies@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz#f0ea29955996f49788bf70996052aa98e7befee4" + integrity sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg== dependencies: - jest-regex-util "^28.0.2" - jest-snapshot "^28.1.3" + jest-regex-util "^29.4.3" + jest-snapshot "^29.5.0" jest-resolve@28.1.1: version "28.1.1" @@ -7959,7 +8483,22 @@ jest-resolve@^28.1.1, jest-resolve@^28.1.3: resolve.exports "^1.1.0" slash "^3.0.0" -jest-runner@^28.1.1, jest-runner@^28.1.3: +jest-resolve@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.5.0.tgz#b053cc95ad1d5f6327f0ac8aae9f98795475ecdc" + integrity sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w== + dependencies: + chalk "^4.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.5.0" + jest-pnp-resolver "^1.2.2" + jest-util "^29.5.0" + jest-validate "^29.5.0" + resolve "^1.20.0" + resolve.exports "^2.0.0" + slash "^3.0.0" + +jest-runner@^28.1.1: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-28.1.3.tgz#5eee25febd730b4713a2cdfd76bdd5557840f9a1" integrity sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA== @@ -7986,6 +8525,33 @@ jest-runner@^28.1.1, jest-runner@^28.1.3: p-limit "^3.1.0" source-map-support "0.5.13" +jest-runner@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.5.0.tgz#6a57c282eb0ef749778d444c1d758c6a7693b6f8" + integrity sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ== + dependencies: + "@jest/console" "^29.5.0" + "@jest/environment" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.13.1" + graceful-fs "^4.2.9" + jest-docblock "^29.4.3" + jest-environment-node "^29.5.0" + jest-haste-map "^29.5.0" + jest-leak-detector "^29.5.0" + jest-message-util "^29.5.0" + jest-resolve "^29.5.0" + jest-runtime "^29.5.0" + jest-util "^29.5.0" + jest-watcher "^29.5.0" + jest-worker "^29.5.0" + p-limit "^3.1.0" + source-map-support "0.5.13" + jest-runtime@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-28.1.3.tgz#a57643458235aa53e8ec7821949e728960d0605f" @@ -8014,6 +8580,34 @@ jest-runtime@^28.1.3: slash "^3.0.0" strip-bom "^4.0.0" +jest-runtime@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.5.0.tgz#c83f943ee0c1da7eb91fa181b0811ebd59b03420" + integrity sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw== + dependencies: + "@jest/environment" "^29.5.0" + "@jest/fake-timers" "^29.5.0" + "@jest/globals" "^29.5.0" + "@jest/source-map" "^29.4.3" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/node" "*" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-haste-map "^29.5.0" + jest-message-util "^29.5.0" + jest-mock "^29.5.0" + jest-regex-util "^29.4.3" + jest-resolve "^29.5.0" + jest-snapshot "^29.5.0" + jest-util "^29.5.0" + slash "^3.0.0" + strip-bom "^4.0.0" + jest-snapshot@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-28.1.3.tgz#17467b3ab8ddb81e2f605db05583d69388fc0668" @@ -8043,6 +8637,35 @@ jest-snapshot@^28.1.3: pretty-format "^28.1.3" semver "^7.3.5" +jest-snapshot@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.5.0.tgz#c9c1ce0331e5b63cd444e2f95a55a73b84b1e8ce" + integrity sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g== + dependencies: + "@babel/core" "^7.11.6" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-jsx" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/traverse" "^7.7.2" + "@babel/types" "^7.3.3" + "@jest/expect-utils" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/babel__traverse" "^7.0.6" + "@types/prettier" "^2.1.5" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^29.5.0" + graceful-fs "^4.2.9" + jest-diff "^29.5.0" + jest-get-type "^29.4.3" + jest-matcher-utils "^29.5.0" + jest-message-util "^29.5.0" + jest-util "^29.5.0" + natural-compare "^1.4.0" + pretty-format "^29.5.0" + semver "^7.3.5" + jest-util@28.1.1: version "28.1.1" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.1.tgz#ff39e436a1aca397c0ab998db5a51ae2b7080d05" @@ -8055,7 +8678,7 @@ jest-util@28.1.1: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-util@^28.0.0, jest-util@^28.1.1, jest-util@^28.1.3: +jest-util@^28.1.1, jest-util@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.3.tgz#f4f932aa0074f0679943220ff9cbba7e497028b0" integrity sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ== @@ -8067,6 +8690,18 @@ jest-util@^28.0.0, jest-util@^28.1.1, jest-util@^28.1.3: graceful-fs "^4.2.9" picomatch "^2.2.3" +jest-util@^29.0.0, jest-util@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.5.0.tgz#24a4d3d92fc39ce90425311b23c27a6e0ef16b8f" + integrity sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ== + dependencies: + "@jest/types" "^29.5.0" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + jest-util@^29.3.1: version "29.3.1" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.3.1.tgz#1dda51e378bbcb7e3bc9d8ab651445591ed373e1" @@ -8079,18 +8714,6 @@ jest-util@^29.3.1: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-util@^29.4.1: - version "29.4.1" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.4.1.tgz#2eeed98ff4563b441b5a656ed1a786e3abc3e4c4" - integrity sha512-bQy9FPGxVutgpN4VRc0hk6w7Hx/m6L53QxpDreTZgJd9gfx/AV2MjyPde9tGyZRINAUrSv57p2inGBu2dRLmkQ== - dependencies: - "@jest/types" "^29.4.1" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - jest-validate@^28.1.1, jest-validate@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-28.1.3.tgz#e322267fd5e7c64cea4629612c357bbda96229df" @@ -8103,6 +8726,18 @@ jest-validate@^28.1.1, jest-validate@^28.1.3: leven "^3.1.0" pretty-format "^28.1.3" +jest-validate@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.5.0.tgz#8e5a8f36178d40e47138dc00866a5f3bd9916ffc" + integrity sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ== + dependencies: + "@jest/types" "^29.5.0" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^29.4.3" + leven "^3.1.0" + pretty-format "^29.5.0" + jest-watcher@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-28.1.3.tgz#c6023a59ba2255e3b4c57179fc94164b3e73abd4" @@ -8117,6 +8752,20 @@ jest-watcher@^28.1.3: jest-util "^28.1.3" string-length "^4.0.1" +jest-watcher@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.5.0.tgz#cf7f0f949828ba65ddbbb45c743a382a4d911363" + integrity sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA== + dependencies: + "@jest/test-result" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.13.1" + jest-util "^29.5.0" + string-length "^4.0.1" + jest-worker@^27.0.2, jest-worker@^27.4.5: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" @@ -8135,15 +8784,25 @@ jest-worker@^28.1.1, jest-worker@^28.1.3: merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest/-/jest-28.1.3.tgz#e9c6a7eecdebe3548ca2b18894a50f45b36dfc6b" - integrity sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA== +jest-worker@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.5.0.tgz#bdaefb06811bd3384d93f009755014d8acb4615d" + integrity sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA== dependencies: - "@jest/core" "^28.1.3" - "@jest/types" "^28.1.3" + "@types/node" "*" + jest-util "^29.5.0" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.5.0.tgz#f75157622f5ce7ad53028f2f8888ab53e1f1f24e" + integrity sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ== + dependencies: + "@jest/core" "^29.5.0" + "@jest/types" "^29.5.0" import-local "^3.0.2" - jest-cli "^28.1.3" + jest-cli "^29.5.0" js-sdsl@^4.1.4: version "4.2.0" @@ -8175,39 +8834,6 @@ js-yaml@^3.10.0, js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" -jsdom@^19.0.0: - version "19.0.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-19.0.0.tgz#93e67c149fe26816d38a849ea30ac93677e16b6a" - integrity sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A== - dependencies: - abab "^2.0.5" - acorn "^8.5.0" - acorn-globals "^6.0.0" - cssom "^0.5.0" - cssstyle "^2.3.0" - data-urls "^3.0.1" - decimal.js "^10.3.1" - domexception "^4.0.0" - escodegen "^2.0.0" - form-data "^4.0.0" - html-encoding-sniffer "^3.0.0" - http-proxy-agent "^5.0.0" - https-proxy-agent "^5.0.0" - is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.0" - parse5 "6.0.1" - saxes "^5.0.1" - symbol-tree "^3.2.4" - tough-cookie "^4.0.0" - w3c-hr-time "^1.0.2" - w3c-xmlserializer "^3.0.0" - webidl-conversions "^7.0.0" - whatwg-encoding "^2.0.0" - whatwg-mimetype "^3.0.0" - whatwg-url "^10.0.0" - ws "^8.2.3" - xml-name-validator "^4.0.0" - jsdom@^20.0.0: version "20.0.3" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-20.0.3.tgz#886a41ba1d4726f67a8858028c99489fed6ad4db" @@ -8280,7 +8906,7 @@ json5@^2.1.2, json5@^2.2.1: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.2.tgz#64471c5bdcc564c18f7c1d4df2e2297f2457c5ab" integrity sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ== -json5@^2.2.2: +json5@^2.2.2, json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== @@ -8413,7 +9039,12 @@ license-webpack-plugin@4.0.2, license-webpack-plugin@^4.0.2: dependencies: webpack-sources "^3.0.0" -lilconfig@2.0.6, lilconfig@^2.0.3: +lilconfig@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" + integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== + +lilconfig@^2.0.3: version "2.0.6" resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.6.tgz#32a384558bd58af3d4c6e077dd1ad1d397bc69d4" integrity sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg== @@ -8428,36 +9059,36 @@ lines-and-columns@~2.0.3: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-2.0.3.tgz#b2f0badedb556b747020ab8ea7f0373e22efac1b" integrity sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w== -lint-staged@^13.1.0: - version "13.1.0" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-13.1.0.tgz#d4c61aec939e789e489fa51987ec5207b50fd37e" - integrity sha512-pn/sR8IrcF/T0vpWLilih8jmVouMlxqXxKuAojmbiGX5n/gDnz+abdPptlj0vYnbfE0SQNl3CY/HwtM0+yfOVQ== +lint-staged@^13.2.0: + version "13.2.0" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-13.2.0.tgz#b7abaf79c91cd36d824f17b23a4ce5209206126a" + integrity sha512-GbyK5iWinax5Dfw5obm2g2ccUiZXNGtAS4mCbJ0Lv4rq6iEtfBSjOYdcbOtAIFtM114t0vdpViDDetjVTSd8Vw== dependencies: + chalk "5.2.0" cli-truncate "^3.1.0" - colorette "^2.0.19" - commander "^9.4.1" + commander "^10.0.0" debug "^4.3.4" - execa "^6.1.0" - lilconfig "2.0.6" - listr2 "^5.0.5" + execa "^7.0.0" + lilconfig "2.1.0" + listr2 "^5.0.7" micromatch "^4.0.5" normalize-path "^3.0.0" - object-inspect "^1.12.2" + object-inspect "^1.12.3" pidtree "^0.6.0" string-argv "^0.3.1" - yaml "^2.1.3" + yaml "^2.2.1" -listr2@^5.0.5: - version "5.0.6" - resolved "https://registry.yarnpkg.com/listr2/-/listr2-5.0.6.tgz#3c61153383869ffaad08a8908d63edfde481dff8" - integrity sha512-u60KxKBy1BR2uLJNTWNptzWQ1ob/gjMzIJPZffAENzpZqbMZ/5PrXXOomDcevIS/+IB7s1mmCEtSlT2qHWMqag== +listr2@^5.0.7: + version "5.0.8" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-5.0.8.tgz#a9379ffeb4bd83a68931a65fb223a11510d6ba23" + integrity sha512-mC73LitKHj9w6v30nLNGPetZIlfpUniNSsxxrbaPcWOjDb92SHPzJPi/t+v1YC/lxKz/AJ9egOjww0qUuFxBpA== dependencies: cli-truncate "^2.1.0" colorette "^2.0.19" log-update "^4.0.0" p-map "^4.0.0" rfdc "^1.3.0" - rxjs "^7.5.7" + rxjs "^7.8.0" through "^2.3.8" wrap-ansi "^7.0.0" @@ -8891,10 +9522,10 @@ mkdirp@^1.0.3, mkdirp@^1.0.4, mkdirp@~1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -monaco-editor@^0.34.0: - version "0.34.1" - resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.34.1.tgz#1b75c4ad6bc4c1f9da656d740d98e0b850a22f87" - integrity sha512-FKc80TyiMaruhJKKPz5SpJPIjL+dflGvz4CpuThaPMc94AyN7SeC9HQ8hrvaxX7EyHdJcUY5i4D0gNyJj1vSZQ== +monaco-editor@^0.36.1: + version "0.36.1" + resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.36.1.tgz#aad528c815605307473a1634612946921d8079b5" + integrity sha512-/CaclMHKQ3A6rnzBzOADfwdSJ25BFoFT0Emxsc4zYVyav5SkK9iA6lEtIeuN/oRYbwPgviJT+t3l+sjFa28jYg== moo@^0.5.1: version "0.5.2" @@ -8973,10 +9604,10 @@ neo-async@^2.6.2: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -ngx-color-picker@^13.0.0: - version "13.0.0" - resolved "https://registry.yarnpkg.com/ngx-color-picker/-/ngx-color-picker-13.0.0.tgz#fe6d3b2def721ebc4f2a1a4ed83e552e0cfe3601" - integrity sha512-3mgMbs21KeqnmmY5p1cn71ckTH3q7gBt6Qn0fMfeF/Ql7ddTZsW4Z7Z8ga6LymMP/ugooGuLOFX+V6yx0dDxAw== +ngx-color-picker@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/ngx-color-picker/-/ngx-color-picker-14.0.0.tgz#4587f517ac5683a705d4e55cd0939afa91faa853" + integrity sha512-w28zx2DyVpIJeNsTB3T2LUI4Ed/Ujf5Uhxuh0dllputfpxXwZG9ocSJM/0L67+fxA3UnfvvXVZNUX1Ny5nZIIw== dependencies: tslib "^2.3.0" @@ -8988,10 +9619,10 @@ ngx-logger@^5.0.11: tslib "^2.3.0" vlq "^1.0.0" -ngx-toastr@^16.0.2: - version "16.0.2" - resolved "https://registry.yarnpkg.com/ngx-toastr/-/ngx-toastr-16.0.2.tgz#349068a16a73867111c6feae68eedb89a638a33f" - integrity sha512-J6SueNCaGwm/gpXdsG56UzMEAcuayYWEW6NmIrNoe5iP7lOUohg4xYXWipkbMH9wGWmLPD9gU8AufUVWMplCvg== +ngx-toastr@^16.1.0: + version "16.1.0" + resolved "https://registry.yarnpkg.com/ngx-toastr/-/ngx-toastr-16.1.0.tgz#cd0264e8f48cddc802dafb6e8413a10465df6b01" + integrity sha512-jMnkauggRE8264vR4NtOgK3QI7zmufO3Z7wrkg3j7A7ZVpkCKDXgklsAZM9aiHxDyttM2K+Na6a6L004uPeRKA== dependencies: tslib "^2.3.0" @@ -9183,7 +9814,7 @@ nth-check@^2.0.1: dependencies: boolbase "^1.0.0" -nwsapi@^2.2.0, nwsapi@^2.2.2: +nwsapi@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0" integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw== @@ -9229,6 +9860,57 @@ nx@15.6.3: yargs "^17.6.2" yargs-parser "21.1.1" +nx@15.8.7: + version "15.8.7" + resolved "https://registry.yarnpkg.com/nx/-/nx-15.8.7.tgz#a89156244f6f94407d7603375ae2f52733c7aff4" + integrity sha512-u6p/1gU20WU61orxK7hcXBsVspPHy3X66XVAAakkYcaOBlsJhJrR7Og191qIyjEkqEWmcekiDQVw3D6XfagL4Q== + dependencies: + "@nrwl/cli" "15.8.7" + "@nrwl/tao" "15.8.7" + "@parcel/watcher" "2.0.4" + "@yarnpkg/lockfile" "^1.1.0" + "@yarnpkg/parsers" "^3.0.0-rc.18" + "@zkochan/js-yaml" "0.0.6" + axios "^1.0.0" + chalk "^4.1.0" + cli-cursor "3.1.0" + cli-spinners "2.6.1" + cliui "^7.0.2" + dotenv "~10.0.0" + enquirer "~2.3.6" + fast-glob "3.2.7" + figures "3.2.0" + flat "^5.0.2" + fs-extra "^11.1.0" + glob "7.1.4" + ignore "^5.0.4" + js-yaml "4.1.0" + jsonc-parser "3.2.0" + lines-and-columns "~2.0.3" + minimatch "3.0.5" + npm-run-path "^4.0.1" + open "^8.4.0" + semver "7.3.4" + string-width "^4.2.3" + strong-log-transformer "^2.1.0" + tar-stream "~2.2.0" + tmp "~0.2.1" + tsconfig-paths "^4.1.2" + tslib "^2.3.0" + v8-compile-cache "2.3.0" + yargs "^17.6.2" + yargs-parser "21.1.1" + optionalDependencies: + "@nrwl/nx-darwin-arm64" "15.8.7" + "@nrwl/nx-darwin-x64" "15.8.7" + "@nrwl/nx-linux-arm-gnueabihf" "15.8.7" + "@nrwl/nx-linux-arm64-gnu" "15.8.7" + "@nrwl/nx-linux-arm64-musl" "15.8.7" + "@nrwl/nx-linux-x64-gnu" "15.8.7" + "@nrwl/nx-linux-x64-musl" "15.8.7" + "@nrwl/nx-win32-arm64-msvc" "15.8.7" + "@nrwl/nx-win32-x64-msvc" "15.8.7" + object-assign@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -9239,7 +9921,12 @@ object-hash@^3.0.0: resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== -object-inspect@^1.12.2, object-inspect@^1.9.0: +object-inspect@^1.12.3: + version "1.12.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" + integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== + +object-inspect@^1.9.0: version "1.12.2" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== @@ -9439,10 +10126,10 @@ pako@^1.0.3: resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== -papaparse@^5.3.2: - version "5.3.2" - resolved "https://registry.yarnpkg.com/papaparse/-/papaparse-5.3.2.tgz#d1abed498a0ee299f103130a6109720404fbd467" - integrity sha512-6dNZu0Ki+gyV0eBsFKJhYr+MdQYAzFUGlBMNj3GNrmHxmz1lfRa24CjFObPXtjcetlOv5Ad299MhIK0znp3afw== +papaparse@^5.4.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/papaparse/-/papaparse-5.4.0.tgz#a3e47c32f32466d30af67ce2fef7910b3c3d4d91" + integrity sha512-ZBQABWG09p+u8rFoJVl/GhgxZ5zy9Zh1Lu/LVc7VX5T4nljjC14/YTcpebYwqP218B9X307eBOP7Tuhoqv7v7w== parent-module@^1.0.0: version "1.0.1" @@ -9493,7 +10180,7 @@ parse5@4.0.0: resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== -parse5@6.0.1, parse5@^6.0.1: +parse5@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== @@ -10197,17 +10884,17 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@2.8.3: - version "2.8.3" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.3.tgz#ab697b1d3dd46fb4626fbe2f543afe0cc98d8632" - integrity sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw== +prettier@2.8.4: + version "2.8.4" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.4.tgz#34dd2595629bfbb79d344ac4a91ff948694463c3" + integrity sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw== pretty-bytes@^5.3.0: version "5.6.0" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== -pretty-format@^28.0.0, pretty-format@^28.1.1, pretty-format@^28.1.3: +pretty-format@^28.1.1, pretty-format@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-28.1.3.tgz#c9fba8cedf99ce50963a11b27d982a9ae90970d5" integrity sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q== @@ -10226,12 +10913,12 @@ pretty-format@^29.0.0, pretty-format@^29.3.1: ansi-styles "^5.0.0" react-is "^18.0.0" -pretty-format@^29.4.1: - version "29.4.1" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.4.1.tgz#0da99b532559097b8254298da7c75a0785b1751c" - integrity sha512-dt/Z761JUVsrIKaY215o1xQJBGlSmTx/h4cSqXqjHLnU1+Kt+mavVE7UgqJJO5ukx5HjSswHfmXz4LjS2oIJfg== +pretty-format@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.5.0.tgz#283134e74f70e2e3e7229336de0e4fce94ccde5a" + integrity sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw== dependencies: - "@jest/schemas" "^29.4.0" + "@jest/schemas" "^29.4.3" ansi-styles "^5.0.0" react-is "^18.0.0" @@ -10268,7 +10955,7 @@ promise-retry@^2.0.1: err-code "^2.0.2" retry "^0.12.0" -prompts@^2.0.1: +prompts@^2.0.1, prompts@~2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== @@ -10304,6 +10991,11 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +pure-rand@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.1.tgz#31207dddd15d43f299fdcdb2f572df65030c19af" + integrity sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg== + qs@6.11.0, qs@^6.11.0, qs@^6.4.0: version "6.11.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" @@ -10484,6 +11176,11 @@ require-from-string@^2.0.2: resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== +requireindex@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/requireindex/-/requireindex-1.2.0.tgz#3463cdb22ee151902635aa6c9535d4de9c2ef1ef" + integrity sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww== + requires-port@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" @@ -10522,6 +11219,11 @@ resolve.exports@1.1.0, resolve.exports@^1.1.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== +resolve.exports@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.1.tgz#cee884cd4e3f355660e501fa3276b27d7ffe5a20" + integrity sha512-OEJWVeimw8mgQuj3HfkNl4KqRevH7lzeQNaWRPfx0PPse7Jk6ozcsG4FKVgtzDsC1KUF+YlTHh17NcgHOPykLw== + resolve@1.22.1, resolve@^1.1.7, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.20.0: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" @@ -10578,6 +11280,19 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +rxjs-report-usage@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/rxjs-report-usage/-/rxjs-report-usage-1.0.6.tgz#6e06034d9e1592e8a45bee877631638e4bac2576" + integrity sha512-omv1DIv5z1kV+zDAEjaDjWSkx8w5TbFp5NZoPwUipwzYVcor/4So9ZU3bUyQ1c8lxY5Q0Es/ztWW7PGjY7to0Q== + dependencies: + "@babel/parser" "^7.10.3" + "@babel/traverse" "^7.10.3" + "@babel/types" "^7.10.3" + bent "~7.3.6" + chalk "~4.1.0" + glob "~7.2.0" + prompts "~2.4.2" + rxjs@6.6.7, rxjs@^6.5.4: version "6.6.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" @@ -10585,7 +11300,7 @@ rxjs@6.6.7, rxjs@^6.5.4: dependencies: tslib "^1.9.0" -rxjs@7.8.0, rxjs@^7.5.5, rxjs@^7.5.7: +rxjs@7.8.0, rxjs@^7.5.5, rxjs@^7.8.0: version "7.8.0" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4" integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== @@ -10651,10 +11366,10 @@ sass@^1.42.1: immutable "^4.0.0" source-map-js ">=0.6.2 <2.0.0" -sass@^1.58.0: - version "1.58.0" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.58.0.tgz#ee8aea3ad5ea5c485c26b3096e2df6087d0bb1cc" - integrity sha512-PiMJcP33DdKtZ/1jSjjqVIKihoDc6yWmYr9K/4r3fVVIEDAluD0q7XZiRKrNJcPK3qkLRF/79DND1H5q1LBjgg== +sass@^1.59.2: + version "1.59.2" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.59.2.tgz#537f6d11614d4f20f97696f23ad358ee398b1937" + integrity sha512-jJyO6SmbzkJexF8MUorHx5tAilcgabioYxT/BHbY4+OvoqmbHxsYlrjZ8Adhqcgl6Zqwie0TgMXLCAmPFxXOuw== dependencies: chokidar ">=3.0.0 <4.0.0" immutable "^4.0.0" @@ -10665,13 +11380,6 @@ sax@^1.2.4, sax@~1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -saxes@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" - integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== - dependencies: - xmlchars "^2.2.0" - saxes@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" @@ -10698,12 +11406,12 @@ schema-utils@^4.0.0: ajv-formats "^2.1.1" ajv-keywords "^5.0.0" -scroll-into-view-if-needed@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.0.4.tgz#4b3101712f0ac8ec982c0a7ab4d071e393241e21" - integrity sha512-s+/F50jwTOUt+u5oEIAzum9MN2lUQNvWBe/zfEsVQcbaERjGkKLq1s+2wCHkahMLC8nMLbzMVKivx9JhunXaZg== +scroll-into-view-if-needed@^3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.0.6.tgz#2c803a509c1036bc4a9c009fecc5c145f87e47cf" + integrity sha512-x+CW0kOzlFNOnseF0DBr0AJ5m+TgGmSOdEZwyiZW0gV87XBvxQKw5A8DvFFgabznA68XqLgVX+PwPX8OzsFvRA== dependencies: - compute-scroll-into-view "^2.0.4" + compute-scroll-into-view "^3.0.0" secure-compare@3.0.1: version "3.0.1" @@ -10936,10 +11644,10 @@ socks@^2.6.2: ip "^2.0.0" smart-buffer "^4.2.0" -sonarqube-scanner@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/sonarqube-scanner/-/sonarqube-scanner-3.0.0.tgz#e8187437d8fa793e69dd3662e48bbdc83f3919a8" - integrity sha512-qJz06on6UZ00emVckmIj6BF7HdBh1xLwkMw44LkTw7IrjKkxqenPDPGAUdb1+OCGL2s6/ccmolxRI5UNTM2o/w== +sonarqube-scanner@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/sonarqube-scanner/-/sonarqube-scanner-3.0.1.tgz#6266a059935ffdb0c1fe3ad90b347305e39d580c" + integrity sha512-u8HJITRe6E6HJWzerbSNAaH20rymSb8TJtnGd5H41jFxQm1hM+87+CqJaWDGzzkyufe2u8TdbTV4vb/4sFfqCA== dependencies: decompress "^4.2.1" fancy-log "^2.0.0" @@ -11200,7 +11908,7 @@ strip-final-newline@^3.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== -strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -11430,6 +12138,13 @@ thunky@^1.0.2: resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== +tmp@0.2.1, tmp@~0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" + integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== + dependencies: + rimraf "^3.0.0" + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -11437,13 +12152,6 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" -tmp@~0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" - integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== - dependencies: - rimraf "^3.0.0" - tmpl@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" @@ -11476,7 +12184,7 @@ totalist@^1.0.0: resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== -tough-cookie@^4.0.0, tough-cookie@^4.1.2: +tough-cookie@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.2.tgz#e53e84b85f24e0b65dd526f46628db6c85f6b874" integrity sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ== @@ -11498,15 +12206,15 @@ tree-kill@1.2.2: resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== -ts-jest@^28.0.0: - version "28.0.8" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-28.0.8.tgz#cd204b8e7a2f78da32cf6c95c9a6165c5b99cc73" - integrity sha512-5FaG0lXmRPzApix8oFG8RKjAz4ehtm8yMKOTy5HX3fY6W8kmvOrmcY0hKDElW52FJov+clhUbrKAqofnj4mXTg== +ts-jest@^29.0.0: + version "29.0.5" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.0.5.tgz#c5557dcec8fe434fcb8b70c3e21c6b143bfce066" + integrity sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA== dependencies: bs-logger "0.x" fast-json-stable-stringify "2.x" - jest-util "^28.0.0" - json5 "^2.2.1" + jest-util "^29.0.0" + json5 "^2.2.3" lodash.memoize "4.x" make-error "1.x" semver "7.x" @@ -11574,7 +12282,15 @@ tslib@^2.5.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== -tsutils@^3.21.0: +tsutils-etc@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/tsutils-etc/-/tsutils-etc-1.4.1.tgz#bd42a0079d534765ab314d087f8a89c77a68723f" + integrity sha512-6UPYgc7OXcIW5tFxlsZF3OVSBvDInl/BkS3Xsu64YITXk7WrnWTVByKWPCThFDBp5gl5IGHOzGMdQuDCE7OL4g== + dependencies: + "@types/yargs" "^17.0.0" + yargs "^17.0.0" + +tsutils@^3.0.0, tsutils@^3.17.1, tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== @@ -11813,20 +12529,6 @@ vlq@^1.0.0: resolved "https://registry.yarnpkg.com/vlq/-/vlq-1.0.1.tgz#c003f6e7c0b4c1edd623fd6ee50bbc0d6a1de468" integrity sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w== -w3c-hr-time@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" - integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== - dependencies: - browser-process-hrtime "^1.0.0" - -w3c-xmlserializer@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz#06cdc3eefb7e4d0b20a560a5a3aeb0d2d9a65923" - integrity sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg== - dependencies: - xml-name-validator "^4.0.0" - w3c-xmlserializer@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073" @@ -11868,11 +12570,12 @@ webidl-conversions@^7.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== -webpack-bundle-analyzer@^4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.7.0.tgz#33c1c485a7fcae8627c547b5c3328b46de733c66" - integrity sha512-j9b8ynpJS4K+zfO5GGwsAcQX4ZHpWV+yRiHDiL+bE0XHJ8NiPYLTNVQdlFYWxtpg9lfAQNlwJg16J9AJtFSXRg== +webpack-bundle-analyzer@^4.8.0: + version "4.8.0" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.8.0.tgz#951b8aaf491f665d2ae325d8b84da229157b1d04" + integrity sha512-ZzoSBePshOKhr+hd8u6oCkZVwpVaXgpw23ScGLFpR6SjYI7+7iIWYarjN6OEYOfRt8o7ZyZZQk0DuMizJ+LEIg== dependencies: + "@discoveryjs/json-ext" "0.5.7" acorn "^8.0.4" acorn-walk "^8.0.0" chalk "^4.1.0" @@ -12003,6 +12706,36 @@ webpack@5.75.0, webpack@^5.75.0: watchpack "^2.4.0" webpack-sources "^3.2.3" +webpack@^5.76.1: + version "5.76.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.76.1.tgz#7773de017e988bccb0f13c7d75ec245f377d295c" + integrity sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^0.0.51" + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/wasm-edit" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + acorn "^8.7.1" + acorn-import-assertions "^1.7.6" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.10.0" + es-module-lexer "^0.9.0" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.9" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.1.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.1.3" + watchpack "^2.4.0" + webpack-sources "^3.2.3" + websocket-driver@>=0.5.1, websocket-driver@^0.7.4: version "0.7.4" resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" @@ -12029,14 +12762,6 @@ whatwg-mimetype@^3.0.0: resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== -whatwg-url@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-10.0.0.tgz#37264f720b575b4a311bd4094ed8c760caaa05da" - integrity sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w== - dependencies: - tr46 "^3.0.0" - webidl-conversions "^7.0.0" - whatwg-url@^11.0.0: version "11.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018" @@ -12148,7 +12873,7 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -write-file-atomic@^4.0.1: +write-file-atomic@^4.0.1, write-file-atomic@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== @@ -12161,7 +12886,7 @@ ws@^7.3.1: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== -ws@^8.11.0, ws@^8.2.3, ws@^8.4.2: +ws@^8.11.0, ws@^8.4.2: version "8.11.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143" integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg== @@ -12222,10 +12947,10 @@ yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yaml@^2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.1.3.tgz#9b3a4c8aff9821b696275c79a8bee8399d945207" - integrity sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg== +yaml@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.2.1.tgz#3014bf0482dcd15147aa8e56109ce8632cd60ce4" + integrity sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw== yargs-parser@21.1.1, yargs-parser@^21.0.1, yargs-parser@^21.1.1: version "21.1.1" @@ -12245,6 +12970,19 @@ yargs@17.6.2, yargs@^17.2.1, yargs@^17.3.1, yargs@^17.6.2: y18n "^5.0.5" yargs-parser "^21.1.1" +yargs@^17.0.0: + version "17.7.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.1.tgz#34a77645201d1a8fc5213ace787c220eabbd0967" + integrity sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + yauzl@^2.4.2: version "2.10.0" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"