feat: add edit template component (#1183)

Fixes: #702
Co-authored-by: Benura Abeywardena <benuraab@gmail.com>
This commit is contained in:
Karan Takalkar
2020-08-08 14:22:03 +05:30
committed by GitHub
co-authored by Benura Abeywardena
parent 9959f1ba04
commit a4adc32cb1
14 changed files with 479 additions and 10 deletions
+2
View File
@@ -50,6 +50,7 @@ import {
faLock,
faLockOpen,
faMinusCircle,
faMinus,
faMoneyBillAlt,
faMoneyCheck,
faPencilAlt,
@@ -142,6 +143,7 @@ library.add(
faLocationArrow,
faLock,
faLockOpen,
faMinus,
faMinusCircle,
faMoneyBillAlt,
faMoneyCheck,
@@ -0,0 +1,31 @@
/** Angular Imports */
import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot } from '@angular/router';
/** rxjs Imports */
import { Observable } from 'rxjs';
/** Custom Services */
import { TemplatesService } from '../templates.service';
/**
* Edit Template data resolver.
*/
@Injectable()
export class EditTemplateResolver implements Resolve<Object> {
/**
* @param {TemplatesService} templatesService Templates service.
*/
constructor(private templatesService: TemplatesService) {}
/**
* Returns the template data.
* @returns {Observable<any>}
*/
resolve(route: ActivatedRouteSnapshot): Observable<any> {
const templateId = route.paramMap.get('id');
return this.templatesService.getEditTemplateData(templateId);
}
}
@@ -6,7 +6,7 @@ import { Resolve, ActivatedRouteSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
/** Custom Services */
import { TemplatesService } from './templates.service';
import { TemplatesService } from '../templates.service';
/**
* Template data resolver.
@@ -6,7 +6,7 @@ import { Resolve } from '@angular/router';
import { Observable } from 'rxjs';
/** Custom Services */
import { TemplatesService } from './templates.service';
import { TemplatesService } from '../templates.service';
/**
* Templates data resolver.
@@ -0,0 +1,143 @@
<div class="container">
<mat-card fxLayout="column" fxLayoutGap="2%">
<form [formGroup]="templateForm">
<mat-card-content>
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
<mat-form-field fxFlex="48%">
<mat-label>Entity</mat-label>
<mat-select formControlName="entity">
<mat-option *ngFor="let entity of editTemplateData.entities" [value]="entity.id">
{{ entity.name }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Type</mat-label>
<mat-select formControlName="type">
<mat-option *ngFor="let type of editTemplateData.types" [value]="type.id">
{{ type.name }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Name</mat-label>
<input matInput formControlName="name">
</mat-form-field>
</div>
</mat-card-content>
</form>
<div role="toggle" fxFlex="13%">
<button mat-raised-button color="primary" (click)="showAdvanceOptions = !showAdvanceOptions">Advance Options</button>
</div>
<ng-container role="mappers" *ngIf="showAdvanceOptions">
<div *ngFor="let mapper of mappers; index as i" fxLayout="row" fxLayoutGap="2%">
<mat-form-field fxFlex="43%">
<mat-label>Mapper Key</mat-label>
<input matInput [formControl]="mapper.mapperskey">
</mat-form-field>
<mat-form-field fxFlex="43%">
<mat-label>Mapper Value</mat-label>
<input matInput [formControl]="mapper.mappersvalue">
</mat-form-field>
<button *ngIf="i === 0" class="mapper-button" fxFlex="8%" mat-raised-button color="primary" (click)="addMapper()">
<fa-icon icon="plus"></fa-icon>&nbsp;&nbsp;Add
</button>
<button *ngIf="!(i === 0)" class="mapper-button" fxFlex="9%" mat-raised-button color="primary" (click)="removeMapper(i)">
<fa-icon icon="minus"></fa-icon>&nbsp;&nbsp;Remove
</button>
</div>
</ng-container>
<div role="editor">
<ckeditor #ckEditor [editor]="Editor" [data]="editTemplateData.template.text"></ckeditor>
</div>
<div role="template-parameters">
<mat-accordion>
<mat-expansion-panel *ngIf="templateForm.controls.entity.value === 0">
<mat-expansion-panel-header>
<mat-panel-title>
Client Parameters
</mat-panel-title>
</mat-expansion-panel-header>
<div fxLayout="row wrap" fxLayout.xs="column" fxLayoutAlign="center">
<button mat-stroked-button color="primary" class="parameter"
*ngFor="let label of clientParameterLabels" (click)="addText(label)">
{{ label }}
</button>
</div>
</mat-expansion-panel>
<mat-expansion-panel *ngIf="templateForm.controls.entity.value === 1">
<mat-expansion-panel-header>
<mat-panel-title>
Loan Parameters
</mat-panel-title>
</mat-expansion-panel-header>
<div fxLayout="row wrap" fxLayout.xs="column" fxLayoutAlign="center">
<button mat-stroked-button color="primary" class="parameter"
*ngFor="let label of loanParameterLabels" (click)="addText(label)">
{{ label }}
</button>
</div>
</mat-expansion-panel>
<mat-expansion-panel *ngIf="templateForm.controls.entity.value === 1">
<mat-expansion-panel-header>
<mat-panel-title>
Repayment Schedule Parameters
</mat-panel-title>
</mat-expansion-panel-header>
<div fxLayout="row wrap" fxLayout.xs="column" fxLayoutAlign="center">
<button mat-stroked-button color="primary" class="parameter"
*ngFor="let label of repaymentParameterLabels" (click)="addText(label)">
{{ label }}
</button>
</div>
</mat-expansion-panel>
</mat-accordion>
</div>
<mat-card-actions class="m-b-20" fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="5px">
<button type="button" mat-raised-button [routerLink]="['../']">Cancel</button>
<button mat-raised-button color="primary" (click)="submit()">Submit</button>
</mat-card-actions>
</mat-card>
</div>
@@ -0,0 +1,12 @@
.container {
.mapper-button {
max-height: 2.25rem;
margin-top: 1rem;
}
.parameter {
margin: 1%;
}
}
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { EditTemplateComponent } from './edit-template.component';
describe('EditTemplateComponent', () => {
let component: EditTemplateComponent;
let fixture: ComponentFixture<EditTemplateComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ EditTemplateComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(EditTemplateComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,176 @@
/** Angular Imports */
import { Component, OnInit, ViewChild } from '@angular/core';
import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router';
import { DatePipe } from '@angular/common';
/** CKEditor5 Imports */
import * as ClassicEditor from '@ckeditor/ckeditor5-build-classic';
/** Custom Imports */
import { clientParameterLabels, loanParameterLabels, repaymentParameterLabels } from '../template-parameter-labels';
/** Custom Services */
import { TemplatesService } from '../templates.service';
/**
* Edit Template Component.
*/
@Component({
selector: 'mifosx-edit-template',
templateUrl: './edit-template.component.html',
styleUrls: ['./edit-template.component.scss']
})
export class EditTemplateComponent implements OnInit {
/** CKEditor5 */
public Editor = ClassicEditor;
/** CKEditor5 Template Reference */
@ViewChild('ckEditor') ckEditor: any;
/** Template form. */
templateForm: FormGroup;
/** Edit Template Data. */
editTemplateData: any;
/** Template Mappers */
mappers: any[] = [];
/** Toggles Visibility of Advance Options */
showAdvanceOptions = false;
/** 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 {DatePipe} datePipe Date Pipe to format date.
*/
constructor(private formBuilder: FormBuilder,
private route: ActivatedRoute,
private router: Router,
private templateService: TemplatesService) {
this.route.data.subscribe((data: { editTemplateData: any }) => {
this.editTemplateData = data.editTemplateData;
this.mappers = this.editTemplateData.template.mappers
.map((mapper: any) => ({
mappersorder: mapper.mapperorder,
mapperskey: new FormControl(mapper.mapperkey),
mappersvalue: new FormControl(mapper.mappervalue)
}));
});
}
ngOnInit() {
this.createTemplateForm();
this.buildDependencies();
}
/**
* Creates the template form.
*/
createTemplateForm() {
this.templateForm = this.formBuilder.group({
'entity': [this.editTemplateData.entities.find((entity: any) => entity.name === this.editTemplateData.template.entity).id],
'type': [this.editTemplateData.types.find((type: any) => type.name === this.editTemplateData.template.type).id],
'name': [this.editTemplateData.template.name]
});
}
/**
* 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 FormControl('client'),
mappersvalue: new FormControl('clients/{{clientId}}?tenantIdentifier=' + tenantIdentifier)
});
} else { // loan
this.mappers.splice(0, 1, {
mappersorder: 0,
mapperskey: new FormControl('loan'),
mappersvalue: new FormControl('loans/{{loanId}}?associations=all&tenantIdentifier=' + tenantIdentifier )
});
}
this.setEditorContent('');
});
}
/**
* Adds a mapper.
*/
addMapper() {
this.mappers.push({
mappersorder: this.mappers.length,
mapperskey: new FormControl(''),
mappersvalue: new FormControl('')
});
}
/**
* Removes a mapper
* @param {any} index Mapper Index
*/
removeMapper(index: any) {
this.mappers.splice(index, 1);
}
/**
* Adds text to CKEditor at cursor position.
* @param {string} label Template parameter label.
*/
addText(label: string) {
if (this.ckEditor && this.ckEditor.editorInstance) {
this.ckEditor.editorInstance.model.change((writer: any) => {
const insertPosition = this.ckEditor.editorInstance.model.document.selection.getFirstPosition();
writer.insertText(label, insertPosition);
} );
}
}
/**
* Gets the contents of CKEditor.
*/
getEditorContent() {
if (this.ckEditor && this.ckEditor.editorInstance) {
return this.ckEditor.editorInstance.getData();
}
return '';
}
/**
* Sets the contents of CKEditor.
* @param {string} content Editor Content
*/
setEditorContent(content: string) {
if (this.ckEditor && this.ckEditor.editorInstance) {
return this.ckEditor.editorInstance.setData(content);
}
return '';
}
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()
};
this.templateService.updateTemplate(template, this.editTemplateData.template.id).subscribe(() => {
this.router.navigate(['../'], { relativeTo: this.route });
});
}
}
@@ -0,0 +1,34 @@
export const clientParameterLabels = [
'{{client.accountNo}}',
'{{client.status.value}}',
'{{client.fullname}}',
'{{client.displayName}}',
'{{client.officeName}}',
'{{#client.groups}}',
'{{/client.groups}}'
];
export const loanParameterLabels = [
'{{loan.accountNo}}',
'{{loan.status.value}}',
'{{loan.loanProductId}}',
'{{loan.loanProductName}}',
'{{loan.loanProductDescription}}'
];
export const repaymentParameterLabels = [
'{{loan.repaymentSchedule.loanTermInDays}}',
'{{loan.repaymentSchedule.totalPrincipalDisbursed}}',
'{{loan.repaymentSchedule.totalPrincipalExpected}}',
'{{loan.repaymentSchedule.totalPrincipalPaid}}',
'{{loan.repaymentSchedule.totalInterestCharged}}',
'{{loan.repaymentSchedule.totalFeeChargesCharged}}',
'{{loan.repaymentSchedule.totalPenaltyChargesCharged}}',
'{{loan.repaymentSchedule.totalWaived}}',
'{{loan.repaymentSchedule.totalWrittenOff}}',
'{{loan.repaymentSchedule.totalRepaymentExpected}}',
'{{loan.repaymentSchedule.totalRepayment}}',
'{{loan.repaymentSchedule.totalPaidInAdvance}}',
'{{loan.repaymentSchedule.totalPaidLate}}',
'{{loan.repaymentSchedule.totalOutstanding}}'
];
+18 -7
View File
@@ -11,10 +11,12 @@ import { extract } from '../core/i18n/i18n.service';
/** Custom Components */
import { TemplatesComponent } from './templates.component';
import { ViewTemplateComponent } from './view-template/view-template.component';
import { EditTemplateComponent } from './edit-template/edit-template.component';
/** Custom Resolvers */
import { TemplatesResolver } from './templates.resolver';
import { TemplateResolver } from './template.resolver';
import { TemplatesResolver } from './common-resolvers/templates.resolver';
import { TemplateResolver } from './common-resolvers/template.resolver';
import { EditTemplateResolver } from './common-resolvers/edit-template.resolver';
/** Templates Routes */
const routes: Routes = [
@@ -32,14 +34,22 @@ const routes: Routes = [
},
{
path: ':id',
data: { title: extract('View Template'), routeResolveBreadcrumb: ['template', 'name'] },
resolve: {
template: TemplateResolver
},
data: { title: extract('View Template'), routeParamBreadcrumb: 'id' },
children: [
{
path: '',
component: ViewTemplateComponent,
resolve: {
template: TemplateResolver
}
},
{
path: 'edit',
component: EditTemplateComponent,
data: { title: extract('Edit Template'), breadcrumb: 'Edit', routeParamBreadcrumb: false },
resolve: {
editTemplateData: EditTemplateResolver
}
}
]
}
@@ -58,7 +68,8 @@ const routes: Routes = [
exports: [RouterModule],
providers: [
TemplatesResolver,
TemplateResolver
TemplateResolver,
EditTemplateResolver
]
})
export class TemplatesRoutingModule { }
+4
View File
@@ -2,12 +2,14 @@
import { NgModule } from '@angular/core';
/** Custom Modules */
import { CKEditorModule } from '@ckeditor/ckeditor5-angular';
import { SharedModule } from '../shared/shared.module';
import { TemplatesRoutingModule } from './templates-routing.module';
/** Custom Components */
import { TemplatesComponent } from './templates.component';
import { ViewTemplateComponent } from './view-template/view-template.component';
import { EditTemplateComponent } from './edit-template/edit-template.component';
/**
* Templates Module
@@ -16,12 +18,14 @@ import { ViewTemplateComponent } from './view-template/view-template.component';
*/
@NgModule({
imports: [
CKEditorModule,
SharedModule,
TemplatesRoutingModule
],
declarations: [
TemplatesComponent,
ViewTemplateComponent,
EditTemplateComponent,
]
})
export class TemplatesModule { }
+17
View File
@@ -33,6 +33,23 @@ export class TemplatesService {
return this.http.get(`/templates/${templateId}`);
}
/**
* @param templateId Template Id.
* @returns {Observable<any>}.
*/
getEditTemplateData(templateId: string): Observable<any> {
return this.http.get(`/templates/${templateId}/template`);
}
/**
* @param templateData Templete Data to be edited.
* @param templateId Template Id.
* @returns {Observable<any>}.
*/
updateTemplate(templateData: any, templateId: any): Observable<any> {
return this.http.put(`/templates/${templateId}`, templateData);
}
/**
* @param templateId Template ID.
* @returns {Observable<any>}
@@ -1,6 +1,6 @@
<div class="container m-b-20" fxLayout="row" fxLayoutAlign="end" fxLayoutGap="20px">
<button mat-raised-button color="primary">
<button mat-raised-button color="primary" [routerLink]="['edit']">
<fa-icon icon="edit"></fa-icon>&nbsp;&nbsp;
Edit
</button>
@@ -20,6 +20,14 @@
<div fxLayout="row wrap" class="content">
<div fxFlex="50%" class="mat-body-strong">
Name
</div>
<div fxFlex="50%">
{{ templateData.name }}
</div>
<div fxFlex="50%" class="mat-body-strong">
Entity
</div>
+6
View File
@@ -12,3 +12,9 @@ interface NodeModule {
}
declare module 'chart.js';
declare module '@ckeditor/ckeditor5-build-classic' {
const ClassicEditorBuild: any;
export = ClassicEditorBuild;
}