Merge branch 'RED-8872' into 'master'

RED-8872: More readonly tabs in edit dossier, rework to signals

Closes RED-8872

See merge request redactmanager/red-ui!429
This commit is contained in:
Valentin-Gabriel Mihai 2024-05-22 13:49:13 +02:00
commit b107237a90
4 changed files with 77 additions and 93 deletions

View File

@ -1,11 +1,5 @@
@use 'common-mixins'; @use 'common-mixins';
//:host {
// display: flex;
// flex-grow: 1;
// overflow: hidden;
//}
.content-container { .content-container {
display: flex; display: flex;
justify-content: center; justify-content: center;

View File

@ -1,18 +1,18 @@
<section *ngIf="dossier$ | async as dossier" class="dialog"> <section class="dialog">
<div <div
[innerHTML]="'edit-dossier-dialog.header' | translate: { dossierName: dossier.dossierName }" [innerHTML]="'edit-dossier-dialog.header' | translate: { dossierName: dossier().dossierName }"
class="dialog-header heading-l" class="dialog-header heading-l"
id="editDossierHeader" id="editDossierHeader"
></div> ></div>
<div class="dialog-content"> <div class="dialog-content">
<iqser-side-nav [title]="'edit-dossier-dialog.side-nav-title' | translate"> <iqser-side-nav [title]="'edit-dossier-dialog.side-nav-title' | translate">
<div *ngFor="let item of navItems"> <div *ngFor="let item of navItems()">
<div <div
*ngIf="!item.hide"
(click)="changeTab(item.key)" (click)="changeTab(item.key)"
[class.active]="item.key === activeNav" *ngIf="!item.hide"
[attr.help-mode-key]="item.helpModeKey" [attr.help-mode-key]="item.helpModeKey"
[class.active]="item.key === activeNav()"
class="item" class="item"
> >
{{ item.sideNavTitle || item.title | translate }} {{ item.sideNavTitle || item.title | translate }}
@ -22,46 +22,46 @@
</iqser-side-nav> </iqser-side-nav>
<div> <div>
<div [class.no-actions]="!showActionButtons" [class.no-padding]="noPaddingTab" class="content"> <div [class.no-actions]="!showActionButtons()" [class.no-padding]="noPaddingTab()" class="content">
<div *ngIf="showHeading" class="heading"> <div *ngIf="showHeading()" class="heading">
{{ activeNavItem.title | translate }} {{ activeNavItem().title | translate }}
</div> </div>
<div *ngIf="activeNavItem.readonly" class="read-only-indicator all-caps-label primary"> <div *ngIf="activeNavItem().readonly" class="read-only-indicator all-caps-label primary">
<mat-icon class="mr-8" svgIcon="red:read-only"></mat-icon> <mat-icon class="mr-8" svgIcon="red:read-only"></mat-icon>
{{ 'readonly' | translate }} {{ 'readonly' | translate }}
</div> </div>
<redaction-edit-dossier-general-info <redaction-edit-dossier-general-info
*ngIf="activeNav === 'dossierInfo'" *ngIf="activeNav() === 'dossierInfo'"
[dossier]="dossier" [dossier]="dossier()"
></redaction-edit-dossier-general-info> ></redaction-edit-dossier-general-info>
<redaction-edit-dossier-download-package <redaction-edit-dossier-download-package
*ngIf="activeNav === 'downloadPackage'" *ngIf="activeNav() === 'downloadPackage'"
[dossier]="dossier" [dossier]="dossier()"
></redaction-edit-dossier-download-package> ></redaction-edit-dossier-download-package>
<redaction-edit-dossier-dictionary <redaction-edit-dossier-dictionary
*ngIf="activeNav === 'dossierDictionary'" *ngIf="activeNav() === 'dossierDictionary'"
[dossier]="dossier" [dossier]="dossier()"
></redaction-edit-dossier-dictionary> ></redaction-edit-dossier-dictionary>
<redaction-edit-dossier-team *ngIf="activeNav === 'members'" [dossier]="dossier"></redaction-edit-dossier-team> <redaction-edit-dossier-team *ngIf="activeNav() === 'members'" [dossier]="dossier()"></redaction-edit-dossier-team>
<redaction-edit-dossier-attributes <redaction-edit-dossier-attributes
*ngIf="activeNav === 'dossierAttributes'" *ngIf="activeNav() === 'dossierAttributes'"
[dossier]="dossier" [dossier]="dossier()"
></redaction-edit-dossier-attributes> ></redaction-edit-dossier-attributes>
</div> </div>
<div *ngIf="showActionButtons" class="dialog-actions"> <div *ngIf="showActionButtons" class="dialog-actions">
<iqser-icon-button <iqser-icon-button
(action)="save()" (action)="save()"
[buttonId]="'saveButton'"
[disabled]="disabled || !valid || !changed" [disabled]="disabled || !valid || !changed"
[label]="'edit-dossier-dialog.actions.save' | translate" [label]="'edit-dossier-dialog.actions.save' | translate"
[type]="iconButtonTypes.primary" [type]="iconButtonTypes.primary"
[buttonId]="'saveButton'"
></iqser-icon-button> ></iqser-icon-button>
<iqser-icon-button <iqser-icon-button

View File

@ -1,22 +1,20 @@
import { AfterViewInit, Component, Inject, ViewChild } from '@angular/core'; import { AfterViewInit, Component, computed, Inject, Signal, signal, untracked, ViewChild, WritableSignal } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Dossier, User } from '@red/domain'; import { Dossier } from '@red/domain';
import { EditDossierGeneralInfoComponent } from './general-info/edit-dossier-general-info.component'; import { EditDossierGeneralInfoComponent } from './general-info/edit-dossier-general-info.component';
import { EditDossierDownloadPackageComponent } from './download-package/edit-dossier-download-package.component'; import { EditDossierDownloadPackageComponent } from './download-package/edit-dossier-download-package.component';
import { EditDossierSectionInterface } from './edit-dossier-section.interface'; import { EditDossierSectionInterface } from './edit-dossier-section.interface';
import { BaseDialogComponent, ConfirmOptions, IconButtonTypes, IqserPermissionsService, SaveOptions } from '@iqser/common-ui'; import { BaseDialogComponent, ConfirmOptions, IconButtonTypes, SaveOptions } from '@iqser/common-ui';
import { EditDossierDictionaryComponent } from './dictionary/edit-dossier-dictionary.component'; import { EditDossierDictionaryComponent } from './dictionary/edit-dossier-dictionary.component';
import { EditDossierAttributesComponent } from './attributes/edit-dossier-attributes.component'; import { EditDossierAttributesComponent } from './attributes/edit-dossier-attributes.component';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { EditDossierTeamComponent } from './edit-dossier-team/edit-dossier-team.component'; import { EditDossierTeamComponent } from './edit-dossier-team/edit-dossier-team.component';
import { PermissionsService } from '@services/permissions.service'; import { PermissionsService } from '@services/permissions.service';
import { DossiersService } from '@services/dossiers/dossiers.service'; import { DossiersService } from '@services/dossiers/dossiers.service';
import { dossiersServiceProvider } from '@services/entity-services/dossiers.service.provider'; import { dossiersServiceProvider } from '@services/entity-services/dossiers.service.provider';
import { Roles } from '@users/roles'; import { Roles } from '@users/roles';
import { getCurrentUser } from '@iqser/common-ui/lib/users';
import { ConfigService } from '@services/config.service'; import { ConfigService } from '@services/config.service';
import { toSignal } from '@angular/core/rxjs-interop';
type Section = 'dossierInfo' | 'downloadPackage' | 'dossierDictionary' | 'members' | 'dossierAttributes'; type Section = 'dossierInfo' | 'downloadPackage' | 'dossierDictionary' | 'members' | 'dossierAttributes';
@ -36,20 +34,24 @@ interface NavItem {
}) })
export class EditDossierDialogComponent extends BaseDialogComponent implements AfterViewInit { export class EditDossierDialogComponent extends BaseDialogComponent implements AfterViewInit {
readonly roles = Roles; readonly roles = Roles;
navItems: NavItem[] = [];
readonly iconButtonTypes = IconButtonTypes; readonly iconButtonTypes = IconButtonTypes;
activeNav: Section;
readonly dossier$: Observable<Dossier>; readonly activeNav: WritableSignal<Section>;
readonly dossier: Signal<Dossier>;
readonly navItems: Signal<NavItem[]>;
readonly activeNavItem: Signal<NavItem>;
readonly activeComponent: Signal<EditDossierSectionInterface>;
readonly noPaddingTab: Signal<boolean>;
readonly showHeading: Signal<boolean>;
readonly showActionButtons: Signal<boolean>;
@ViewChild(EditDossierGeneralInfoComponent) generalInfoComponent: EditDossierGeneralInfoComponent; @ViewChild(EditDossierGeneralInfoComponent) generalInfoComponent: EditDossierGeneralInfoComponent;
@ViewChild(EditDossierDownloadPackageComponent) downloadPackageComponent: EditDossierDownloadPackageComponent; @ViewChild(EditDossierDownloadPackageComponent) downloadPackageComponent: EditDossierDownloadPackageComponent;
@ViewChild(EditDossierDictionaryComponent) dictionaryComponent: EditDossierDictionaryComponent; @ViewChild(EditDossierDictionaryComponent) dictionaryComponent: EditDossierDictionaryComponent;
@ViewChild(EditDossierTeamComponent) membersComponent: EditDossierTeamComponent; @ViewChild(EditDossierTeamComponent) membersComponent: EditDossierTeamComponent;
@ViewChild(EditDossierAttributesComponent) attributesComponent: EditDossierAttributesComponent; @ViewChild(EditDossierAttributesComponent) attributesComponent: EditDossierAttributesComponent;
readonly #currentUser = getCurrentUser<User>();
#dossier: Dossier;
constructor( constructor(
readonly iqserPermissionsService: IqserPermissionsService,
private readonly _dossiersService: DossiersService, private readonly _dossiersService: DossiersService,
private readonly _permissionsService: PermissionsService, private readonly _permissionsService: PermissionsService,
protected readonly _dialogRef: MatDialogRef<EditDossierDialogComponent>, protected readonly _dialogRef: MatDialogRef<EditDossierDialogComponent>,
@ -61,72 +63,43 @@ export class EditDossierDialogComponent extends BaseDialogComponent implements A
readonly configService: ConfigService, readonly configService: ConfigService,
) { ) {
super(_dialogRef, true); super(_dialogRef, true);
this.dossier$ = this._dossiersService.getEntityChanged$(this._data.dossierId).pipe( this.dossier = toSignal(this._dossiersService.getEntityChanged$(this._data.dossierId));
tap(dossier => { this.navItems = computed(() => this._getNavItems(this.dossier()));
this.#dossier = dossier; this.activeNav = signal(this._data.section || 'dossierInfo');
this._initializeNavItems(); this.activeNavItem = computed(() => this.navItems().find(item => item.key === this.activeNav()));
}), this.activeComponent = computed(() => this._getActiveComponent(this.activeNav()));
); this.noPaddingTab = computed(() => ['dossierAttributes', 'dossierDictionary'].includes(this.activeNav()));
this.activeNav = this._data.section || 'dossierInfo'; this.showHeading = computed(() => !['dossierAttributes', 'dossierDictionary'].includes(this.activeNav()));
} this.showActionButtons = computed(() => !this.activeNavItem().readonly);
get activeNavItem(): NavItem {
return this.navItems.find(item => item.key === this.activeNav);
}
get activeComponent(): EditDossierSectionInterface {
return {
dossierInfo: this.generalInfoComponent,
downloadPackage: this.downloadPackageComponent,
dossierDictionary: this.dictionaryComponent,
members: this.membersComponent,
dossierAttributes: this.attributesComponent,
}[this.activeNav];
}
get noPaddingTab(): boolean {
return ['dossierAttributes', 'dossierDictionary'].includes(this.activeNav);
}
get showHeading(): boolean {
return !['dossierAttributes', 'dossierDictionary'].includes(this.activeNav);
}
get showActionButtons(): boolean {
return (
(['dossierDictionary'].includes(this.activeNav) && this._permissionsService.canEditDossierDictionary(this.#dossier)) ||
(['members'].includes(this.activeNav) &&
this.#currentUser.isManager &&
this.iqserPermissionsService.has(Roles.dossiers.edit)) ||
this._permissionsService.canEditDossier(this.#dossier)
);
} }
get changed(): boolean { get changed(): boolean {
return this.activeComponent?.changed; return this.activeComponent()?.changed;
} }
get valid(): boolean { get valid(): boolean {
return this.activeComponent?.valid; return this.activeComponent()?.valid;
} }
get disabled(): boolean { get disabled(): boolean {
return this.activeComponent?.disabled; return this.activeComponent()?.disabled;
} }
ngAfterViewInit() { ngAfterViewInit() {
if (!this.#dossier.ownerId) { if (!untracked(this.dossier).ownerId) {
this._toaster.error(_('edit-dossier-dialog.missing-owner')); this._toaster.error(_('edit-dossier-dialog.missing-owner'));
} }
} }
async save(options?: SaveOptions) { async save(options?: SaveOptions) {
this._loadingService.start(); this._loadingService.start();
const result = await this.activeComponent.save(); const result = await untracked(this.activeComponent).save();
this._loadingService.stop(); this._loadingService.stop();
if (result.success) { if (result.success) {
this._toaster.success(_('edit-dossier-dialog.change-successful'), { params: { dossierName: this.#dossier.dossierName } }); this._toaster.success(_('edit-dossier-dialog.change-successful'), {
params: { dossierName: untracked(this.dossier).dossierName },
});
} }
if (result.success && options?.closeAfterSave) { if (result.success && options?.closeAfterSave) {
@ -135,7 +108,7 @@ export class EditDossierDialogComponent extends BaseDialogComponent implements A
} }
revert() { revert() {
this.activeComponent.revert(); untracked(this.activeComponent).revert();
} }
changeTab(key: Section) { changeTab(key: Section) {
@ -146,34 +119,44 @@ export class EditDossierDialogComponent extends BaseDialogComponent implements A
} else { } else {
this.revert(); this.revert();
} }
this.activeNav = key; this.activeNav.set(key);
}); });
} else { } else {
this.activeNav = key; this.activeNav.set(key);
} }
} }
private _initializeNavItems(): void { private _getActiveComponent(section: Section): EditDossierSectionInterface {
this.navItems = [ return {
dossierInfo: this.generalInfoComponent,
downloadPackage: this.downloadPackageComponent,
dossierDictionary: this.dictionaryComponent,
members: this.membersComponent,
dossierAttributes: this.attributesComponent,
}[section];
}
private _getNavItems(dossier: Dossier): NavItem[] {
return [
{ {
key: 'dossierInfo', key: 'dossierInfo',
title: _('edit-dossier-dialog.nav-items.general-info'), title: _('edit-dossier-dialog.nav-items.general-info'),
sideNavTitle: _('edit-dossier-dialog.nav-items.dossier-info'), sideNavTitle: _('edit-dossier-dialog.nav-items.dossier-info'),
readonly: !this.#dossier.isActive || !this._permissionsService.canEditDossier(this.#dossier), readonly: !this._permissionsService.canEditDossier(dossier),
helpModeKey: 'edit_dossier_dossier_info_DIALOG', helpModeKey: 'edit_dossier_dossier_info_DIALOG',
}, },
{ {
key: 'downloadPackage', key: 'downloadPackage',
title: _('edit-dossier-dialog.nav-items.choose-download'), title: _('edit-dossier-dialog.nav-items.choose-download'),
sideNavTitle: _('edit-dossier-dialog.nav-items.download-package'), sideNavTitle: _('edit-dossier-dialog.nav-items.download-package'),
readonly: !this._permissionsService.canEditDossier(this.#dossier), readonly: !this._permissionsService.canEditDossier(dossier),
helpModeKey: 'edit_dossier_download_package_DIALOG', helpModeKey: 'edit_dossier_download_package_DIALOG',
}, },
{ {
key: 'dossierDictionary', key: 'dossierDictionary',
sideNavTitle: _('edit-dossier-dialog.nav-items.dictionary'), sideNavTitle: _('edit-dossier-dialog.nav-items.dictionary'),
title: _('edit-dossier-dialog.nav-items.dossier-dictionary'), title: _('edit-dossier-dialog.nav-items.dossier-dictionary'),
readonly: !this._permissionsService.canEditDossierDictionary(this.#dossier), readonly: !this._permissionsService.canEditDossierDictionary(dossier),
helpModeKey: 'edit_dossier_dossier_dictionary_DIALOG', helpModeKey: 'edit_dossier_dossier_dictionary_DIALOG',
hide: this.configService.values.IS_DOCUMINE, hide: this.configService.values.IS_DOCUMINE,
}, },
@ -181,13 +164,13 @@ export class EditDossierDialogComponent extends BaseDialogComponent implements A
key: 'members', key: 'members',
title: _('edit-dossier-dialog.nav-items.team-members'), title: _('edit-dossier-dialog.nav-items.team-members'),
sideNavTitle: _('edit-dossier-dialog.nav-items.members'), sideNavTitle: _('edit-dossier-dialog.nav-items.members'),
readonly: !this._permissionsService.canEditTeamMembers(this.#dossier), readonly: !this._permissionsService.canEditTeamMembers(dossier),
helpModeKey: 'edit_dossier_members_DIALOG', helpModeKey: 'edit_dossier_members_DIALOG',
}, },
{ {
key: 'dossierAttributes', key: 'dossierAttributes',
title: _('edit-dossier-dialog.nav-items.dossier-attributes'), title: _('edit-dossier-dialog.nav-items.dossier-attributes'),
readonly: !this._permissionsService.canEditDossierAttributes(this.#dossier), readonly: !this._permissionsService.canEditDossierAttributes(dossier),
helpModeKey: 'edit_dossier_dossier_attributes_DIALOG', helpModeKey: 'edit_dossier_dossier_attributes_DIALOG',
}, },
]; ];

View File

@ -302,7 +302,14 @@ export class PermissionsService {
} }
canEditDossier(dossier: Dossier): boolean { canEditDossier(dossier: Dossier): boolean {
return this._iqserPermissionsService.has(Roles.dossiers.edit) && this.isManager() && !!dossier?.ownerId; const dossierTemplate = this._dossierTemplatesService.find(dossier.dossierTemplateId);
return (
this._iqserPermissionsService.has(Roles.dossiers.edit) &&
this.isManager() &&
!!dossier?.ownerId &&
dossier.isActive &&
dossierTemplate.isActive
);
} }
canEditDossierDictionary(dossier: Dossier): boolean { canEditDossierDictionary(dossier: Dossier): boolean {