Pull request #314: GettersForObservables

Merge in RED/ui from GettersForObservables to master

* commit '0c3ec3fc0b9d79e2fb4f94c62442ade2be5fae84':
  admin module refactoring finished
  general-config and smtp forms in separate components
  readonly forms in admin module
  renamed forms to "form"
  formBuilder in separate method
  linting
  moved formBuilders in dialog module in separate methods
  _formBuilder in separate _getForm method
This commit is contained in:
Eduard Cziszter 2021-11-22 10:51:21 +01:00 committed by Dan Percic
commit edceeef21c
49 changed files with 714 additions and 609 deletions

View File

@ -40,11 +40,7 @@ export class NotificationsComponent extends AutoUnsubscribe implements OnInit {
super();
this.notifications$ = this._notifications$.asObservable().pipe(shareLast());
this.groupedNotifications$ = this.notifications$.pipe(map(notifications => this._groupNotifications(notifications)));
this.hasUnreadNotifications$ = this.notifications$.pipe(
map(notifications => notifications.filter(n => !n.readDate).length > 0),
distinctUntilChanged(),
shareLast(),
);
this.hasUnreadNotifications$ = this._hasUnreadNotifications$;
}
async ngOnInit(): Promise<void> {
@ -58,6 +54,14 @@ export class NotificationsComponent extends AutoUnsubscribe implements OnInit {
.subscribe();
}
private get _hasUnreadNotifications$(): Observable<boolean> {
return this.notifications$.pipe(
map(notifications => notifications.filter(n => !n.readDate).length > 0),
distinctUntilChanged(),
shareLast(),
);
}
async markRead($event, notifications: List<string> = this._notifications$.getValue().map(n => n.id), isRead = true): Promise<void> {
$event.stopPropagation();
await this._notificationsService.toggleNotificationRead(notifications, isRead).toPromise();

View File

@ -20,15 +20,21 @@ export class NotificationsScreenComponent implements OnInit {
readonly notificationGroupsValues = NotificationGroupsValues;
readonly translations = notificationsTranslations;
formGroup: FormGroup;
formGroup: FormGroup = this._getForm();
constructor(
private readonly _toaster: Toaster,
private readonly _formBuilder: FormBuilder,
private readonly _loadingService: LoadingService,
private readonly _notificationPreferencesService: NotificationPreferencesService,
) {
this.formGroup = this._formBuilder.group({
) {}
async ngOnInit(): Promise<void> {
await this._initializeForm();
}
private _getForm(): FormGroup {
return this._formBuilder.group({
inAppNotificationsEnabled: [undefined],
emailNotificationsEnabled: [undefined],
emailNotificationType: [undefined],
@ -37,10 +43,6 @@ export class NotificationsScreenComponent implements OnInit {
});
}
async ngOnInit(): Promise<void> {
await this._initializeForm();
}
isCategoryActive(category: string) {
return this.formGroup.get(`${category}Enabled`).value;
}

View File

@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { DomSanitizer } from '@angular/platform-browser';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
import { LoadingService } from '@iqser/common-ui';
import { IProfile } from '@red/domain';
@ -17,8 +17,8 @@ import { LanguageService } from '../../../../../i18n/language.service';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class UserProfileScreenComponent implements OnInit {
formGroup: FormGroup;
changePasswordUrl: any;
formGroup: FormGroup = this._getForm();
changePasswordUrl: SafeResourceUrl;
translations = languagesTranslations;
private _profileModel: IProfile;
@ -34,16 +34,19 @@ export class UserProfileScreenComponent implements OnInit {
private readonly _loadingService: LoadingService,
) {
this._loadingService.start();
this.formGroup = this._formBuilder.group({
this.changePasswordUrl = this._domSanitizer.bypassSecurityTrustResourceUrl(
`${this._configService.values.OAUTH_URL}/account/password`,
);
}
private _getForm(): FormGroup {
return this._formBuilder.group({
email: [undefined, [Validators.required, Validators.email]],
firstName: [undefined],
lastName: [undefined],
language: [undefined],
});
this.changePasswordUrl = this._domSanitizer.bypassSecurityTrustResourceUrl(
`${this._configService.values.OAUTH_URL}/account/password`,
);
}
get languageChanged(): boolean {

View File

@ -46,6 +46,8 @@ import { LicenseReportService } from './services/licence-report.service';
import { RulesService } from './services/rules.service';
import { SmtpConfigService } from './services/smtp-config.service';
import { UploadDictionaryDialogComponent } from './dialogs/upload-dictionary-dialog/upload-dictionary-dialog.component';
import { GeneralConfigFormComponent } from './screens/general-config/general-config-form/general-config-form.component';
import { SmtpFormComponent } from './screens/general-config/smtp-form/smtp-form.component';
const dialogs = [
AddEditDossierTemplateDialogComponent,
@ -96,7 +98,7 @@ const components = [
];
@NgModule({
declarations: [...components],
declarations: [...components, GeneralConfigFormComponent, SmtpFormComponent],
providers: [AdminDialogService, AuditService, DigitalSignatureService, LicenseReportService, RulesService, SmtpConfigService],
imports: [CommonModule, SharedModule, AdminRoutingModule, NgxChartsModule, ColorPickerModule, MonacoEditorModule],
})

View File

@ -20,8 +20,8 @@ import { HttpStatusCode } from '@angular/common/http';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AddEditDictionaryDialogComponent extends BaseDialogComponent {
readonly form: FormGroup;
readonly dictionary = this._data.dictionary;
readonly form: FormGroup = this._getForm(this.dictionary);
readonly canEditLabel$ = this._canEditLabel$;
readonly technicalName$: Observable<string>;
readonly dialogHeader = this._translateService.instant('add-edit-dictionary.title', {
@ -43,21 +43,24 @@ export class AddEditDictionaryDialogComponent extends BaseDialogComponent {
private readonly _data: { readonly dictionary: Dictionary; readonly dossierTemplateId: string },
) {
super();
this.form = _formBuilder.group({
label: [this.dictionary?.label, [Validators.required, Validators.minLength(3)]],
description: [this.dictionary?.description],
rank: [this.dictionary?.rank, Validators.required],
hexColor: [this.dictionary?.hexColor, [Validators.required, Validators.minLength(7)]],
hint: [!!this.dictionary?.hint],
addToDictionaryAction: [!!this.dictionary?.addToDictionaryAction],
caseSensitive: [this.dictCaseSensitive],
});
this.hasColor$ = this._colorEmpty$;
this.technicalName$ = this.form.get('label').valueChanges.pipe(map(value => this._toTechnicalName(value)));
}
get dictCaseSensitive(): boolean {
return this.dictionary ? !this.dictionary.caseInsensitive : false;
private _getForm(dictionary: Dictionary): FormGroup {
return this._formBuilder.group({
label: [dictionary?.label, [Validators.required, Validators.minLength(3)]],
description: [dictionary?.description],
rank: [dictionary?.rank, Validators.required],
hexColor: [dictionary?.hexColor, [Validators.required, Validators.minLength(7)]],
hint: [!!dictionary?.hint],
addToDictionaryAction: [!!dictionary?.addToDictionaryAction],
caseSensitive: [this.getDictCaseSensitive(dictionary)],
});
}
getDictCaseSensitive(dictionary: Dictionary): boolean {
return dictionary ? !dictionary.caseInsensitive : false;
}
get changed(): boolean {
@ -67,7 +70,7 @@ export class AddEditDictionaryDialogComponent extends BaseDialogComponent {
for (const key of Object.keys(this.form.getRawValue())) {
if (key === 'caseSensitive') {
if (this.dictCaseSensitive !== this.form.get(key).value) {
if (this.getDictCaseSensitive(this.dictionary) !== this.form.get(key).value) {
return true;
}
} else if (this.dictionary[key] !== this.form.get(key).value) {

View File

@ -8,7 +8,7 @@
class="dialog-header heading-l"
></div>
<form (submit)="saveDossierAttribute()" [formGroup]="dossierAttributeForm">
<form (submit)="saveDossierAttribute()" [formGroup]="form">
<div class="dialog-content">
<div class="iqser-input-group required w-300">
<label translate="add-edit-dossier-attribute.form.label"></label>
@ -35,7 +35,7 @@
</div>
</div>
<div class="dialog-actions">
<button [disabled]="dossierAttributeForm.invalid || !changed" color="primary" mat-flat-button type="submit">
<button [disabled]="form.invalid || !changed" color="primary" mat-flat-button type="submit">
{{ 'add-edit-dossier-attribute.save' | translate }}
</button>
</div>

View File

@ -13,8 +13,8 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
styleUrls: ['./add-edit-dossier-attribute-dialog.component.scss'],
})
export class AddEditDossierAttributeDialogComponent extends AutoUnsubscribe implements OnDestroy {
dossierAttributeForm: FormGroup;
dossierAttribute: IDossierAttributeConfig;
dossierAttribute: IDossierAttributeConfig = this.data.dossierAttribute;
readonly form: FormGroup = this._getForm(this.dossierAttribute);
readonly translations = dossierAttributeTypesTranslations;
readonly typeOptions = Object.keys(DossierAttributeConfigTypes);
@ -28,17 +28,18 @@ export class AddEditDossierAttributeDialogComponent extends AutoUnsubscribe impl
readonly data: { readonly dossierAttribute: IDossierAttributeConfig },
) {
super();
this.dossierAttribute = data.dossierAttribute;
}
this.dossierAttributeForm = this._formBuilder.group({
label: [this.dossierAttribute?.label, Validators.required],
...(!!this.dossierAttribute && {
private _getForm(dossierAttribute: IDossierAttributeConfig): FormGroup {
return this._formBuilder.group({
label: [dossierAttribute?.label, Validators.required],
...(!!dossierAttribute && {
placeholder: {
value: this.dossierAttribute.placeholder,
value: dossierAttribute.placeholder,
disabled: true,
},
}),
type: [this.dossierAttribute?.type || FileAttributeConfigTypes.TEXT, Validators.required],
type: [dossierAttribute?.type || FileAttributeConfigTypes.TEXT, Validators.required],
});
}
@ -47,8 +48,8 @@ export class AddEditDossierAttributeDialogComponent extends AutoUnsubscribe impl
return true;
}
for (const key of Object.keys(this.dossierAttributeForm.getRawValue())) {
if (this.dossierAttribute[key] !== this.dossierAttributeForm.get(key).value) {
for (const key of Object.keys(this.form.getRawValue())) {
if (this.dossierAttribute[key] !== this.form.get(key).value) {
return true;
}
}
@ -62,7 +63,7 @@ export class AddEditDossierAttributeDialogComponent extends AutoUnsubscribe impl
const attribute: IDossierAttributeConfig = {
id: this.dossierAttribute?.id,
editable: true,
...this.dossierAttributeForm.getRawValue(),
...this.form.getRawValue(),
};
this._dossierAttributesService.createOrUpdate(attribute).subscribe(
@ -79,7 +80,7 @@ export class AddEditDossierAttributeDialogComponent extends AutoUnsubscribe impl
@HostListener('window:keydown.Enter', ['$event'])
onEnter(event: KeyboardEvent): void {
const node = (event.target as IqserEventTarget).localName;
if (this.dossierAttributeForm.valid && this.changed && node !== 'textarea') {
if (this.form.valid && this.changed && node !== 'textarea') {
this.saveDossierAttribute();
}
}

View File

@ -17,7 +17,7 @@ import { HttpStatusCode } from '@angular/common/http';
styleUrls: ['./add-edit-dossier-template-dialog.component.scss'],
})
export class AddEditDossierTemplateDialogComponent extends BaseDialogComponent {
form: FormGroup;
readonly form: FormGroup = this._getForm();
hasValidFrom: boolean;
hasValidTo: boolean;
downloadTypesEnum: DownloadFileType[] = ['ORIGINAL', 'PREVIEW', 'REDACTED'];
@ -38,7 +38,19 @@ export class AddEditDossierTemplateDialogComponent extends BaseDialogComponent {
@Inject(MAT_DIALOG_DATA) readonly dossierTemplate: IDossierTemplate,
) {
super();
this.form = this._formBuilder.group({
this.hasValidFrom = !!this.dossierTemplate?.validFrom;
this.hasValidTo = !!this.dossierTemplate?.validTo;
this._previousValidFrom = this.form.get('validFrom').value;
this._previousValidTo = this.form.get('validTo').value;
this.form.valueChanges.subscribe(value => {
this._applyValidityIntervalConstraints(value);
});
}
private _getForm(): FormGroup {
return this._formBuilder.group({
name: [this.dossierTemplate?.name, Validators.required],
description: [this.dossierTemplate?.description],
validFrom: [
@ -51,15 +63,6 @@ export class AddEditDossierTemplateDialogComponent extends BaseDialogComponent {
],
downloadFileTypes: [this.dossierTemplate?.downloadFileTypes || ['PREVIEW', 'REDACTED']],
});
this.hasValidFrom = !!this.dossierTemplate?.validFrom;
this.hasValidTo = !!this.dossierTemplate?.validTo;
this._previousValidFrom = this.form.get('validFrom').value;
this._previousValidTo = this.form.get('validTo').value;
this.form.valueChanges.subscribe(value => {
this._applyValidityIntervalConstraints(value);
});
}
get changed(): boolean {

View File

@ -12,9 +12,9 @@ import { BaseDialogComponent } from '@iqser/common-ui';
styleUrls: ['./add-edit-file-attribute-dialog.component.scss'],
})
export class AddEditFileAttributeDialogComponent extends BaseDialogComponent {
form: FormGroup;
fileAttribute: IFileAttributeConfig;
dossierTemplateId: string;
fileAttribute: IFileAttributeConfig = this.data.fileAttribute;
dossierTemplateId: string = this.data.dossierTemplateId;
readonly form: FormGroup = this._getForm(this.fileAttribute);
readonly typeOptions = Object.keys(FileAttributeConfigTypes);
translations = fileAttributeTypesTranslations;
@ -26,17 +26,17 @@ export class AddEditFileAttributeDialogComponent extends BaseDialogComponent {
public data: { fileAttribute: IFileAttributeConfig; dossierTemplateId: string },
) {
super();
this.fileAttribute = data.fileAttribute;
this.dossierTemplateId = data.dossierTemplateId;
}
this.form = this._formBuilder.group({
label: [this.fileAttribute?.label, Validators.required],
csvColumnHeader: [this.fileAttribute?.csvColumnHeader],
type: [this.fileAttribute?.type || FileAttributeConfigTypes.TEXT, Validators.required],
readonly: [this.fileAttribute ? !this.fileAttribute.editable : false],
primaryAttribute: [this.fileAttribute?.primaryAttribute],
filterable: [this.fileAttribute?.filterable],
displayedInFileList: [this.fileAttribute?.displayedInFileList],
private _getForm(fileAttribute: IFileAttributeConfig): FormGroup {
return this._formBuilder.group({
label: [fileAttribute?.label, Validators.required],
csvColumnHeader: [fileAttribute?.csvColumnHeader],
type: [fileAttribute?.type || FileAttributeConfigTypes.TEXT, Validators.required],
readonly: [fileAttribute ? !fileAttribute.editable : false],
primaryAttribute: [fileAttribute?.primaryAttribute],
filterable: [fileAttribute?.filterable],
displayedInFileList: [fileAttribute?.displayedInFileList],
});
}

View File

@ -1,6 +1,6 @@
<div [translateParams]="{ userName: user | name }" [translate]="'reset-password-dialog.header'" class="dialog-header heading-l"></div>
<form (submit)="save()" [formGroup]="passwordForm">
<form (submit)="save()" [formGroup]="form">
<div class="dialog-content">
<div class="iqser-input-group required w-300">
<label translate="reset-password-dialog.form.password"></label>
@ -9,7 +9,7 @@
</div>
<div class="dialog-actions">
<button [disabled]="passwordForm.invalid" color="primary" mat-flat-button type="submit">
<button [disabled]="form.invalid" color="primary" mat-flat-button type="submit">
{{ 'reset-password-dialog.actions.save' | translate }}
</button>

View File

@ -1,5 +1,5 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { UserService } from '@services/user.service';
import { LoadingService } from '@iqser/common-ui';
import { User } from '@red/domain';
@ -10,9 +10,7 @@ import { User } from '@red/domain';
styleUrls: ['./reset-password.component.scss'],
})
export class ResetPasswordComponent {
readonly passwordForm = this._formBuilder.group({
temporaryPassword: [null, Validators.required],
});
readonly form = this._getForm();
@Input() user: User;
@Output() readonly toggleResetPassword = new EventEmitter();
@ -27,7 +25,7 @@ export class ResetPasswordComponent {
await this._userService
.resetPassword(
{
password: this.passwordForm.get('temporaryPassword').value,
password: this.form.get('temporaryPassword').value,
temporary: true,
},
this.user.id,
@ -36,4 +34,10 @@ export class ResetPasswordComponent {
this._loadingService.stop();
this.toggleResetPassword.emit();
}
private _getForm(): FormGroup {
return this._formBuilder.group({
temporaryPassword: [null, Validators.required],
});
}
}

View File

@ -6,7 +6,7 @@
class="dialog-header heading-l"
></div>
<form (submit)="save()" [formGroup]="userForm">
<form (submit)="save()" [formGroup]="form">
<div class="dialog-content">
<div class="iqser-input-group required w-300">
<label translate="add-edit-user.form.first-name"></label>
@ -41,7 +41,7 @@
</div>
<div class="dialog-actions">
<button [disabled]="userForm.invalid || !changed" color="primary" mat-flat-button type="submit">
<button [disabled]="form.invalid || !changed" color="primary" mat-flat-button type="submit">
{{ (user ? 'add-edit-user.actions.save-changes' : 'add-edit-user.actions.save') | translate }}
</button>

View File

@ -1,4 +1,4 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { AdminDialogService } from '../../../services/admin-dialog.service';
import { IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
@ -13,17 +13,17 @@ import { HttpStatusCode } from '@angular/common/http';
templateUrl: './user-details.component.html',
styleUrls: ['./user-details.component.scss'],
})
export class UserDetailsComponent implements OnInit {
export class UserDetailsComponent {
readonly iconButtonTypes = IconButtonTypes;
@Input() user: User;
@Output() readonly toggleResetPassword = new EventEmitter();
@Output() readonly closeDialog = new EventEmitter();
userForm: FormGroup;
readonly ROLES = ['RED_USER', 'RED_MANAGER', 'RED_USER_ADMIN', 'RED_ADMIN'];
readonly translations = rolesTranslations;
private readonly _ROLE_REQUIREMENTS = { RED_MANAGER: 'RED_USER', RED_ADMIN: 'RED_USER_ADMIN' };
readonly form: FormGroup = this._getForm();
constructor(
private readonly _formBuilder: FormBuilder,
@ -31,7 +31,9 @@ export class UserDetailsComponent implements OnInit {
private readonly _dialogService: AdminDialogService,
private readonly _loadingService: LoadingService,
readonly userService: UserService,
) {}
) {
this._setRolesRequirements();
}
get changed(): boolean {
if (!this.user) {
@ -42,8 +44,8 @@ export class UserDetailsComponent implements OnInit {
return true;
}
for (const key of Object.keys(this.userForm.getRawValue())) {
const keyValue = this.userForm.get(key).value;
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;
@ -58,7 +60,7 @@ export class UserDetailsComponent implements OnInit {
get activeRoles(): string[] {
return this.ROLES.reduce((acc, role) => {
if (this.userForm.get(role).value) {
if (this.form.get(role).value) {
acc.push(role);
}
return acc;
@ -80,8 +82,23 @@ export class UserDetailsComponent implements OnInit {
);
}
ngOnInit() {
const rolesControls = this.ROLES.reduce(
private _getForm(): FormGroup {
return this._formBuilder.group({
firstName: [this.user?.firstName, Validators.required],
lastName: [this.user?.lastName, Validators.required],
email: [
{
value: this.user?.email,
disabled: !!this.user,
},
[Validators.required, Validators.email],
],
...this._rolesControls,
});
}
private get _rolesControls(): any {
return this.ROLES.reduce(
(prev, role) => ({
...prev,
[role]: [
@ -93,24 +110,11 @@ export class UserDetailsComponent implements OnInit {
}),
{},
);
this.userForm = this._formBuilder.group({
firstName: [this.user?.firstName, Validators.required],
lastName: [this.user?.lastName, Validators.required],
email: [
{
value: this.user?.email,
disabled: !!this.user,
},
[Validators.required, Validators.email],
],
...rolesControls,
});
this._setRolesRequirements();
}
async save() {
this._loadingService.start();
const userData = { ...this.userForm.getRawValue(), roles: this.activeRoles };
const userData = { ...this.form.getRawValue(), roles: this.activeRoles };
if (!this.user) {
await this.userService
@ -141,12 +145,12 @@ export class UserDetailsComponent implements OnInit {
private _setRolesRequirements() {
for (const key of Object.keys(this._ROLE_REQUIREMENTS)) {
this.userForm.controls[key].valueChanges.subscribe(checked => {
this.form.controls[key].valueChanges.subscribe(checked => {
if (checked) {
this.userForm.patchValue({ [this._ROLE_REQUIREMENTS[key]]: true });
this.userForm.controls[this._ROLE_REQUIREMENTS[key]].disable();
this.form.patchValue({ [this._ROLE_REQUIREMENTS[key]]: true });
this.form.controls[this._ROLE_REQUIREMENTS[key]].disable();
} else {
this.userForm.controls[this._ROLE_REQUIREMENTS[key]].enable();
this.form.controls[this._ROLE_REQUIREMENTS[key]].enable();
}
});
}

View File

@ -25,15 +25,18 @@ export class ConfirmDeleteUsersDialogComponent {
@Inject(MAT_DIALOG_DATA) readonly userIds: List<string>,
readonly dialogRef: MatDialogRef<ConfirmDeleteUsersDialogComponent>,
) {
this.dossiersCount = this._dossiersService.all.filter(dw =>
this.userIds.reduce((prev, userId) => prev || dw.memberIds.includes(userId), false),
).length;
this.dossiersCount = this._dossierCount;
}
get valid() {
return this.checkboxes[0].value && this.checkboxes[1].value;
}
private get _dossierCount(): number {
return this._dossiersService.all.filter(dw => this.userIds.reduce((prev, userId) => prev || dw.memberIds.includes(userId), false))
.length;
}
async deleteUser() {
if (this.valid) {
this._loadingService.start();

View File

@ -1,5 +1,5 @@
<section class="dialog">
<div [translate]="translations[colorKey]" class="dialog-header heading-l"></div>
<div [translate]="translations[data.colorKey]" class="dialog-header heading-l"></div>
<form (submit)="save()" [formGroup]="form">
<div class="dialog-content">

View File

@ -8,15 +8,18 @@ import { defaultColorsTranslations } from '../../translations/default-colors-tra
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { DictionaryService } from '@shared/services/dictionary.service';
interface IEditColorData {
colors: IColors;
colorKey: DefaultColorType;
dossierTemplateId: string;
}
@Component({
selector: 'redaction-edit-color-dialog',
templateUrl: './edit-color-dialog.component.html',
styleUrls: ['./edit-color-dialog.component.scss'],
})
export class EditColorDialogComponent extends BaseDialogComponent {
readonly colors: IColors;
readonly colorKey: DefaultColorType;
form: FormGroup;
readonly form: FormGroup;
translations = defaultColorsTranslations;
private readonly _initialColor: string;
private readonly _dossierTemplateId: string;
@ -28,33 +31,35 @@ export class EditColorDialogComponent extends BaseDialogComponent {
private readonly _translateService: TranslateService,
private readonly _dialogRef: MatDialogRef<EditColorDialogComponent>,
@Inject(MAT_DIALOG_DATA)
private readonly _data: { colors: IColors; colorKey: DefaultColorType; dossierTemplateId: string },
readonly data: IEditColorData,
) {
super();
this.colors = _data.colors;
this.colorKey = _data.colorKey;
this._dossierTemplateId = _data.dossierTemplateId;
this._initialColor = _data.colors[this.colorKey];
this._dossierTemplateId = data.dossierTemplateId;
this._initialColor = data.colors[data.colorKey];
this.form = this._formBuilder.group({
color: [this.colors[this.colorKey], [Validators.required, Validators.minLength(7)]],
});
this.form = this._getForm();
}
get changed(): boolean {
return this.form.get('color').value !== this._initialColor;
}
private _getForm(): FormGroup {
return this._formBuilder.group({
color: [this.data.colors[this.data.colorKey], [Validators.required, Validators.minLength(7)]],
});
}
async save() {
const colors = {
...this.colors,
[this.colorKey]: this.form.get('color').value,
...this.data.colors,
[this.data.colorKey]: this.form.get('color').value,
};
try {
await this._dictionaryService.setColors(colors, this._dossierTemplateId).toPromise();
this._dialogRef.close(true);
const color = this._translateService.instant(defaultColorsTranslations[this.colorKey]);
const color = this._translateService.instant(defaultColorsTranslations[this.data.colorKey]);
this._toaster.info(_('edit-color-dialog.success'), { params: { color: color } });
} catch (e) {
this._toaster.error(_('edit-color-dialog.error'));

View File

@ -10,7 +10,7 @@
</div>
</div>
<div class="right">
<form (submit)="changedParseConfig && readFile()" [formGroup]="baseConfigForm">
<form (submit)="changedParseConfig && readFile()" [formGroup]="form">
<div class="iqser-input-group required w-250">
<mat-form-field floatLabel="always">
<mat-label>{{ 'file-attributes-csv-import.key-column' | translate }}</mat-label>
@ -154,7 +154,7 @@
</div>
<div class="dialog-actions">
<button (click)="save()" [disabled]="changedParseConfig || baseConfigForm.invalid" color="primary" mat-flat-button>
<button (click)="save()" [disabled]="changedParseConfig || form.invalid" color="primary" mat-flat-button>
{{ 'file-attributes-csv-import.save.label' | translate }}
</button>

View File

@ -9,6 +9,12 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { FileAttributeConfig, FileAttributeConfigTypes, IField, IFileAttributesConfig } from '@red/domain';
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
interface IFileAttributesCSVImportData {
readonly csv: File;
readonly dossierTemplateId: string;
readonly existingConfiguration: IFileAttributesConfig;
}
@Component({
templateUrl: './file-attributes-csv-import-dialog.component.html',
styleUrls: ['./file-attributes-csv-import-dialog.component.scss'],
@ -20,7 +26,7 @@ export class FileAttributesCsvImportDialogComponent extends ListingComponent<IFi
parseResult: { data: any[]; errors: any[]; meta: any; fields: IField[] };
hoveredColumn: string;
activeFields: IField[] = [];
readonly baseConfigForm: FormGroup;
readonly form: FormGroup;
isSearchOpen = false;
previewExpanded = true;
filteredKeyOptions: Observable<string[]>;
@ -36,40 +42,37 @@ export class FileAttributesCsvImportDialogComponent extends ListingComponent<IFi
private readonly _formBuilder: FormBuilder,
readonly dialogRef: MatDialogRef<FileAttributesCsvImportDialogComponent>,
private readonly _fileAttributesService: FileAttributesService,
@Inject(MAT_DIALOG_DATA)
readonly data: {
readonly csv: File;
readonly dossierTemplateId: string;
readonly existingConfiguration: IFileAttributesConfig;
},
@Inject(MAT_DIALOG_DATA) readonly data: IFileAttributesCSVImportData,
) {
super(_injector);
this.baseConfigForm = this._formBuilder.group({
filenameMappingColumnHeaderName: ['', [Validators.required, this._autocompleteStringValidator()]],
delimiter: [undefined, Validators.required],
encoding: ['UTF-8', Validators.required],
});
this.form = this._getForm();
this.readFile();
}
get changedParseConfig(): boolean {
return (
this.initialParseConfig.delimiter !== this.baseConfigForm.get('delimiter').value ||
this.initialParseConfig.encoding !== this.baseConfigForm.get('encoding').value
this.initialParseConfig.delimiter !== this.form.get('delimiter').value ||
this.initialParseConfig.encoding !== this.form.get('encoding').value
);
}
private _getForm(): FormGroup {
return this._formBuilder.group({
filenameMappingColumnHeaderName: ['', [Validators.required, this._autocompleteStringValidator()]],
delimiter: [undefined, Validators.required],
encoding: ['UTF-8', Validators.required],
});
}
readFile() {
const reader = new FileReader();
reader.addEventListener('load', event => {
const parsedCsv = <any>event.target.result;
this.parseResult = Papa.parse(parsedCsv, {
header: true,
delimiter: this.baseConfigForm.get('delimiter').value,
delimiter: this.form.get('delimiter').value,
});
this.baseConfigForm.patchValue({ delimiter: this.parseResult.meta.delimiter });
this.form.patchValue({ delimiter: this.parseResult.meta.delimiter });
// Filter duplicate columns
if (this.parseResult?.data?.length) {
@ -91,8 +94,8 @@ export class FileAttributesCsvImportDialogComponent extends ListingComponent<IFi
}
}
this.filteredKeyOptions = this.baseConfigForm.get('filenameMappingColumnHeaderName').valueChanges.pipe(
startWith(this.baseConfigForm.get('filenameMappingColumnHeaderName').value as string),
this.filteredKeyOptions = this.form.get('filenameMappingColumnHeaderName').valueChanges.pipe(
startWith(this.form.get('filenameMappingColumnHeaderName').value as string),
map((value: string) =>
this.allEntities
.filter(field => field.csvColumn.toLowerCase().indexOf(value.toLowerCase()) !== -1)
@ -104,17 +107,17 @@ export class FileAttributesCsvImportDialogComponent extends ListingComponent<IFi
this.data.existingConfiguration &&
this.allEntities.find(entity => entity.csvColumn === this.data.existingConfiguration.filenameMappingColumnHeaderName)
) {
this.baseConfigForm.patchValue({
this.form.patchValue({
filenameMappingColumnHeaderName: this.data.existingConfiguration.filenameMappingColumnHeaderName,
});
}
this.initialParseConfig = {
delimiter: this.baseConfigForm.get('delimiter').value,
encoding: this.baseConfigForm.get('encoding').value,
delimiter: this.form.get('delimiter').value,
encoding: this.form.get('encoding').value,
};
});
reader.readAsText(this.data.csv, this.baseConfigForm.get('encoding').value);
reader.readAsText(this.data.csv, this.form.get('encoding').value);
}
getSample(csvColumn: string) {
@ -163,7 +166,7 @@ export class FileAttributesCsvImportDialogComponent extends ListingComponent<IFi
}
const fileAttributes: IFileAttributesConfig = {
...this.baseConfigForm.getRawValue(),
...this.form.getRawValue(),
fileAttributeConfigs: [
...fileAttributeConfigs.filter(a => !this.allEntities.find(entity => entity.csvColumn === a.csvColumnHeader)),
...this.activeFields.map(field => ({

View File

@ -1,7 +1,7 @@
<section class="dialog">
<div class="dialog-header heading-l" translate="smtp-auth-config.title"></div>
<form (submit)="save()" [formGroup]="authForm">
<form (submit)="save()" [formGroup]="form">
<div class="dialog-content">
<div class="iqser-input-group required w-300">
<label translate="smtp-auth-config.form.username"></label>
@ -20,7 +20,7 @@
</div>
<div class="dialog-actions">
<button [disabled]="authForm.invalid" color="primary" mat-flat-button type="submit">
<button [disabled]="form.invalid" color="primary" mat-flat-button type="submit">
{{ 'smtp-auth-config.actions.save' | translate }}
</button>
<div class="all-caps-label cancel" mat-dialog-close translate="smtp-auth-config.actions.cancel"></div>

View File

@ -10,21 +10,23 @@ import { ISmtpConfiguration } from '@red/domain';
styleUrls: ['./smtp-auth-dialog.component.scss'],
})
export class SmtpAuthDialogComponent {
authForm: FormGroup;
readonly form: FormGroup = this._getForm();
constructor(
private readonly _formBuilder: FormBuilder,
private readonly _userService: UserService,
public dialogRef: MatDialogRef<SmtpAuthDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: ISmtpConfiguration,
) {
this.authForm = this._formBuilder.group({
user: [data?.user || this._userService.currentUser.email, [Validators.required]],
password: [data?.password, Validators.required],
) {}
private _getForm(): FormGroup {
return this._formBuilder.group({
user: [this.data?.user || this._userService.currentUser.email, [Validators.required]],
password: [this.data?.password, Validators.required],
});
}
save() {
this.dialogRef.close(this.authForm.getRawValue());
this.dialogRef.close(this.form.getRawValue());
}
}

View File

@ -41,7 +41,7 @@
class="mr-0"
></redaction-pagination>
<div class="separator">·</div>
<form [formGroup]="filterForm">
<form [formGroup]="form">
<div class="iqser-input-group w-150 mr-20">
<mat-form-field class="no-label">
<mat-select formControlName="category">
@ -56,12 +56,12 @@
<mat-select formControlName="userId">
<mat-select-trigger>
<redaction-initials-avatar
*ngIf="filterForm.get('userId').value !== ALL_USERS"
[user]="filterForm.get('userId').value"
*ngIf="form.get('userId').value !== ALL_USERS"
[user]="form.get('userId').value"
[withName]="true"
size="small"
></redaction-initials-avatar>
<div *ngIf="filterForm.get('userId').value === ALL_USERS" [translate]="ALL_USERS"></div>
<div *ngIf="form.get('userId').value === ALL_USERS" [translate]="ALL_USERS"></div>
</mat-select-trigger>
<mat-option *ngFor="let userId of userIds" [value]="userId">
<redaction-initials-avatar

View File

@ -22,7 +22,7 @@ export class AuditScreenComponent extends ListingComponent<Audit> implements OnD
readonly ALL_USERS = _('audit-screen.all-users');
readonly translations = auditCategoriesTranslations;
readonly currentUser = this._userService.currentUser;
filterForm: FormGroup;
readonly form: FormGroup = this._getForm();
categories: string[] = [];
userIds: Set<string>;
logs: IAuditResponse;
@ -44,18 +44,21 @@ export class AuditScreenComponent extends ListingComponent<Audit> implements OnD
private readonly _auditService: AuditService,
) {
super(_injector);
this.filterForm = this._formBuilder.group({
this.addSubscription = this.form.valueChanges.subscribe(async value => {
if (!this._updateDateFilters(value)) {
await this._fetchData();
}
});
}
private _getForm(): FormGroup {
return this._formBuilder.group({
category: [this.ALL_CATEGORIES],
userId: [this.ALL_USERS],
from: [],
to: [],
});
this.addSubscription = this.filterForm.valueChanges.subscribe(async value => {
if (!this._updateDateFilters(value)) {
await this._fetchData();
}
});
}
get totalPages(): number {
@ -74,22 +77,22 @@ export class AuditScreenComponent extends ListingComponent<Audit> implements OnD
}
private _updateDateFilters(value): boolean {
if (applyIntervalConstraints(value, this._previousFrom, this._previousTo, this.filterForm, 'from', 'to')) {
if (applyIntervalConstraints(value, this._previousFrom, this._previousTo, this.form, 'from', 'to')) {
return true;
}
this._previousFrom = this.filterForm.get('from').value;
this._previousTo = this.filterForm.get('to').value;
this._previousFrom = this.form.get('from').value;
this._previousTo = this.form.get('to').value;
return false;
}
private async _fetchData(page?: number) {
this._loadingService.start();
const promises = [];
const category = this.filterForm.get('category').value;
const userId = this.filterForm.get('userId').value;
const from = this.filterForm.get('from').value;
let to = this.filterForm.get('to').value;
const category = this.form.get('category').value;
const userId = this.form.get('userId').value;
const from = this.form.get('from').value;
let to = this.form.get('to').value;
if (to) {
to = to.clone().add(1, 'd');
}

View File

@ -13,7 +13,7 @@
<div class="content-inner">
<div class="content-container">
<div class="content-container-content">
<form *ngIf="digitalSignatureForm" [formGroup]="digitalSignatureForm" autocomplete="off">
<form *ngIf="form" [formGroup]="form" autocomplete="off">
<input #fileInput (change)="fileChanged($event, fileInput)" class="file-upload-input" hidden type="file" />
<iqser-empty-state
@ -76,7 +76,7 @@
<div [class.hidden]="!hasDigitalSignatureSet" class="changes-box">
<iqser-icon-button
(action)="saveDigitalSignature()"
[disabled]="digitalSignatureForm.invalid"
[disabled]="form.invalid"
[label]="'digital-signature-screen.action.save' | translate"
[type]="iconButtonTypes.primary"
icon="iqser:check"

View File

@ -19,7 +19,7 @@ export class DigitalSignatureScreenComponent extends AutoUnsubscribe implements
readonly currentUser = this._userService.currentUser;
digitalSignature: IDigitalSignature;
digitalSignatureForm: FormGroup;
form: FormGroup;
digitalSignatureExists = false;
@ -36,12 +36,12 @@ export class DigitalSignatureScreenComponent extends AutoUnsubscribe implements
}
get hasDigitalSignatureSet() {
return this.digitalSignatureExists || !!this.digitalSignatureForm.get('base64EncodedPrivateKey').value;
return this.digitalSignatureExists || !!this.form.get('base64EncodedPrivateKey').value;
}
saveDigitalSignature() {
const digitalSignature = {
...this.digitalSignatureForm.getRawValue(),
...this.form.getRawValue(),
};
//adjusted for chrome auto-complete / password manager
digitalSignature.password = digitalSignature.keySecret;
@ -81,8 +81,8 @@ export class DigitalSignatureScreenComponent extends AutoUnsubscribe implements
fileReader.onload = () => {
const dataUrl = <string>fileReader.result;
const actualBase64Value = dataUrl.substring(lastIndexOfEnd(dataUrl, ';base64,'));
this.digitalSignatureForm.get('base64EncodedPrivateKey').setValue(actualBase64Value);
this.digitalSignatureForm.get('certificateName').setValue(file.name);
this.form.get('base64EncodedPrivateKey').setValue(actualBase64Value);
this.form.get('certificateName').setValue(file.name);
input.value = null;
};
fileReader.readAsDataURL(file);
@ -103,13 +103,13 @@ export class DigitalSignatureScreenComponent extends AutoUnsubscribe implements
},
)
.add(() => {
this._initForm();
this.form = this._getForm();
this._loadingService.stop();
});
}
private _initForm() {
this.digitalSignatureForm = this._formBuilder.group({
private _getForm(): FormGroup {
return this._formBuilder.group({
certificateName: [this.digitalSignature.certificateName, Validators.required],
contactInfo: this.digitalSignature.contactInfo,
location: this.digitalSignature.location,

View File

@ -0,0 +1,29 @@
<div class="dialog-header">
<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">
<div class="dialog-content">
<div class="dialog-content-left">
<div class="iqser-input-group">
<label translate="general-config-screen.app-name.label"></label>
<input
formControlName="auxiliaryName"
name="auxiliaryName"
placeholder="{{ 'general-config-screen.app-name.placeholder' | translate }}"
type="text"
/>
</div>
<div class="inline-input-group flex-align-items-center">
<mat-slide-toggle color="primary" formControlName="forgotPasswordFunctionEnabled"></mat-slide-toggle>
<span class="ml-8" translate="general-config-screen.general.form.forgot-password"></span>
</div>
</div>
</div>
<div class="dialog-actions">
<button [disabled]="form.invalid || !generalConfigurationChanged" color="primary" mat-flat-button type="submit">
{{ 'general-config-screen.actions.save' | translate }}
</button>
</div>
</form>

View File

@ -0,0 +1,71 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { AutoUnsubscribe, LoadingService } from '@iqser/common-ui';
import { GeneralSettingsService } from '@services/general-settings.service';
import { IGeneralConfiguration } from '@red/domain';
import { ConfigService } from '@services/config.service';
import { FormBuilder, FormGroup } from '@angular/forms';
@Component({
selector: 'redaction-general-config-form',
templateUrl: './general-config-form.component.html',
styleUrls: ['./general-config-form.component.scss'],
})
export class GeneralConfigFormComponent extends AutoUnsubscribe implements OnInit, OnDestroy {
private _initialConfiguration: IGeneralConfiguration;
readonly form: FormGroup = this._getForm();
constructor(
private readonly _loadingService: LoadingService,
private readonly _generalSettingsService: GeneralSettingsService,
private readonly _configService: ConfigService,
private readonly _formBuilder: FormBuilder,
) {
super();
}
private _getForm(): FormGroup {
return this._formBuilder.group({
forgotPasswordFunctionEnabled: [false],
auxiliaryName: [undefined],
});
}
async ngOnInit(): Promise<void> {
await this._loadData();
}
async saveGeneralConfig() {
this._loadingService.start();
const configFormValues = this.form.getRawValue();
await this._generalSettingsService.updateGeneralConfigurations(configFormValues).toPromise();
this._initialConfiguration = await this._generalSettingsService.getGeneralConfigurations().toPromise();
this._configService.updateDisplayName(this._initialConfiguration.displayName);
this._loadingService.stop();
}
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;
}
private async _loadData() {
this._loadingService.start();
try {
this._initialConfiguration = await this._generalSettingsService.getGeneralConfigurations().toPromise();
this.form.patchValue(this._initialConfiguration, { emitEvent: false });
} catch (e) {}
this._loadingService.stop();
}
}

View File

@ -22,156 +22,10 @@
<div class="content-inner">
<div class="content-container">
<div class="dialog mb-8">
<div class="dialog-header">
<div class="heading-l" translate="general-config-screen.general.title"></div>
<div translate="general-config-screen.general.subtitle"></div>
</div>
<form (submit)="saveGeneralConfig()" [formGroup]="configForm">
<div class="dialog-content">
<div class="dialog-content-left">
<div class="iqser-input-group">
<label translate="general-config-screen.app-name.label"></label>
<input
formControlName="auxiliaryName"
name="auxiliaryName"
placeholder="{{ 'general-config-screen.app-name.placeholder' | translate }}"
type="text"
/>
</div>
<div class="inline-input-group flex-align-items-center">
<mat-slide-toggle color="primary" formControlName="forgotPasswordFunctionEnabled"></mat-slide-toggle>
<span class="ml-8" translate="general-config-screen.general.form.forgot-password"></span>
</div>
</div>
</div>
<div class="dialog-actions">
<button
[disabled]="configForm.invalid || !generalConfigurationChanged"
color="primary"
mat-flat-button
type="submit"
>
{{ 'general-config-screen.actions.save' | translate }}
</button>
</div>
</form>
<redaction-general-config-form></redaction-general-config-form>
</div>
<div class="dialog mt-16">
<div class="dialog-header">
<div class="heading-l" translate="general-config-screen.title"></div>
<div translate="general-config-screen.subtitle"></div>
</div>
<form (submit)="save()" [formGroup]="smtpForm">
<div class="dialog-content">
<div class="dialog-content-left">
<div class="iqser-input-group required">
<label translate="general-config-screen.form.host"></label>
<input
formControlName="host"
name="host"
placeholder="{{ 'general-config-screen.form.host-placeholder' | translate }}"
type="text"
/>
</div>
<div class="iqser-input-group w-100">
<label translate="general-config-screen.form.port"></label>
<input formControlName="port" name="port" type="number" />
</div>
<div class="iqser-input-group required">
<label translate="general-config-screen.form.from"></label>
<input
formControlName="from"
name="from"
placeholder="{{ 'general-config-screen.form.from-placeholder' | translate }}"
type="email"
/>
</div>
<div class="iqser-input-group">
<label translate="general-config-screen.form.from-display-name"></label>
<input
formControlName="fromDisplayName"
name="fromDisplayName"
placeholder="{{ 'general-config-screen.form.from-display-name-placeholder' | translate }}"
type="text"
/>
<span class="hint" translate="general-config-screen.form.from-display-name-hint"></span>
</div>
<div class="iqser-input-group">
<label translate="general-config-screen.form.reply-to"></label>
<input
formControlName="replyTo"
name="replyTo"
placeholder="{{ 'general-config-screen.form.reply-to-placeholder' | translate }}"
type="text"
/>
</div>
<div class="iqser-input-group">
<label translate="general-config-screen.form.reply-to-display-name"></label>
<input
formControlName="replyToDisplayName"
name="replyToDisplayName"
placeholder="{{ 'general-config-screen.form.reply-to-display-name-placeholder' | translate }}"
type="text"
/>
</div>
<div class="iqser-input-group">
<label translate="general-config-screen.form.envelope-from"></label>
<input
formControlName="envelopeFrom"
name="envelopeFrom"
placeholder="{{ 'general-config-screen.form.envelope-from-placeholder' | translate }}"
type="text"
/>
<span class="hint" translate="general-config-screen.form.envelope-from-hint"></span>
</div>
</div>
<div>
<div class="iqser-input-group">
<label translate="general-config-screen.form.ssl"></label>
<mat-slide-toggle color="primary" formControlName="ssl"></mat-slide-toggle>
</div>
<div class="iqser-input-group">
<label translate="general-config-screen.form.starttls"></label>
<mat-slide-toggle color="primary" formControlName="starttls"></mat-slide-toggle>
</div>
<div class="iqser-input-group">
<label translate="general-config-screen.form.auth"></label>
<mat-slide-toggle color="primary" formControlName="auth"></mat-slide-toggle>
</div>
<div
(click)="openAuthConfigDialog(true)"
*ngIf="smtpForm.get('auth').value"
class="link-action"
translate="general-config-screen.form.change-credentials"
></div>
</div>
</div>
<div class="dialog-actions">
<button
[disabled]="smtpForm.invalid || !smtpConfigurationChanged"
color="primary"
mat-flat-button
type="submit"
>
{{ 'general-config-screen.actions.save' | translate }}
</button>
<iqser-icon-button
(action)="testConnection()"
[disabled]="smtpForm.invalid"
[label]="'general-config-screen.actions.test-connection' | translate"
[type]="iconButtonTypes.dark"
></iqser-icon-button>
</div>
</form>
<redaction-smtp-form></redaction-smtp-form>
</div>
</div>
</div>

View File

@ -1,152 +1,13 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { IGeneralConfiguration, ISmtpConfiguration } from '@red/domain';
import { ConfigService } from '@services/config.service';
import { AutoUnsubscribe, IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { Component } from '@angular/core';
import { UserService } from '@services/user.service';
import { GeneralSettingsService } from '@services/general-settings.service';
import { SmtpConfigService } from '../../services/smtp-config.service';
@Component({
selector: 'redaction-general-config-screen',
templateUrl: './general-config-screen.component.html',
styleUrls: ['./general-config-screen.component.scss'],
})
export class GeneralConfigScreenComponent extends AutoUnsubscribe implements OnInit, OnDestroy {
readonly iconButtonTypes = IconButtonTypes;
export class GeneralConfigScreenComponent {
readonly currentUser = this._userService.currentUser;
readonly configForm: FormGroup;
readonly smtpForm: FormGroup;
private _initialGeneralConfiguration: IGeneralConfiguration;
private _initialSMTPConfiguration: ISmtpConfiguration;
constructor(
private readonly _toaster: Toaster,
private readonly _userService: UserService,
private readonly _formBuilder: FormBuilder,
private readonly _loadingService: LoadingService,
private readonly _dialogService: AdminDialogService,
private readonly _configService: ConfigService,
private readonly _smtpConfigService: SmtpConfigService,
private readonly _generalSettingsService: GeneralSettingsService,
) {
super();
this.configForm = this._formBuilder.group({
forgotPasswordFunctionEnabled: [false],
auxiliaryName: [undefined],
});
this.smtpForm = this._formBuilder.group({
host: [undefined, Validators.required],
port: [25],
from: [undefined, [Validators.required, Validators.email]],
fromDisplayName: [undefined],
replyTo: [undefined],
replyToDisplayName: [undefined],
envelopeFrom: [undefined],
ssl: [false],
starttls: [false],
auth: [false],
user: [undefined],
password: [undefined],
});
this.addSubscription = this.smtpForm.controls.auth.valueChanges.subscribe(auth => {
if (auth) {
this.openAuthConfigDialog();
}
});
}
get smtpConfigurationChanged(): boolean {
if (!this._initialSMTPConfiguration) {
return true;
}
for (const key of Object.keys(this.smtpForm.getRawValue())) {
if (this._initialSMTPConfiguration[key] !== this.smtpForm.get(key).value) {
return true;
}
}
return false;
}
get generalConfigurationChanged(): boolean {
if (!this._initialGeneralConfiguration) {
return true;
}
for (const key of Object.keys(this.configForm.getRawValue())) {
if (this._initialGeneralConfiguration[key] !== this.configForm.get(key).value) {
return true;
}
}
return false;
}
async ngOnInit() {
await this._loadData();
}
async save() {
this._loadingService.start();
await this._smtpConfigService.updateSMTPConfiguration(this.smtpForm.getRawValue()).toPromise();
this._initialSMTPConfiguration = this.smtpForm.getRawValue();
this._loadingService.stop();
}
async saveGeneralConfig() {
this._loadingService.start();
const configFormValues = this.configForm.getRawValue();
await this._generalSettingsService.updateGeneralConfigurations(configFormValues).toPromise();
this._initialGeneralConfiguration = await this._generalSettingsService.getGeneralConfigurations().toPromise();
this._configService.updateDisplayName(this._initialGeneralConfiguration.displayName);
this._loadingService.stop();
}
openAuthConfigDialog(skipDisableOnCancel?: boolean) {
this._dialogService.openDialog('smtpAuthConfig', null, this.smtpForm.getRawValue(), null, authConfig => {
if (authConfig) {
this.smtpForm.patchValue(authConfig);
} else if (!skipDisableOnCancel) {
this.smtpForm.patchValue({ auth: false }, { emitEvent: false });
}
});
}
async testConnection() {
this._loadingService.start();
try {
await this._smtpConfigService.testSMTPConfiguration(this.smtpForm.getRawValue()).toPromise();
this._toaster.success(_('general-config-screen.test.success'));
} catch (e) {
this._toaster.error(_('general-config-screen.test.error'));
} finally {
this._loadingService.stop();
}
}
private async _loadData() {
this._loadingService.start();
try {
this._initialGeneralConfiguration = await this._generalSettingsService.getGeneralConfigurations().toPromise();
this.configForm.patchValue(this._initialGeneralConfiguration, { emitEvent: false });
} catch (e) {}
try {
this._initialSMTPConfiguration = await this._smtpConfigService.getCurrentSMTPConfiguration().toPromise();
this.smtpForm.patchValue(this._initialSMTPConfiguration, { emitEvent: false });
} catch (e) {}
this._loadingService.stop();
}
constructor(private readonly _userService: UserService) {}
}

View File

@ -0,0 +1,108 @@
<div class="dialog-header">
<div class="heading-l" translate="general-config-screen.title"></div>
<div translate="general-config-screen.subtitle"></div>
</div>
<form (submit)="save()" [formGroup]="form">
<div class="dialog-content">
<div class="dialog-content-left">
<div class="iqser-input-group required">
<label translate="general-config-screen.form.host"></label>
<input
formControlName="host"
name="host"
placeholder="{{ 'general-config-screen.form.host-placeholder' | translate }}"
type="text"
/>
</div>
<div class="iqser-input-group w-100">
<label translate="general-config-screen.form.port"></label>
<input formControlName="port" name="port" type="number" />
</div>
<div class="iqser-input-group required">
<label translate="general-config-screen.form.from"></label>
<input
formControlName="from"
name="from"
placeholder="{{ 'general-config-screen.form.from-placeholder' | translate }}"
type="email"
/>
</div>
<div class="iqser-input-group">
<label translate="general-config-screen.form.from-display-name"></label>
<input
formControlName="fromDisplayName"
name="fromDisplayName"
placeholder="{{ 'general-config-screen.form.from-display-name-placeholder' | translate }}"
type="text"
/>
<span class="hint" translate="general-config-screen.form.from-display-name-hint"></span>
</div>
<div class="iqser-input-group">
<label translate="general-config-screen.form.reply-to"></label>
<input
formControlName="replyTo"
name="replyTo"
placeholder="{{ 'general-config-screen.form.reply-to-placeholder' | translate }}"
type="text"
/>
</div>
<div class="iqser-input-group">
<label translate="general-config-screen.form.reply-to-display-name"></label>
<input
formControlName="replyToDisplayName"
name="replyToDisplayName"
placeholder="{{ 'general-config-screen.form.reply-to-display-name-placeholder' | translate }}"
type="text"
/>
</div>
<div class="iqser-input-group">
<label translate="general-config-screen.form.envelope-from"></label>
<input
formControlName="envelopeFrom"
name="envelopeFrom"
placeholder="{{ 'general-config-screen.form.envelope-from-placeholder' | translate }}"
type="text"
/>
<span class="hint" translate="general-config-screen.form.envelope-from-hint"></span>
</div>
</div>
<div>
<div class="iqser-input-group">
<label translate="general-config-screen.form.ssl"></label>
<mat-slide-toggle color="primary" formControlName="ssl"></mat-slide-toggle>
</div>
<div class="iqser-input-group">
<label translate="general-config-screen.form.starttls"></label>
<mat-slide-toggle color="primary" formControlName="starttls"></mat-slide-toggle>
</div>
<div class="iqser-input-group">
<label translate="general-config-screen.form.auth"></label>
<mat-slide-toggle color="primary" formControlName="auth"></mat-slide-toggle>
</div>
<div
(click)="openAuthConfigDialog(true)"
*ngIf="form.get('auth').value"
class="link-action"
translate="general-config-screen.form.change-credentials"
></div>
</div>
</div>
<div class="dialog-actions">
<button [disabled]="form.invalid || !smtpConfigurationChanged" color="primary" mat-flat-button type="submit">
{{ 'general-config-screen.actions.save' | translate }}
</button>
<iqser-icon-button
(action)="testConnection()"
[disabled]="form.invalid"
[label]="'general-config-screen.actions.test-connection' | translate"
[type]="iconButtonTypes.dark"
></iqser-icon-button>
</div>
</form>

View File

@ -0,0 +1,108 @@
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 { AdminDialogService } from '../../../services/admin-dialog.service';
import { SmtpConfigService } from '../../../services/smtp-config.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@Component({
selector: 'redaction-smtp-form',
templateUrl: './smtp-form.component.html',
styleUrls: ['./smtp-form.component.scss'],
})
export class SmtpFormComponent extends AutoUnsubscribe implements OnInit, OnDestroy {
readonly iconButtonTypes = IconButtonTypes;
private _initialConfiguration: ISmtpConfiguration;
readonly form: FormGroup = this._getForm();
constructor(
private readonly _formBuilder: FormBuilder,
private readonly _dialogService: AdminDialogService,
private readonly _smtpConfigService: SmtpConfigService,
private readonly _loadingService: LoadingService,
private readonly _toaster: Toaster,
) {
super();
this.addSubscription = this.form.controls.auth.valueChanges.subscribe(auth => {
if (auth) {
this.openAuthConfigDialog();
}
});
}
async ngOnInit(): Promise<void> {
await this._loadData();
}
private _getForm(): FormGroup {
return this._formBuilder.group({
host: [undefined, Validators.required],
port: [25],
from: [undefined, [Validators.required, Validators.email]],
fromDisplayName: [undefined],
replyTo: [undefined],
replyToDisplayName: [undefined],
envelopeFrom: [undefined],
ssl: [false],
starttls: [false],
auth: [false],
user: [undefined],
password: [undefined],
});
}
openAuthConfigDialog(skipDisableOnCancel?: boolean) {
this._dialogService.openDialog('smtpAuthConfig', null, this.form.getRawValue(), null, authConfig => {
if (authConfig) {
this.form.patchValue(authConfig);
} else if (!skipDisableOnCancel) {
this.form.patchValue({ auth: false }, { emitEvent: false });
}
});
}
async save() {
this._loadingService.start();
await this._smtpConfigService.updateSMTPConfiguration(this.form.getRawValue()).toPromise();
this._initialConfiguration = this.form.getRawValue();
this._loadingService.stop();
}
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 testConnection() {
this._loadingService.start();
try {
await this._smtpConfigService.testSMTPConfiguration(this.form.getRawValue()).toPromise();
this._toaster.success(_('general-config-screen.test.success'));
} catch (e) {
this._toaster.error(_('general-config-screen.test.error'));
} finally {
this._loadingService.stop();
}
}
private async _loadData() {
this._loadingService.start();
try {
this._initialConfiguration = await this._smtpConfigService.getCurrentSMTPConfiguration().toPromise();
this.form.patchValue(this._initialConfiguration, { emitEvent: false });
} catch (e) {}
this._loadingService.stop();
}
}

View File

@ -13,7 +13,7 @@ import { BaseDialogComponent, LoadingService } from '@iqser/common-ui';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AddEditJustificationDialogComponent extends BaseDialogComponent {
form: FormGroup;
readonly form: FormGroup = this._getForm();
constructor(
private readonly _formBuilder: FormBuilder,
@ -24,7 +24,10 @@ export class AddEditJustificationDialogComponent extends BaseDialogComponent {
@Inject(MAT_DIALOG_DATA) public justification: Justification,
) {
super();
this.form = this._formBuilder.group({
}
private _getForm(): FormGroup {
return this._formBuilder.group({
name: [{ value: this.justification?.name, disabled: !!this.justification }, Validators.required],
reason: [this.justification?.reason, Validators.required],
description: [this.justification?.description, Validators.required],

View File

@ -24,7 +24,7 @@
<div *ngIf="changed && permissionsService.isAdmin()" class="changes-box">
<iqser-icon-button
(action)="save()"
[disabled]="configForm.invalid"
[disabled]="form.invalid"
[label]="'watermark-screen.action.save' | translate"
[type]="iconButtonTypes.primary"
icon="iqser:check"
@ -35,7 +35,7 @@
<div class="right-container" iqserHasScrollbar>
<div class="heading-xl" translate="watermark-screen.title"></div>
<form (keyup)="configChanged()" [formGroup]="configForm">
<form (keyup)="configChanged()" [formGroup]="form">
<div class="iqser-input-group w-300">
<textarea
(mousemove)="triggerChanges()"
@ -55,8 +55,8 @@
<div
(click)="setValue('orientation', option)"
*ngFor="let option of ['VERTICAL', 'HORIZONTAL', 'DIAGONAL']"
[class.active]="configForm.get('orientation').value === option"
[class.disabled]="configForm.get('orientation').disabled"
[class.active]="form.get('orientation').value === option"
[class.disabled]="form.get('orientation').disabled"
[ngClass]="option"
>
<span>ABC</span>
@ -85,17 +85,17 @@
/>
<div
(colorPickerChange)="setValue('hexColor', $event)"
[class.disabled]="configForm.get('hexColor').disabled"
[colorPicker]="configForm.get('hexColor').value"
[cpDisabled]="configForm.get('hexColor').disabled"
[class.disabled]="form.get('hexColor').disabled"
[colorPicker]="form.get('hexColor').value"
[cpDisabled]="form.get('hexColor').disabled"
[cpOutputFormat]="'hex'"
[cpPosition]="'top-right'"
[cpUseRootViewContainer]="true"
[style.background]="configForm.get('hexColor').value"
[style.background]="form.get('hexColor').value"
class="input-icon"
>
<mat-icon
*ngIf="!configForm.get('hexColor')?.value || configForm.get('hexColor').value?.length === 0"
*ngIf="!form.get('hexColor')?.value || form.get('hexColor').value?.length === 0"
svgIcon="red:color-picker"
></mat-icon>
</div>
@ -113,8 +113,8 @@
{ value: 'courier', display: 'Courier' }
]
"
[class.active]="configForm.get('fontType').value === option.value"
[class.disabled]="configForm.get('fontType').disabled"
[class.active]="form.get('fontType').value === option.value"
[class.disabled]="form.get('fontType').disabled"
[ngClass]="option.value"
>
{{ option.display }}

View File

@ -28,7 +28,7 @@ export const DEFAULT_WATERMARK: IWatermark = {
})
export class WatermarkScreenComponent implements OnInit {
readonly iconButtonTypes = IconButtonTypes;
configForm: FormGroup;
readonly form: FormGroup = this._getForm();
private _instance: WebViewerInstance;
private _watermark: IWatermark = {};
@ViewChild('viewer', { static: true })
@ -46,7 +46,6 @@ export class WatermarkScreenComponent implements OnInit {
private readonly _loadingService: LoadingService,
) {
this._loadingService.start();
this._initForm();
}
get changed(): boolean {
@ -54,7 +53,7 @@ export class WatermarkScreenComponent implements OnInit {
return true;
}
for (const key of Object.keys(this._watermark)) {
if (this._watermark[key] !== this.configForm.get(key)?.value) {
if (this._watermark[key] !== this.form.get(key)?.value) {
return true;
}
}
@ -72,7 +71,7 @@ export class WatermarkScreenComponent implements OnInit {
save() {
const watermark = {
...this.configForm.getRawValue(),
...this.form.getRawValue(),
};
const dossierTemplateId = this._dossierTemplatesService.activeDossierTemplateId;
@ -93,15 +92,15 @@ export class WatermarkScreenComponent implements OnInit {
}
async revert() {
this.configForm.setValue({ ...this._watermark });
this.form.setValue({ ...this._watermark });
await this.configChanged();
}
triggerChanges() {}
async setValue(type: 'fontType' | 'orientation' | 'hexColor', value: any) {
if (!this.configForm.get(type).disabled) {
this.configForm.get(type).setValue(value);
if (!this.form.get(type).disabled) {
this.form.get(type).setValue(value);
await this.configChanged();
}
}
@ -110,12 +109,12 @@ export class WatermarkScreenComponent implements OnInit {
this._watermarkService.getWatermark(this._dossierTemplatesService.activeDossierTemplateId).subscribe(
watermark => {
this._watermark = watermark;
this.configForm.setValue({ ...this._watermark });
this.form.setValue({ ...this._watermark });
this._loadViewer();
},
() => {
this._watermark = DEFAULT_WATERMARK;
this.configForm.setValue({ ...this._watermark });
this.form.setValue({ ...this._watermark });
this._loadViewer();
},
);
@ -162,12 +161,12 @@ export class WatermarkScreenComponent implements OnInit {
const pdfNet = this._instance.Core.PDFNet;
const document = await this._instance.Core.documentViewer.getDocument().getPDFDoc();
const text = this.configForm.get('text').value || '';
const fontSize = this.configForm.get('fontSize').value;
const fontType = this.configForm.get('fontType').value;
const orientation: WatermarkOrientation = this.configForm.get('orientation').value;
const opacity = this.configForm.get('opacity').value;
const color = this.configForm.get('hexColor').value;
const text = this.form.get('text').value || '';
const fontSize = this.form.get('fontSize').value;
const fontType = this.form.get('fontType').value;
const orientation: WatermarkOrientation = this.form.get('orientation').value;
const opacity = this.form.get('opacity').value;
const color = this.form.get('hexColor').value;
await stampPDFPage(document, pdfNet, text, fontSize, fontType, orientation, opacity, color, [1]);
this._instance.Core.documentViewer.refreshAll();
@ -175,8 +174,8 @@ export class WatermarkScreenComponent implements OnInit {
this._changeDetectorRef.detectChanges();
}
private _initForm() {
this.configForm = this._formBuilder.group({
private _getForm(): FormGroup {
return this._formBuilder.group({
text: [{ value: null, disabled: !this.permissionsService.isAdmin() }],
hexColor: [
{

View File

@ -1,5 +1,5 @@
<section class="dialog">
<form (submit)="saveDossier()" [formGroup]="dossierForm">
<form (submit)="saveDossier()" [formGroup]="form">
<div class="dialog-header heading-l" translate="add-dossier-dialog.header-new"></div>
<div class="dialog-content">

View File

@ -16,7 +16,7 @@ import { ReportTemplateService } from '@services/report-template.service';
export class AddDossierDialogComponent {
readonly iconButtonTypes = IconButtonTypes;
dossierForm: FormGroup;
readonly form: FormGroup;
hasDueDate = false;
downloadTypes: { key: DownloadFileType; label: string }[] = ['ORIGINAL', 'PREVIEW', 'REDACTED'].map((type: DownloadFileType) => ({
key: type,
@ -33,7 +33,11 @@ export class AddDossierDialogComponent {
readonly dialogRef: MatDialogRef<AddDossierDialogComponent>,
) {
this._getDossierTemplates();
this.dossierForm = this._formBuilder.group(
this.form = this._getForm();
}
private _getForm(): FormGroup {
return this._formBuilder.group(
{
dossierName: [null, Validators.required],
dossierTemplateId: [null, Validators.required],
@ -54,19 +58,19 @@ export class AddDossierDialogComponent {
}
get reportTemplateIdsLength() {
return this.dossierForm.controls['reportTemplateIds']?.value?.length || 0;
return this.form.controls['reportTemplateIds']?.value?.length || 0;
}
get downloadFileTypesLength() {
return this.dossierForm.controls['downloadFileTypes']?.value?.length || 0;
return this.form.controls['downloadFileTypes']?.value?.length || 0;
}
get disabled() {
if (this.hasDueDate && this.dossierForm.get('dueDate').value === null) {
if (this.hasDueDate && this.form.get('dueDate').value === null) {
return true;
}
return this.dossierForm.invalid;
return this.form.invalid;
}
reportTemplateValueMapper = (reportTemplate: IReportTemplate) => reportTemplate.templateId;
@ -86,7 +90,7 @@ export class AddDossierDialogComponent {
this.availableReportTypes =
(await this._reportTemplateController.getAvailableReportTemplates(dossierTemplate.dossierTemplateId).toPromise()) || [];
// update dropdown values
this.dossierForm.patchValue(
this.form.patchValue(
{
downloadFileTypes: dossierTemplate.downloadFileTypes,
reportTemplateIds: [], // TODO DEFAULT
@ -95,7 +99,7 @@ export class AddDossierDialogComponent {
);
} else {
this.availableReportTypes = [];
this.dossierForm.patchValue(
this.form.patchValue(
{
downloadFileTypes: [],
reportTemplateIds: [],
@ -117,14 +121,14 @@ export class AddDossierDialogComponent {
private _formToObject(): IDossierRequest {
return {
dossierName: this.dossierForm.get('dossierName').value,
description: this.dossierForm.get('description').value,
dueDate: this.hasDueDate ? this.dossierForm.get('dueDate').value : undefined,
dossierTemplateId: this.dossierForm.get('dossierTemplateId').value,
downloadFileTypes: this.dossierForm.get('downloadFileTypes').value,
reportTemplateIds: this.dossierForm.get('reportTemplateIds').value,
watermarkEnabled: this.dossierForm.get('watermarkEnabled').value,
watermarkPreviewEnabled: this.dossierForm.get('watermarkPreviewEnabled').value,
dossierName: this.form.get('dossierName').value,
description: this.form.get('description').value,
dueDate: this.hasDueDate ? this.form.get('dueDate').value : undefined,
dossierTemplateId: this.form.get('dossierTemplateId').value,
downloadFileTypes: this.form.get('downloadFileTypes').value,
reportTemplateIds: this.form.get('reportTemplateIds').value,
watermarkEnabled: this.form.get('watermarkEnabled').value,
watermarkPreviewEnabled: this.form.get('watermarkPreviewEnabled').value,
};
}
}

View File

@ -21,7 +21,7 @@ class DialogData {
styleUrls: ['./assign-reviewer-approver-dialog.component.scss'],
})
export class AssignReviewerApproverDialogComponent {
form: FormGroup;
readonly form: FormGroup;
dossier: Dossier;
constructor(
@ -36,7 +36,7 @@ export class AssignReviewerApproverDialogComponent {
@Inject(MAT_DIALOG_DATA) readonly data: DialogData,
) {
this.dossier = this._dossiersService.find(this.data.files[0].dossierId);
this._loadData();
this.form = this._getForm();
}
get selectedUser(): string {
@ -102,20 +102,27 @@ export class AssignReviewerApproverDialogComponent {
* the id of the current reviewer of the files list if there is only one reviewer for all of them;
* or the id of the current user
**/
private _loadData() {
private get _uniqueReviewers(): Set<string> {
const uniqueReviewers = new Set<string>();
for (const file of this.data.files) {
if (file.currentReviewer) {
uniqueReviewers.add(file.currentReviewer);
}
}
let user: string = uniqueReviewers.size === 1 ? uniqueReviewers.values().next().value : this.userService.currentUser.id;
return uniqueReviewers;
}
private get _user(): string {
let user: string = this._uniqueReviewers.size === 1 ? this._uniqueReviewers.values().next().value : this.userService.currentUser.id;
user = this.userOptions.indexOf(user) >= 0 ? user : this.userOptions[0];
return user;
}
this.form = this._formBuilder.group({
private _getForm(): FormGroup {
return this._formBuilder.group({
// Allow a null reviewer if a previous reviewer exists (= it's not the first assignment) & current user is allowed to unassign
user: [user, this._canUnassignFiles && !user ? Validators.required : null],
user: [this._user, this._canUnassignFiles && !this._user ? Validators.required : null],
});
}
}

View File

@ -1,5 +1,5 @@
<section class="dialog">
<form (submit)="save()" [formGroup]="legalBasisForm">
<form (submit)="save()" [formGroup]="form">
<div class="dialog-header heading-l" translate="change-legal-basis-dialog.header"></div>
<div class="dialog-content">
@ -18,7 +18,7 @@
<div class="iqser-input-group w-400">
<label translate="change-legal-basis-dialog.content.legalBasis"></label>
<input [value]="legalBasisForm.get('reason').value?.legalBasis" disabled type="text" />
<input [value]="form.get('reason').value?.legalBasis" disabled type="text" />
</div>
<div [class.required]="!isDocumentAdmin" class="iqser-input-group w-300">
@ -28,7 +28,7 @@
</div>
<div class="dialog-actions">
<button [disabled]="!legalBasisForm.valid || !changed" color="primary" mat-flat-button type="submit">
<button [disabled]="!form.valid || !changed" color="primary" mat-flat-button type="submit">
{{ 'change-legal-basis-dialog.actions.save' | translate }}
</button>
<div class="all-caps-label cancel" mat-dialog-close translate="change-legal-basis-dialog.actions.cancel"></div>

View File

@ -17,7 +17,7 @@ export interface LegalBasisOption {
templateUrl: './change-legal-basis-dialog.component.html',
})
export class ChangeLegalBasisDialogComponent implements OnInit {
legalBasisForm: FormGroup;
form: FormGroup = this._getForm();
isDocumentAdmin: boolean;
legalOptions: LegalBasisOption[] = [];
@ -31,17 +31,10 @@ export class ChangeLegalBasisDialogComponent implements OnInit {
) {}
get changed(): boolean {
return this.legalBasisForm.get('reason').value.legalBasis !== this._data.annotations[0].legalBasis;
return this.form.get('reason').value.legalBasis !== this._data.annotations[0].legalBasis;
}
async ngOnInit() {
this.isDocumentAdmin = this._permissionsService.isApprover(this._data.dossier);
this.legalBasisForm = this._formBuilder.group({
reason: [null, Validators.required],
comment: this.isDocumentAdmin ? [null] : [null, Validators.required],
});
const data = await this._justificationsService.getForDossierTemplate(this._data.dossier.dossierTemplateId).toPromise();
this.legalOptions = data
@ -52,15 +45,23 @@ export class ChangeLegalBasisDialogComponent implements OnInit {
}))
.sort((a, b) => a.label.localeCompare(b.label));
this.legalBasisForm.patchValue({
this.form.patchValue({
reason: this.legalOptions.find(option => option.legalBasis === this._data.annotations[0].legalBasis),
});
}
private _getForm(): FormGroup {
this.isDocumentAdmin = this._permissionsService.isApprover(this._data.dossier);
return this._formBuilder.group({
reason: [null, Validators.required],
comment: this.isDocumentAdmin ? [null] : [null, Validators.required],
});
}
save() {
this.dialogRef.close({
legalBasis: this.legalBasisForm.get('reason').value.legalBasis,
comment: this.legalBasisForm.get('comment').value,
legalBasis: this.form.get('reason').value.legalBasis,
comment: this.form.get('comment').value,
});
}
}

View File

@ -1,7 +1,7 @@
<section *ngIf="!!documentInfoForm" class="dialog">
<section *ngIf="!!form" class="dialog">
<div class="dialog-header heading-l" translate="document-info.title"></div>
<form (submit)="saveDocumentInfo()" [formGroup]="documentInfoForm">
<form (submit)="saveDocumentInfo()" [formGroup]="form">
<div class="dialog-content">
<div *ngFor="let attr of attributes" class="iqser-input-group w-300">
<label>{{ attr.label }}</label>
@ -9,7 +9,7 @@
</div>
</div>
<div class="dialog-actions">
<button [disabled]="documentInfoForm.invalid" color="primary" mat-flat-button type="submit">
<button [disabled]="form.invalid" color="primary" mat-flat-button type="submit">
{{ 'document-info.save' | translate }}
</button>
</div>

View File

@ -11,8 +11,7 @@ import { DossiersService } from '@services/entity-services/dossiers.service';
styleUrls: ['./document-info-dialog.component.scss'],
})
export class DocumentInfoDialogComponent implements OnInit {
documentInfoForm: FormGroup;
file: IFile;
form: FormGroup;
attributes: IFileAttributeConfig[];
private readonly _dossier: Dossier;
@ -25,31 +24,35 @@ export class DocumentInfoDialogComponent implements OnInit {
public dialogRef: MatDialogRef<DocumentInfoDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: IFile,
) {
this.file = this.data;
this._dossier = this._dossiersService.find(this.file.dossierId);
this._dossier = this._dossiersService.find(this.data.dossierId);
}
async ngOnInit() {
this.attributes = (
await this._fileAttributesService.getFileAttributesConfig(this._dossier.dossierTemplateId).toPromise()
).fileAttributeConfigs.filter(attr => attr.editable);
const formConfig = this.attributes.reduce(
(acc, attr) => ({
...acc,
[attr.id]: [this.file.fileAttributes?.attributeIdToValue[attr.id]],
}),
{},
this.form = this._getForm();
}
private _getForm(): FormGroup {
return this._formBuilder.group(
this.attributes.reduce(
(acc, attr) => ({
...acc,
[attr.id]: [this.data.fileAttributes?.attributeIdToValue[attr.id]],
}),
{},
),
);
this.documentInfoForm = this._formBuilder.group(formConfig);
}
async saveDocumentInfo() {
const attributeIdToValue = {
...this.file.fileAttributes?.attributeIdToValue,
...this.documentInfoForm.getRawValue(),
...this.data.fileAttributes?.attributeIdToValue,
...this.form.getRawValue(),
};
await this._fileAttributesService.setFileAttributes({ attributeIdToValue }, this.file.dossierId, this.file.fileId).toPromise();
this.file.fileAttributes = { attributeIdToValue };
await this._fileAttributesService.setFileAttributes({ attributeIdToValue }, this.data.dossierId, this.data.fileId).toPromise();
this.data.fileAttributes = { attributeIdToValue };
this.dialogRef.close(true);
}
}

View File

@ -1,4 +1,4 @@
<form (submit)="save()" *ngIf="attributesForm" [formGroup]="attributesForm">
<form (submit)="save()" *ngIf="form" [formGroup]="form">
<div>
<div *ngIf="customAttributes.length" class="heading">
{{ 'edit-dossier-dialog.attributes.custom-attributes' | translate }}
@ -10,16 +10,20 @@
icon="red:attribute"
></iqser-empty-state>
<div *ngFor="let attr of customAttributes" [class.datepicker-wrapper]="isDate(attr)" class="iqser-input-group">
<div
*ngFor="let attr of customAttributes"
[class.datepicker-wrapper]="isSpecificType(attr, dossierAttributeConfigTypes.DATE)"
class="iqser-input-group"
>
<label>{{ attr.label }}</label>
<input
*ngIf="isNumber(attr) || isText(attr)"
*ngIf="isSpecificType(attr, dossierAttributeConfigTypes.NUMBER) || isSpecificType(attr, dossierAttributeConfigTypes.TEXT)"
[formControlName]="attr.id"
[name]="attr.id"
[type]="isNumber(attr) ? 'number' : 'text'"
[type]="isSpecificType(attr, dossierAttributeConfigTypes.NUMBER) ? 'number' : 'text'"
/>
<ng-container *ngIf="isDate(attr)">
<ng-container *ngIf="isSpecificType(attr, dossierAttributeConfigTypes.DATE)">
<input [formControlName]="attr.id" [matDatepicker]="picker" placeholder="dd/mm/yy" />
<mat-datepicker-toggle [for]="picker" matSuffix>
<mat-icon matDatepickerToggleIcon svgIcon="red:calendar"></mat-icon>

View File

@ -1,6 +1,6 @@
import { Component, ElementRef, EventEmitter, Input, OnInit, Output, QueryList, ViewChildren } from '@angular/core';
import { EditDossierSectionInterface } from '../edit-dossier-section.interface';
import { Dossier, DossierAttributeWithValue } from '@red/domain';
import { Dossier, DossierAttributeConfigType, DossierAttributeConfigTypes, DossierAttributeWithValue } from '@red/domain';
import { PermissionsService } from '@services/permissions.service';
import { CircleButtonTypes, IconButtonTypes, LoadingService } from '@iqser/common-ui';
import { FormBuilder, FormGroup } from '@angular/forms';
@ -15,6 +15,7 @@ import { DossierAttributesService } from '@shared/services/controller-wrappers/d
export class EditDossierAttributesComponent implements EditDossierSectionInterface, OnInit {
readonly iconButtonTypes = IconButtonTypes;
readonly circleButtonTypes = CircleButtonTypes;
readonly dossierAttributeConfigTypes = DossierAttributeConfigTypes;
@Input() dossier: Dossier;
@Output() readonly updateDossier = new EventEmitter();
@ -22,7 +23,7 @@ export class EditDossierAttributesComponent implements EditDossierSectionInterfa
imageAttributes: DossierAttributeWithValue[] = [];
attributes: DossierAttributeWithValue[] = [];
attributesForm: FormGroup;
form: FormGroup;
@ViewChildren('fileInput') private _fileInputs: QueryList<ElementRef>;
constructor(
@ -34,7 +35,7 @@ export class EditDossierAttributesComponent implements EditDossierSectionInterfa
get changed() {
for (const attr of this.attributes) {
if (this.isDate(attr) && attr.value) {
if (this.isSpecificType(attr, this.dossierAttributeConfigTypes.DATE) && attr.value) {
if (!moment(attr.value).isSame(moment(this.currentAttrValue(attr)))) {
return true;
}
@ -57,7 +58,7 @@ export class EditDossierAttributesComponent implements EditDossierSectionInterfa
async ngOnInit() {
this._loadingService.start();
await this._loadAttributes();
this._initForm();
this.form = this._getForm();
this._loadingService.stop();
}
@ -75,20 +76,8 @@ export class EditDossierAttributesComponent implements EditDossierSectionInterfa
this._getFileInputById(attr.id).nativeElement.click();
}
isNumber(attr: DossierAttributeWithValue): boolean {
return attr.type === 'NUMBER';
}
isDate(attr: DossierAttributeWithValue): boolean {
return attr.type === 'DATE';
}
isImage(attr: DossierAttributeWithValue): boolean {
return attr.type === 'IMAGE';
}
isText(attr: DossierAttributeWithValue): boolean {
return attr.type === 'TEXT';
isSpecificType(attr: DossierAttributeWithValue, type: DossierAttributeConfigType): boolean {
return attr.type === type;
}
async uploadImage($event, attr: DossierAttributeWithValue) {
@ -102,22 +91,22 @@ export class EditDossierAttributesComponent implements EditDossierSectionInterfa
const image = $event.target.files[0];
const result = await toBase64(image);
this.attributesForm.patchValue({
this.form.patchValue({
[attr.id]: result,
});
this._getFileInputById(attr.id).nativeElement.value = null;
}
revert() {
this._initForm();
this.form = this._getForm();
}
currentAttrValue(attr: DossierAttributeWithValue): string {
return this.attributesForm.get(attr.id).value;
return this.form.get(attr.id).value;
}
deleteAttr(attr: DossierAttributeWithValue) {
this.attributesForm.patchValue({
this.form.patchValue({
[attr.id]: null,
});
}
@ -132,15 +121,15 @@ export class EditDossierAttributesComponent implements EditDossierSectionInterfa
private async _loadAttributes() {
this.attributes = await this._dossierAttributesService.getWithValues(this.dossier);
this.customAttributes = this.attributes.filter(attr => !this.isImage(attr));
this.imageAttributes = this.attributes.filter(attr => this.isImage(attr));
this.customAttributes = this.attributes.filter(attr => !this.isSpecificType(attr, this.dossierAttributeConfigTypes.IMAGE));
this.imageAttributes = this.attributes.filter(attr => this.isSpecificType(attr, this.dossierAttributeConfigTypes.IMAGE));
}
private _initForm() {
private _getForm(): FormGroup {
const controlsConfig = {};
for (const attribute of this.attributes) {
controlsConfig[attribute.id] = [{ value: attribute.value, disabled: !this.canEdit }];
}
this.attributesForm = this._formBuilder.group(controlsConfig);
return this._formBuilder.group(controlsConfig);
}
}

View File

@ -4,7 +4,6 @@ import { EditDossierSectionInterface } from '../edit-dossier-section.interface';
import { PermissionsService } from '@services/permissions.service';
import { DictionaryManagerComponent } from '@shared/components/dictionary-manager/dictionary-manager.component';
import { DictionaryService } from '@shared/services/dictionary.service';
import { FormBuilder } from '@angular/forms';
import { CircleButtonTypes, LoadingService } from '@iqser/common-ui';
import { DossiersService } from '@services/entity-services/dossiers.service';
@ -27,7 +26,6 @@ export class EditDossierDictionaryComponent implements EditDossierSectionInterfa
private readonly _dictionaryService: DictionaryService,
private readonly _permissionsService: PermissionsService,
private readonly _loadingService: LoadingService,
private readonly _formBuilder: FormBuilder,
) {}
get changed() {

View File

@ -67,7 +67,11 @@ export class EditDossierDownloadPackageComponent implements OnInit, EditDossierS
this.availableReportTypes =
(await this._reportTemplateController.getAvailableReportTemplates(this.dossier.dossierTemplateId).toPromise()) || [];
this.dossierForm = this._formBuilder.group(
this.dossierForm = this._getForm();
}
private _getForm(): FormGroup {
return this._formBuilder.group(
{
reportTemplateIds: [this.dossier.reportTemplateIds],
downloadFileTypes: [this.dossier.downloadFileTypes],

View File

@ -68,7 +68,12 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
ngOnInit() {
this._filterInvalidDossierTemplates();
this.dossierForm = this._formBuilder.group({
this.dossierForm = this._getForm();
this.hasDueDate = !!this.dossier.dueDate;
}
private _getForm(): FormGroup {
return this._formBuilder.group({
dossierName: [this.dossier.dossierName, Validators.required],
dossierTemplateId: [
{
@ -82,7 +87,6 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
watermarkEnabled: [this.dossier.watermarkEnabled],
watermarkPreviewEnabled: [this.dossier.watermarkPreviewEnabled],
});
this.hasDueDate = !!this.dossier.dueDate;
}
revert() {

View File

@ -38,9 +38,13 @@ export class ForceRedactionDialogComponent implements OnInit {
public dialogRef: MatDialogRef<ForceRedactionDialogComponent>,
@Inject(MAT_DIALOG_DATA) private readonly _data: { readonly dossier: Dossier },
) {
this.redactionForm = this._getForm();
}
private _getForm(): FormGroup {
this.isDocumentAdmin = this._permissionsService.isApprover(this._data.dossier);
this.redactionForm = this._formBuilder.group({
return this._formBuilder.group({
reason: [null, Validators.required],
comment: this.isDocumentAdmin ? [null] : [null, Validators.required],
});

View File

@ -44,21 +44,33 @@ export class ManualAnnotationDialogComponent implements OnInit {
this.isFalsePositiveRequest = this.data.manualRedactionEntryWrapper.type === 'FALSE_POSITIVE';
this.isDictionaryRequest = this.data.manualRedactionEntryWrapper.type === 'DICTIONARY' || this.isFalsePositiveRequest;
this.redactionForm = this._formBuilder.group({
this.redactionForm = this._getForm();
this.redactionDictionaries = this._redactionDictionaries;
}
private _getForm(): FormGroup {
return this._formBuilder.group({
reason: this.isDictionaryRequest ? [null] : [null, Validators.required],
dictionary: this.isDictionaryRequest
? [this.isFalsePositiveRequest ? 'false_positive' : null, Validators.required]
: ['manual', Validators.required],
comment: this.isDocumentAdmin ? [null] : [null, Validators.required],
});
}
for (const key of Object.keys(this._appStateService.dictionaryData[data.dossier.dossierTemplateId])) {
const dictionaryData = this._appStateService.getDictionary(key, data.dossier.dossierTemplateId);
private get _redactionDictionaries(): Dictionary[] {
const redactionDictionaries: Dictionary[] = [];
for (const key of Object.keys(this._appStateService.dictionaryData[this.data.dossier.dossierTemplateId])) {
const dictionaryData = this._appStateService.getDictionary(key, this.data.dossier.dossierTemplateId);
if (!dictionaryData.virtual && dictionaryData.addToDictionaryAction) {
this.redactionDictionaries.push(dictionaryData);
redactionDictionaries.push(dictionaryData);
}
}
this.redactionDictionaries.sort((a, b) => a.label.localeCompare(b.label));
redactionDictionaries.sort((a, b) => a.label.localeCompare(b.label));
return redactionDictionaries;
}
get title() {