Files
cbsnexora/src/app/templates/create-edit-template/create-edit-template.component.ts
T
shubhamsharma9199 534d2e071e feat: replace CKEditor with self-hosted TinyMCE
WEB-435 Unit Test Coverage for ViewCollateralComponent

feat: replace CKEditor with self-hosted TinyMCE (no API key)

Change ViewChild static option to false
2025-11-26 19:14:07 +05:30

306 lines
8.5 KiB
TypeScript

/** Angular Imports */
import { Component, OnInit, ViewChild } from '@angular/core';
import {
UntypedFormGroup,
UntypedFormBuilder,
UntypedFormControl,
Validators,
ReactiveFormsModule
} from '@angular/forms';
import { Router, ActivatedRoute, RouterLink } from '@angular/router';
/** Custom Imports */
import { clientParameterLabels, loanParameterLabels, repaymentParameterLabels } from '../template-parameter-labels';
/** Custom Services */
import { TemplatesService } from '../templates.service';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { EditorComponent, EditorModule, TINYMCE_SCRIPT_SRC } from '@tinymce/tinymce-angular';
import {
MatAccordion,
MatExpansionPanel,
MatExpansionPanelHeader,
MatExpansionPanelTitle
} from '@angular/material/expansion';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
/**
* Create Template Component.
*/
@Component({
selector: 'mifosx-create-edit-template',
templateUrl: './create-edit-template.component.html',
styleUrls: ['./create-edit-template.component.scss'],
imports: [
...STANDALONE_SHARED_IMPORTS,
FaIconComponent,
EditorModule,
MatAccordion,
MatExpansionPanel,
MatExpansionPanelHeader,
MatExpansionPanelTitle
],
providers: [
{
provide: TINYMCE_SCRIPT_SRC,
useValue: 'assets/tinymce/tinymce.min.js'
}
]
})
export class CreateEditComponent implements OnInit {
/** TinyMCE instance configuration */
tinymceConfig = {
base_url: 'assets/tinymce',
suffix: '.min',
menubar: false,
branding: false,
height: 320,
forced_root_block: false,
statusbar: false,
elementpath: false,
resize: false,
plugins: 'lists link table media codesample',
toolbar:
'undo redo | blocks | bold italic underline | link | numlist bullist outdent indent | alignleft aligncenter alignright alignjustify | table media | removeformat'
};
/** TinyMCE component reference */
@ViewChild('tinymceEditor', { static: false }) tinymceEditor: EditorComponent;
/** Template form. */
templateForm: UntypedFormGroup;
/** Create or Edit Template Data. */
templateData: any;
/** Template Mappers */
mappers: any[] = [];
/** Toggles Visibility of Advanced Options */
showAdvanceOptions = false;
/** mode */
mode: 'create' | 'edit';
/** Client Parameter Labels */
clientParameterLabels: string[] = clientParameterLabels;
/** Loan Parameter Labels */
loanParameterLabels: string[] = loanParameterLabels;
/** Repayment Parameter Labels */
repaymentParameterLabels: string[] = repaymentParameterLabels;
/**
* Retrieves the template data from `resolve`.
* @param {FormBuilder} formBuilder Form Builder.
* @param {ActivatedRoute} route Activated Route.
* @param {Router} router Router for navigation.
* @param {TemplateService} templateService Templates Service
*/
constructor(
private formBuilder: UntypedFormBuilder,
private route: ActivatedRoute,
private router: Router,
private templateService: TemplatesService
) {
this.route.data.subscribe((data: { templateData: any; mode: 'create' | 'edit' }) => {
this.templateData = data.templateData;
this.mode = data.mode;
if (this.mode === 'edit') {
this.mappers = this.templateData.template.mappers.map((mapper: any) => ({
mappersorder: mapper.mapperorder,
mapperskey: new UntypedFormControl(mapper.mapperkey),
mappersvalue: new UntypedFormControl(mapper.mappervalue)
}));
}
});
}
ngOnInit() {
this.createTemplateForm();
this.buildDependencies();
}
/**
* Creates the template form.
*/
createTemplateForm() {
if (this.mode === 'create') {
this.templateForm = this.formBuilder.group({
entity: [
'',
Validators.required
],
type: [
'',
Validators.required
],
name: [
'',
Validators.required
],
text: [
'',
Validators.required
]
});
} else {
this.templateForm = this.formBuilder.group({
entity: [
this.templateData.entities.find((entity: any) => entity.name === this.templateData.template.entity).id,
Validators.required
],
type: [
this.templateData.types.find((type: any) => type.name === this.templateData.template.type).id,
Validators.required
],
name: [
this.templateData.template.name,
Validators.required
],
text: [
this.templateData.template.text,
Validators.required
]
});
}
}
/**
* Subscribe to value changes of entity to set default mapper.
*/
buildDependencies() {
const tenantIdentifier = 'default'; // update once global settings are setup.
this.templateForm.get('entity').valueChanges.subscribe((value: any) => {
if (value === 0) {
// client
this.mappers.splice(0, 1, {
mappersorder: 0,
mapperskey: new UntypedFormControl('client'),
mappersvalue: new UntypedFormControl('clients/{{clientId}}?tenantIdentifier=' + tenantIdentifier)
});
} else {
// loan
this.mappers.splice(0, 1, {
mappersorder: 0,
mapperskey: new UntypedFormControl('loan'),
mappersvalue: new UntypedFormControl('loans/{{loanId}}?associations=all&tenantIdentifier=' + tenantIdentifier)
});
}
this.setEditorContent('');
this.templateForm.get('text').setValue('');
});
if (this.mode === 'create') {
this.templateForm.get('entity').patchValue(0);
}
}
/**
* Adds a mapper.
*/
addMapper() {
this.mappers.push({
mappersorder: this.mappers.length,
mapperskey: new UntypedFormControl(''),
mappersvalue: new UntypedFormControl('')
});
}
/**
* Removes a mapper
* @param {any} index Mapper Index
*/
removeMapper(index: any) {
this.mappers.splice(index, 1);
}
/**
* Adds text to the editor at cursor position.
* @param {string} label Template parameter label.
*/
addText(label: string) {
this.tinymceEditor?.editor?.insertContent(label);
}
/**
* Gets the contents of the editor.
*/
getEditorContent() {
return this.tinymceEditor?.editor?.getContent({ format: 'html' }) || '';
}
/**
* Sets the contents of the editor.
* @param {string} content Editor Content
*/
setEditorContent(content: string) {
if (this.tinymceEditor?.editor) {
this.tinymceEditor.editor.setContent(content || '');
}
return '';
}
/**
* Submits a template.
*/
submit() {
const template: any = {
...this.templateForm.value,
mappers: this.mappers.map((mapper: any) => ({
mappersorder: mapper.mappersorder,
mapperskey: mapper.mapperskey.value,
mappersvalue: mapper.mappersvalue.value
})),
text: this.getEditorContent()
};
if (this.mode === 'create') {
this.templateService.createTemplate(template).subscribe((response: any) => {
this.router.navigate(
[
'../',
response.resourceId
],
{ relativeTo: this.route }
);
});
} else {
this.templateService.updateTemplate(template, this.templateData.template.id).subscribe(() => {
this.router.navigate(['../'], { relativeTo: this.route });
});
}
}
/**
* TrackBy function for mappers array to improve ngFor performance.
* @param {number} index Index of the item
* @returns {number} Index as unique identifier
*/
trackByMapperIndex(index: number): number {
return index;
}
/**
* TrackBy function for parameter labels to improve ngFor performance.
* @param {number} index Index of the item
* @param {string} label Label string
* @returns {string} Label as unique identifier
*/
trackByLabel(index: number, label: string): string {
return label;
}
/**
* TrackBy function for entities dropdown to improve ngFor performance.
* @param {number} index Index of the item
* @param {any} entity Entity object
* @returns {number} Entity ID as unique identifier
*/
trackByEntityId(index: number, entity: any): number {
return entity.id;
}
/**
* TrackBy function for types dropdown to improve ngFor performance.
* @param {number} index Index of the item
* @param {any} type Type object
* @returns {number} Type ID as unique identifier
*/
trackByTypeId(index: number, type: any): number {
return type.id;
}
}