RED-4085: System preferences

This commit is contained in:
Adina Țeudan 2022-05-24 00:10:33 +03:00
parent fd10e270b2
commit 301ffb9204
18 changed files with 170 additions and 35 deletions

View File

@ -51,6 +51,7 @@ import { MAT_TOOLTIP_DEFAULT_OPTIONS } from '@angular/material/tooltip';
import { LoggerModule, NgxLoggerLevel, TOKEN_LOGGER_CONFIG, TOKEN_LOGGER_RULES_SERVICE } from 'ngx-logger';
import { LoggerRulesService } from '@services/logger-rules.service';
import { ILoggerConfig } from '@red/domain';
import { SystemPreferencesService } from '@services/system-preferences.service';
export function httpLoaderFactory(httpClient: HttpClient, configService: ConfigService): PruningTranslationLoader {
return new PruningTranslationLoader(httpClient, '/assets/i18n/', `.json?version=${configService.values.FRONTEND_APP_VERSION}`);
@ -167,6 +168,7 @@ const components = [AppComponent, AuthErrorComponent, NotificationsComponent, Sp
KeycloakService,
Title,
ConfigService,
SystemPreferencesService,
FeaturesService,
GeneralSettingsService,
LanguageService,

View File

@ -43,6 +43,7 @@ import { ConfirmDeleteDossierStateDialogComponent } from './dialogs/confirm-dele
import { BaseEntityScreenComponent } from './base-entity-screen/base-entity-screen.component';
import { CloneDossierTemplateDialogComponent } from './dialogs/clone-dossier-template-dialog/clone-dossier-template-dialog.component';
import { AdminSideNavComponent } from './admin-side-nav/admin-side-nav.component';
import { SystemPreferencesFormComponent } from './screens/general-config/system-preferences-form/system-preferences-form.component';
const dialogs = [
AddEditDossierTemplateDialogComponent,
@ -84,6 +85,7 @@ const components = [
BaseEntityScreenComponent,
GeneralConfigFormComponent,
SmtpFormComponent,
SystemPreferencesFormComponent,
...dialogs,
...screens,

View File

@ -24,20 +24,6 @@ export class GeneralConfigFormComponent extends BaseFormComponent implements OnI
this.form = this._getForm();
}
get generalConfigurationChanged(): 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();
}

View File

@ -24,6 +24,9 @@
<div class="dialog mb-0">
<redaction-general-config-form></redaction-general-config-form>
</div>
<div class="dialog mt-24 mb-0">
<redaction-system-preferences-form></redaction-system-preferences-form>
</div>
<div class="dialog mt-24">
<redaction-smtp-form></redaction-smtp-form>
</div>

View File

@ -3,6 +3,7 @@ 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';
import { SystemPreferencesFormComponent } from './system-preferences-form/system-preferences-form.component';
@Component({
selector: 'redaction-general-config-screen',
@ -13,6 +14,7 @@ export class GeneralConfigScreenComponent extends BaseFormComponent implements A
readonly currentUser = this._userService.currentUser;
@ViewChild(GeneralConfigFormComponent) generalConfigFormComponent: GeneralConfigFormComponent;
@ViewChild(SystemPreferencesFormComponent) systemPreferencesFormComponent: SystemPreferencesFormComponent;
@ViewChild(SmtpFormComponent) smtpFormComponent: SmtpFormComponent;
children: BaseFormComponent[];
@ -20,10 +22,6 @@ export class GeneralConfigScreenComponent extends BaseFormComponent implements A
super();
}
ngAfterViewInit() {
this.children = [this.generalConfigFormComponent, this.smtpFormComponent];
}
get changed(): boolean {
for (const child of this.children) {
if (child.changed) {
@ -42,6 +40,10 @@ export class GeneralConfigScreenComponent extends BaseFormComponent implements A
return true;
}
ngAfterViewInit() {
this.children = [this.generalConfigFormComponent, this.systemPreferencesFormComponent, this.smtpFormComponent];
}
async save(): Promise<void> {
for (const child of this.children) {
if (child.changed) {

View File

@ -0,0 +1,18 @@
<div class="dialog-header">
<div class="heading-l" translate="general-config-screen.system-preferences.title"></div>
</div>
<form (submit)="save()" *ngIf="form" [formGroup]="form">
<div class="dialog-content">
<div class="dialog-content-left">
<div *ngFor="let key of keys" class="iqser-input-group required">
<label [translate]="translations.label[key]"></label>
<input [formControlName]="key" [name]="key" [placeholder]="translations.placeholder[key] | translate" type="number" />
</div>
</div>
</div>
<div class="dialog-actions">
<button [disabled]="form?.invalid || !changed" color="primary" mat-flat-button type="submit">
{{ 'general-config-screen.actions.save' | translate }}
</button>
</div>
</form>

View File

@ -0,0 +1,51 @@
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { SystemPreferences } from '@red/domain';
import { BaseFormComponent, KeysOf, LoadingService } from '@iqser/common-ui';
import { firstValueFrom } from 'rxjs';
import { SystemPreferencesService } from '@services/system-preferences.service';
import { systemPreferencesTranslations } from '@translations/system-preferences-translations';
@Component({
selector: 'redaction-system-preferences-form',
templateUrl: './system-preferences-form.component.html',
styleUrls: ['./system-preferences-form.component.scss'],
})
export class SystemPreferencesFormComponent extends BaseFormComponent {
readonly translations = systemPreferencesTranslations;
readonly keys: KeysOf<SystemPreferences>[] = [
'softDeleteCleanupTime',
'downloadCleanupDownloadFilesHours',
'downloadCleanupNotDownloadFilesHours',
];
private _initialConfiguration: SystemPreferences;
constructor(
private readonly _loadingService: LoadingService,
private readonly _systemPreferencesService: SystemPreferencesService,
private readonly _formBuilder: FormBuilder,
) {
super();
this.form = this._getForm();
this._loadData();
}
async save(): Promise<void> {
this._loadingService.start();
await firstValueFrom(this._systemPreferencesService.update(this.form.getRawValue()));
this._loadData();
this._loadingService.stop();
}
private _getForm(): FormGroup {
const controlsConfig = {};
this.keys.forEach(key => (controlsConfig[key] = [this._systemPreferencesService.values[key], Validators.required]));
return this._formBuilder.group(controlsConfig);
}
private _loadData() {
this._initialConfiguration = this._systemPreferencesService.values;
this.form.patchValue(this._initialConfiguration, { emitEvent: false });
this.initialFormValue = this.form.getRawValue();
}
}

View File

@ -28,15 +28,6 @@ export class ConfigService {
return this._values;
}
loadAppConfig(): Observable<any> {
return this._httpClient.get('/app-config').pipe(
tap(config => {
console.log('[REDACTION] Loaded config: ', config);
this._values = { ...this._values, ...config };
}),
);
}
loadLocalConfig(): Observable<any> {
return this._httpClient.get<any>('/assets/config/config.json').pipe(
tap(config => {

View File

@ -4,13 +4,13 @@ import { Dossier, File, IDossier, IFile, TrashDossier, TrashFile, TrashItem } fr
import { catchError, switchMap, take, tap } from 'rxjs/operators';
import { forkJoin, map, Observable, of } from 'rxjs';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { ConfigService } from '../config.service';
import { PermissionsService } from '../permissions.service';
import { ActiveDossiersService } from '../dossiers/active-dossiers.service';
import { UserService } from '../user.service';
import { flatMap } from 'lodash-es';
import { DossierStatsService } from '../dossiers/dossier-stats.service';
import { FilesService } from '../files/files.service';
import { SystemPreferencesService } from '@services/system-preferences.service';
@Injectable({
providedIn: 'root',
@ -19,7 +19,7 @@ export class TrashService extends EntitiesService<TrashItem> {
constructor(
protected readonly _injector: Injector,
private readonly _toaster: Toaster,
private readonly _configService: ConfigService,
private readonly _systemPreferencesService: SystemPreferencesService,
private readonly _permissionsService: PermissionsService,
private readonly _activeDossiersService: ActiveDossiersService,
private readonly _userService: UserService,
@ -64,7 +64,7 @@ export class TrashService extends EntitiesService<TrashItem> {
dossier =>
new TrashDossier(
dossier,
this._configService.values.softDeleteCleanupTime,
this._systemPreferencesService.values.softDeleteCleanupTime,
this._permissionsService.canRestoreDossier(dossier),
this._permissionsService.canHardDeleteDossier(dossier),
),
@ -82,7 +82,7 @@ export class TrashService extends EntitiesService<TrashItem> {
return new TrashFile(
file,
dossier.dossierTemplateId,
this._configService.values.softDeleteCleanupTime,
this._systemPreferencesService.values.softDeleteCleanupTime,
this._permissionsService.canRestoreFile(file, dossier),
this._permissionsService.canHardDeleteFile(file, dossier),
);

View File

@ -0,0 +1,29 @@
import { Injectable, Injector } from '@angular/core';
import { GenericService } from '@iqser/common-ui';
import { SystemPreferences } from '@red/domain';
import { Observable, switchMap } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable({
providedIn: 'root',
})
export class SystemPreferencesService extends GenericService<SystemPreferences> {
values: SystemPreferences;
constructor(protected readonly _injector: Injector) {
super(_injector, 'app-config');
}
loadPreferences(): Observable<SystemPreferences> {
return this.get<SystemPreferences>().pipe(
tap((config: SystemPreferences) => {
console.log('[REDACTION] Loaded config: ', config);
this.values = config;
}),
);
}
update(value: SystemPreferences): Observable<SystemPreferences> {
return this._post(value).pipe(switchMap(() => this.loadPreferences()));
}
}

View File

@ -0,0 +1,20 @@
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { SystemPreferences } from '@red/domain';
import { KeysOf } from '@iqser/common-ui';
export const systemPreferencesTranslations: Record<'label' | 'placeholder', Record<KeysOf<SystemPreferences>, string>> = {
label: {
softDeleteCleanupTime: _('general-config-screen.system-preferences.labels.soft-delete-cleanup-time'),
downloadCleanupDownloadFilesHours: _('general-config-screen.system-preferences.labels.download-cleanup-download-files-hours'),
downloadCleanupNotDownloadFilesHours: _(
'general-config-screen.system-preferences.labels.download-cleanup-not-download-files-hours',
),
},
placeholder: {
softDeleteCleanupTime: _('general-config-screen.system-preferences.placeholders.soft-delete-cleanup-time'),
downloadCleanupDownloadFilesHours: _('general-config-screen.system-preferences.placeholders.download-cleanup-download-files-hours'),
downloadCleanupNotDownloadFilesHours: _(
'general-config-screen.system-preferences.placeholders.download-cleanup-not-download-files-hours',
),
},
} as const;

View File

@ -8,6 +8,7 @@ import { LanguageService } from '@i18n/language.service';
import { UserPreferenceService } from '@services/user-preference.service';
import { UserService } from '@services/user.service';
import { FeaturesService } from '@services/features.service';
import { SystemPreferencesService } from '@services/system-preferences.service';
function lastDossierTemplateRedirect(baseHref: string, userPreferenceService: UserPreferenceService) {
const url = window.location.href.split('/').filter(s => s.length > 0);
@ -22,6 +23,7 @@ export function configurationInitializer(
keycloakService: KeycloakService,
title: Title,
configService: ConfigService,
systemPreferencesService: SystemPreferencesService,
featuresService: FeaturesService,
generalSettingsService: GeneralSettingsService,
languageService: LanguageService,
@ -39,7 +41,7 @@ export function configurationInitializer(
switchMap(user => (!user.hasAnyREDRoles ? throwError('Not user has no red roles') : of({}))),
mergeMap(() => generalSettingsService.getGeneralConfigurations()),
tap(configuration => configService.updateDisplayName(configuration.displayName)),
switchMap(() => configService.loadAppConfig()),
switchMap(() => systemPreferencesService.loadPreferences()),
switchMap(() => userPreferenceService.reload()),
catchError(e => {
console.log('[Redaction] Initialization error:', e);

View File

@ -1444,6 +1444,19 @@
"title": "Allgemeine Einstellungen"
},
"subtitle": "SMTP (Simple Mail Transfer Protocol) ermöglicht es Ihnen, Ihre E-Mails über die angegebenen Servereinstellungen zu versenden.",
"system-preferences": {
"labels": {
"download-cleanup-download-files-hours": "",
"download-cleanup-not-download-files-hours": "",
"soft-delete-cleanup-time": ""
},
"placeholders": {
"download-cleanup-download-files-hours": "",
"download-cleanup-not-download-files-hours": "",
"soft-delete-cleanup-time": ""
},
"title": ""
},
"test": {
"error": "Die Test-E-Mail konnte nicht gesendet werden! Bitte überprüfen Sie die E-Mail-Adresse.",
"success": "Die Test-E-Mail wurde erfolgreich versendet!"

View File

@ -1444,6 +1444,19 @@
"title": "General Configurations"
},
"subtitle": "SMTP (Simple Mail Transfer Protocol) enables you to send your emails through the specified server settings.",
"system-preferences": {
"labels": {
"download-cleanup-download-files-hours": "Cleanup time for downloaded files (hours)",
"download-cleanup-not-download-files-hours": "Cleanup time for not downloaded files (hours)",
"soft-delete-cleanup-time": "Soft delete cleanup time (hours)"
},
"placeholders": {
"download-cleanup-download-files-hours": "(hours)",
"download-cleanup-not-download-files-hours": "(hours)",
"soft-delete-cleanup-time": "(hours)"
},
"title": "System Preferences"
},
"test": {
"error": "Test email could not be sent! Please revise the email address.",
"success": "Test email was sent successfully!"

View File

@ -20,7 +20,4 @@ export interface AppConfig {
RECENT_PERIOD_IN_HOURS: number;
SELECTION_MODE: string;
MANUAL_BASE_URL: string;
softDeleteCleanupTime?: number;
downloadCleanupDownloadFilesHours?: number;
downloadCleanupNotDownloadFilesHours?: number;
}

View File

@ -12,3 +12,4 @@ export * from './logger-config';
export * from './admin-side-nav-types';
export * from './charts';
export * from './app-config';
export * from './system-preferences';

View File

@ -0,0 +1,5 @@
export interface SystemPreferences {
softDeleteCleanupTime: number;
downloadCleanupDownloadFilesHours: number;
downloadCleanupNotDownloadFilesHours: number;
}