RED-4085: System preferences
This commit is contained in:
parent
fd10e270b2
commit
301ffb9204
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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>
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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 => {
|
||||
|
||||
@ -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),
|
||||
);
|
||||
|
||||
29
apps/red-ui/src/app/services/system-preferences.service.ts
Normal file
29
apps/red-ui/src/app/services/system-preferences.service.ts
Normal 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()));
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
@ -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);
|
||||
|
||||
@ -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!"
|
||||
|
||||
@ -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!"
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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';
|
||||
|
||||
5
libs/red-domain/src/lib/shared/system-preferences.ts
Normal file
5
libs/red-domain/src/lib/shared/system-preferences.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export interface SystemPreferences {
|
||||
softDeleteCleanupTime: number;
|
||||
downloadCleanupDownloadFilesHours: number;
|
||||
downloadCleanupNotDownloadFilesHours: number;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user