change supportive material editor model

This commit is contained in:
amentis 2023-11-28 19:06:15 +02:00
parent 319e1f0dca
commit 3d5c718d52
8 changed files with 322 additions and 150 deletions

View File

@ -26,6 +26,8 @@ public class SupportiveMaterialPersist {
@NotEmpty(message = "{validation.empty}")
private String payload;
private String hash;
public UUID getId() {
return id;
}
@ -57,4 +59,12 @@ public class SupportiveMaterialPersist {
public void setPayload(String payload) {
this.payload = payload;
}
public String getHash() {
return hash;
}
public void setHash(String hash) {
this.hash = hash;
}
}

View File

@ -1,20 +1,14 @@
import { IsActive } from "@app/core/common/enum/is-active.enum";
import { SupportiveMaterialFieldType } from "@app/core/common/enum/supportive-material-field-type";
import { Guid } from "@common/types/guid";
import { BaseEntity, BaseEntityPersist } from "@common/base/base-entity.model";
export interface SupportiveMaterial{
id: Guid;
type: SupportiveMaterialFieldType;
languageCode: string;
payload: string;
createdAt: Date;
updatedAt: Date;
isActive: IsActive;
}
export interface SupportiveMaterialPersist{
id: Guid;
export interface SupportiveMaterial extends BaseEntity{
type: SupportiveMaterialFieldType;
languageCode: string;
payload: string;
}
export interface SupportiveMaterialPersist extends BaseEntityPersist{
type: SupportiveMaterialFieldType;
languageCode: string;
payload: string;

View File

@ -24,6 +24,7 @@ import { ReferenceTypeExternalApiHTTPMethodType } from '@app/core/common/enum/re
import { UserDescriptionTemplateRole } from '@app/core/common/enum/user-description-template-role';
import { ReferenceType } from '@app/core/common/enum/reference-type';
import { ReferenceSourceType } from '@app/core/common/enum/reference-source-type';
import { SupportiveMaterialFieldType } from '@app/core/common/enum/supportive-material-field-type';
@Injectable()
export class EnumUtils {
@ -340,4 +341,14 @@ export class EnumUtils {
}
}
toSupportiveMaterialTypeString(status: SupportiveMaterialFieldType): string {
switch (status) {
case SupportiveMaterialFieldType.Faq: return this.language.instant('FOOTER.FAQ');
case SupportiveMaterialFieldType.About: return this.language.instant('FOOTER.ABOUT');
case SupportiveMaterialFieldType.Glossary: return this.language.instant('FOOTER.GLOSSARY');
case SupportiveMaterialFieldType.TermsOfService: return this.language.instant('FOOTER.TERMS-OF-SERVICE');
case SupportiveMaterialFieldType.UserGuide: return this.language.instant('FOOTER.GUIDE');
}
}
}

View File

@ -1,31 +1,35 @@
<div class="supportive-material-editor">
<form (ngSubmit)="submit()" [formGroup]="formGroup">
<form *ngIf="formGroup" (ngSubmit)="formSubmit()">
<div>
<mat-card class="col-md-8 offset-md-2">
<div>
<div class ="material">
<mat-form-field>
<mat-label>{{'SUPPORTIVE-MATERIAL-EDITOR.FIELDS.MATERIAL-TYPE' | translate}}</mat-label>
<mat-select [value] ="selectedMaterial" (selectionChange)="selectedMaterialChanged($event.value)">
<mat-option *ngFor="let vis of visiblesMaterialsTypes" [value]="vis">
{{vis.name | translate}}
<mat-select (selectionChange)="selectedMaterialChanged($event.value)" name="type" [formControl]="formGroup.get('type')" required>
<mat-option *ngFor="let type of supportiveMaterialTypeEnum" [value]="type">
{{enumUtils.toSupportiveMaterialTypeString(type)}}
</mat-option>
</mat-select>
<mat-error *ngIf="formGroup.get('type').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class ="lang">
<mat-form-field>
<mat-label>{{'SUPPORTIVE-MATERIAL-EDITOR.FIELDS.LANGUAGE' | translate}}</mat-label>
<mat-select [value] ="selectedLang" (selectionChange)="selectedLangChanged($event.value)">
<mat-option *ngFor="let vis of visiblesLangTypes" [value]="vis">
<mat-select (selectionChange)="selectedLangChanged($event.value)" name= "languageCode" [formControl]="formGroup.get('languageCode')">
<mat-option *ngFor="let vis of visiblesLangTypes" [value]="vis.type">
{{vis.name | translate}}
</mat-option>
</mat-select>
<mat-error *ngIf="formGroup.get('languageCode').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
</div>
<!-- <mat-card-title><div>{{selectedMaterial.name | translate}}</div></mat-card-title> -->
<mat-card-content>
<mat-card-content *ngIf="formGroup.get('type').value && formGroup.get('languageCode').value">
<editor [init]="{
base_url: '/tinymce',
suffix: '.min',
@ -45,13 +49,12 @@
'undo redo | formatselect | bold italic backcolor | \
alignleft aligncenter alignright alignjustify | \
bullist numlist outdent indent | code codesample | searchreplace | preview | removeformat | help'
}" formControlName="html"></editor>
}" [formControl]="formGroup.get('payload')"></editor>
</mat-card-content>
</mat-card>
<button mat-fab class="mat-fab-bottom-right save-btn" matTooltip="{{'SUPPORTIVE-MATERIAL-EDITOR.ACTIONS.SAVE' | translate}}" type="submit">
<mat-icon class="mat-24">save</mat-icon>
</button>
</div>
</form>
</div>

View File

@ -1,26 +1,39 @@
import { Component, OnInit } from '@angular/core';
import { UntypedFormGroup, UntypedFormBuilder } from '@angular/forms';
import { BaseComponent } from '@common/base/base.component';
import { takeUntil } from 'rxjs/operators';
import { map, takeUntil } from 'rxjs/operators';
import { UiNotificationService, SnackBarNotificationLevel } from '@app/core/services/notification/ui-notification-service';
import { TranslateService } from '@ngx-translate/core';
import { Router } from '@angular/router';
import { ActivatedRoute, Router } from '@angular/router';
import { isNullOrUndefined } from '@app/utilities/enhancers/utils';
import { environment } from 'environments/environment';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { AuthService } from '@app/core/services/auth/auth.service';
import { LanguageService } from '@app/core/services/language/language.service';
import { MatomoService } from '@app/core/services/matomo/matomo-service';
import { HttpClient } from '@angular/common/http';
import { SupportiveMaterialService } from '@app/core/services/supportive-material/supportive-material.service';
import { SupportiveMaterialFieldType } from '@app/core/common/enum/supportive-material-field-type';
import { SupportiveMaterialPersist } from '@app/core/model/supportive-material/supportive-material';
import { SupportiveMaterial, SupportiveMaterialPersist } from '@app/core/model/supportive-material/supportive-material';
import { Guid } from '@common/types/guid';
import { BaseEditor } from '@common/base/base-editor';
import { SupportiveMaterialEditorModel } from './supportive-material-editor.model';
import { MatDialog } from '@angular/material/dialog';
import { FormService } from '@common/forms/form-service';
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
import { FilterService } from '@common/modules/text-filter/filter-service';
import { DatePipe } from '@angular/common';
import { QueryParamsService } from '@app/core/services/utilities/query-params.service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { LoggingService } from '@app/core/services/logging/logging-service';
import { FileUtils } from '@app/core/services/utilities/file-utils.service';
import { LanguageService } from '@app/core/services/language/language.service';
import { SupportiveMaterialEditorService } from './supportive-material-editor.service';
import { SupportiveMaterialEditorResolver } from './supportive-material-editor.resolver';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
import { LanguageV2Service } from '@app/core/services/language/language-v2.service';
import { nameof } from 'ts-simple-nameof';
import { Language } from '@app/core/model/language/language';
interface VisibleMaterialType{
name: string;
type: SupportiveMaterialFieldType;
}
interface VisibleLangType{
name: string;
@ -31,31 +44,45 @@ interface VisibleLangType{
@Component({
selector: 'app-supportive-material-editor',
templateUrl: './supportive-material-editor.component.html',
styleUrls: ['./supportive-material-editor.component.scss']
styleUrls: ['./supportive-material-editor.component.scss'],
providers: [SupportiveMaterialEditorService]
})
export class SupportiveMaterialEditorComponent extends BaseComponent implements OnInit {
public formGroup: UntypedFormGroup;
private formBuilder: UntypedFormBuilder;
export class SupportiveMaterialEditorComponent extends BaseEditor<SupportiveMaterialEditorModel, SupportiveMaterial> implements OnInit {
isNew = true;
isDeleted = false;
formGroup: UntypedFormGroup = null;
showInactiveDetails = false;
constructor(private supportiveMaterialService: SupportiveMaterialService,
private uiNotificationService: UiNotificationService,
private translate: TranslateService,
private router: Router,
private configurationService: ConfigurationService,
public supportiveMaterialTypeEnum = this.enumUtils.getEnumValues(SupportiveMaterialFieldType);
constructor(
// BaseFormEditor injected dependencies
protected dialog: MatDialog,
protected language: TranslateService,
protected formService: FormService,
protected router: Router,
protected uiNotificationService: UiNotificationService,
protected httpErrorHandlingService: HttpErrorHandlingService,
protected filterService: FilterService,
protected datePipe: DatePipe,
protected route: ActivatedRoute,
protected queryParamsService: QueryParamsService,
// Rest dependencies. Inject any other needed deps here:
public authService: AuthService,
public enumUtils: EnumUtils,
private supportiveMaterialService: SupportiveMaterialService,
private languageService: LanguageService,
private httpClient: HttpClient,
private languageV2Service: LanguageV2Service,
private logger: LoggingService,
private supportiveMaterialEditorService: SupportiveMaterialEditorService,
private fileUtils: FileUtils,
private matomoService: MatomoService
) { super(); }
) {
super(dialog, language, formService, router, uiNotificationService, httpErrorHandlingService, filterService, datePipe, route, queryParamsService);
}
visiblesMaterialsTypes: VisibleMaterialType[] = [
{name: "FOOTER.FAQ", type: SupportiveMaterialFieldType.Faq},
{name: "FOOTER.ABOUT", type: SupportiveMaterialFieldType.About},
{name: "FOOTER.GLOSSARY", type: SupportiveMaterialFieldType.Glossary},
{name: "FOOTER.TERMS-OF-SERVICE", type: SupportiveMaterialFieldType.TermsOfService},
{name: "FOOTER.GUIDE", type: SupportiveMaterialFieldType.UserGuide}
]
visiblesLangTypes: VisibleLangType[] = [
{name: "GENERAL.LANGUAGES.ENGLISH", type: "en"},
{name: "GENERAL.LANGUAGES.GREEK", type: "gr"},
@ -70,103 +97,117 @@ export class SupportiveMaterialEditorComponent extends BaseComponent implements
]
selectedMaterial: VisibleMaterialType;
selectedLang: VisibleLangType;
materialId: Guid = null;
ngOnInit() {
this.selectedMaterial = this.visiblesMaterialsTypes.find(x => x.type == SupportiveMaterialFieldType.Faq);
this.selectedLang = this.visiblesLangTypes.find(x => x.type == this.languageService.getCurrentLanguage());
this.getSupportiveMaterialData();
getItem(itemId: Guid, successFunction: (item: SupportiveMaterial) => void) {
this.supportiveMaterialService.getSingle(itemId, SupportiveMaterialEditorResolver.lookupFields())
.pipe(map(data => data as SupportiveMaterial), takeUntil(this._destroyed))
.subscribe(
data => successFunction(data),
error => this.onCallbackError(error)
);
}
public selectedMaterialChanged(item: VisibleMaterialType){
this.selectedMaterial = item;
prepareForm(data: SupportiveMaterial) {
try {
this.editorModel = data ? new SupportiveMaterialEditorModel().fromModel(data) : new SupportiveMaterialEditorModel();
this.isDeleted = data ? data.isActive === IsActive.Inactive : false;
this.buildForm();
} catch (error) {
this.logger.error('Could not parse Supportive Material item: ' + data + error);
this.uiNotificationService.snackBarNotification(this.language.instant('COMMONS.ERRORS.DEFAULT'), SnackBarNotificationLevel.Error);
}
}
buildForm() {
this.formGroup = this.editorModel.buildForm(null, this.isDeleted || !this.authService.hasPermission(AppPermission.EditLanguage));
this.supportiveMaterialEditorService.setValidationErrorModel(this.editorModel.validationErrorModel);
}
refreshData(): void {
this.getItem(this.editorModel.id, (data: SupportiveMaterial) => this.prepareForm(data));
}
refreshOnNavigateToData(id?: Guid): void {
this.formGroup.markAsPristine();
let route = [];
if (id === null) {
route.push('../..');
} else if (this.isNew) {
route.push('../' + id);
} else {
route.push('..');
}
this.router.navigate(route, { queryParams: { 'lookup': this.queryParamsService.serializeLookup(this.lookupParams), 'lv': ++this.lv }, replaceUrl: true, relativeTo: this.route });
}
persistEntity(onSuccess?: (response) => void): void {
const formData = this.formService.getValue(this.formGroup.value) as SupportiveMaterialPersist;
this.supportiveMaterialService.persist(formData)
.pipe(takeUntil(this._destroyed)).subscribe(
complete => onSuccess ? onSuccess(complete) : this.onCallbackSuccess(complete),
error => this.onCallbackError(error)
);
}
formSubmit(): void {
this.formService.touchAllFormFields(this.formGroup);
if (!this.isFormValid()) {
return;
}
this.persistEntity();
}
public delete() {
const value = this.formGroup.value;
if (value.id) {
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
maxWidth: '300px',
data: {
message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'),
confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'),
cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL')
}
});
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
if (result) {
this.supportiveMaterialService.delete(value.id).pipe(takeUntil(this._destroyed))
.subscribe(
complete => this.onCallbackSuccess(),
error => this.onCallbackError(error)
);
}
});
}
}
public selectedMaterialChanged(){
this.getSupportiveMaterialData();
}
public selectedLangChanged(item: VisibleLangType){
this.selectedLang = item;
public selectedLangChanged(){
this.getSupportiveMaterialData();
}
private getSupportiveMaterialData(){
this.matomoService.trackPageView(`Admin: ${this.selectedMaterial.name} Editor`);
this.formBuilder = new UntypedFormBuilder();
this.formGroup = this.formBuilder.group({
name: [''],
html: ['']
});
// this.supportiveMaterialService.getPublic(this.selectedLang.type, this.selectedMaterial.type).pipe(takeUntil(this._destroyed)).subscribe(data => {
// const contentDispositionHeader = data.headers.get('Content-Disposition');
// const filename = contentDispositionHeader.split(';')[1].trim().split('=')[1].replace(/"/g, '');
// this.formGroup.get('name').patchValue(filename);
// this.loadFile(data.body);
// });
if(this.formGroup.get('type').value >= 0 && this.formGroup.get('languageCode').value){
const lookup = SupportiveMaterialService.DefaultSupportiveMaterialLookup();
lookup.types = [this.selectedMaterial.type];
lookup.languageCodes = [this.selectedLang.type];
lookup.types = [this.formGroup.get('type').value];
lookup.languageCodes = [this.formGroup.get('languageCode').value];
this.supportiveMaterialService.query(lookup).pipe(takeUntil(this._destroyed)).subscribe(data => {
if(data.count == 1){
this.materialId = data.items[0].id;
this.formGroup.get('html').patchValue(data.items[0].payload);
this.formGroup.get('id').patchValue(data.items[0].id);
this.formGroup.get('payload').patchValue(data.items[0].payload);
}else{
this.formGroup.get('id').patchValue(null);
this.formGroup.get('payload').patchValue('');
}
});
}
private parseText(source: string): string {
source = source.replace(/src="images/g, `src="${this.configurationService.guideAssets}`);
source = source.replace(/\r\n +>/g, '>\n');
const brokenElements = Array.from(new Set(source.match(/&lt;\/\w+\d* &gt;/g)));
if (!isNullOrUndefined(brokenElements)) {
brokenElements.forEach((brokenElement) => {
const tempvalue = brokenElement.match(/\/\w+\d*/)[0];
source = source.replace(new RegExp(brokenElement, 'g'), `<${tempvalue}>\n`);
});
}
return source;
}
// loadFile(data: Blob) {
// const reader = new FileReader();
// reader.addEventListener('load', () => {
// let result = this.parseText(reader.result as string);
// //result = result.replace(/class="href" path="/g, 'href="#');
// this.formGroup.get('html').patchValue(result);
// }, false);
// reader.readAsText(data);
// }
submit() {
let result = this.parseText(this.formGroup.get('html').value);
this.formGroup.get('html').patchValue(result);
const item = {id: this.materialId,
type: this.selectedMaterial.type,
languageCode: this.selectedLang.type,
payload: this.formGroup.value['html']} as SupportiveMaterialPersist;
this.supportiveMaterialService.persist(item).pipe(takeUntil(this._destroyed))
.subscribe(
complete => {
this.onCallbackSuccess();
},
error => {
this.onCallbackError(error);
}
);
}
onCallbackSuccess(): void {
this.uiNotificationService.snackBarNotification( this.translate.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success);
this.router.navigate(['/reload']).then(() => this.router.navigate(['/supportive-material']));
}
onCallbackError(error: any) {
this.uiNotificationService.snackBarNotification( error, SnackBarNotificationLevel.Error);
//this.validateAllFormFields(this.formGroup);
}
}

View File

@ -0,0 +1,55 @@
import { UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms";
import { SupportiveMaterialFieldType } from "@app/core/common/enum/supportive-material-field-type";
import { SupportiveMaterial, SupportiveMaterialPersist } from "@app/core/model/supportive-material/supportive-material";
import { BaseEditorModel } from "@common/base/base-form-editor-model";
import { BackendErrorValidator } from "@common/forms/validation/custom-validator";
import { ValidationErrorModel } from "@common/forms/validation/error-model/validation-error-model";
import { Validation, ValidationContext } from "@common/forms/validation/validation-context";
export class SupportiveMaterialEditorModel extends BaseEditorModel implements SupportiveMaterialPersist {
type: SupportiveMaterialFieldType;
languageCode: string;
payload: string;
permissions: string[];
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel();
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
constructor() { super(); }
public fromModel(item: SupportiveMaterial): SupportiveMaterialEditorModel {
if (item) {
super.fromModel(item);
this.type = item.type;
this.languageCode = item.languageCode;
this.payload = item.payload;
}
return this;
}
buildForm(context: ValidationContext = null, disabled: boolean = false): UntypedFormGroup {
if (context == null) { context = this.createValidationContext(); }
return this.formBuilder.group({
id: [{ value: this.id, disabled: disabled }, context.getValidation('id').validators],
type: [{ value: this.type, disabled: disabled }, context.getValidation('type').validators],
languageCode: [{ value: this.languageCode, disabled: disabled }, context.getValidation('languageCode').validators],
payload: [{ value: this.payload, disabled: disabled }, context.getValidation('payload').validators],
hash: [{ value: this.hash, disabled: disabled }, context.getValidation('hash').validators]
});
}
createValidationContext(): ValidationContext {
const baseContext: ValidationContext = new ValidationContext();
const baseValidationArray: Validation[] = new Array<Validation>();
baseValidationArray.push({ key: 'id', validators: [BackendErrorValidator(this.validationErrorModel, 'id')] });
baseValidationArray.push({ key: 'type', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'type')] });
baseValidationArray.push({ key: 'languageCode', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'languageCode')] });
baseValidationArray.push({ key: 'payload', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'payload')] });
baseValidationArray.push({ key: 'hash', validators: [] });
baseContext.validation = baseValidationArray;
return baseContext;
}
}

View File

@ -0,0 +1,43 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { SupportiveMaterial } from '@app/core/model/supportive-material/supportive-material';
import { SupportiveMaterialService } from '@app/core/services/supportive-material/supportive-material.service';
import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service';
import { BaseEditorResolver } from '@common/base/base-editor.resolver';
import { Guid } from '@common/types/guid';
import { takeUntil, tap } from 'rxjs/operators';
import { nameof } from 'ts-simple-nameof';
@Injectable()
export class SupportiveMaterialEditorResolver extends BaseEditorResolver {
constructor(private supportiveMaterialService: SupportiveMaterialService, private breadcrumbService: BreadcrumbService) {
super();
}
public static lookupFields(): string[] {
return [
...BaseEditorResolver.lookupFields(),
nameof<SupportiveMaterial>(x => x.id),
nameof<SupportiveMaterial>(x => x.type),
nameof<SupportiveMaterial>(x => x.languageCode),
nameof<SupportiveMaterial>(x => x.payload),
nameof<SupportiveMaterial>(x => x.createdAt),
nameof<SupportiveMaterial>(x => x.updatedAt),
nameof<SupportiveMaterial>(x => x.hash),
nameof<SupportiveMaterial>(x => x.isActive)
]
}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const fields = [
...SupportiveMaterialEditorResolver.lookupFields()
];
const id = route.paramMap.get('id');
if (id != null) {
return this.supportiveMaterialService.getSingle(Guid.parse(id), fields).pipe(tap(x => this.breadcrumbService.addIdResolvedValue(x.id?.toString(), x.languageCode)), takeUntil(this._destroyed));
}
}
}

View File

@ -0,0 +1,15 @@
import { Injectable } from "@angular/core";
import { ValidationErrorModel } from "@common/forms/validation/error-model/validation-error-model";
@Injectable()
export class SupportiveMaterialEditorService {
private validationErrorModel: ValidationErrorModel;
public setValidationErrorModel(validationErrorModel: ValidationErrorModel): void {
this.validationErrorModel = validationErrorModel;
}
public getValidationErrorModel(): ValidationErrorModel {
return this.validationErrorModel;
}
}