Pull request #338: VM/RED-2614
Merge in RED/ui from VM/RED-2614 to master * commit '5db44d09c477a7d8e2c13c4f39833463aa01fc9d': (21 commits) fixed one more import in 'can-deactivate.guard.ts' fixed more imports extended base dialog for 'add edit justifications' component extended base dialog for 'add edit user dialog' component added PendingChangesGuard for Configurations updates for rules screen to can use PendingChangesGuard added PendingChangesGuard for dictionary overview component updated PendingChangesGuard and used it for user profile and notifications preferences screens added warn box for all needed dialogs extended base dialog for 'recategorize image dialog' component and 'resize annotation dialog' compoenent extended base dialog for 'document info dialog' component and 'force annotation dialog' compoenent extended base dialog for 'change legal basis dialog' component extended base dialog for 'manual annotation dialog' component extended base dialog for 'add dossier' component moved 'base-dialog component' and 'confirmation-dialog service' in common stored initial form as a simple object instead of a form changed 'click' to 'action' removed commented code, updated 'close' and 'changeTab' methods updated general logic for 'valid' and 'changed' methods to be used by more classes that inherit BaseDialog updated base dialog component to display the confirmation dialog whenever the user wants to leave the modal or change its tab with unsaved changes ...
This commit is contained in:
commit
bb1235b700
@ -1,40 +1,34 @@
|
||||
import { CanDeactivate } from '@angular/router';
|
||||
import { Directive, HostListener, Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { map, Observable } from 'rxjs';
|
||||
import { ConfirmationDialogService, ConfirmOptions } from '@iqser/common-ui';
|
||||
|
||||
export interface ComponentCanDeactivate {
|
||||
hasChanges: boolean;
|
||||
}
|
||||
|
||||
@Directive()
|
||||
export abstract class ComponentHasChanges implements ComponentCanDeactivate {
|
||||
abstract hasChanges: boolean;
|
||||
|
||||
protected constructor(protected _translateService: TranslateService) {}
|
||||
|
||||
@HostListener('window:beforeunload', ['$event'])
|
||||
unloadNotification($event: any) {
|
||||
if (this.hasChanges) {
|
||||
// This message will be displayed in IE/Edge
|
||||
$event.returnValue = this._translateService.instant('pending-changes-guard');
|
||||
}
|
||||
}
|
||||
changed: boolean;
|
||||
valid?: boolean;
|
||||
isLeavingPage?: boolean;
|
||||
save: () => Promise<void>;
|
||||
}
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class PendingChangesGuard implements CanDeactivate<ComponentCanDeactivate> {
|
||||
constructor(private readonly _translateService: TranslateService) {}
|
||||
constructor(private _dialogService: ConfirmationDialogService) {}
|
||||
|
||||
canDeactivate(component: ComponentCanDeactivate): boolean | Observable<boolean> {
|
||||
// if there are no pending changes, just allow deactivation; else confirm first
|
||||
return !component.hasChanges
|
||||
? true
|
||||
: // NOTE: this warning message will only be shown when navigating elsewhere
|
||||
// within your angular app;
|
||||
// when navigating away from your angular app,
|
||||
// the browser will show a generic warning message
|
||||
// see http://stackoverflow.com/a/42207299/7307355
|
||||
confirm(this._translateService.instant('pending-changes-guard'));
|
||||
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();
|
||||
}
|
||||
component.isLeavingPage = false;
|
||||
return !!result;
|
||||
}),
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<form (submit)="save()" [formGroup]="formGroup">
|
||||
<form (submit)="save()" [formGroup]="form">
|
||||
<div class="dialog-content">
|
||||
<div *ngFor="let category of notificationCategories">
|
||||
<div class="iqser-input-group header w-full">
|
||||
@ -36,7 +36,7 @@
|
||||
</div>
|
||||
|
||||
<div class="dialog-actions">
|
||||
<button [disabled]="formGroup.invalid" color="primary" mat-flat-button type="submit">
|
||||
<button [disabled]="form.invalid" color="primary" mat-flat-button type="submit">
|
||||
{{ 'user-profile-screen.actions.save' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { notificationsTranslations } from '../../../translations/notifications-translations';
|
||||
import { NotificationPreferencesService } from '../../../services/notification-preferences.service';
|
||||
import { LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { BaseFormComponent, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import {
|
||||
EmailNotificationScheduleTypesValues,
|
||||
@ -18,57 +18,57 @@ import { firstValueFrom } from 'rxjs';
|
||||
styleUrls: ['./notifications-screen.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class NotificationsScreenComponent implements OnInit {
|
||||
export class NotificationsScreenComponent extends BaseFormComponent implements OnInit {
|
||||
readonly emailNotificationScheduleTypes = EmailNotificationScheduleTypesValues;
|
||||
readonly notificationCategories = NotificationCategoriesValues;
|
||||
readonly notificationGroupsKeys = NotificationGroupsKeys;
|
||||
readonly notificationGroupsValues = NotificationGroupsValues;
|
||||
readonly translations = notificationsTranslations;
|
||||
|
||||
readonly formGroup: FormGroup = this._getForm();
|
||||
|
||||
constructor(
|
||||
private readonly _toaster: Toaster,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _notificationPreferencesService: NotificationPreferencesService,
|
||||
) {}
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
await this._initializeForm();
|
||||
}
|
||||
|
||||
isCategoryActive(category: string) {
|
||||
return this.formGroup.get(`${category}Enabled`).value;
|
||||
return this.form.get(`${category}Enabled`).value;
|
||||
}
|
||||
|
||||
setEmailNotificationType(type: string) {
|
||||
this.formGroup.get('emailNotificationType').setValue(type);
|
||||
this.form.get('emailNotificationType').setValue(type);
|
||||
}
|
||||
|
||||
getEmailNotificationType() {
|
||||
return this.formGroup.get('emailNotificationType').value;
|
||||
return this.form.get('emailNotificationType').value;
|
||||
}
|
||||
|
||||
isPreferenceChecked(category: string, preference: string) {
|
||||
return this.formGroup.get(category).value.includes(preference);
|
||||
return this.form.get(category).value.includes(preference);
|
||||
}
|
||||
|
||||
addRemovePreference(checked: boolean, category: string, preference: string) {
|
||||
const preferences = this.formGroup.get(category).value;
|
||||
const preferences = this.form.get(category).value;
|
||||
if (checked) {
|
||||
preferences.push(preference);
|
||||
} else {
|
||||
const indexOfPreference = preferences.indexOf(preference);
|
||||
preferences.splice(indexOfPreference, 1);
|
||||
}
|
||||
this.formGroup.get(category).setValue(preferences);
|
||||
this.form.get(category).setValue(preferences);
|
||||
}
|
||||
|
||||
async save() {
|
||||
this._loadingService.start();
|
||||
try {
|
||||
await firstValueFrom(this._notificationPreferencesService.update(this.formGroup.value));
|
||||
await firstValueFrom(this._notificationPreferencesService.update(this.form.value));
|
||||
} catch (e) {
|
||||
this._toaster.error(_('notifications-screen.error.generic'));
|
||||
}
|
||||
@ -88,8 +88,10 @@ export class NotificationsScreenComponent implements OnInit {
|
||||
private async _initializeForm() {
|
||||
this._loadingService.start();
|
||||
|
||||
this.form = this._getForm();
|
||||
const notificationPreferences = await firstValueFrom(this._notificationPreferencesService.get());
|
||||
this.formGroup.patchValue(notificationPreferences);
|
||||
this.form.patchValue(notificationPreferences);
|
||||
this.initialFormValue = JSON.parse(JSON.stringify(this.form.getRawValue()));
|
||||
|
||||
this._loadingService.stop();
|
||||
}
|
||||
|
||||
@ -3,8 +3,9 @@ import { RouterModule } from '@angular/router';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { SharedModule } from '@shared/shared.module';
|
||||
import { NotificationsScreenComponent } from './notifications-screen/notifications-screen.component';
|
||||
import { PendingChangesGuard } from '../../../../guards/can-deactivate.guard';
|
||||
|
||||
const routes = [{ path: '', component: NotificationsScreenComponent }];
|
||||
const routes = [{ path: '', component: NotificationsScreenComponent, canDeactivate: [PendingChangesGuard] }];
|
||||
|
||||
@NgModule({
|
||||
declarations: [NotificationsScreenComponent],
|
||||
|
||||
@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { LoadingService } from '@iqser/common-ui';
|
||||
import { BaseFormComponent, LoadingService } from '@iqser/common-ui';
|
||||
import { IProfile } from '@red/domain';
|
||||
import { languagesTranslations } from '../../../translations/languages-translations';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
@ -17,8 +17,7 @@ import { firstValueFrom } from 'rxjs';
|
||||
styleUrls: ['./user-profile-screen.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class UserProfileScreenComponent implements OnInit {
|
||||
readonly form: FormGroup = this._getForm();
|
||||
export class UserProfileScreenComponent extends BaseFormComponent implements OnInit {
|
||||
changePasswordUrl: SafeResourceUrl;
|
||||
translations = languagesTranslations;
|
||||
|
||||
@ -31,11 +30,11 @@ export class UserProfileScreenComponent implements OnInit {
|
||||
private readonly _configService: ConfigService,
|
||||
private readonly _languageService: LanguageService,
|
||||
private readonly _domSanitizer: DomSanitizer,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
protected readonly _translateService: TranslateService,
|
||||
) {
|
||||
super();
|
||||
this._loadingService.start();
|
||||
|
||||
this.changePasswordUrl = this._domSanitizer.bypassSecurityTrustResourceUrl(
|
||||
`${this._configService.values.OAUTH_URL}/account/password`,
|
||||
);
|
||||
@ -101,6 +100,7 @@ export class UserProfileScreenComponent implements OnInit {
|
||||
|
||||
private _initializeForm(): void {
|
||||
try {
|
||||
this.form = this._getForm();
|
||||
this._profileModel = {
|
||||
email: this._userService.currentUser.email,
|
||||
firstName: this._userService.currentUser.firstName,
|
||||
@ -112,6 +112,7 @@ export class UserProfileScreenComponent implements OnInit {
|
||||
this.form.get('email').disable();
|
||||
}
|
||||
this.form.patchValue(this._profileModel, { emitEvent: false });
|
||||
this.initialFormValue = this.form.getRawValue();
|
||||
} catch (e) {
|
||||
} finally {
|
||||
this._loadingService.stop();
|
||||
|
||||
@ -3,8 +3,9 @@ import { RouterModule } from '@angular/router';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { SharedModule } from '@shared/shared.module';
|
||||
import { UserProfileScreenComponent } from './user-profile-screen/user-profile-screen.component';
|
||||
import { PendingChangesGuard } from '../../../../guards/can-deactivate.guard';
|
||||
|
||||
const routes = [{ path: '', component: UserProfileScreenComponent }];
|
||||
const routes = [{ path: '', component: UserProfileScreenComponent, canDeactivate: [PendingChangesGuard] }];
|
||||
|
||||
@NgModule({
|
||||
declarations: [UserProfileScreenComponent],
|
||||
|
||||
@ -174,6 +174,7 @@ const routes: Routes = [
|
||||
path: 'general-config',
|
||||
component: GeneralConfigScreenComponent,
|
||||
canActivate: [CompositeRouteGuard],
|
||||
canDeactivate: [PendingChangesGuard],
|
||||
data: {
|
||||
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard],
|
||||
requiredRoles: ['RED_ADMIN'],
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
<section class="dialog">
|
||||
<redaction-user-details
|
||||
(closeDialog)="dialogRef.close($event)"
|
||||
(closeDialog)="closeDialog($event)"
|
||||
(cancel)="close()"
|
||||
(toggleResetPassword)="toggleResetPassword()"
|
||||
*ngIf="!resettingPassword"
|
||||
[hidden]="resettingPassword"
|
||||
[user]="user"
|
||||
></redaction-user-details>
|
||||
|
||||
<redaction-reset-password
|
||||
(close)="dialogRef.close($event)"
|
||||
(close)="closeDialog($event)"
|
||||
(toggleResetPassword)="toggleResetPassword()"
|
||||
*ngIf="resettingPassword"
|
||||
[hidden]="!resettingPassword"
|
||||
[user]="user"
|
||||
></redaction-reset-password>
|
||||
|
||||
<iqser-circle-button class="dialog-close" icon="iqser:close" mat-dialog-close></iqser-circle-button>
|
||||
<iqser-circle-button class="dialog-close" icon="iqser:close" (action)="close()"></iqser-circle-button>
|
||||
</section>
|
||||
|
||||
@ -1,18 +1,44 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { Component, Inject, Injector, ViewChild } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { User } from '@red/domain';
|
||||
import { UserDetailsComponent } from './user-details/user-details.component';
|
||||
import { BaseDialogComponent } from '@iqser/common-ui';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-add-edit-user-dialog',
|
||||
templateUrl: './add-edit-user-dialog.component.html',
|
||||
styleUrls: ['./add-edit-user-dialog.component.scss'],
|
||||
})
|
||||
export class AddEditUserDialogComponent {
|
||||
export class AddEditUserDialogComponent extends BaseDialogComponent {
|
||||
@ViewChild(UserDetailsComponent) private readonly _userDetailsComponent: UserDetailsComponent;
|
||||
|
||||
resettingPassword = false;
|
||||
|
||||
constructor(readonly dialogRef: MatDialogRef<AddEditUserDialogComponent>, @Inject(MAT_DIALOG_DATA) readonly user: User) {}
|
||||
constructor(
|
||||
protected readonly _injector: Injector,
|
||||
protected readonly _dialogRef: MatDialogRef<AddEditUserDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) readonly user: User,
|
||||
) {
|
||||
super(_injector, _dialogRef);
|
||||
}
|
||||
|
||||
toggleResetPassword() {
|
||||
this.resettingPassword = !this.resettingPassword;
|
||||
}
|
||||
|
||||
async save(): Promise<void> {
|
||||
await this._userDetailsComponent.save();
|
||||
}
|
||||
|
||||
get changed(): boolean {
|
||||
return this._userDetailsComponent.changed;
|
||||
}
|
||||
|
||||
get valid(): boolean {
|
||||
return this._userDetailsComponent.valid;
|
||||
}
|
||||
|
||||
closeDialog(event) {
|
||||
this._dialogRef.close(event);
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,6 +53,6 @@
|
||||
icon="iqser:trash"
|
||||
></iqser-icon-button>
|
||||
|
||||
<div class="all-caps-label cancel" mat-dialog-close translate="add-edit-user.actions.cancel"></div>
|
||||
<div class="all-caps-label cancel" translate="add-edit-user.actions.cancel" (click)="cancel.emit()"></div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Component, EventEmitter, Input, OnChanges, OnDestroy, Output } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { AdminDialogService } from '../../../services/admin-dialog.service';
|
||||
import { AutoUnsubscribe, IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { BaseFormComponent, IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { rolesTranslations } from '../../../../../translations/roles-translations';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { User } from '@red/domain';
|
||||
@ -14,16 +14,16 @@ import { firstValueFrom } from 'rxjs';
|
||||
templateUrl: './user-details.component.html',
|
||||
styleUrls: ['./user-details.component.scss'],
|
||||
})
|
||||
export class UserDetailsComponent extends AutoUnsubscribe implements OnChanges, OnDestroy {
|
||||
export class UserDetailsComponent extends BaseFormComponent implements OnChanges, OnDestroy {
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
|
||||
@Input() user: User;
|
||||
@Output() readonly toggleResetPassword = new EventEmitter();
|
||||
@Output() readonly closeDialog = new EventEmitter();
|
||||
@Output() readonly cancel = new EventEmitter();
|
||||
|
||||
readonly ROLES = ['RED_USER', 'RED_MANAGER', 'RED_USER_ADMIN', 'RED_ADMIN'];
|
||||
readonly translations = rolesTranslations;
|
||||
form: FormGroup;
|
||||
private readonly _ROLE_REQUIREMENTS = { RED_MANAGER: 'RED_USER', RED_ADMIN: 'RED_USER_ADMIN' };
|
||||
|
||||
constructor(
|
||||
@ -36,29 +36,6 @@ export class UserDetailsComponent extends AutoUnsubscribe implements OnChanges,
|
||||
super();
|
||||
}
|
||||
|
||||
get changed(): boolean {
|
||||
if (!this.user) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.user.roles.length !== this.activeRoles.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const key of Object.keys(this.form.getRawValue())) {
|
||||
const keyValue = this.form.get(key).value;
|
||||
if (key.startsWith('RED_')) {
|
||||
if (this.user.roles.includes(key) !== keyValue) {
|
||||
return true;
|
||||
}
|
||||
} else if (this.user[key] !== keyValue) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
get activeRoles(): string[] {
|
||||
return this.ROLES.reduce((acc, role) => {
|
||||
if (this.form.get(role).value) {
|
||||
@ -86,6 +63,7 @@ export class UserDetailsComponent extends AutoUnsubscribe implements OnChanges,
|
||||
ngOnChanges() {
|
||||
this.form = this._getForm();
|
||||
this._setRolesRequirements();
|
||||
this.initialFormValue = this.form.getRawValue();
|
||||
}
|
||||
|
||||
shouldBeDisabled(role: string): boolean {
|
||||
@ -133,7 +111,6 @@ export class UserDetailsComponent extends AutoUnsubscribe implements OnChanges,
|
||||
}
|
||||
|
||||
private _getForm(): FormGroup {
|
||||
console.log(this.user);
|
||||
return this._formBuilder.group({
|
||||
firstName: [this.user?.firstName, Validators.required],
|
||||
lastName: [this.user?.lastName, Validators.required],
|
||||
|
||||
@ -27,5 +27,5 @@
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<iqser-circle-button class="dialog-close" icon="iqser:close" mat-dialog-close></iqser-circle-button>
|
||||
<iqser-circle-button class="dialog-close" icon="iqser:close" (action)="close()"></iqser-circle-button>
|
||||
</section>
|
||||
|
||||
@ -1,23 +1,27 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { Component, Inject, Injector } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { ISmtpConfiguration } from '@red/domain';
|
||||
import { BaseDialogComponent } from '@iqser/common-ui';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-smtp-auth-dialog',
|
||||
templateUrl: './smtp-auth-dialog.component.html',
|
||||
styleUrls: ['./smtp-auth-dialog.component.scss'],
|
||||
})
|
||||
export class SmtpAuthDialogComponent {
|
||||
readonly form: FormGroup = this._getForm();
|
||||
|
||||
export class SmtpAuthDialogComponent extends BaseDialogComponent {
|
||||
constructor(
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _userService: UserService,
|
||||
public dialogRef: MatDialogRef<SmtpAuthDialogComponent>,
|
||||
protected readonly _injector: Injector,
|
||||
protected readonly _dialogRef: MatDialogRef<SmtpAuthDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: ISmtpConfiguration,
|
||||
) {}
|
||||
) {
|
||||
super(_injector, _dialogRef);
|
||||
this.form = this._getForm();
|
||||
this.initialFormValue = this.form.getRawValue();
|
||||
}
|
||||
|
||||
private _getForm(): FormGroup {
|
||||
return this._formBuilder.group({
|
||||
@ -27,6 +31,6 @@ export class SmtpAuthDialogComponent {
|
||||
}
|
||||
|
||||
save() {
|
||||
this.dialogRef.close(this.form.getRawValue());
|
||||
this._dialogRef.close(this.form.getRawValue());
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,8 +55,9 @@
|
||||
|
||||
<redaction-dictionary-manager
|
||||
#dictionaryManager
|
||||
(saveDictionary)="saveEntries($event)"
|
||||
(saveDictionary)="save()"
|
||||
[canEdit]="currentUser.isAdmin"
|
||||
[isLeavingPage]="isLeavingPage"
|
||||
[filterByDossierTemplate]="true"
|
||||
[initialEntries]="initialEntries"
|
||||
></redaction-dictionary-manager>
|
||||
|
||||
@ -3,7 +3,6 @@ import { AppStateService } from '@state/app-state.service';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { saveAs } from 'file-saver';
|
||||
import { ComponentHasChanges } from '@guards/can-deactivate.guard';
|
||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
||||
import { DictionaryManagerComponent } from '@shared/components/dictionary-manager/dictionary-manager.component';
|
||||
import { DictionaryService } from '@shared/services/dictionary.service';
|
||||
@ -17,12 +16,13 @@ import { firstValueFrom } from 'rxjs';
|
||||
templateUrl: './dictionary-overview-screen.component.html',
|
||||
styleUrls: ['./dictionary-overview-screen.component.scss'],
|
||||
})
|
||||
export class DictionaryOverviewScreenComponent extends ComponentHasChanges implements OnInit, OnDestroy {
|
||||
export class DictionaryOverviewScreenComponent implements OnInit, OnDestroy {
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
readonly currentUser = this._userService.currentUser;
|
||||
|
||||
initialEntries: string[] = [];
|
||||
dictionary: Dictionary;
|
||||
isLeavingPage = false;
|
||||
|
||||
@ViewChild('dictionaryManager', { static: false })
|
||||
private readonly _dictionaryManager: DictionaryManagerComponent;
|
||||
@ -38,11 +38,9 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
||||
private readonly _dialogService: AdminDialogService,
|
||||
protected readonly _translateService: TranslateService,
|
||||
private readonly _dictionaryService: DictionaryService,
|
||||
) {
|
||||
super(_translateService);
|
||||
}
|
||||
) {}
|
||||
|
||||
get hasChanges() {
|
||||
get changed() {
|
||||
return this._dictionaryManager.editor.hasChanges;
|
||||
}
|
||||
|
||||
@ -117,7 +115,9 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
||||
}
|
||||
}
|
||||
|
||||
saveEntries(entries: string[]) {
|
||||
save() {
|
||||
const entries = this._dictionaryManager.editor?.currentEntries;
|
||||
|
||||
this._loadingService.start();
|
||||
this._dictionaryService
|
||||
.saveEntries(entries, this.initialEntries, this.dictionary.dossierTemplateId, this.dictionary.type, null)
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<div class="heading-l" translate="general-config-screen.general.title"></div>
|
||||
<div translate="general-config-screen.general.subtitle"></div>
|
||||
</div>
|
||||
<form (submit)="saveGeneralConfig()" [formGroup]="form">
|
||||
<form (submit)="save()" [formGroup]="form" *ngIf="form">
|
||||
<div class="dialog-content">
|
||||
<div class="dialog-content-left">
|
||||
<div class="iqser-input-group">
|
||||
@ -23,7 +23,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="dialog-actions">
|
||||
<button [disabled]="form.invalid || !generalConfigurationChanged" color="primary" mat-flat-button type="submit">
|
||||
<button [disabled]="form?.invalid || !changed" color="primary" mat-flat-button type="submit">
|
||||
{{ 'general-config-screen.actions.save' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { AutoUnsubscribe, LoadingService } from '@iqser/common-ui';
|
||||
import { BaseFormComponent, LoadingService } from '@iqser/common-ui';
|
||||
import { GeneralSettingsService } from '@services/general-settings.service';
|
||||
import { IGeneralConfiguration } from '@red/domain';
|
||||
import { ConfigService } from '@services/config.service';
|
||||
@ -11,8 +11,7 @@ import { firstValueFrom } from 'rxjs';
|
||||
templateUrl: './general-config-form.component.html',
|
||||
styleUrls: ['./general-config-form.component.scss'],
|
||||
})
|
||||
export class GeneralConfigFormComponent extends AutoUnsubscribe implements OnInit, OnDestroy {
|
||||
readonly form: FormGroup = this._getForm();
|
||||
export class GeneralConfigFormComponent extends BaseFormComponent implements OnInit, OnDestroy {
|
||||
private _initialConfiguration: IGeneralConfiguration;
|
||||
|
||||
constructor(
|
||||
@ -22,6 +21,7 @@ export class GeneralConfigFormComponent extends AutoUnsubscribe implements OnIni
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
) {
|
||||
super();
|
||||
this.form = this._getForm();
|
||||
}
|
||||
|
||||
get generalConfigurationChanged(): boolean {
|
||||
@ -42,7 +42,7 @@ export class GeneralConfigFormComponent extends AutoUnsubscribe implements OnIni
|
||||
await this._loadData();
|
||||
}
|
||||
|
||||
async saveGeneralConfig() {
|
||||
async save() {
|
||||
this._loadingService.start();
|
||||
|
||||
const configFormValues = this.form.getRawValue();
|
||||
@ -51,6 +51,7 @@ export class GeneralConfigFormComponent extends AutoUnsubscribe implements OnIni
|
||||
this._initialConfiguration = await firstValueFrom(this._generalSettingsService.getGeneralConfigurations());
|
||||
this._configService.updateDisplayName(this._initialConfiguration.displayName);
|
||||
this._loadingService.stop();
|
||||
await this._loadData();
|
||||
}
|
||||
|
||||
private _getForm(): FormGroup {
|
||||
@ -65,6 +66,7 @@ export class GeneralConfigFormComponent extends AutoUnsubscribe implements OnIni
|
||||
try {
|
||||
this._initialConfiguration = await firstValueFrom(this._generalSettingsService.getGeneralConfigurations());
|
||||
this.form.patchValue(this._initialConfiguration, { emitEvent: false });
|
||||
this.initialFormValue = this.form.getRawValue();
|
||||
} catch (e) {}
|
||||
|
||||
this._loadingService.stop();
|
||||
|
||||
@ -1,13 +1,52 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { AfterViewInit, Component, ViewChild } from '@angular/core';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { GeneralConfigFormComponent } from './general-config-form/general-config-form.component';
|
||||
import { SmtpFormComponent } from './smtp-form/smtp-form.component';
|
||||
import { BaseFormComponent } from '@iqser/common-ui';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-general-config-screen',
|
||||
templateUrl: './general-config-screen.component.html',
|
||||
styleUrls: ['./general-config-screen.component.scss'],
|
||||
})
|
||||
export class GeneralConfigScreenComponent {
|
||||
export class GeneralConfigScreenComponent extends BaseFormComponent implements AfterViewInit {
|
||||
readonly currentUser = this._userService.currentUser;
|
||||
|
||||
constructor(private readonly _userService: UserService) {}
|
||||
@ViewChild(GeneralConfigFormComponent) generalConfigFormComponent: GeneralConfigFormComponent;
|
||||
@ViewChild(SmtpFormComponent) smtpFormComponent: SmtpFormComponent;
|
||||
children: BaseFormComponent[];
|
||||
|
||||
constructor(private readonly _userService: UserService) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.children = [this.generalConfigFormComponent, this.smtpFormComponent];
|
||||
}
|
||||
|
||||
get changed(): boolean {
|
||||
for (const child of this.children) {
|
||||
if (child.changed) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
get valid() {
|
||||
for (const child of this.children) {
|
||||
if (!child.valid) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async save(): Promise<void> {
|
||||
for (const child of this.children) {
|
||||
if (child.changed) {
|
||||
await child.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,7 +94,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="dialog-actions">
|
||||
<button [disabled]="form.invalid || !smtpConfigurationChanged" color="primary" mat-flat-button type="submit">
|
||||
<button [disabled]="form.invalid || !changed" color="primary" mat-flat-button type="submit">
|
||||
{{ 'general-config-screen.actions.save' | translate }}
|
||||
</button>
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { ISmtpConfiguration } from '@red/domain';
|
||||
import { AutoUnsubscribe, IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { BaseFormComponent, IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { AdminDialogService } from '../../../services/admin-dialog.service';
|
||||
import { SmtpConfigService } from '../../../services/smtp-config.service';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
@ -12,9 +12,8 @@ import { firstValueFrom } from 'rxjs';
|
||||
templateUrl: './smtp-form.component.html',
|
||||
styleUrls: ['./smtp-form.component.scss'],
|
||||
})
|
||||
export class SmtpFormComponent extends AutoUnsubscribe implements OnInit, OnDestroy {
|
||||
export class SmtpFormComponent extends BaseFormComponent implements OnInit, OnDestroy {
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly form: FormGroup = this._getForm();
|
||||
private _initialConfiguration: ISmtpConfiguration;
|
||||
|
||||
constructor(
|
||||
@ -25,6 +24,7 @@ export class SmtpFormComponent extends AutoUnsubscribe implements OnInit, OnDest
|
||||
private readonly _toaster: Toaster,
|
||||
) {
|
||||
super();
|
||||
this.form = this._getForm();
|
||||
this.addSubscription = this.form.controls.auth.valueChanges.subscribe(auth => {
|
||||
if (auth) {
|
||||
this.openAuthConfigDialog();
|
||||
@ -32,20 +32,6 @@ export class SmtpFormComponent extends AutoUnsubscribe implements OnInit, OnDest
|
||||
});
|
||||
}
|
||||
|
||||
get smtpConfigurationChanged(): boolean {
|
||||
if (!this._initialConfiguration) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const key of Object.keys(this.form.getRawValue())) {
|
||||
if (this._initialConfiguration[key] !== this.form.get(key).value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
await this._loadData();
|
||||
}
|
||||
@ -65,6 +51,7 @@ export class SmtpFormComponent extends AutoUnsubscribe implements OnInit, OnDest
|
||||
await firstValueFrom(this._smtpConfigService.updateSMTPConfiguration(this.form.getRawValue()));
|
||||
this._initialConfiguration = this.form.getRawValue();
|
||||
this._loadingService.stop();
|
||||
this._loadData();
|
||||
}
|
||||
|
||||
async testConnection() {
|
||||
@ -104,6 +91,7 @@ export class SmtpFormComponent extends AutoUnsubscribe implements OnInit, OnDest
|
||||
this.form.patchValue(this._initialConfiguration, { emitEvent: false });
|
||||
} catch (e) {}
|
||||
|
||||
this.initialFormValue = this.form.getRawValue();
|
||||
this._loadingService.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,13 +43,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="dialog-actions">
|
||||
<button [disabled]="form.invalid || !changed" color="primary" mat-flat-button type="submit">
|
||||
<button [disabled]="disabled" color="primary" mat-flat-button type="submit">
|
||||
{{ 'add-edit-justification.actions.save' | translate }}
|
||||
</button>
|
||||
|
||||
<div class="all-caps-label cancel" mat-dialog-close translate="add-edit-justification.actions.cancel"></div>
|
||||
<div class="all-caps-label cancel" translate="add-edit-justification.actions.cancel" (click)="close()"></div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<iqser-circle-button class="dialog-close" icon="iqser:close" mat-dialog-close></iqser-circle-button>
|
||||
<iqser-circle-button class="dialog-close" icon="iqser:close" (action)="close()"></iqser-circle-button>
|
||||
</section>
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, Inject, Injector } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { Justification } from '@red/domain';
|
||||
import { JustificationsService } from '@services/entity-services/justifications.service';
|
||||
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
||||
import { LoadingService } from '@iqser/common-ui';
|
||||
import { BaseDialogComponent, LoadingService } from '@iqser/common-ui';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
@ -13,23 +13,21 @@ import { firstValueFrom } from 'rxjs';
|
||||
styleUrls: ['./add-edit-justification-dialog.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class AddEditJustificationDialogComponent {
|
||||
readonly form: FormGroup = this._getForm();
|
||||
export class AddEditJustificationDialogComponent extends BaseDialogComponent {
|
||||
|
||||
constructor(
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _justificationService: JustificationsService,
|
||||
private readonly _dossierTemplatesService: DossierTemplatesService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
public dialogRef: MatDialogRef<AddEditJustificationDialogComponent>,
|
||||
protected readonly _injector: Injector,
|
||||
protected readonly _dialogRef: MatDialogRef<AddEditJustificationDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public justification: Justification,
|
||||
) {}
|
||||
) {
|
||||
super(_injector, _dialogRef);
|
||||
|
||||
get changed(): boolean {
|
||||
return (
|
||||
!this.justification ||
|
||||
Object.keys(this.form.getRawValue()).reduce((prev, key) => prev || this.justification[key] !== this.form.get(key).value, false)
|
||||
);
|
||||
this.form = this._getForm();
|
||||
this.initialFormValue = this.form.getRawValue();
|
||||
}
|
||||
|
||||
async save() {
|
||||
@ -39,7 +37,7 @@ export class AddEditJustificationDialogComponent {
|
||||
await firstValueFrom(this._justificationService.createOrUpdate(this.form.getRawValue(), dossierTemplateId));
|
||||
await firstValueFrom(this._justificationService.loadAll(dossierTemplateId));
|
||||
this._loadingService.stop();
|
||||
this.dialogRef.close(true);
|
||||
this._dialogRef.close(true);
|
||||
}
|
||||
|
||||
private _getForm(): FormGroup {
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
<div class="editor-container">
|
||||
<ngx-monaco-editor (init)="onCodeEditorInit($event)" [(ngModel)]="codeEditorText" [options]="editorOptions"></ngx-monaco-editor>
|
||||
</div>
|
||||
<div *ngIf="hasChanges && permissionsService.isAdmin()" class="changes-box">
|
||||
<div *ngIf="changed && permissionsService.isAdmin() && !isLeaving" class="changes-box">
|
||||
<iqser-icon-button
|
||||
(action)="save()"
|
||||
[label]="'rules-screen.save-changes' | translate"
|
||||
|
||||
@ -3,7 +3,6 @@ import { PermissionsService } from '@services/permissions.service';
|
||||
import { Debounce, IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { saveAs } from 'file-saver';
|
||||
import { ComponentHasChanges } from '@guards/can-deactivate.guard';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
||||
import { RulesService } from '../../services/rules.service';
|
||||
@ -18,7 +17,7 @@ import IStandaloneEditorConstructionOptions = monaco.editor.IStandaloneEditorCon
|
||||
styleUrls: ['./rules-screen.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class RulesScreenComponent extends ComponentHasChanges implements OnInit {
|
||||
export class RulesScreenComponent implements OnInit {
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly editorOptions: IStandaloneEditorConstructionOptions = {
|
||||
theme: 'vs',
|
||||
@ -30,6 +29,8 @@ export class RulesScreenComponent extends ComponentHasChanges implements OnInit
|
||||
initialLines: string[] = [];
|
||||
currentLines: string[] = [];
|
||||
|
||||
isLeaving = false;
|
||||
|
||||
@ViewChild('fileInput')
|
||||
private _fileInput: ElementRef;
|
||||
|
||||
@ -44,11 +45,14 @@ export class RulesScreenComponent extends ComponentHasChanges implements OnInit
|
||||
private readonly _toaster: Toaster,
|
||||
protected readonly _translateService: TranslateService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
) {
|
||||
super(_translateService);
|
||||
) {}
|
||||
|
||||
set isLeavingPage(isLeaving: boolean) {
|
||||
this.isLeaving = isLeaving;
|
||||
this._changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
get hasChanges(): boolean {
|
||||
get changed(): boolean {
|
||||
return this.currentLines.toString() !== this.initialLines.toString();
|
||||
}
|
||||
|
||||
|
||||
@ -62,7 +62,7 @@ export class AdminDialogService extends DialogService<DialogType> {
|
||||
},
|
||||
addEditUser: {
|
||||
component: AddEditUserDialogComponent,
|
||||
dialogConfig: { autoFocus: true, disableClose: false },
|
||||
dialogConfig: { autoFocus: true },
|
||||
},
|
||||
smtpAuthConfig: {
|
||||
component: SmtpAuthDialogComponent,
|
||||
|
||||
@ -100,7 +100,7 @@
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<iqser-circle-button class="dialog-close" icon="iqser:close" mat-dialog-close></iqser-circle-button>
|
||||
<iqser-circle-button class="dialog-close" icon="iqser:close" (action)="close()"></iqser-circle-button>
|
||||
</section>
|
||||
|
||||
<ng-template #reportTemplateOptionTemplate let-option="option">
|
||||
|
||||
@ -9,8 +9,8 @@ import { PermissionsService } from '@services/permissions.service';
|
||||
import { JustificationsService } from '@services/entity-services/justifications.service';
|
||||
import { Dictionary, Dossier, File, IAddRedactionRequest } from '@red/domain';
|
||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||
import { BaseDialogComponent } from '@iqser/common-ui';
|
||||
import { DictionaryService } from '@shared/services/dictionary.service';
|
||||
import { BaseDialogComponent } from '@iqser/common-ui';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
|
||||
export interface LegalBasisOption {
|
||||
@ -42,7 +42,6 @@ export class ManualAnnotationDialogComponent extends BaseDialogComponent impleme
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
private readonly _dossiersService: DossiersService,
|
||||
private readonly _dictionaryService: DictionaryService,
|
||||
public dialogRef: MatDialogRef<ManualAnnotationDialogComponent>,
|
||||
protected readonly _injector: Injector,
|
||||
protected readonly _dialogRef: MatDialogRef<ManualAnnotationDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: { manualRedactionEntryWrapper: ManualRedactionEntryWrapper; file: File },
|
||||
@ -77,7 +76,6 @@ export class ManualAnnotationDialogComponent extends BaseDialogComponent impleme
|
||||
async ngOnInit() {
|
||||
super.ngOnInit();
|
||||
this.possibleDictionaries = await this._getPossibleDictionaries();
|
||||
|
||||
const data = await firstValueFrom(this._justificationsService.getForDossierTemplate(this._dossier.dossierTemplateId));
|
||||
this.legalOptions = data.map(lbm => ({
|
||||
legalBasis: lbm.reason,
|
||||
|
||||
@ -76,9 +76,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="withFloatingActions && !!editor?.hasChanges && canEdit" [class.offset]="compare" class="changes-box">
|
||||
<div *ngIf="withFloatingActions && !!editor?.hasChanges && canEdit && !isLeavingPage" [class.offset]="compare" class="changes-box">
|
||||
<iqser-icon-button
|
||||
(action)="saveDictionary.emit(editor?.currentEntries)"
|
||||
(action)="saveDictionary.emit()"
|
||||
[label]="'dictionary-overview.save-changes' | translate"
|
||||
[type]="iconButtonTypes.primary"
|
||||
icon="iqser:check"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Component, EventEmitter, Input, OnChanges, Output, ViewChild } from '@angular/core';
|
||||
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewChild } from '@angular/core';
|
||||
import { Debounce, IconButtonTypes, List } from '@iqser/common-ui';
|
||||
import { firstValueFrom, Observable, of } from 'rxjs';
|
||||
import { catchError, map, take, tap } from 'rxjs/operators';
|
||||
@ -26,6 +26,7 @@ export class DictionaryManagerComponent implements OnChanges {
|
||||
@Input() filterByDossierTemplate = false;
|
||||
@Input() initialEntries: List;
|
||||
@Input() canEdit = false;
|
||||
@Input() isLeavingPage = false;
|
||||
@Output() readonly saveDictionary = new EventEmitter<string[]>();
|
||||
@ViewChild(EditorComponent) readonly editor: EditorComponent;
|
||||
|
||||
@ -175,8 +176,10 @@ export class DictionaryManagerComponent implements OnChanges {
|
||||
this._scrollToCurrentMatch();
|
||||
}
|
||||
|
||||
ngOnChanges(): void {
|
||||
this.revert();
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (!changes.isLeavingPage) {
|
||||
this.revert();
|
||||
}
|
||||
}
|
||||
|
||||
private _applySearchDecorations() {
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit f4e0445212824788d10cd81adc0bce484a8dbb02
|
||||
Subproject commit 47dc55206bdea193eec78b021e04ccbaaf5b6915
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user