diff --git a/apps/red-ui/src/app/guards/can-deactivate.guard.ts b/apps/red-ui/src/app/guards/can-deactivate.guard.ts index b92aabb6c..38a494c00 100644 --- a/apps/red-ui/src/app/guards/can-deactivate.guard.ts +++ b/apps/red-ui/src/app/guards/can-deactivate.guard.ts @@ -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; } @Injectable({ providedIn: 'root' }) export class PendingChangesGuard implements CanDeactivate { - constructor(private readonly _translateService: TranslateService) {} + constructor(private _dialogService: ConfirmationDialogService) {} canDeactivate(component: ComponentCanDeactivate): boolean | Observable { - // 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; } } diff --git a/apps/red-ui/src/app/modules/account/screens/notifications/notifications-screen/notifications-screen.component.html b/apps/red-ui/src/app/modules/account/screens/notifications/notifications-screen/notifications-screen.component.html index 94a52a5c3..cf0f4412e 100644 --- a/apps/red-ui/src/app/modules/account/screens/notifications/notifications-screen/notifications-screen.component.html +++ b/apps/red-ui/src/app/modules/account/screens/notifications/notifications-screen/notifications-screen.component.html @@ -1,4 +1,4 @@ -
+
@@ -36,7 +36,7 @@
-
diff --git a/apps/red-ui/src/app/modules/account/screens/notifications/notifications-screen/notifications-screen.component.ts b/apps/red-ui/src/app/modules/account/screens/notifications/notifications-screen/notifications-screen.component.ts index 0038d6377..a862021bd 100644 --- a/apps/red-ui/src/app/modules/account/screens/notifications/notifications-screen/notifications-screen.component.ts +++ b/apps/red-ui/src/app/modules/account/screens/notifications/notifications-screen/notifications-screen.component.ts @@ -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 { 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(); } diff --git a/apps/red-ui/src/app/modules/account/screens/notifications/notifications.module.ts b/apps/red-ui/src/app/modules/account/screens/notifications/notifications.module.ts index d7a134836..ef7874558 100644 --- a/apps/red-ui/src/app/modules/account/screens/notifications/notifications.module.ts +++ b/apps/red-ui/src/app/modules/account/screens/notifications/notifications.module.ts @@ -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], diff --git a/apps/red-ui/src/app/modules/account/screens/user-profile/user-profile-screen/user-profile-screen.component.ts b/apps/red-ui/src/app/modules/account/screens/user-profile/user-profile-screen/user-profile-screen.component.ts index 731d5c6a0..421d54e39 100644 --- a/apps/red-ui/src/app/modules/account/screens/user-profile/user-profile-screen/user-profile-screen.component.ts +++ b/apps/red-ui/src/app/modules/account/screens/user-profile/user-profile-screen/user-profile-screen.component.ts @@ -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(); diff --git a/apps/red-ui/src/app/modules/account/screens/user-profile/user-profile.module.ts b/apps/red-ui/src/app/modules/account/screens/user-profile/user-profile.module.ts index 64bf3d6f4..775a99569 100644 --- a/apps/red-ui/src/app/modules/account/screens/user-profile/user-profile.module.ts +++ b/apps/red-ui/src/app/modules/account/screens/user-profile/user-profile.module.ts @@ -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], 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 f9335bfd3..4593549ea 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 @@ -174,6 +174,7 @@ const routes: Routes = [ path: 'general-config', component: GeneralConfigScreenComponent, canActivate: [CompositeRouteGuard], + canDeactivate: [PendingChangesGuard], data: { routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard], requiredRoles: ['RED_ADMIN'], diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/add-edit-user-dialog.component.html b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/add-edit-user-dialog.component.html index 1f17a0d25..51cae0fa7 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/add-edit-user-dialog.component.html +++ b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/add-edit-user-dialog.component.html @@ -1,17 +1,18 @@
- +
diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/add-edit-user-dialog.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/add-edit-user-dialog.component.ts index 30b9e7c21..a35e42d2e 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/add-edit-user-dialog.component.ts +++ b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/add-edit-user-dialog.component.ts @@ -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, @Inject(MAT_DIALOG_DATA) readonly user: User) {} + constructor( + protected readonly _injector: Injector, + protected readonly _dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) readonly user: User, + ) { + super(_injector, _dialogRef); + } toggleResetPassword() { this.resettingPassword = !this.resettingPassword; } + + async save(): Promise { + await this._userDetailsComponent.save(); + } + + get changed(): boolean { + return this._userDetailsComponent.changed; + } + + get valid(): boolean { + return this._userDetailsComponent.valid; + } + + closeDialog(event) { + this._dialogRef.close(event); + } } 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 75615f9ec..4ca949f7f 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 @@ -53,6 +53,6 @@ icon="iqser:trash" > -
+
diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/user-details/user-details.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/user-details/user-details.component.ts index 689d3434b..220f6aa12 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/user-details/user-details.component.ts +++ b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/user-details/user-details.component.ts @@ -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], diff --git a/apps/red-ui/src/app/modules/admin/dialogs/smtp-auth-dialog/smtp-auth-dialog.component.html b/apps/red-ui/src/app/modules/admin/dialogs/smtp-auth-dialog/smtp-auth-dialog.component.html index 55d87f45c..ab82c0fe3 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/smtp-auth-dialog/smtp-auth-dialog.component.html +++ b/apps/red-ui/src/app/modules/admin/dialogs/smtp-auth-dialog/smtp-auth-dialog.component.html @@ -27,5 +27,5 @@
- + diff --git a/apps/red-ui/src/app/modules/admin/dialogs/smtp-auth-dialog/smtp-auth-dialog.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/smtp-auth-dialog/smtp-auth-dialog.component.ts index a30311fcf..d5cc95383 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/smtp-auth-dialog/smtp-auth-dialog.component.ts +++ b/apps/red-ui/src/app/modules/admin/dialogs/smtp-auth-dialog/smtp-auth-dialog.component.ts @@ -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, + protected readonly _injector: Injector, + protected readonly _dialogRef: MatDialogRef, @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()); } } diff --git a/apps/red-ui/src/app/modules/admin/screens/dictionary-overview/dictionary-overview-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/dictionary-overview/dictionary-overview-screen.component.html index 8a3b92a69..6c6387dba 100644 --- a/apps/red-ui/src/app/modules/admin/screens/dictionary-overview/dictionary-overview-screen.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/dictionary-overview/dictionary-overview-screen.component.html @@ -55,8 +55,9 @@ diff --git a/apps/red-ui/src/app/modules/admin/screens/dictionary-overview/dictionary-overview-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/dictionary-overview/dictionary-overview-screen.component.ts index 7f017178c..b0bd3c4b4 100644 --- a/apps/red-ui/src/app/modules/admin/screens/dictionary-overview/dictionary-overview-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/dictionary-overview/dictionary-overview-screen.component.ts @@ -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) diff --git a/apps/red-ui/src/app/modules/admin/screens/general-config/general-config-form/general-config-form.component.html b/apps/red-ui/src/app/modules/admin/screens/general-config/general-config-form/general-config-form.component.html index d85baac5a..3c999db20 100644 --- a/apps/red-ui/src/app/modules/admin/screens/general-config/general-config-form/general-config-form.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/general-config/general-config-form/general-config-form.component.html @@ -2,7 +2,7 @@
-
+
@@ -23,7 +23,7 @@
-
diff --git a/apps/red-ui/src/app/modules/admin/screens/general-config/general-config-form/general-config-form.component.ts b/apps/red-ui/src/app/modules/admin/screens/general-config/general-config-form/general-config-form.component.ts index 1a6b61772..70b032385 100644 --- a/apps/red-ui/src/app/modules/admin/screens/general-config/general-config-form/general-config-form.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/general-config/general-config-form/general-config-form.component.ts @@ -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(); diff --git a/apps/red-ui/src/app/modules/admin/screens/general-config/general-config-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/general-config/general-config-screen.component.ts index aceb558f7..94b036eca 100644 --- a/apps/red-ui/src/app/modules/admin/screens/general-config/general-config-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/general-config/general-config-screen.component.ts @@ -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 { + for (const child of this.children) { + if (child.changed) { + await child.save(); + } + } + } } diff --git a/apps/red-ui/src/app/modules/admin/screens/general-config/smtp-form/smtp-form.component.html b/apps/red-ui/src/app/modules/admin/screens/general-config/smtp-form/smtp-form.component.html index bc4fee456..17402e94d 100644 --- a/apps/red-ui/src/app/modules/admin/screens/general-config/smtp-form/smtp-form.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/general-config/smtp-form/smtp-form.component.html @@ -94,7 +94,7 @@
- diff --git a/apps/red-ui/src/app/modules/admin/screens/general-config/smtp-form/smtp-form.component.ts b/apps/red-ui/src/app/modules/admin/screens/general-config/smtp-form/smtp-form.component.ts index 102695006..9625ecac9 100644 --- a/apps/red-ui/src/app/modules/admin/screens/general-config/smtp-form/smtp-form.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/general-config/smtp-form/smtp-form.component.ts @@ -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 { 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(); } } diff --git a/apps/red-ui/src/app/modules/admin/screens/justifications/add-edit-justification-dialog/add-edit-justification-dialog.component.html b/apps/red-ui/src/app/modules/admin/screens/justifications/add-edit-justification-dialog/add-edit-justification-dialog.component.html index 4a86c9beb..3e36230b7 100644 --- a/apps/red-ui/src/app/modules/admin/screens/justifications/add-edit-justification-dialog/add-edit-justification-dialog.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/justifications/add-edit-justification-dialog/add-edit-justification-dialog.component.html @@ -43,13 +43,13 @@
- -
+
- + diff --git a/apps/red-ui/src/app/modules/admin/screens/justifications/add-edit-justification-dialog/add-edit-justification-dialog.component.ts b/apps/red-ui/src/app/modules/admin/screens/justifications/add-edit-justification-dialog/add-edit-justification-dialog.component.ts index 6ee90987d..41d47bbef 100644 --- a/apps/red-ui/src/app/modules/admin/screens/justifications/add-edit-justification-dialog/add-edit-justification-dialog.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/justifications/add-edit-justification-dialog/add-edit-justification-dialog.component.ts @@ -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, + protected readonly _injector: Injector, + protected readonly _dialogRef: MatDialogRef, @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 { diff --git a/apps/red-ui/src/app/modules/admin/screens/rules/rules-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/rules/rules-screen.component.html index 78fd087bd..8f63c3a6d 100644 --- a/apps/red-ui/src/app/modules/admin/screens/rules/rules-screen.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/rules/rules-screen.component.html @@ -22,7 +22,7 @@
-
+
{ }, addEditUser: { component: AddEditUserDialogComponent, - dialogConfig: { autoFocus: true, disableClose: false }, + dialogConfig: { autoFocus: true }, }, smtpAuthConfig: { component: SmtpAuthDialogComponent, diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/add-dossier-dialog/add-dossier-dialog.component.html b/apps/red-ui/src/app/modules/dossier/dialogs/add-dossier-dialog/add-dossier-dialog.component.html index 726110f51..96a77da55 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/add-dossier-dialog/add-dossier-dialog.component.html +++ b/apps/red-ui/src/app/modules/dossier/dialogs/add-dossier-dialog/add-dossier-dialog.component.html @@ -100,7 +100,7 @@
- + diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts b/apps/red-ui/src/app/modules/dossier/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts index 9da3045d9..5f813eca6 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts +++ b/apps/red-ui/src/app/modules/dossier/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts @@ -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, protected readonly _injector: Injector, protected readonly _dialogRef: MatDialogRef, @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, diff --git a/apps/red-ui/src/app/modules/shared/components/dictionary-manager/dictionary-manager.component.html b/apps/red-ui/src/app/modules/shared/components/dictionary-manager/dictionary-manager.component.html index 9ea892f02..8b54d5c02 100644 --- a/apps/red-ui/src/app/modules/shared/components/dictionary-manager/dictionary-manager.component.html +++ b/apps/red-ui/src/app/modules/shared/components/dictionary-manager/dictionary-manager.component.html @@ -76,9 +76,9 @@
-
+
(); @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() { diff --git a/libs/common-ui b/libs/common-ui index f4e044521..47dc55206 160000 --- a/libs/common-ui +++ b/libs/common-ui @@ -1 +1 @@ -Subproject commit f4e0445212824788d10cd81adc0bce484a8dbb02 +Subproject commit 47dc55206bdea193eec78b021e04ccbaaf5b6915 diff --git a/paligo-theme/paligo-styles/redacto-theme.css b/paligo-theme/paligo-styles/redacto-theme.css index fc5511db6..f5622f730 100644 --- a/paligo-theme/paligo-styles/redacto-theme.css +++ b/paligo-theme/paligo-styles/redacto-theme.css @@ -1,482 +1,482 @@ @charset "UTF-8"; -@import url("https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;1,300;1,400;1,500;1,600;1,700;1,800&display=swap"); +@import url('https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;1,300;1,400;1,500;1,600;1,700;1,800&display=swap'); .portal-header { - z-index: 1; - height: 450px; + z-index: 1; + height: 450px; } .portal-header::before { - background-color: #283241; + background-color: #283241; } .portal-header h1 { - font-size: 64px; - font-weight: 300; - line-height: 87px; - margin-top: 68px; - margin-bottom: 40px; + font-size: 64px; + font-weight: 300; + line-height: 87px; + margin-top: 68px; + margin-bottom: 40px; } .portal-header .portal-search { - max-width: 600px; - margin: auto; - position: relative; + max-width: 600px; + margin: auto; + position: relative; } .portal-header .portal-search .search-field { - width: 100%; - border: 1px solid #d3d5da; - border-radius: 8px; - background-color: #fff; + width: 100%; + border: 1px solid #d3d5da; + border-radius: 8px; + background-color: #fff; } .portal-header .portal-search .search-field::placeholder { - opacity: 0.7; + opacity: 0.7; } .portal-header .portal-search .search-field, .portal-header .portal-search .search-field::placeholder { - color: #283241; - font-size: 14px; - line-height: 18px; + color: #283241; + font-size: 14px; + line-height: 18px; } .portal-header .portal-search .search-field { - padding: 12px 17px; + padding: 12px 17px; } .portal-header .portal-search .btn { - position: absolute; - right: 0; - padding: 11px 18px; - background-color: transparent; - color: #283241; - cursor: pointer; - border-radius: 0 8px 8px 0; + position: absolute; + right: 0; + padding: 11px 18px; + background-color: transparent; + color: #283241; + cursor: pointer; + border-radius: 0 8px 8px 0; } .portal-header .portal-search .btn:hover { - background-color: #dd4d50; + background-color: #dd4d50; } @media only screen and (max-width: 768px) { - .portal-header h1 { - font-size: 42px; - font-weight: 300; - line-height: 57px; - } + .portal-header h1 { + font-size: 42px; + font-weight: 300; + line-height: 57px; + } } .featured-content-label { - display: none; + display: none; } .featured-content { - display: none; + display: none; } .portal-single-publication { - background-color: transparent; + background-color: transparent; } .portal-single-publication > a { - border-radius: 4px; + border-radius: 4px; } .portal-single-publication .publication-icon { - background-color: #dd4d50; + background-color: #dd4d50; } .portal-contents { - margin-top: 100px; - margin-bottom: 0; + margin-top: 100px; + margin-bottom: 0; } .portal-contents .inner { - margin: 0; - display: grid; - grid-template-columns: 1fr 1fr; - grid-gap: 24px; + margin: 0; + display: grid; + grid-template-columns: 1fr 1fr; + grid-gap: 24px; } .portal-contents .inner::before { - content: none; + content: none; } .publication-contents { - padding: 24px 40px; - border: 1px solid #e2e4e9; - width: 100%; - margin: 0; - background-color: #fff; - border-radius: 4px; + padding: 24px 40px; + border: 1px solid #e2e4e9; + width: 100%; + margin: 0; + background-color: #fff; + border-radius: 4px; } .publication-contents:first-child { - grid-column: 1/span 2; + grid-column: 1 / span 2; } .publication-contents h4.featured-title, .publication-contents .section-toc-title { - font-size: 28px; - font-weight: 300; - line-height: 36px; - margin: 0; + font-size: 28px; + font-weight: 300; + line-height: 36px; + margin: 0; } .publication-contents h4.featured-title a, .publication-contents .section-toc-title a { - color: #283241; + color: #283241; } .publication-contents h4.featured-title a:hover, .publication-contents .section-toc-title a:hover { - color: #283241; - text-decoration: underline; + color: #283241; + text-decoration: underline; } .publication-contents ul { - margin: 0; - padding: 0; + margin: 0; + padding: 0; } .publication-contents li { - margin: 4px 0; + margin: 4px 0; } .publication-contents li:first-child { - margin-top: 20px; + margin-top: 20px; } .publication-contents li:last-child { - margin-bottom: 40px; + margin-bottom: 40px; } .publication-contents li a { - color: #dd4d50; - font-size: 16px; - line-height: 24px; + color: #dd4d50; + font-size: 16px; + line-height: 24px; } .publication-contents li a:hover { - color: #dd4d50; - text-decoration: underline; + color: #dd4d50; + text-decoration: underline; } .publication-contents h4 span, .publication-contents li::before { - display: none; + display: none; } @media only screen and (max-width: 768px) { - .portal-contents .inner { - grid-template-columns: 1fr; - } + .portal-contents .inner { + grid-template-columns: 1fr; + } - .publication-contents:first-child { - grid-column: auto; - } + .publication-contents:first-child { + grid-column: auto; + } } /* Einleitung */ .cat-panel-1:before { - content: "\f277"; + content: '\f277'; } /* Workflow */ .cat-panel-2:before { - content: "\f0c1"; + content: '\f0c1'; } /* Voraussetzungen */ .cat-panel-3:before { - content: "\f109"; + content: '\f109'; } /* Benutzermenü und -profil */ .cat-panel-4:before { - content: "\f007"; + content: '\f007'; } /* Benachrichtigungen */ .cat-panel-5:before { - content: "\f0f3"; + content: '\f0f3'; } /* Suchfunktion */ .cat-panel-6:before { - content: "\f002"; + content: '\f002'; } /* Ebenen in der Benutzeroberfläche des RedactManagers */ .cat-panel-7:before { - content: "\f248"; + content: '\f248'; } /* Rollen und Berechtigungen */ .cat-panel-8:before { - content: "\f084"; + content: '\f084'; } /* Dossier erstellen und verwalten */ .cat-panel-9:before { - content: "\f07c"; + content: '\f07c'; } /* Dokumente bearbeiten im Editor */ .cat-panel-10:before { - content: "\f15c"; + content: '\f15c'; } /* Dossier abschließen und herunterladen */ .cat-panel-11:before { - content: "\f019"; + content: '\f019'; } /* Funktionsübersicht */ .cat-panel-12:before { - content: "\f03a"; + content: '\f03a'; } /* Glossar */ .cat-panel-13:before { - content: "\f02d"; + content: '\f02d'; } /* FAQ’s (häufige Fragen) */ .cat-panel-14:before { - content: "\f059"; + content: '\f059'; } .portal-search-result { - background-color: #f5f5f7; + background-color: #f5f5f7; } .search-container { - padding-bottom: 100px; + padding-bottom: 100px; } .portal-search-result { - padding: 80px 0 0 0; + padding: 80px 0 0 0; } ul.searchresults { - border: 1px solid #e2e4e9; - background-color: #fff; - border-radius: 4px; - margin-top: 32px; + border: 1px solid #e2e4e9; + background-color: #fff; + border-radius: 4px; + margin-top: 32px; } ul.searchresults .search-highlight { - font-style: normal; + font-style: normal; } li.searchresultitem { - margin: 0 32px; - border-bottom: 1px solid #e2e4e9; - padding: 32px 8px; + margin: 0 32px; + border-bottom: 1px solid #e2e4e9; + padding: 32px 8px; } .searchresultitem.selected-searchresultitem { - background-color: transparent; - border-radius: 0; + background-color: transparent; + border-radius: 0; } .searchresulttitle { - font-size: 28px; - font-weight: 300; - line-height: 36px; - color: #283241; + font-size: 28px; + font-weight: 300; + line-height: 36px; + color: #283241; } .searchresultsnippet { - margin: 16px 0; - color: #283241; + margin: 16px 0; + color: #283241; } .search-result-breadcrumbs { - color: #dd4d50; + color: #dd4d50; } .portal-footer, .site-footer { - border-top: 1px solid #e2e4e9; - padding: 0; + border-top: 1px solid #e2e4e9; + padding: 0; } .portal-footer.portal-footer, .site-footer.portal-footer { - margin-top: 100px; + margin-top: 100px; } .portal-footer .inner, .site-footer .inner { - margin: 0; - padding: 8px 0 64px 0; - font-size: 16px; - line-height: 24px; + margin: 0; + padding: 8px 0 64px 0; + font-size: 16px; + line-height: 24px; } .portal-footer .inner > *, .site-footer .inner > * { - padding: 0; + padding: 0; } .portal-footer .inner .copyright, .site-footer .inner .copyright { - width: 50%; + width: 50%; } :root { - --iqser-primary: lightblue; - --iqser-primary-rgb: 220, 230, 234; - --iqser-primary-2: orange; - --iqser-accent: blue; - --iqser-accent-rgb: 123, 234, 111; - --iqser-disabled: #9398a0; - --iqser-not-disabled-table-item: #f9fafb; - --iqser-btn-bg-hover: #e2e4e9; - --iqser-btn-bg: #f0f1f4; - --iqser-warn: #fdbd00; - --iqser-white: white; - --iqser-black: black; - --iqser-light: white; - --iqser-separator: rgba(226, 228, 233, 0.9); - --iqser-quick-filter-border: #d3d5da; - --iqser-grey-1: #283241; - --iqser-grey-2: #f4f5f7; - --iqser-grey-3: #aaacb3; - --iqser-grey-4: #e2e4e9; - --iqser-grey-5: #d3d5da; - --iqser-grey-6: #f0f1f4; - --iqser-grey-7: #9398a0; - --iqser-grey-8: #f9fafb; - --iqser-green-1: #00ff00; - --iqser-green-2: #5ce594; - --iqser-yellow-1: #ffb83b; - --iqser-yellow-2: #fdbd00; - --iqser-yellow-rgb: 253, 189, 0; - --iqser-red-1: #dd4d50; - --iqser-blue-5: #c5d3eb; - --iqser-helpmode-primary: green; + --iqser-primary: lightblue; + --iqser-primary-rgb: 220, 230, 234; + --iqser-primary-2: orange; + --iqser-accent: blue; + --iqser-accent-rgb: 123, 234, 111; + --iqser-disabled: #9398a0; + --iqser-not-disabled-table-item: #f9fafb; + --iqser-btn-bg-hover: #e2e4e9; + --iqser-btn-bg: #f0f1f4; + --iqser-warn: #fdbd00; + --iqser-white: white; + --iqser-black: black; + --iqser-light: white; + --iqser-separator: rgba(226, 228, 233, 0.9); + --iqser-quick-filter-border: #d3d5da; + --iqser-grey-1: #283241; + --iqser-grey-2: #f4f5f7; + --iqser-grey-3: #aaacb3; + --iqser-grey-4: #e2e4e9; + --iqser-grey-5: #d3d5da; + --iqser-grey-6: #f0f1f4; + --iqser-grey-7: #9398a0; + --iqser-grey-8: #f9fafb; + --iqser-green-1: #00ff00; + --iqser-green-2: #5ce594; + --iqser-yellow-1: #ffb83b; + --iqser-yellow-2: #fdbd00; + --iqser-yellow-rgb: 253, 189, 0; + --iqser-red-1: #dd4d50; + --iqser-blue-5: #c5d3eb; + --iqser-helpmode-primary: green; } .site-sidebar { - background-color: #283241; - scrollbar-color: var(--iqser-quick-filter-border) var(--iqser-grey-2); - scrollbar-width: thin; - /* Track */ - /* Handle */ + background-color: #283241; + scrollbar-color: var(--iqser-quick-filter-border) var(--iqser-grey-2); + scrollbar-width: thin; + /* Track */ + /* Handle */ } .site-sidebar .logo { - padding: 24px 0 30px 0 !important; + padding: 24px 0 30px 0 !important; } .site-sidebar::-webkit-scrollbar { - width: 11px; + width: 11px; } .site-sidebar::-webkit-scrollbar-track { - background: var(--iqser-grey-2); + background: var(--iqser-grey-2); } .site-sidebar::-webkit-scrollbar-thumb { - background: var(--iqser-quick-filter-border); + background: var(--iqser-quick-filter-border); } .site-sidebar-search { - padding: 0 24px; + padding: 0 24px; } .site-sidebar-search .search-field { - width: 100%; - border: 1px solid #d3d5da; - border-radius: 8px; - background-color: #fff; + width: 100%; + border: 1px solid #d3d5da; + border-radius: 8px; + background-color: #fff; } .site-sidebar-search .search-field::placeholder { - opacity: 0.7; + opacity: 0.7; } .site-sidebar-search .search-field, .site-sidebar-search .search-field::placeholder { - color: #283241; - font-size: 14px; - line-height: 18px; + color: #283241; + font-size: 14px; + line-height: 18px; } .site-sidebar-search .search-field { - padding: 7px 13px; + padding: 7px 13px; } .nav-site-sidebar { - margin-top: 16px; + margin-top: 16px; } .nav-site-sidebar .topic-link { - padding-top: 11px; - padding-bottom: 11px; - font-size: 14px; - line-height: 18px; - color: #d3d5da; + padding-top: 11px; + padding-bottom: 11px; + font-size: 14px; + line-height: 18px; + color: #d3d5da; } .nav-site-sidebar .topic-link:hover { - background-color: #313d4e; + background-color: #313d4e; } .nav-site-sidebar .active > .topic-link { - background-color: #313d4e; + background-color: #313d4e; } .nav-site-sidebar .active > a { - color: #fff; - font-weight: 600; + color: #fff; + font-weight: 600; } .nav-site-sidebar > li > a { - padding-left: 24px; + padding-left: 24px; } .nav-site-sidebar > li > ul > li > a { - padding-left: 32px; + padding-left: 32px; } .nav-site-sidebar > li > ul > li > ul > li > a { - padding-left: 40px; + padding-left: 40px; } .toc .glyphicon { - top: 5px; + top: 5px; } .toc > li > .topic-link > .glyphicon { - margin-top: 4px; + margin-top: 4px; } .toolbar { - box-shadow: none; - padding: 21px 24px; - margin-bottom: 50px; + box-shadow: none; + padding: 21px 24px; + margin-bottom: 50px; } .topic-content .breadcrumb-container { - margin-top: 40px; + margin-top: 40px; } .topic-content .breadcrumb { - font-size: 14px; - line-height: 18px; - font-weight: 600; + font-size: 14px; + line-height: 18px; + font-weight: 600; } .topic-content .breadcrumb a { - color: #283241; + color: #283241; } .topic-content .breadcrumb a:hover { - color: #dd4d50; - text-decoration: underline; + color: #dd4d50; + text-decoration: underline; } .topic-content .breadcrumb .breadcrumb-node { - color: #dd4d50; + color: #dd4d50; } main article { - margin-bottom: 0; - padding: 0; + margin-bottom: 0; + padding: 0; } section > .titlepage .title { - margin: 24px 0 16px 0; + margin: 24px 0 16px 0; } #topic-content > section > .titlepage h2.title { - margin: 0 0 24px; + margin: 0 0 24px; } .image-viewport { - margin: auto; + margin: auto; } .image-viewport img { - margin: 16px auto; + margin: 16px auto; } .pager { - margin-top: 30px; - margin-bottom: 30px; - padding: 0; + margin-top: 30px; + margin-bottom: 30px; + padding: 0; } .pager li > a, .pager li > span { - color: #dd4d50; - font-size: 14px; - font-weight: 600; - line-height: 19px; - text-transform: uppercase; - padding: 0; - background-color: transparent; - border: none; - border-radius: 0; + color: #dd4d50; + font-size: 14px; + font-weight: 600; + line-height: 19px; + text-transform: uppercase; + padding: 0; + background-color: transparent; + border: none; + border-radius: 0; } .pager li > a:hover, .pager li > span:hover { - text-decoration: underline; - background-color: transparent; - color: #dd4d50; + text-decoration: underline; + background-color: transparent; + color: #dd4d50; } .warning, @@ -484,230 +484,241 @@ section > .titlepage .title { .important, .caution, .tip { - margin-top: 32px; - margin-bottom: 32px; - padding: 16px 24px 16px 68px; - background-color: #fff; - border-left: 4px solid #dd4d50; - border-radius: 4px; + margin-top: 32px; + margin-bottom: 32px; + padding: 16px 24px 16px 68px; + background-color: #fff; + border-left: 4px solid #dd4d50; + border-radius: 4px; } .warning:before, .note:before, .important:before, .caution:before, .tip:before { - color: #dd4d50; - width: 20px; - height: 20px; - text-align: center; - left: 24px; - top: calc(50% - 15px); + color: #dd4d50; + width: 20px; + height: 20px; + text-align: center; + left: 24px; + top: calc(50% - 15px); } .warning h3, .note h3, .important h3, .caution h3, .tip h3 { - padding: 0; - font-size: 18px; - font-weight: 600; - line-height: 24px; - margin-bottom: 8px; + padding: 0; + font-size: 18px; + font-weight: 600; + line-height: 24px; + margin-bottom: 8px; } .warning p, .note p, .important p, .caution p, .tip p { - line-height: 20px; + line-height: 20px; } .topic-content > section > p { - margin: 12px 0; + margin: 12px 0; } .panel { - padding: 12px 0; - border-radius: 4px; - border: none; + padding: 12px 0; + border-radius: 4px; + border: none; } .panel .panel-body > p { - margin-bottom: 12px; + margin-bottom: 12px; } .panel .panel-body > p:not(:first-of-type) { - margin-top: 18px; + margin-top: 18px; } .mediaobject { - margin-top: 20px; + margin-top: 20px; } .mediaobject img { - border-radius: 4px; - margin: 0; - box-shadow: 0 3px 12px 5px rgba(40, 50, 65, 0.14); + border-radius: 4px; + margin: 0; + box-shadow: 0 3px 12px 5px rgba(40, 50, 65, 0.14); } .mediaobject .caption > p { - font-size: 14px; - text-align: center; - font-style: italic; - margin: 0; + font-size: 14px; + text-align: center; + font-style: italic; + margin: 0; } .inlinemediaobject { - vertical-align: unset; + vertical-align: unset; } main ol, main ul { - margin: 0 0 24px; + margin: 0 0 24px; } .section-toc { - padding: 24px 40px; - border: 1px solid #e2e4e9; - width: 100%; - margin: 0; - background-color: #fff; - border-radius: 4px; + padding: 24px 40px; + border: 1px solid #e2e4e9; + width: 100%; + margin: 0; + background-color: #fff; + border-radius: 4px; } .section-toc:first-child { - grid-column: 1/span 2; + grid-column: 1 / span 2; } .section-toc h4.featured-title, .section-toc .section-toc-title { - font-size: 28px; - font-weight: 300; - line-height: 36px; - margin: 0; + font-size: 28px; + font-weight: 300; + line-height: 36px; + margin: 0; } .section-toc h4.featured-title a, .section-toc .section-toc-title a { - color: #283241; + color: #283241; } .section-toc h4.featured-title a:hover, .section-toc .section-toc-title a:hover { - color: #283241; - text-decoration: underline; + color: #283241; + text-decoration: underline; } .section-toc ul { - margin: 0; - padding: 0; + margin: 0; + padding: 0; } .section-toc li { - margin: 4px 0; + margin: 4px 0; } .section-toc li:first-child { - margin-top: 20px; + margin-top: 20px; } .section-toc li:last-child { - margin-bottom: 40px; + margin-bottom: 40px; } .section-toc li a { - color: #dd4d50; - font-size: 16px; - line-height: 24px; + color: #dd4d50; + font-size: 16px; + line-height: 24px; } .section-toc li a:hover { - color: #dd4d50; - text-decoration: underline; + color: #dd4d50; + text-decoration: underline; } .section-toc h4 span, .section-toc li::before { - display: none; + display: none; } .section-toc li:first-child { - margin-top: 16px; + margin-top: 16px; } .section-toc li:last-child { - margin-bottom: 8px; + margin-bottom: 8px; } .procedure > li.step::before { - background-color: transparent; - border: 1px solid #dd4d50; - color: #dd4d50; - line-height: 23px; + background-color: transparent; + border: 1px solid #dd4d50; + color: #dd4d50; + line-height: 23px; } .question { - font-weight: 600; + font-weight: 600; } .question > td > p { - margin: 32px 0 18px 0; + margin: 32px 0 18px 0; } .question > td:first-child { - padding-right: 4px; + padding-right: 4px; } .fixed-toolbar article.topic :target.question:before { - content: none; + content: none; } body { - color: #283241; - background-color: #f5f5f7; - font-family: "Open Sans", sans-serif; - scrollbar-color: var(--iqser-quick-filter-border) var(--iqser-grey-2); - scrollbar-width: thin; - /* Track */ - /* Handle */ + color: #283241; + background-color: #f5f5f7; + font-family: 'Open Sans', sans-serif; + scrollbar-color: var(--iqser-quick-filter-border) var(--iqser-grey-2); + scrollbar-width: thin; + /* Track */ + /* Handle */ } -body h1, body .h1, -body h2, body .h2, -body h3, body .h3, -body h4, body .h4, -body h5, body .h5, -body h6, body .h6, +body h1, +body .h1, +body h2, +body .h2, +body h3, +body .h3, +body h4, +body .h4, +body h5, +body .h5, +body h6, +body .h6, body p, body pre { - margin: 0; - font-family: "Open Sans", sans-serif; + margin: 0; + font-family: 'Open Sans', sans-serif; } body::-webkit-scrollbar { - width: 11px; + width: 11px; } body::-webkit-scrollbar-track { - background: var(--iqser-grey-2); + background: var(--iqser-grey-2); } body::-webkit-scrollbar-thumb { - background: var(--iqser-quick-filter-border); + background: var(--iqser-quick-filter-border); } -body h1, body .h1 { - font-size: 64px; - font-weight: 300; - line-height: 87px; +body h1, +body .h1 { + font-size: 64px; + font-weight: 300; + line-height: 87px; } -body h2, body .h2 { - font-size: 42px; - font-weight: 300; - line-height: 57px; +body h2, +body .h2 { + font-size: 42px; + font-weight: 300; + line-height: 57px; } -body h3, body .h3 { - font-size: 32px; - font-weight: 300; - line-height: 43px; +body h3, +body .h3 { + font-size: 32px; + font-weight: 300; + line-height: 43px; } -body h4, body .h4 { - font-size: 28px; - font-weight: 300; - line-height: 36px; +body h4, +body .h4 { + font-size: 28px; + font-weight: 300; + line-height: 36px; } -body h5, body .h5 { - font-size: 18px; - font-weight: 600; - line-height: 24px; +body h5, +body .h5 { + font-size: 18px; + font-weight: 600; + line-height: 24px; } body p { - font-size: 16px; - line-height: 24px; + font-size: 16px; + line-height: 24px; } body a { - color: #dd4d50; + color: #dd4d50; } body a:hover { - text-decoration: underline; - color: #dd4d50; + text-decoration: underline; + color: #dd4d50; } body a:focus { - color: #dd4d50; + color: #dd4d50; }