diff --git a/apps/red-ui/src/app/components/dossier-watermark-selector/dossier-watermark-selector.component.html b/apps/red-ui/src/app/components/dossier-watermark-selector/dossier-watermark-selector.component.html deleted file mode 100644 index de078ba1e..000000000 --- a/apps/red-ui/src/app/components/dossier-watermark-selector/dossier-watermark-selector.component.html +++ /dev/null @@ -1,37 +0,0 @@ -
-
- - {{ 'dossier-watermark-selector.watermark' | translate }} - - - - - {{ selectedWatermark(watermarkId, watermarks).name }} - - - - {{ watermark.name }} - - -
- -
- - {{ 'dossier-watermark-selector.preview' | translate }} - - - - - {{ selectedWatermark(previewWatermarkId, watermarks).name }} - - - - {{ watermark.name }} - - -
-
diff --git a/apps/red-ui/src/app/components/dossier-watermark-selector/dossier-watermark-selector.component.ts b/apps/red-ui/src/app/components/dossier-watermark-selector/dossier-watermark-selector.component.ts deleted file mode 100644 index 91d837f6b..000000000 --- a/apps/red-ui/src/app/components/dossier-watermark-selector/dossier-watermark-selector.component.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { Observable } from 'rxjs'; -import { IWatermark } from '@red/domain'; -import { WatermarkService } from '@services/entity-services/watermark.service'; -import { mapEach, Required } from '@iqser/common-ui'; -import { OptionWatermark } from '@components/dossier-watermark-selector/option-watermark.model'; -import { map, tap } from 'rxjs/operators'; - -const DocumentTypes = { - REDACTED: 'REDACTED', - PREVIEW: 'PREVIEW', -} as const; -type DocumentType = keyof typeof DocumentTypes; - -export interface UpdateWatermarkIdsEvent { - watermarkId: string | null; - previewWatermarkId: string | null; -} - -@Component({ - selector: 'redaction-dossier-watermark-selector', - templateUrl: './dossier-watermark-selector.component.html', - styleUrls: ['./dossier-watermark-selector.component.scss'], -}) -export class DossierWatermarkSelectorComponent implements OnInit { - @Input() @Required() dossierTemplateId!: string; - @Input() watermarkId: string | null; - @Input() previewWatermarkId: string | null; - @Output() readonly idsUpdate = new EventEmitter(); - watermarks$: Observable; - readonly documentTypes = DocumentTypes; - #oldWatermarkId: string | null; - #oldPreviewWatermarkId: string | null; - - constructor(private readonly _watermarkService: WatermarkService) {} - - ngOnInit(): void { - this.watermarks$ = this._watermarkService.getWatermarks(this.dossierTemplateId).pipe( - map((watermarks: IWatermark[]) => watermarks.sort(this.sortByStatusFn)), - mapEach((watermark: IWatermark) => new OptionWatermark(watermark)), - tap((watermarks: OptionWatermark[]) => (this.#oldWatermarkId = watermarks[0].id)), - tap((watermarks: OptionWatermark[]) => (this.#oldPreviewWatermarkId = watermarks[0].id)), - ); - } - - sortByStatusFn = (a, b) => { - if (a.enabled === b.enabled) { - return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1; - } - return a.enabled ? -1 : 1; - }; - - toggleWatermarkSelection(checked: boolean, documentType: DocumentType): void { - if (checked) { - if (documentType === DocumentTypes.REDACTED) { - this.watermarkId = this.#oldWatermarkId; - } else { - this.previewWatermarkId = this.#oldPreviewWatermarkId; - } - } else { - if (documentType === DocumentTypes.REDACTED) { - this.#oldWatermarkId = this.watermarkId; - this.watermarkId = null; - } else { - this.#oldPreviewWatermarkId = this.previewWatermarkId; - this.previewWatermarkId = null; - } - } - this.emitNewValues(); - } - - emitNewValues(): void { - this.idsUpdate.emit({ - watermarkId: this.watermarkId, - previewWatermarkId: this.previewWatermarkId, - }); - } - - selectedWatermark(id: string, watermarks: OptionWatermark[]): OptionWatermark { - return watermarks.find(watermark => watermark.id === id); - } -} diff --git a/apps/red-ui/src/app/components/dossier-watermark-selector/option-watermark.model.ts b/apps/red-ui/src/app/components/dossier-watermark-selector/option-watermark.model.ts deleted file mode 100644 index 98656a261..000000000 --- a/apps/red-ui/src/app/components/dossier-watermark-selector/option-watermark.model.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { IWatermark } from '@red/domain'; -import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; - -export class OptionWatermark { - id!: string; - name!: string; - enabled!: boolean; - - constructor(watermark: IWatermark) { - this.id = watermark.id; - this.name = watermark.enabled ? watermark.name : `${_('disabled')} - ${watermark.name}`; - this.enabled = watermark.enabled; - } -} 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 2b065449e..6a035632a 100644 --- a/apps/red-ui/src/app/guards/dossier-files-guard.ts +++ b/apps/red-ui/src/app/guards/dossier-files-guard.ts @@ -5,6 +5,7 @@ import { FilesService } from '@services/files/files.service'; 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'; @Injectable({ providedIn: 'root' }) export class DossierFilesGuard implements CanActivate { @@ -12,6 +13,7 @@ export class DossierFilesGuard implements CanActivate { private readonly _injector: Injector, private readonly _filesMapService: FilesMapService, private readonly _filesService: FilesService, + private readonly _dictionaryService: DictionaryService, private readonly _router: Router, ) {} @@ -21,6 +23,8 @@ export class DossierFilesGuard implements CanActivate { const token: ProviderToken = route.data.dossiersService; const dossiersService: DossiersService = this._injector.get(token); + await firstValueFrom(this._dictionaryService.loadDossierDictionary(dossierTemplateId, dossierId)); + if (!dossiersService.has(dossierId)) { await this._router.navigate(['/main', dossierTemplateId]); return false; diff --git a/apps/red-ui/src/app/guards/watermark-exists.guard.ts b/apps/red-ui/src/app/guards/watermark-exists.guard.ts new file mode 100644 index 000000000..0fab7ef6c --- /dev/null +++ b/apps/red-ui/src/app/guards/watermark-exists.guard.ts @@ -0,0 +1,21 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router'; +import { WatermarksMapService } from '@services/entity-services/watermarks-map.service'; +import { DOSSIER_TEMPLATE_ID, WATERMARK_ID } from '@red/domain'; + +@Injectable({ providedIn: 'root' }) +export class WatermarkExistsGuard implements CanActivate { + constructor(private readonly _watermarksMapService: WatermarksMapService, private readonly _router: Router) {} + + async canActivate(route: ActivatedRouteSnapshot): Promise { + const dossierTemplateId = route.parent.paramMap.get(DOSSIER_TEMPLATE_ID); + const watermarkId = route.paramMap.get(WATERMARK_ID); + + if (this._watermarksMapService.get(dossierTemplateId, watermarkId)) { + return true; + } + + await this._router.navigate(['main', 'admin', 'dossier-templates', dossierTemplateId, 'watermarks']); + return false; + } +} 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 4836ff31e..88fe96921 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 @@ -15,14 +15,12 @@ import { GeneralConfigScreenComponent } from './screens/general-config/general-c import { BaseAdminScreenComponent } from './base-admin-screen/base-admin-screen.component'; import { BaseDossierTemplateScreenComponent } from './base-dossier-templates-screen/base-dossier-template-screen.component'; import { DossierTemplatesGuard } from '@guards/dossier-templates.guard'; -import { DOSSIER_TEMPLATE_ID, ENTITY_TYPE, WATERMARK_ID } from '@red/domain'; +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 { WatermarksListingScreenComponent } from './screens/watermarks-listing/watermarks-listing-screen.component'; -import { BaseWatermarkScreenComponent } from './base-watermark-screen/base-watermark-screen.component'; +import { PermissionsGuard } from '@guards/permissions-guard'; const dossierTemplateIdRoutes: Routes = [ { @@ -72,25 +70,8 @@ const dossierTemplateIdRoutes: Routes = [ }, { path: 'watermarks', - children: [ - { - path: '', - component: WatermarksListingScreenComponent, - canActivate: [CompositeRouteGuard], - data: { - routeGuards: [AuthGuard, RedRoleGuard], - }, - }, - { - path: `:${WATERMARK_ID}`, - component: BaseWatermarkScreenComponent, - canActivate: [CompositeRouteGuard], - loadChildren: () => import('./screens/watermark/watermark.module').then(m => m.WatermarkModule), - data: { - routeGuards: [AuthGuard, RedRoleGuard], - }, - }, - ], + component: BaseDossierTemplateScreenComponent, + loadChildren: () => import('./screens/watermark/watermark.module').then(m => m.WatermarkModule), }, { path: 'reports', 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 89bbcaa60..ec6c82e40 100644 --- a/apps/red-ui/src/app/modules/admin/admin.module.ts +++ b/apps/red-ui/src/app/modules/admin/admin.module.ts @@ -45,8 +45,6 @@ import { SystemPreferencesFormComponent } from './screens/general-config/system- 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 { WatermarksListingScreenComponent } from './screens/watermarks-listing/watermarks-listing-screen.component'; -import { BaseWatermarkScreenComponent } from './base-watermark-screen/base-watermark-screen.component'; const dialogs = [ AddEditCloneDossierTemplateDialogComponent, @@ -74,7 +72,6 @@ const screens = [ GeneralConfigScreenComponent, DossierAttributesListingScreenComponent, DossierStatesListingScreenComponent, - WatermarksListingScreenComponent, ]; const components = [ @@ -87,7 +84,6 @@ const components = [ BaseAdminScreenComponent, BaseDossierTemplateScreenComponent, BaseEntityScreenComponent, - BaseWatermarkScreenComponent, GeneralConfigFormComponent, SmtpFormComponent, SystemPreferencesFormComponent, diff --git a/apps/red-ui/src/app/modules/admin/base-watermark-screen/base-watermark-screen.component.html b/apps/red-ui/src/app/modules/admin/base-watermark-screen/base-watermark-screen.component.html deleted file mode 100644 index ebc6f12c6..000000000 --- a/apps/red-ui/src/app/modules/admin/base-watermark-screen/base-watermark-screen.component.html +++ /dev/null @@ -1,27 +0,0 @@ -
- - -
-
- - -
-
diff --git a/apps/red-ui/src/app/modules/admin/base-watermark-screen/base-watermark-screen.component.ts b/apps/red-ui/src/app/modules/admin/base-watermark-screen/base-watermark-screen.component.ts deleted file mode 100644 index 5a7dd87db..000000000 --- a/apps/red-ui/src/app/modules/admin/base-watermark-screen/base-watermark-screen.component.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { firstValueFrom } from 'rxjs'; -import { AdminDialogService } from '../services/admin-dialog.service'; -import { ConfirmationDialogInput, getParam, LoadingService, Toaster } from '@iqser/common-ui'; -import { WatermarkService } from '../../../services/entity-services/watermark.service'; -import { DOSSIER_TEMPLATE_ID, WATERMARK_ID } from '@red/domain'; -import { Router } from '@angular/router'; -import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; - -@Component({ - templateUrl: './base-watermark-screen.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class BaseWatermarkScreenComponent { - readonly #dossierTemplateId: string = getParam(DOSSIER_TEMPLATE_ID); - readonly #watermarkId: string = getParam(WATERMARK_ID); - - constructor( - private readonly _dialogService: AdminDialogService, - private readonly _loadingService: LoadingService, - private readonly _watermarkService: WatermarkService, - private readonly _toaster: Toaster, - private readonly _router: Router, - ) {} - - get editMode(): boolean { - return !!Number(this.#watermarkId); - } - - async openDeleteWatermarkDialog($event: MouseEvent): Promise { - const isUsed = await firstValueFrom(this._watermarkService.isWatermarkUsed(this.#watermarkId)); - - if (!isUsed) { - await this._deleteWatermark(this.#watermarkId); - return; - } - - const data = new ConfirmationDialogInput({ - question: _('watermarks-listing.watermark-is-used'), - }); - this._dialogService.openDialog('confirm', $event, data, async () => { - await this._deleteWatermark(this.#watermarkId); - }); - } - - private async _deleteWatermark(watermarkId: string): Promise { - this._loadingService.start(); - await firstValueFrom(this._watermarkService.deleteWatermark(watermarkId)); - this._toaster.success(_('watermarks-listing.action.delete-success')); - await this._router.navigate([`/main/admin/dossier-templates/${this.#dossierTemplateId}/watermarks`]); - this._loadingService.stop(); - } -} diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/user-details/user-details.component.html b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/user-details/user-details.component.html index 2eeaa6155..24bb4ea3d 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/user-details/user-details.component.html +++ b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/user-details/user-details.component.html @@ -52,7 +52,7 @@ value || (role === this._ROLE_REQUIREMENTS[key] && this.user?.roles.includes(key)), false, @@ -86,7 +86,7 @@ export class UserDetailsComponent extends BaseFormComponent implements OnChanges const userData: IProfileUpdateRequest = { ...this.form.getRawValue(), roles: this.activeRoles }; if (!this.user) { - await firstValueFrom(this.userService.create(userData)) + await firstValueFrom(this._userService.create(userData)) .then(() => { this.closeDialog.emit(true); }) @@ -99,7 +99,7 @@ export class UserDetailsComponent extends BaseFormComponent implements OnChanges this._loadingService.stop(); }); } else { - await firstValueFrom(this.userService.updateProfile(userData, this.user.id)); + await firstValueFrom(this._userService.updateProfile(userData, this.user.id)); this.closeDialog.emit(true); } } @@ -119,6 +119,12 @@ export class UserDetailsComponent extends BaseFormComponent implements OnChanges } } + disabledDelete(user: User): boolean { + const userAdmin = user.roles.includes('RED_ADMIN'); + const currentUserAdmin = this._userService.currentUser.roles.includes('RED_ADMIN'); + return user.id === this._userService.currentUser.id || (userAdmin && !currentUserAdmin); + } + private _getForm(): UntypedFormGroup { return this._formBuilder.group({ firstName: [this.user?.firstName, Validators.required], diff --git a/apps/red-ui/src/app/modules/admin/screens/digital-signature/digital-signature-screen.component.scss b/apps/red-ui/src/app/modules/admin/screens/digital-signature/digital-signature-screen.component.scss index 4526cdf81..d309c5343 100644 --- a/apps/red-ui/src/app/modules/admin/screens/digital-signature/digital-signature-screen.component.scss +++ b/apps/red-ui/src/app/modules/admin/screens/digital-signature/digital-signature-screen.component.scss @@ -18,5 +18,5 @@ form { /* target the input field inexplicably to throw Chrome's AI off. * feel free to use a more complicated selector */ input[name='keySecret']:not(:placeholder-shown) { - font-family: 'secret'; + font-family: 'secret', serif; } diff --git a/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/combo-series-vertical.component.ts b/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/combo-series-vertical.component.ts index ee6c92cb5..b8d9fe052 100644 --- a/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/combo-series-vertical.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/combo-series-vertical.component.ts @@ -193,7 +193,7 @@ export class ComboSeriesVerticalComponent implements OnChanges { return item !== undefined; } - trackBy(index, bar): string { + trackBy(_index, bar): string { return bar.label; } } 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 aabf5e7e7..09310f37b 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 @@ -73,7 +73,7 @@ implement readonly translations = rolesTranslations; readonly iconButtonTypes = IconButtonTypes; readonly circleButtonTypes = CircleButtonTypes; - readonly currentUser = this.userService.currentUser; + readonly currentUser = this._userService.currentUser; readonly canDeleteSelected$ = this.#canDeleteSelected$; readonly tableHeaderLabel = _('user-listing.table-header.title'); readonly tableColumnConfigs: TableColumnConfig[] = [ @@ -58,14 +58,14 @@ export class UserListingScreenComponent extends ListingComponent implement action: (): void => this.openAddEditUserDialog(), type: IconButtonTypes.primary, icon: 'iqser:plus', - disabled$: this.userService.currentUser$.pipe(map(user => !user.isAdmin)), + disabled$: this._userService.currentUser$.pipe(map(user => !user.isAdmin)), }, ]; collapsedDetails = false; chartConfig: DonutChartConfig[] = []; constructor( - readonly userService: UserService, + private readonly _userService: UserService, private readonly _loadingService: LoadingService, private readonly _dialogService: AdminDialogService, private readonly _translateService: TranslateService, @@ -110,7 +110,7 @@ export class UserListingScreenComponent extends ListingComponent implement async toggleActive(user: User) { this._loadingService.start(); const requestBody = { ...user, roles: user.isActive ? [] : ['RED_USER'] }; - await firstValueFrom(this.userService.updateProfile(requestBody, user.id)); + await firstValueFrom(this._userService.updateProfile(requestBody, user.id)); await this.#loadData(); } @@ -119,7 +119,7 @@ export class UserListingScreenComponent extends ListingComponent implement } async #loadData() { - await firstValueFrom(this.userService.loadAll()); + await firstValueFrom(this._userService.loadAll()); this.#computeStats(); this._loadingService.stop(); } @@ -145,4 +145,10 @@ export class UserListingScreenComponent extends ListingComponent implement }; this.filterService.addFilterGroups([roleFiltersGroup]); } + + deleteDisabled(user: User): boolean { + const userAdmin = user.roles.includes('RED_ADMIN'); + const currentUserAdmin = this._userService.currentUser.roles.includes('RED_ADMIN'); + return user.id === this._userService.currentUser.id || (userAdmin && !currentUserAdmin); + } } 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 9a05c9caa..da25b5949 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 @@ -12,7 +12,7 @@ -
+
@@ -22,7 +22,6 @@
- - + + + + +
@@ -92,10 +103,10 @@ 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 3ae34e23b..5d3435e9d 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 @@ -11,7 +11,6 @@ import { firstValueFrom } from 'rxjs'; import dayjs from 'dayjs'; import { Router } from '@angular/router'; import { DossiersDialogService } from '../../../shared-dossiers/services/dossiers-dialog.service'; -import { UpdateWatermarkIdsEvent } from '@components/dossier-watermark-selector/dossier-watermark-selector.component'; interface DialogData { readonly dossierTemplateId?: string; @@ -67,17 +66,11 @@ export class AddDossierDialogComponent extends BaseDialogComponent implements On } async ngOnInit(): Promise { - this.dossierTemplateId = this.form.get('dossierTemplateId').value; - await this.dossierTemplateChanged(this.dossierTemplateId); + await this.dossierTemplateChanged(this.form.get('dossierTemplateId').value); } reportTemplateValueMapper = (reportTemplate: IReportTemplate) => reportTemplate.templateId; - updateWatermarkIds(event: UpdateWatermarkIdsEvent) { - this.form.get('watermarkId').setValue(event.watermarkId); - this.form.get('previewWatermarkId').setValue(event.previewWatermarkId); - } - async save(options?: SaveOptions) { this._loadingService.start(); const savedDossier = await firstValueFrom(this._activeDossiersService.createOrUpdate(this._formToObject())); @@ -94,7 +87,9 @@ export class AddDossierDialogComponent extends BaseDialogComponent implements On this._loadingService.stop(); } - async dossierTemplateChanged(dossierTemplateId) { + async dossierTemplateChanged(dossierTemplateId: string) { + this.dossierTemplateId = dossierTemplateId; + // get current selected dossierTemplate const dossierTemplate = this.dossierTemplates.find(r => r.dossierTemplateId === dossierTemplateId); diff --git a/apps/red-ui/src/app/modules/shared/pipes/name.pipe.ts b/apps/red-ui/src/app/modules/shared/pipes/name.pipe.ts index aba1521d5..fd4c06bb3 100644 --- a/apps/red-ui/src/app/modules/shared/pipes/name.pipe.ts +++ b/apps/red-ui/src/app/modules/shared/pipes/name.pipe.ts @@ -10,7 +10,7 @@ function getInitials(name: string) { const splittedName = name.split(' ').filter(value => value !== ' ' && value !== ''); return splittedName - .filter((value, index) => index < 2) + .filter((_value, index) => index < 2) .map(str => str[0]) .join(''); } 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 787a6f98d..02f0b6fa5 100644 --- a/apps/red-ui/src/app/modules/shared/shared.module.ts +++ b/apps/red-ui/src/app/modules/shared/shared.module.ts @@ -23,20 +23,20 @@ import { TypeFilterComponent } from './components/type-filter/type-filter.compon import { TeamMembersComponent } from './components/team-members/team-members.component'; import { EditorComponent } from './components/editor/editor.component'; import { ExpandableFileActionsComponent } from './components/expandable-file-actions/expandable-file-actions.component'; -import { ProcessingIndicatorComponent } from '@shared/components/processing-indicator/processing-indicator.component'; -import { DossierStateComponent } from '@shared/components/dossier-state/dossier-state.component'; +import { ProcessingIndicatorComponent } from './components/processing-indicator/processing-indicator.component'; +import { DossierStateComponent } from './components/dossier-state/dossier-state.component'; import { FileStatsComponent } from './components/file-stats/file-stats.component'; -import { FileNameColumnComponent } from '@shared/components/file-name-column/file-name-column.component'; -import { DossierNameColumnComponent } from '@shared/components/dossier-name-column/dossier-name-column.component'; +import { FileNameColumnComponent } from './components/file-name-column/file-name-column.component'; +import { DossierNameColumnComponent } from './components/dossier-name-column/dossier-name-column.component'; import { MAT_DATE_FORMATS } from '@angular/material/core'; -import { DossiersTypeSwitchComponent } from '@shared/components/dossiers-type-switch/dossiers-type-switch.component'; +import { DossiersTypeSwitchComponent } from './components/dossiers-type-switch/dossiers-type-switch.component'; import { TranslateModule } from '@ngx-translate/core'; import { RouterModule } from '@angular/router'; -import { AddDossierDialogComponent } from '@shared/dialogs/add-dossier-dialog/add-dossier-dialog.component'; -import { SharedDialogService } from '@shared/services/dialog.service'; -import { DossierWatermarkSelectorComponent } from '@components/dossier-watermark-selector/dossier-watermark-selector.component'; -import { AddEditEntityComponent } from '@shared/components/add-edit-entity/add-edit-entity.component'; +import { AddDossierDialogComponent } from './dialogs/add-dossier-dialog/add-dossier-dialog.component'; +import { SharedDialogService } from './services/dialog.service'; +import { AddEditEntityComponent } from './components/add-edit-entity/add-edit-entity.component'; import { ColorPickerModule } from 'ngx-color-picker'; +import { WatermarkSelectorComponent } from './components/dossier-watermark-selector/watermark-selector.component'; const buttons = [FileDownloadBtnComponent, UserButtonComponent]; @@ -58,7 +58,7 @@ const components = [ FileNameColumnComponent, DossiersTypeSwitchComponent, AddDossierDialogComponent, - DossierWatermarkSelectorComponent, + WatermarkSelectorComponent, AddEditEntityComponent, ...buttons, diff --git a/apps/red-ui/src/app/modules/trash/trash-screen/trash-screen.component.scss b/apps/red-ui/src/app/modules/trash/trash-screen/trash-screen.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/apps/red-ui/src/app/modules/trash/trash-screen/trash-screen.component.ts b/apps/red-ui/src/app/modules/trash/trash-screen/trash-screen.component.ts index a5b277f3e..2a4b16926 100644 --- a/apps/red-ui/src/app/modules/trash/trash-screen/trash-screen.component.ts +++ b/apps/red-ui/src/app/modules/trash/trash-screen/trash-screen.component.ts @@ -17,7 +17,6 @@ import { TrashDialogService } from '../services/trash-dialog.service'; @Component({ templateUrl: './trash-screen.component.html', - styleUrls: ['./trash-screen.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, providers: listingProvidersFactory({ entitiesService: TrashService, diff --git a/apps/red-ui/src/app/modules/upload-download/file-drop/file-drop.component.scss b/apps/red-ui/src/app/modules/upload-download/file-drop/file-drop.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/apps/red-ui/src/app/modules/upload-download/file-drop/file-drop.component.ts b/apps/red-ui/src/app/modules/upload-download/file-drop/file-drop.component.ts index 29b4dc609..aa5f4f56a 100644 --- a/apps/red-ui/src/app/modules/upload-download/file-drop/file-drop.component.ts +++ b/apps/red-ui/src/app/modules/upload-download/file-drop/file-drop.component.ts @@ -9,7 +9,6 @@ import { DOSSIER_ID } from '../../../tokens'; @Component({ templateUrl: './file-drop.component.html', - styleUrls: ['./file-drop.component.scss'], }) export class FileDropComponent { constructor( diff --git a/apps/red-ui/src/app/services/dossier-templates/dossier-templates.service.ts b/apps/red-ui/src/app/services/dossier-templates/dossier-templates.service.ts index 2b019a522..2922875c4 100644 --- a/apps/red-ui/src/app/services/dossier-templates/dossier-templates.service.ts +++ b/apps/red-ui/src/app/services/dossier-templates/dossier-templates.service.ts @@ -9,6 +9,7 @@ import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { DictionaryService } from '../entity-services/dictionary.service'; import { DefaultColorsService } from '@services/entity-services/default-colors.service'; +import { WatermarkService } from '@services/entity-services/watermark.service'; const DOSSIER_TEMPLATE_CONFLICT_MSG = _('dossier-templates-listing.error.conflict'); const GENERIC_MSG = _('dossier-templates-listing.error.generic'); @@ -26,6 +27,7 @@ export class DossierTemplatesService extends EntitiesService templates)); } return of(templates); diff --git a/apps/red-ui/src/app/services/entity-services/default-colors.service.ts b/apps/red-ui/src/app/services/entity-services/default-colors.service.ts index 89bf9d835..de87a6b04 100644 --- a/apps/red-ui/src/app/services/entity-services/default-colors.service.ts +++ b/apps/red-ui/src/app/services/entity-services/default-colors.service.ts @@ -15,6 +15,13 @@ export class DefaultColorsService extends EntitiesService { + return this.getEntityChanged$(dossierTemplateId).pipe( + map(c => c[colorType]), + map(c => c || fallback), + ); + } + loadAll(dossierTemplateIds: string[]): Observable { return forkJoin(dossierTemplateIds.map(id => super.getFor(id))).pipe( mapEach(defaultColors => new DefaultColors(defaultColors)), diff --git a/apps/red-ui/src/app/services/entity-services/dictionary.service.ts b/apps/red-ui/src/app/services/entity-services/dictionary.service.ts index aaf0ab737..1899e9db9 100644 --- a/apps/red-ui/src/app/services/entity-services/dictionary.service.ts +++ b/apps/red-ui/src/app/services/entity-services/dictionary.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { firstValueFrom, forkJoin, Observable, throwError } from 'rxjs'; +import { forkJoin, Observable, throwError } from 'rxjs'; import { EntitiesService, List, QueryParam, RequiredParam, Toaster, Validate } from '@iqser/common-ui'; import { Dictionary, DictionaryEntryType, DictionaryEntryTypes, IDictionary, IUpdateDictionary, SuperTypes } from '@red/domain'; import { catchError, map, switchMap, tap } from 'rxjs/operators'; @@ -8,6 +8,7 @@ import { DossierTemplateStatsService } from './dossier-template-stats.service'; import { DictionariesMapService } from './dictionaries-map.service'; import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http'; import { FALLBACK_COLOR } from '@utils/constants'; +import { DossierDictionariesMapService } from '@services/entity-services/dossier-dictionaries-map.service'; const MIN_WORD_LENGTH = 2; @@ -22,6 +23,7 @@ export class DictionaryService extends EntitiesService private readonly _toaster: Toaster, private readonly _dossierTemplateStatsService: DossierTemplateStatsService, private readonly _dictionariesMapService: DictionariesMapService, + private readonly _dossierDictionariesMapService: DossierDictionariesMapService, ) { super(); } @@ -144,7 +146,7 @@ export class DictionaryService extends EntitiesService async getDictionariesOptions(dossierTemplateId: string, dossierId: string): Promise { const possibleDictionaries: Dictionary[] = []; - const dossierDictionary: Dictionary = await firstValueFrom(this.getDossierDictionary(dossierTemplateId, dossierId)); + const dossierDictionary: Dictionary = this._dossierDictionariesMapService.get(dossierId, 'dossier_redaction'); for (const dictionary of this._dictionariesMapService.get(dossierTemplateId)) { if (!dictionary.virtual && dictionary.addToDictionaryAction) { @@ -161,7 +163,7 @@ export class DictionaryService extends EntitiesService return possibleDictionaries; } - getDossierDictionary(dossierTemplateId: string, dossierId: string): Observable { + loadDossierDictionary(dossierTemplateId: string, dossierId: string): Observable { return this.getForType(dossierTemplateId, 'dossier_redaction', dossierId).pipe( map( dictionary => @@ -170,6 +172,9 @@ export class DictionaryService extends EntitiesService type: 'dossier_redaction', }), ), + tap(dictionary => { + this._dossierDictionariesMapService.set(dossierId, [dictionary]); + }), ); } diff --git a/apps/red-ui/src/app/services/entity-services/dossier-dictionaries-map.service.ts b/apps/red-ui/src/app/services/entity-services/dossier-dictionaries-map.service.ts new file mode 100644 index 000000000..a4f605e87 --- /dev/null +++ b/apps/red-ui/src/app/services/entity-services/dossier-dictionaries-map.service.ts @@ -0,0 +1,8 @@ +import { Injectable } from '@angular/core'; +import { Dictionary, DOSSIER_ID, IDictionary } from '@red/domain'; +import { EntitiesMapService } from '@iqser/common-ui'; + +@Injectable({ providedIn: 'root' }) +export class DossierDictionariesMapService extends EntitiesMapService { + protected readonly _primaryKey = DOSSIER_ID; +} diff --git a/apps/red-ui/src/app/services/entity-services/watermark.service.ts b/apps/red-ui/src/app/services/entity-services/watermark.service.ts index 1f2af26c3..5d296d215 100644 --- a/apps/red-ui/src/app/services/entity-services/watermark.service.ts +++ b/apps/red-ui/src/app/services/entity-services/watermark.service.ts @@ -1,8 +1,9 @@ import { Injectable } from '@angular/core'; -import { GenericService, QueryParam, RequiredParam, Validate } from '@iqser/common-ui'; -import { IWatermark } from '@red/domain'; -import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { GenericService, mapEach, QueryParam, RequiredParam, Validate } from '@iqser/common-ui'; +import { IWatermark, Watermark } from '@red/domain'; +import { forkJoin, Observable } from 'rxjs'; +import { map, switchMap, tap } from 'rxjs/operators'; +import { WatermarksMapService } from '@services/entity-services/watermarks-map.service'; interface IsUsedResponse { value: boolean; @@ -14,25 +15,40 @@ interface IsUsedResponse { export class WatermarkService extends GenericService { protected readonly _defaultModelPath = 'watermark'; - @Validate() - saveWatermark(@RequiredParam() body: IWatermark) { - return this._post(body, `${this._defaultModelPath}`); + constructor(private readonly _watermarksMapService: WatermarksMapService) { + super(); } @Validate() - deleteWatermark(@RequiredParam() watermarkId: string) { - return super.delete({}, `${this._defaultModelPath}/${watermarkId}`); + deleteWatermark(@RequiredParam() dossierTemplateId: string, @RequiredParam() watermarkId: string): Observable { + return super + .delete(null, `${this._defaultModelPath}/${watermarkId}`) + .pipe(switchMap(() => this.loadForDossierTemplate(dossierTemplateId))); } @Validate() - getWatermark(@RequiredParam() watermarkId: string) { - return this._getOne([watermarkId]); + saveWatermark(@RequiredParam() body: IWatermark): Observable { + return this._post(body, `${this._defaultModelPath}`).pipe( + switchMap(watermark => + this.loadForDossierTemplate(watermark.dossierTemplateId).pipe( + map(() => this._watermarksMapService.get(watermark.dossierTemplateId, watermark.id.toString())), + ), + ), + ); } @Validate() - getWatermarks(@RequiredParam() dossierTemplateId: string): Observable { + loadForDossierTemplate(@RequiredParam() dossierTemplateId: string): Observable { const queryParams: QueryParam[] = [{ key: 'dossierTemplateId', value: dossierTemplateId }]; - return this.getAll(this._defaultModelPath, queryParams); + return this.getAll(this._defaultModelPath, queryParams).pipe( + mapEach(entity => new Watermark(entity)), + tap(entities => this._watermarksMapService.set(dossierTemplateId, entities.sort(this.sortByStatusFn))), + ); + } + + @Validate() + loadAll(@RequiredParam() dossierTemplateIds: string[]): Observable { + return forkJoin(dossierTemplateIds.map(id => this.loadForDossierTemplate(id))).pipe(map(arrays => [].concat(...arrays))); } @Validate() @@ -40,4 +56,11 @@ export class WatermarkService extends GenericService { const queryParams: QueryParam[] = [{ key: 'watermarkId', value: watermarkId }]; return this.getAll(`${this._defaultModelPath}/used`, queryParams).pipe(map(result => result.value)); } + + sortByStatusFn = (a: Watermark, b: Watermark) => { + if (a.enabled === b.enabled) { + return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1; + } + return a.enabled ? -1 : 1; + }; } diff --git a/apps/red-ui/src/app/services/entity-services/watermarks-map.service.ts b/apps/red-ui/src/app/services/entity-services/watermarks-map.service.ts new file mode 100644 index 000000000..ed55b3931 --- /dev/null +++ b/apps/red-ui/src/app/services/entity-services/watermarks-map.service.ts @@ -0,0 +1,8 @@ +import { Injectable } from '@angular/core'; +import { DOSSIER_TEMPLATE_ID, IWatermark, Watermark } from '@red/domain'; +import { EntitiesMapService } from '@iqser/common-ui'; + +@Injectable({ providedIn: 'root' }) +export class WatermarksMapService extends EntitiesMapService { + protected readonly _primaryKey = DOSSIER_TEMPLATE_ID; +} diff --git a/apps/red-ui/src/app/utils/missing-translations-handler.ts b/apps/red-ui/src/app/utils/missing-translations-handler.ts index 0e642ca73..ebeb631a9 100644 --- a/apps/red-ui/src/app/utils/missing-translations-handler.ts +++ b/apps/red-ui/src/app/utils/missing-translations-handler.ts @@ -3,7 +3,6 @@ import { MissingTranslationHandler, MissingTranslationHandlerParams } from '@ngx export class REDMissingTranslationHandler implements MissingTranslationHandler { handle(params: MissingTranslationHandlerParams): any { const missingKey = params.key; - // console.error('Missing translation: ' + missingKey); return `?${missingKey}?`; } } diff --git a/apps/red-ui/src/assets/config/config.json b/apps/red-ui/src/assets/config/config.json index 8e7d077a8..62578148a 100644 --- a/apps/red-ui/src/assets/config/config.json +++ b/apps/red-ui/src/assets/config/config.json @@ -1,7 +1,7 @@ { "ADMIN_CONTACT_NAME": null, "ADMIN_CONTACT_URL": null, - "API_URL": "https://dev-05.iqser.cloud/redaction-gateway-v1", + "API_URL": "https://dev-08.iqser.cloud/redaction-gateway-v1", "APP_NAME": "RedactManager", "AUTO_READ_TIME": 3, "BACKEND_APP_VERSION": "4.4.40", @@ -11,7 +11,7 @@ "MAX_RETRIES_ON_SERVER_ERROR": 3, "OAUTH_CLIENT_ID": "redaction", "OAUTH_IDP_HINT": null, - "OAUTH_URL": "https://dev-05.iqser.cloud/auth/realms/redaction", + "OAUTH_URL": "https://dev-08.iqser.cloud/auth/realms/redaction", "RECENT_PERIOD_IN_HOURS": 24, "SELECTION_MODE": "structural", "MANUAL_BASE_URL": "https://docs.redactmanager.com/preview" diff --git a/apps/red-ui/src/assets/i18n/en.json b/apps/red-ui/src/assets/i18n/en.json index 427017859..876f57998 100644 --- a/apps/red-ui/src/assets/i18n/en.json +++ b/apps/red-ui/src/assets/i18n/en.json @@ -627,7 +627,7 @@ "cancel": "Cancel", "save": "Save" }, - "title": "Edit Dossier Dictionary" + "title": "{readOnly, select, false{Edit } other{}}Dossier Dictionary" }, "dictionary-overview": { "compare": { @@ -799,7 +799,7 @@ "total-people": "Total users" }, "table-col-names": { - "documents-status": "Documents Status", + "documents-status": "Documents State", "dossier-state": "Dossier State", "last-modified": "Last modified", "name": "Name", @@ -1413,7 +1413,7 @@ }, "filters": { "assigned-people": "Assignee(s)", - "documents-status": "Documents Status", + "documents-status": "Documents State", "dossier-state": "Dossier State", "dossier-templates": "Dossier Templates", "empty": "Empty", diff --git a/libs/common-ui b/libs/common-ui index 345454be9..d3e45f5c9 160000 --- a/libs/common-ui +++ b/libs/common-ui @@ -1 +1 @@ -Subproject commit 345454be9d260a3fbb27e9e5b57902dda2e350e0 +Subproject commit d3e45f5c9868f5ada0af084d93b42f9e92d68c5e diff --git a/libs/red-domain/src/lib/dictionaries/dictionary.model.ts b/libs/red-domain/src/lib/dictionaries/dictionary.model.ts index 821bbf702..8c5e2e5c1 100644 --- a/libs/red-domain/src/lib/dictionaries/dictionary.model.ts +++ b/libs/red-domain/src/lib/dictionaries/dictionary.model.ts @@ -15,7 +15,6 @@ export class Dictionary extends Entity implements IDictionary { readonly rank?: number; readonly recommendation: boolean; readonly type: string; - readonly typeId?: string; readonly hasDictionary?: boolean; readonly systemManaged?: boolean; @@ -40,7 +39,6 @@ export class Dictionary extends Entity implements IDictionary { this.rank = entity.rank; this.recommendation = !!entity.recommendation; this.type = entity.type; - this.typeId = entity.typeId; this.hasDictionary = entity.hasDictionary; this.systemManaged = entity.systemManaged; } diff --git a/libs/red-domain/src/lib/dictionaries/dictionary.ts b/libs/red-domain/src/lib/dictionaries/dictionary.ts index 8cf003a69..bb525afe2 100644 --- a/libs/red-domain/src/lib/dictionaries/dictionary.ts +++ b/libs/red-domain/src/lib/dictionaries/dictionary.ts @@ -24,7 +24,6 @@ export interface IDictionary { * The nonnull entry type. */ readonly type: string; - readonly typeId?: string; /** * The list of dictionary entries of an entry type. */ diff --git a/libs/red-domain/src/lib/dossiers/dossier.model.ts b/libs/red-domain/src/lib/dossiers/dossier.model.ts index 78edaac1d..939cdb268 100644 --- a/libs/red-domain/src/lib/dossiers/dossier.model.ts +++ b/libs/red-domain/src/lib/dossiers/dossier.model.ts @@ -19,8 +19,8 @@ export class Dossier implements IDossier, IListable { readonly hardDeletedTime?: string; readonly softDeletedTime?: string; readonly startDate?: string; - readonly watermarkId: number; - readonly previewWatermarkId: number; + readonly watermarkId: string; + readonly previewWatermarkId: string; readonly archivedTime: string; readonly hasReviewers: boolean; readonly routerLink: string; diff --git a/libs/red-domain/src/lib/dossiers/dossier.request.ts b/libs/red-domain/src/lib/dossiers/dossier.request.ts index d31e01c90..2e65c95ba 100644 --- a/libs/red-domain/src/lib/dossiers/dossier.request.ts +++ b/libs/red-domain/src/lib/dossiers/dossier.request.ts @@ -56,9 +56,9 @@ export interface IDossierRequest { /** * Watermark id that will be applied to the redacted files. */ - readonly watermarkId?: number; + readonly watermarkId?: string; /** * Watermark id that will be applied to the preview files. */ - readonly previewWatermarkId?: number; + readonly previewWatermarkId?: string; } diff --git a/libs/red-domain/src/lib/dossiers/dossier.ts b/libs/red-domain/src/lib/dossiers/dossier.ts index 04c081e33..d8ebe113b 100644 --- a/libs/red-domain/src/lib/dossiers/dossier.ts +++ b/libs/red-domain/src/lib/dossiers/dossier.ts @@ -17,7 +17,7 @@ export interface IDossier { readonly reportTemplateIds: List; readonly softDeletedTime?: string; readonly startDate?: string; - readonly watermarkId: number; - readonly previewWatermarkId: number; + readonly watermarkId: string; + readonly previewWatermarkId: string; readonly archivedTime: string; } diff --git a/libs/red-domain/src/lib/watermarks/watermark.model.ts b/libs/red-domain/src/lib/watermarks/watermark.model.ts index 7cc6100dc..e63a8072c 100644 --- a/libs/red-domain/src/lib/watermarks/watermark.model.ts +++ b/libs/red-domain/src/lib/watermarks/watermark.model.ts @@ -1,10 +1,10 @@ import { IWatermark, WatermarkOrientation } from './watermark'; -import { IListable } from '@iqser/common-ui'; +import { Entity } from '@iqser/common-ui'; -export class Watermark implements IListable, IWatermark { +export class Watermark extends Entity { readonly id: string; readonly dossierTemplateId: string; - enabled: boolean; + readonly enabled: boolean; readonly fontSize: number; readonly fontType: string; readonly hexColor: string; @@ -16,12 +16,15 @@ export class Watermark implements IListable, IWatermark { readonly dateAdded?: string; readonly dateModified?: string; + readonly routerLink = undefined; + constructor(watermark: IWatermark) { - this.id = watermark.id; + super(watermark); + this.id = watermark.id.toString(); this.dossierTemplateId = watermark.dossierTemplateId; this.enabled = watermark.enabled; this.fontSize = watermark.fontSize; - this.fontType = watermark.fontType; + this.fontType = watermark.fontType === 'arial' ? 'helvetica' : watermark.fontType; this.hexColor = watermark.hexColor; this.opacity = watermark.opacity; this.orientation = watermark.orientation; diff --git a/package.json b/package.json index ccecee76c..b6b298e27 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redaction", - "version": "3.629.0", + "version": "3.641.0", "private": true, "license": "MIT", "scripts": { diff --git a/paligo-theme.tar.gz b/paligo-theme.tar.gz index e479f2955..4b520c951 100644 Binary files a/paligo-theme.tar.gz and b/paligo-theme.tar.gz differ