feat: add create client page (#1153)

Fixes: #215
This commit is contained in:
Karan Takalkar
2020-07-24 16:32:58 +05:30
committed by GitHub
parent cb62fefbfa
commit 9c15545cd4
28 changed files with 1713 additions and 6 deletions
@@ -0,0 +1,69 @@
<div class="mat-typography">
<h3>Address</h3>
<div fxLayout="row" fxLayoutAlign="flex-end" class="m-b-20">
<button mat-raised-button color="primary" (click)="addAddress()">
<fa-icon icon="plus"></fa-icon>&nbsp;&nbsp;Add
</button>
</div>
<mat-accordion>
<mat-expansion-panel *ngFor="let address of clientAddressData; index as i" class="address">
<mat-expansion-panel-header>
<mat-panel-title>
{{ getSelectedValue('addressTypeIdOptions',address.addressTypeId)?.name }}
</mat-panel-title>
<mat-panel-description>
{{ address.relationship }}
</mat-panel-description>
</mat-expansion-panel-header>
<mat-divider></mat-divider>
<div class="address-actions" fxLayout="row" fxLayoutAlign="flex-end center">
<button mat-button color="primary" (click)="editAddress(address,i)">
<fa-icon icon="edit"></fa-icon>
</button>
<button mat-button color="warn" (click)="deleteAddress(address,i)">
<fa-icon icon="trash"></fa-icon>
</button>
<mat-slide-toggle [checked]="address.isActive" (change)="toggleAddress(address)"></mat-slide-toggle>
</div>
<p>
<span *ngIf="isFieldEnabled('street')">Street : {{address.street}}<br /></span>
<span *ngIf="isFieldEnabled('addressLine1')">Address Line 1 : {{address.addressLine1}}<br /></span>
<span *ngIf="isFieldEnabled('addressLine2')">Address Line 2 : {{address.addressLine2}}<br /></span>
<span *ngIf="isFieldEnabled('addressLine3')">Address Line 3 : {{address.addressLine3}}<br /></span>
<span *ngIf="isFieldEnabled('townVillage')">Town / Village : {{address.townVillage}}<br /></span>
<span *ngIf="isFieldEnabled('city')">City : {{address.city}}<br /></span>
<span *ngIf="isFieldEnabled('stateProvinceId')">State / Province :
{{getSelectedValue('stateProvinceIdOptions',address.stateProvinceId)?.name}}<br /></span>
<span *ngIf="isFieldEnabled('countryId')">Country :
{{getSelectedValue('countryIdOptions',address.countryId)?.name}}<br /></span>
<span *ngIf="isFieldEnabled('postalCode')">Postal Code : {{address.postalCode}}<br /></span>
<span *ngIf="isFieldEnabled('isActive')">Active Status : {{address.isActive}}<br /></span>
</p>
</mat-expansion-panel>
</mat-accordion>
<div fxLayout="row" class="margin-t" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
<button mat-raised-button matStepperPrevious>
<fa-icon icon="arrow-left"></fa-icon>&nbsp;&nbsp;
Previous
</button>
<button mat-raised-button matStepperNext>
Next&nbsp;&nbsp;
<fa-icon icon="arrow-right"></fa-icon>
</button>
</div>
</div>
@@ -0,0 +1,19 @@
.address {
.address-actions{
margin-top: 1%;
button{
margin-right: 1%;
}
}
h3 {
margin: 1% auto;
}
p {
line-height: 30px;
margin-left: 2%;
}
}
.margin-t {
margin-top: 2em;
}
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ClientAddressStepComponent } from './client-address-step.component';
describe('ClientAddressStepComponent', () => {
let component: ClientAddressStepComponent;
let fixture: ComponentFixture<ClientAddressStepComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ClientAddressStepComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ClientAddressStepComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,222 @@
/** Angular Imports */
import { Component, Input } from '@angular/core';
import { MatDialog } from '@angular/material';
/** Custom Models */
import { FormfieldBase } from 'app/shared/form-dialog/formfield/model/formfield-base';
import { InputBase } from 'app/shared/form-dialog/formfield/model/input-base';
import { SelectBase } from 'app/shared/form-dialog/formfield/model/select-base';
/** Custom Dialogs */
import { FormDialogComponent } from 'app/shared/form-dialog/form-dialog.component';
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component';
/**
* Client Address Step Component
*/
@Component({
selector: 'mifosx-client-address-step',
templateUrl: './client-address-step.component.html',
styleUrls: ['./client-address-step.component.scss']
})
export class ClientAddressStepComponent {
/** Client Address Field Config */
@Input() clientAddressFieldConfig: any;
/** Client Template */
@Input() clientTemplate: any;
/** Client Address Data */
clientAddressData: any[] = [];
/**
* @param {MatDialog} dialog Mat Dialog
*/
constructor(private dialog: MatDialog) { }
/**
* Adds a client address
*/
addAddress() {
const data = {
title: 'Add Client Address',
formfields: this.getAddressFormFields()
};
const addAddressDialogRef = this.dialog.open(FormDialogComponent, { data });
addAddressDialogRef.afterClosed().subscribe((response: any) => {
if (response.data) {
const addressData = response.data.value;
addressData.isActive = false;
for (const key in addressData) {
if (addressData[key] === '' || addressData[key] === undefined) {
delete addressData[key];
}
}
this.clientAddressData.push(addressData);
}
});
}
/**
* Edit Address
* @param {any} address Address
* @param {number} index Address index
*/
editAddress(address: any, index: number) {
const data = {
title: 'Edit Client Address',
formfields: this.getAddressFormFields(address),
layout: { addButtonText: 'Edit' }
};
const editAddressDialogRef = this.dialog.open(FormDialogComponent, { data });
editAddressDialogRef.afterClosed().subscribe((response: any) => {
if (response.data) {
const addressData = response.data.value;
addressData.isActive = address.isActive;
for (const key in addressData) {
if (addressData[key] === '' || addressData[key] === undefined) {
delete addressData[key];
}
}
this.clientAddressData[index] = addressData;
}
});
}
/**
* @param {any} address Client Address
* @param {number} index Address index
*/
deleteAddress(address: any, index: number) {
const deleteAddressDialogRef = this.dialog.open(DeleteDialogComponent, {
data: { deleteContext: `address type : ${address.addressType} ${index}` }
});
deleteAddressDialogRef.afterClosed().subscribe((response: any) => {
if (response.delete) {
this.clientAddressData.splice(index, 1);
}
});
}
/**
* Toggles address activity
* @param {any} address Address
*/
toggleAddress(address: any) {
address.isActive = address.isActive ? false : true;
}
/**
* Checks if field is enabled in address configuration
* @param {any} address Address
*/
isFieldEnabled(fieldName: any) {
return (this.clientAddressFieldConfig.find((fieldObj: any) => fieldObj.field === fieldName)).is_enabled;
}
/**
* Retrieves field Id from name.
* Find pipe doesn't work with accordian.
* @param {any} address Address
*/
getSelectedValue(fieldName: any, fieldId: any) {
return (this.clientTemplate.address[fieldName].find((fieldObj: any) => fieldObj.id === fieldId));
}
/**
* Gets formfields for form dialog.
* @param {any} address Address
*/
getAddressFormFields(address?: any) {
let formfields: FormfieldBase[] = [];
formfields.push(this.isFieldEnabled('addressType') ? new SelectBase({
controlName: 'addressTypeId',
label: 'Address Type',
value: address ? address.addressTypeId : '',
options: { label: 'name', value: 'id', data: this.clientTemplate.address.addressTypeIdOptions },
order: 1,
required: true
}) : null);
formfields.push(this.isFieldEnabled('street') ? new InputBase({
controlName: 'street',
label: 'Street',
value: address ? address.street : '',
type: 'text',
required: true,
order: 2
}) : null);
formfields.push(this.isFieldEnabled('addressLine1') ? new InputBase({
controlName: 'addressLine1',
label: 'Address Line 1',
value: address ? address.addressLine1 : '',
type: 'text',
order: 3
}) : null);
formfields.push(this.isFieldEnabled('addressLine2') ? new InputBase({
controlName: 'addressLine2',
label: 'Address Line 2',
value: address ? address.addressLine2 : '',
type: 'text',
order: 4
}) : null);
formfields.push(this.isFieldEnabled('addressLine3') ? new InputBase({
controlName: 'addressLine3',
label: 'Address Line 3',
value: address ? address.addressLine3 : '',
type: 'text',
order: 5
}) : null);
formfields.push(this.isFieldEnabled('townVillage') ? new InputBase({
controlName: 'townVillage',
label: 'Town / Village',
value: address ? address.townVillage : '',
type: 'text',
order: 6
}) : null);
formfields.push(this.isFieldEnabled('city') ? new InputBase({
controlName: 'city',
label: 'City',
value: address ? address.city : '',
type: 'text',
order: 7
}) : null);
formfields.push(this.isFieldEnabled('stateProvinceId') ? new SelectBase({
controlName: 'stateProvinceId',
label: 'State / Province',
value: address ? address.stateProvinceId : '',
options: { label: 'name', value: 'id', data: this.clientTemplate.address.stateProvinceIdOptions },
order: 8
}) : null);
formfields.push(this.isFieldEnabled('countyDistrict') ? new InputBase({
controlName: 'countryDistrict',
label: 'Country District',
value: address ? address.countyDistrict : '',
type: 'text',
order: 11
}) : null);
formfields.push(this.isFieldEnabled('countryId') ? new SelectBase({
controlName: 'countryId',
label: 'Country',
value: address ? address.countryId : '',
options: { label: 'name', value: 'id', data: this.clientTemplate.address.countryIdOptions },
order: 10
}) : null);
formfields.push(this.isFieldEnabled('postalCode') ? new InputBase({
controlName: 'postalCode',
label: 'Postal Code',
value: address ? address.postalCode : '',
type: 'text',
order: 11
}) : null);
formfields = formfields.filter(field => field !== null);
return formfields;
}
/**
* Returns the array of client addresses
*/
get address() {
return { address: this.clientAddressData };
}
}
@@ -0,0 +1,104 @@
<h1 mat-dialog-title>{{ data.context }} Family Member</h1>
<form [formGroup]="familyMemberForm">
<div fxLayout="row wrap" fxLayout.lt-md="column" fxLayoutAlign="start center" fxLayoutGap="2%">
<mat-form-field fxFlex="48%">
<mat-label>First Name</mat-label>
<input formControlName="firstName" required matInput />
<mat-error *ngIf="familyMemberForm.controls.firstName.hasError('required')">
First Name is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Middle Name</mat-label>
<input formControlName="middleName" matInput />
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Last Name</mat-label>
<input formControlName="lastName" required matInput />
<mat-error *ngIf="familyMemberForm.controls.lastName.hasError('required')">
Last Name is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Qualification</mat-label>
<input formControlName="qualification" matInput />
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Age</mat-label>
<input type="number" formControlName="age" matInput />
<mat-error *ngIf="familyMemberForm.controls.age.hasError('required')">
Age is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-checkbox formControlName="isDependent" labelPosition="before" fxFlex="48%">Is Dependent?</mat-checkbox>
<mat-form-field fxFlex="48%">
<mat-label>Relationship</mat-label>
<mat-select formControlName="relationshipId" required>
<mat-option *ngFor="let relation of data.options.relationshipIdOptions" [value]="relation.id">
{{ relation.name }}
</mat-option>
</mat-select>
<mat-error *ngIf="familyMemberForm.controls.relationshipId.hasError('required')">
Relationship is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Gender</mat-label>
<mat-select formControlName="genderId">
<mat-option *ngFor="let gender of data.options.genderIdOptions" [value]="gender.id">
{{ gender.name }}
</mat-option>
</mat-select>
<mat-error *ngIf="familyMemberForm.controls.genderId.hasError('required')">
Gender is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Profession</mat-label>
<mat-select formControlName="professionId">
<mat-option *ngFor="let profession of data.options.professionIdOptions" [value]="profession.id">
{{ profession.name }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Marital Status</mat-label>
<mat-select formControlName="maritalStatusId">
<mat-option *ngFor="let maritalStatus of data.options.maritalStatusIdOptions"
[value]="maritalStatus.id">
{{ maritalStatus.name }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex="98%">
<mat-label>Date Of Birth</mat-label>
<input formControlName="dateOfBirth" matInput [min]="minDate" [max]="maxDate" [matDatepicker]="dueDatePicker">
<mat-datepicker-toggle matSuffix [for]="dueDatePicker"></mat-datepicker-toggle>
<mat-datepicker #dueDatePicker></mat-datepicker>
<mat-error *ngIf="familyMemberForm.controls.dateOfBirth.hasError('required')">
Date of birth is <strong>required</strong>
</mat-error>
</mat-form-field>
</div>
<mat-dialog-actions align="center">
<button mat-raised-button mat-dialog-close>Cancel</button>
<button mat-raised-button color="primary" [disabled]="!familyMemberForm.valid" [mat-dialog-close]="{ member: familyMember }">Confirm</button>
</mat-dialog-actions>
</form>
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ClientFamilyMemberDialogComponent } from './client-family-member-dialog.component';
describe('ClientFamilyMemberDialogComponent', () => {
let component: ClientFamilyMemberDialogComponent;
let fixture: ComponentFixture<ClientFamilyMemberDialogComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ClientFamilyMemberDialogComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ClientFamilyMemberDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,97 @@
/** Angular Imports */
import { Component, OnInit, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { DatePipe } from '@angular/common';
/**
* Client Family Members Dialog
*/
@Component({
selector: 'mifosx-client-family-member-dialog',
templateUrl: './client-family-member-dialog.component.html',
styleUrls: ['./client-family-member-dialog.component.scss']
})
export class ClientFamilyMemberDialogComponent implements OnInit {
/** Minimum Due Date allowed. */
minDate = new Date(2000, 0, 1);
/** Maximum Due Date allowed. */
maxDate = new Date();
/** Add/Edit family member form. */
familyMemberForm: FormGroup;
/**
* @param {MatDialogRef} dialogRef Client Family Member Dialog Reference
* @param {FormBuilder} formBuilder Form Builder
* @param {DatePipe} datePipe Date Pipe
* @param {any} data Dialog Data
*/
constructor(public dialogRef: MatDialogRef<ClientFamilyMemberDialogComponent>,
private formBuilder: FormBuilder,
private datePipe: DatePipe,
@Inject(MAT_DIALOG_DATA) public data: any) { }
ngOnInit() {
this.createFamilyMemberForm();
if (this.data.context === 'Edit') {
this.familyMemberForm.patchValue({
'firstName': this.data.member.firstName,
'middleName': this.data.member.middleName,
'lastName': this.data.member.lastName,
'qualification': this.data.member.qualification,
'age': this.data.member.age,
'isDependent': this.data.member.isDependent,
'relationshipId': this.data.member.relationshipId,
'genderId': this.data.member.genderId,
'professionId': this.data.member.professionId,
'maritalStatusId': this.data.member.maritalStatusId,
'dateOfBirth': this.data.member.dateOfBirth && new Date(this.data.member.dateOfBirth)
});
}
}
/**
* Creates Family Member Form
*/
createFamilyMemberForm() {
this.familyMemberForm = this.formBuilder.group({
'firstName': ['', Validators.required],
'middleName': [''],
'lastName': ['', Validators.required],
'qualification': [''],
'age': [''],
'isDependent': [''],
'relationshipId': ['', Validators.required],
'genderId': [''],
'professionId': [''],
'maritalStatusId': [''],
'dateOfBirth': ['']
});
}
/**
* Returns Formatted Family Member
*/
get familyMember() {
const locale = 'en';
const dateFormat = 'dd MMMM yyyy';
// TODO: Update once language and date settings are setup
const familyMember = {
...this.familyMemberForm.value,
dateFormat,
locale
};
for (const key in familyMember) {
if (familyMember[key] === '' || familyMember[key] === undefined) {
delete familyMember[key];
}
}
if (familyMember.dateOfBirth) {
familyMember.dateOfBirth = this.datePipe.transform(familyMember.dateOfBirth, dateFormat);
}
return familyMember;
}
}
@@ -0,0 +1,67 @@
<div class="mat-typography">
<h3>Family Members</h3>
<div fxLayout="row" fxLayoutAlign="flex-end" class="m-b-20">
<button mat-raised-button color="primary" (click)="addFamilyMember()">
<fa-icon icon="plus"></fa-icon>&nbsp;&nbsp;Add
</button>
</div>
<mat-accordion>
<mat-expansion-panel *ngFor="let member of clientFamilyMembers;index as i" class="family-member">
<mat-expansion-panel-header>
<mat-panel-title>
{{ member.firstName }} {{ member.middleName ? member.middleName + ' ' + member.lastName : member.lastName }}
</mat-panel-title>
<mat-panel-description>
{{member.relationship}}
</mat-panel-description>
</mat-expansion-panel-header>
<mat-divider></mat-divider>
<div class="family-member-actions" fxLayout="row" fxLayoutAlign="flex-end">
<button mat-button color="primary">
<fa-icon icon="edit" (click)="editFamilyMember(member, i)"></fa-icon>
</button>
<button mat-button color="warn" (click)="deleteFamilyMember(member.firstName,i)">
<fa-icon icon="trash"></fa-icon>
</button>
</div>
<p>
First Name : {{ member.firstName }}<br />
Middle Name : {{ member.middleName }}<br />
Last Name : {{ member.lastName }}<br />
Qualification : {{ member.qualification }}<br />
Relationship : {{ member.relationshipId | find:clientTemplate.familyMemberOptions.relationshipIdOptions:'id':'name' }}<br />
Age : {{ member.age }}<br />
Is Dependent : {{ member.isDependent ? 'Yes' : 'No' }}<br />
Marital Status : {{ member.maritalStatusId | find:clientTemplate.familyMemberOptions.maritalStatusIdOptions:'id':'name' }}<br />
Gender : {{ member.genderId | find:clientTemplate.familyMemberOptions.genderIdOptions:'id':'name' }}<br />
Profession : {{member.professionId | find:clientTemplate.familyMemberOptions.professionIdOptions:'id':'name' }}<br />
Date Of Birth : {{member.dateOfBirth | date}}<br />
</p>
</mat-expansion-panel>
</mat-accordion>
<div fxLayout="row" class="margin-t" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
<button mat-raised-button matStepperPrevious>
<fa-icon icon="arrow-left"></fa-icon>&nbsp;&nbsp;
Previous
</button>
<button mat-raised-button matStepperNext>
Next&nbsp;&nbsp;
<fa-icon icon="arrow-right"></fa-icon>
</button>
</div>
</div>
@@ -0,0 +1,19 @@
.family-member {
.family-member-actions{
margin-top: 1%;
button{
margin-right: 1%;
}
}
h3 {
margin: 1% auto;
}
p {
line-height: 30px;
margin-left: 2%;
}
}
.margin-t {
margin-top: 2em;
}
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ClientFamilyMembersStepComponent } from './client-family-members-step.component';
describe('ClientFamilyMembersStepComponent', () => {
let component: ClientFamilyMembersStepComponent;
let fixture: ComponentFixture<ClientFamilyMembersStepComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ClientFamilyMembersStepComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ClientFamilyMembersStepComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,90 @@
/** Angular Imports */
import { Component, OnInit, Input } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { MatDialog } from '@angular/material';
/** Custom Components */
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component';
import { ClientFamilyMemberDialogComponent } from './client-family-member-dialog/client-family-member-dialog.component';
/**
* Client Family Members Step
*/
@Component({
selector: 'mifosx-client-family-members-step',
templateUrl: './client-family-members-step.component.html',
styleUrls: ['./client-family-members-step.component.scss']
})
export class ClientFamilyMembersStepComponent {
/** Cient Template */
@Input() clientTemplate: any;
/** Client Family Members */
clientFamilyMembers: any[] = [];
/**
* @param {MatDialog} dialog Mat Dialog
*/
constructor(public dialog: MatDialog) { }
/**
* Adds a family member.
*/
addFamilyMember() {
const addFamilyMemberDialogRef = this.dialog.open(ClientFamilyMemberDialogComponent, {
data: {
context: 'Add',
options: this.clientTemplate.familyMemberOptions,
},
width: '50rem'
});
addFamilyMemberDialogRef.afterClosed().subscribe((response: any) => {
if (response.member) {
this.clientFamilyMembers.push(response.member);
}
});
}
/**
* Edits the family member.
* @param {any} member Family Member
* @param {any} index Tree Index
*/
editFamilyMember(member: any, index: any) {
const addFamilyMemberDialogRef = this.dialog.open(ClientFamilyMemberDialogComponent, {
data: {
context: 'Edit',
member: member,
options: this.clientTemplate.familyMemberOptions,
},
width: '50rem'
});
addFamilyMemberDialogRef.afterClosed().subscribe((response: any) => {
if (response.member) {
this.clientFamilyMembers.splice(index, 1, response.member);
}
});
}
/**
* Deletes the family member
*/
deleteFamilyMember(name: string, index: number) {
const deleteFamilyMemberDialogRef = this.dialog.open(DeleteDialogComponent, {
data: { deleteContext: `Family member name : ${name} ${index}` }
});
deleteFamilyMemberDialogRef.afterClosed().subscribe((response: any) => {
if (response.delete) {
this.clientFamilyMembers.splice(index, 1);
}
});
}
/**
* Returns the array of client family members.
*/
get familyMembers() {
return { familyMembers: this.clientFamilyMembers };
}
}
@@ -0,0 +1,215 @@
<form [formGroup]="createClientForm">
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
<mat-form-field fxFlex="48%">
<mat-label>Office</mat-label>
<mat-select required formControlName="officeId">
<mat-option *ngFor="let office of officeOptions" [value]="office.id">
{{ office.name }}
</mat-option>
</mat-select>
<mat-error *ngIf="createClientForm.controls.officeId.hasError('required')">
Office is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Staff</mat-label>
<mat-select formControlName="staffId">
<mat-option *ngFor="let staff of staffOptions" [value]="staff.id">
{{ staff.displayName }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Legal Form</mat-label>
<mat-select formControlName="legalFormId">
<mat-option *ngFor="let legalForm of legalFormOptions" [value]="legalForm.id">
{{ legalForm.value }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field *ngIf="createClientForm.contains('fullname')" fxFlex="48%">
<mat-label>Name</mat-label>
<input matInput required formControlName="fullname">
<mat-error *ngIf="createClientForm.controls.fullname.hasError('required')">
Client name is <strong>required</strong>
</mat-error>
<mat-error *ngIf="createClientForm.controls.fullname.hasError('pattern')">
Client name <strong>cannot</strong> begin with a special character or number
</mat-error>
</mat-form-field>
<mat-form-field *ngIf="createClientForm.contains('firstname')" fxFlex="48%">
<mat-label>First Name</mat-label>
<input matInput required formControlName="firstname">
<mat-error *ngIf="createClientForm.controls.firstname.hasError('required')">
Client first name is <strong>required</strong>
</mat-error>
<mat-error *ngIf="createClientForm.controls.firstname.hasError('pattern')">
Client first name <strong>cannot</strong> begin with a special character or number
</mat-error>
</mat-form-field>
<mat-form-field *ngIf="createClientForm.contains('middlename')" fxFlex="48%">
<mat-label>Middle Name</mat-label>
<input matInput formControlName="middlename">
<mat-error *ngIf="createClientForm.controls.middlename.hasError('pattern')">
Client middle name <strong>cannot</strong> begin with a special character or number
</mat-error>
</mat-form-field>
<mat-form-field *ngIf="createClientForm.contains('lastname')" fxFlex="48%">
<mat-label>Last Name</mat-label>
<input matInput required formControlName="lastname">
<mat-error *ngIf="createClientForm.controls.lastname.hasError('required')">
Client last name is <strong>required</strong>
</mat-error>
<mat-error *ngIf="createClientForm.controls.lastname.hasError('pattern')">
Client last name <strong>cannot</strong> begin with a special character or number
</mat-error>
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>{{ createClientForm.value.legalFormId === 1 ? 'Date of Birth' : 'Incorporation Date' }}</mat-label>
<input matInput [max]="maxDate" [matDatepicker]="dateOfBirthDatePicker" formControlName="dateOfBirth">
<mat-datepicker-toggle matSuffix [for]="dateOfBirthDatePicker"></mat-datepicker-toggle>
<mat-datepicker #dateOfBirthDatePicker></mat-datepicker>
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>External Id</mat-label>
<input matInput formControlName="externalId">
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Mobile No</mat-label>
<input matInput type="number" formControlName="mobileNo">
</mat-form-field>
<mat-form-field *ngIf="createClientForm.value.legalFormId === 1" fxFlex="48%">
<mat-label>Gender</mat-label>
<mat-select formControlName="genderId">
<mat-option *ngFor="let gender of genderOptions" [value]="gender.id">
{{ gender.name }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-checkbox *ngIf="createClientForm.value.legalFormId === 1" fxFlex="48%" labelPosition="before" formControlName="isStaff" class="margin-v">
Is staff?
</mat-checkbox>
<mat-form-field fxFlex="48%">
<mat-label>Client Type</mat-label>
<mat-select formControlName="clientTypeId">
<mat-option *ngFor="let clientType of clientTypeOptions" [value]="clientType.id">
{{ clientType.name }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Client Classification</mat-label>
<mat-select formControlName="clientClassificationId">
<mat-option *ngFor="let clientClassification of clientClassificationTypeOptions" [value]="clientClassification.id">
{{ clientClassification.name }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Submitted On</mat-label>
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="submittedOnDatePicker" formControlName="submittedOnDate">
<mat-datepicker-toggle matSuffix [for]="submittedOnDatePicker"></mat-datepicker-toggle>
<mat-datepicker #submittedOnDatePicker></mat-datepicker>
</mat-form-field>
<mat-checkbox fxFlex="48%" labelPosition="before" formControlName="active" class="margin-v">
Active?
</mat-checkbox>
<mat-form-field *ngIf="createClientForm.contains('activationDate')" fxFlex="48%">
<mat-label>Activation Date</mat-label>
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="activatedOnDatePicker" required
formControlName="activationDate">
<mat-datepicker-toggle matSuffix [for]="activatedOnDatePicker"></mat-datepicker-toggle>
<mat-datepicker #activatedOnDatePicker></mat-datepicker>
<mat-error *ngIf="createClientForm.controls.activationDate.hasError('required')">
Activation Date is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-checkbox fxFlex="48%" labelPosition="before" formControlName="addSavings" class="margin-v">
Open Savings Account?
</mat-checkbox>
<mat-form-field *ngIf="createClientForm.contains('savingsProductId')" fxFlex="48%">
<mat-label>Savings Product</mat-label>
<mat-select required formControlName="savingsProductId">
<mat-option *ngFor="let product of savingProductOptions" [value]="product.id">
{{ product.name }}
</mat-option>
</mat-select>
<mat-error *ngIf="createClientForm.controls.savingsProductId.hasError('required')">
Savings product is <strong>required</strong>
</mat-error>
</mat-form-field>
<div fxFlex="100%" fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column" formGroupName="clientNonPersonDetails"
*ngIf="createClientForm.contains('clientNonPersonDetails')">
<mat-form-field fxFlex="48%">
<mat-label>Incorporation Validity Till Date</mat-label>
<input matInput [min]="minDate" [max]="maxDate" [matDatepicker]="incorpValidityTillDateDatePicker" formControlName="incorpValidityTillDate">
<mat-datepicker-toggle matSuffix [for]="incorpValidityTillDateDatePicker"></mat-datepicker-toggle>
<mat-datepicker #incorpValidityTillDateDatePicker></mat-datepicker>
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Incorporation Number</mat-label>
<input matInput formControlName="incorpNumber">
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Main Business Line</mat-label>
<mat-select formControlName="mainBusinessLineId">
<mat-option *ngFor="let business of businessLineOptions" [value]="business.id">
{{ business.name }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex="48%">
<mat-label>Constitution</mat-label>
<mat-select formControlName="constitutionId">
<mat-option *ngFor="let constitution of constitutionOptions" [value]="constitution.id">
{{ constitution.name }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex="98%">
<mat-label>Remarks</mat-label>
<textarea matInput formControlName="remarks"></textarea>
</mat-form-field>
</div>
</div>
<div fxLayout="row" class="margin-t" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
<button mat-raised-button matStepperPrevious disabled>
<fa-icon icon="arrow-left"></fa-icon>&nbsp;&nbsp;
Previous
</button>
<button mat-raised-button matStepperNext>
Next&nbsp;&nbsp;
<fa-icon icon="arrow-right"></fa-icon>
</button>
</div>
</form>
@@ -0,0 +1,7 @@
.margin-v {
margin: 2em 0 0 0;
}
.margin-t {
margin-top: 2em;
}
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ClientGeneralStepComponent } from './client-general-step.component';
describe('ClientGeneralStepComponent', () => {
let component: ClientGeneralStepComponent;
let fixture: ComponentFixture<ClientGeneralStepComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ClientGeneralStepComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ClientGeneralStepComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,172 @@
/** Angular Imports */
import { Component, OnInit, Input } from '@angular/core';
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
import { DatePipe } from '@angular/common';
/**
* Create Client Component
*/
@Component({
selector: 'mifosx-client-general-step',
templateUrl: './client-general-step.component.html',
styleUrls: ['./client-general-step.component.scss']
})
export class ClientGeneralStepComponent implements OnInit {
/** Minimum date allowed. */
minDate = new Date(2000, 0, 1);
/** Maximum date allowed. */
maxDate = new Date();
/** Client Template */
@Input() clientTemplate: any;
/** Create Client Form */
createClientForm: FormGroup;
/** Office Options */
officeOptions: any;
/** Staff Options */
staffOptions: any;
/** Legal Form Options */
legalFormOptions: any;
/** Client Type Options */
clientTypeOptions: any;
/** Client Classification Options */
clientClassificationTypeOptions: any;
/** Business Line Options */
businessLineOptions: any;
/** Constitution Options */
constitutionOptions: any;
/** Gender Options */
genderOptions: any;
/** Saving Product Options */
savingProductOptions: any;
/**
* @param {FormBuilder} formBuilder Form Builder
* @param {DatePipe} datePipe Date Pipe
*/
constructor(private formBuilder: FormBuilder,
private datePipe: DatePipe) {
this.setClientForm();
}
ngOnInit() {
this.setOptions();
this.buildDependencies();
}
/**
* Creates the client form.
*/
setClientForm() {
this.createClientForm = this.formBuilder.group({
'officeId': ['', Validators.required],
'staffId': [''],
'legalFormId': [''],
'isStaff': [false],
'active': [false],
'addSavings': [false],
'accountNo': [''],
'externalId': [''],
'genderId': [''],
'mobileNo': [''],
'dateOfBirth': [''],
'clientTypeId': [''],
'clientClassificationId': [''],
'submittedOnDate': ['']
});
}
/**
* Sets select dropdown options.
*/
setOptions() {
this.officeOptions = this.clientTemplate.officeOptions;
this.staffOptions = this.clientTemplate.staffOptions;
this.legalFormOptions = this.clientTemplate.clientLegalFormOptions;
this.clientTypeOptions = this.clientTemplate.clientTypeOptions;
this.clientClassificationTypeOptions = this.clientTemplate.clientClassificationOptions;
this.businessLineOptions = this.clientTemplate.clientNonPersonMainBusinessLineOptions;
this.constitutionOptions = this.clientTemplate.clientNonPersonConstitutionOptions;
this.genderOptions = this.clientTemplate.genderOptions;
this.savingProductOptions = this.clientTemplate.savingProductOptions;
}
/**
* Adds controls conditionally.
*/
buildDependencies() {
this.createClientForm.get('legalFormId').valueChanges.subscribe((legalFormId: any) => {
if (legalFormId === 1) {
this.createClientForm.removeControl('fullname');
this.createClientForm.removeControl('clientNonPersonDetails');
this.createClientForm.addControl('firstname', new FormControl('', [Validators.required, Validators.pattern('(^[A-z]).*')]));
this.createClientForm.addControl('middlename', new FormControl('', Validators.pattern('(^[A-z]).*')));
this.createClientForm.addControl('lastname', new FormControl('', [Validators.required, Validators.pattern('(^[A-z]).*')]));
} else {
this.createClientForm.removeControl('firstname');
this.createClientForm.removeControl('middlename');
this.createClientForm.removeControl('lastname');
this.createClientForm.addControl('fullname', new FormControl('', [Validators.required, Validators.pattern('(^[A-z]).*')]));
this.createClientForm.addControl('clientNonPersonDetails', this.formBuilder.group({
'constitutionId': [''],
'incorpValidityTillDate': [''],
'incorpNumber': [''],
'mainBusinessLineId': [''],
'remarks': ['']
}));
}
});
this.createClientForm.get('legalFormId').patchValue(1);
this.createClientForm.get('active').valueChanges.subscribe((active: boolean) => {
if (active) {
this.createClientForm.addControl('activationDate', new FormControl('', Validators.required));
} else {
this.createClientForm.removeControl('activationDate');
}
});
this.createClientForm.get('addSavings').valueChanges.subscribe((active: boolean) => {
if (active) {
this.createClientForm.addControl('savingsProductId', new FormControl('', Validators.required));
} else {
this.createClientForm.removeControl('savingsProductId');
}
});
}
/**
* Client General Details
*/
get clientGeneralDetails() {
const generalDetails = this.createClientForm.value;
const dateFormat = 'dd MMMM yyyy';
const locale = 'en';
// TODO: Update once language and date settings are setup
for (const key in generalDetails) {
if (generalDetails[key] === '' || key === 'addSavings') {
delete generalDetails[key];
}
}
if (generalDetails.submittedOnDate) {
generalDetails.submittedOnDate = this.datePipe.transform(generalDetails.submittedOnDate, dateFormat);
}
if (generalDetails.activationDate) {
generalDetails.activationDate = this.datePipe.transform(generalDetails.activationDate, dateFormat);
}
if (generalDetails.dateOfBirth) {
generalDetails.dateOfBirth = this.datePipe.transform(generalDetails.dateOfBirth, dateFormat);
}
if (generalDetails.clientNonPersonDetails && generalDetails.clientNonPersonDetails.incorpValidityTillDate) {
generalDetails.clientNonPersonDetails = {
...generalDetails.clientNonPersonDetails,
incorpValidityTillDate: this.datePipe.transform(generalDetails.dateOfBirth, dateFormat),
dateFormat,
locale
};
}
return generalDetails;
}
}
@@ -0,0 +1,218 @@
<div fxLayout="row wrap" fxLayout.lt-md="column">
<h3 class="mat-h3" fxFlexFill>General</h3>
<mat-divider fxFlexFill></mat-divider>
<div fxFlexFill>
<span fxFlex="40%">Name</span>
<span fxFlex="60%" *ngIf="client.legalFormId === 2">{{ client.fullname }}</span>
<span fxFlex="60%" *ngIf="client.legalFormId === 1">{{ client.firstname }} {{ client.middlename ? client.middlename + ' ' + client.lastname : client.lastname }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Office</span>
<span fxFlex="60%">{{ client.officeId | find:clientTemplate.officeOptions:'id':'name' }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Legal Form</span>
<span fxFlex="60%">{{ client.legalFormId | find:clientTemplate.clientLegalFormOptions:'id':'value' }}</span>
</div>
<div fxFlexFill *ngIf="client.staffId">
<span fxFlex="40%">Staff</span>
<span fxFlex="60%">{{ client.staffId | find:clientTemplate.staffOptions:'id':'displayName' }}</span>
</div>
<div fxFlexFill *ngIf="client.dateOfBirth">
<span fxFlex="40%">{{ client.legalFormId === 1 ? 'Date of Birth' : 'Incorporation Date' }}</span>
<span fxFlex="60%">{{ client.dateOfBirth | date }}</span>
</div>
<div fxFlexFill *ngIf="client.externalId">
<span fxFlex="40%">External Id</span>
<span fxFlex="60%">{{ client.externalId }}</span>
</div>
<div fxFlexFill *ngIf="client.mobileNo">
<span fxFlex="40%">Mobile No</span>
<span fxFlex="60%">{{ client.mobileNo }}</span>
</div>
<div fxFlexFill *ngIf="client.clientTypeId">
<span fxFlex="40%">Client Type</span>
<span fxFlex="60%">{{ client.clientTypeId | find:clientTemplate.clientTypeOptions:'id':'name' }}</span>
</div>
<div fxFlexFill *ngIf="client.clientClassificationId">
<span fxFlex="40%">Client Classification</span>
<span fxFlex="60%">{{ client.clientClassificationId | find:clientTemplate.clientClassificationOptions:'id':'name' }}</span>
</div>
<div fxFlexFill *ngIf="client.savingsProductId">
<span fxFlex="40%">Savings Product</span>
<span fxFlex="60%">{{ client.savingsProductId | find:clientTemplate.savingProductOptions:'id':'name' }}</span>
</div>
<div fxFlexFill *ngIf="client.submittedOnDate">
<span fxFlex="40%">Submitted On Date</span>
<span fxFlex="60%">{{ client.submittedOnDate | date }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Active?</span>
<span fxFlex="60%">{{ client.active ? 'Yes' : 'No' }}</span>
</div>
<div fxFlexFill *ngIf="client.activationDate">
<span fxFlex="40%">Activation Date</span>
<span fxFlex="60%">{{ client.activationDate | date }}</span>
</div>
<ng-container *ngIf="client.legalFormId === 1">
<div fxFlexFill *ngIf="client.genderId">
<span fxFlex="40%">Gender</span>
<span fxFlex="60%">{{ client.genderId | find:clientTemplate.genderOptions:'id':'name' }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Is Staff?</span>
<span fxFlex="60%">{{ client.isStaff ? 'Yes' : 'No' }}</span>
</div>
</ng-container>
<ng-container *ngIf="client.legalFormId === 2">
<div fxFlexFill *ngIf="client.clientNonPersonDetails.incorpValidityTillDate">
<span fxFlex="40%">Incorporation Validity Till Date</span>
<span fxFlex="60%">{{ client.clientNonPersonDetails.incorpValidityTillDate | date }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Incorportation Number</span>
<span fxFlex="60%">{{ client.clientNonPersonDetails.incorpNumber || 'Not Provided' }}</span>
</div>
<div fxFlexFill *ngIf="client.clientNonPersonDetails.mainBusinessLineId">
<span fxFlex="40%">Main Business Line</span>
<span fxFlex="60%">{{ client.clientNonPersonDetails.mainBusinessLineId | find:clientTemplate.clientNonPersonMainBusinessLineOptions:'id':'name' }}</span>
</div>
<div fxFlexFill *ngIf="client.clientNonPersonDetails.constitutionId">
<span fxFlex="40%">Constitution</span>
<span fxFlex="60%">{{ client.clientNonPersonDetails.constitutionId | find:clientTemplate.clientNonPersonConstitutionOptions:'id':'name'}}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Remarks</span>
<span fxFlex="60%">{{ client.clientNonPersonDetails.remarks || 'N/A' }}</span>
</div>
</ng-container>
<div fxFlexFill *ngIf="client.familyMembers.length" fxLayout="row wrap" fxLayout.lt-md="column" class="m-b-20">
<h3 class="mat-h3" fxFlexFill>Family Members</h3>
<mat-divider fxFlexFill></mat-divider>
<mat-accordion fxFlexFill class="m-t-5">
<mat-expansion-panel *ngFor="let member of client.familyMembers" class="family-member">
<mat-expansion-panel-header>
<mat-panel-title>
{{ member.firstName }} {{ member.middleName ? member.middleName + ' ' + member.lastName : member.lastName }}
</mat-panel-title>
<mat-panel-description>
{{member.relationship}}
</mat-panel-description>
</mat-expansion-panel-header>
<mat-divider></mat-divider>
<p>
First Name : {{ member.firstName }}<br />
Middle Name : {{ member.middleName }}<br />
Last Name : {{ member.lastName }}<br />
Qualification : {{ member.qualification }}<br />
Relationship : {{ member.relationshipId | find:clientTemplate.familyMemberOptions.relationshipIdOptions:'id':'name' }}<br />
Age : {{ member.age }}<br />
Is Dependent : {{ member.isDependent ? 'Yes' : 'No' }}<br />
Marital Status : {{ member.maritalStatusId | find:clientTemplate.familyMemberOptions.maritalStatusIdOptions:'id':'name' }}<br />
Gender : {{ member.genderId | find:clientTemplate.familyMemberOptions.genderIdOptions:'id':'name' }}<br />
Profession : {{ member.professionId | find:clientTemplate.familyMemberOptions.professionIdOptions:'id':'name' }}<br />
Date Of Birth : {{ member.dateOfBirth | date }}<br />
</p>
</mat-expansion-panel>
</mat-accordion>
</div>
<div fxFlexFill *ngIf="client.address.length" fxLayout="row wrap" fxLayout.lt-md="column">
<h3 class="mat-h3" fxFlexFill>Address</h3>
<mat-divider fxFlexFill></mat-divider>
<mat-accordion fxFlexFill class="m-t-5">
<mat-expansion-panel *ngFor="let address of client.address" class="address">
<mat-expansion-panel-header>
<mat-panel-title>
{{ getSelectedValue('addressTypeIdOptions',address.addressTypeId)?.name }}
</mat-panel-title>
<mat-panel-description>
{{address.relationship}}
</mat-panel-description>
</mat-expansion-panel-header>
<mat-divider></mat-divider>
<p>
<span *ngIf="isFieldEnabled('street')">Street : {{address.street}}<br /></span>
<span *ngIf="isFieldEnabled('addressLine1')">Address Line 1 : {{address.addressLine1}}<br /></span>
<span *ngIf="isFieldEnabled('addressLine2')">Address Line 2 : {{address.addressLine2}}<br /></span>
<span *ngIf="isFieldEnabled('addressLine3')">Address Line 3 : {{address.addressLine3}}<br /></span>
<span *ngIf="isFieldEnabled('townVillage')">Town / Village : {{address.townVillage}}<br /></span>
<span *ngIf="isFieldEnabled('city')">City : {{address.city}}<br /></span>
<span *ngIf="isFieldEnabled('stateProvinceId')">State / Province :
{{getSelectedValue('stateProvinceIdOptions',address.stateProvinceId)?.name}}<br /></span>
<span *ngIf="isFieldEnabled('countryId')">Country :
{{getSelectedValue('countryIdOptions',address.countryId)?.name}}<br /></span>
<span *ngIf="isFieldEnabled('postalCode')">Postal Code : {{address.postalCode}}<br /></span>
<span *ngIf="isFieldEnabled('isActive')">Active Status : {{address.isActive}}<br /></span>
</p>
</mat-expansion-panel>
</mat-accordion>
</div>
</div>
<div fxLayout="row" class="margin-t" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
<button mat-raised-button matStepperPrevious>
<fa-icon icon="arrow-left"></fa-icon>&nbsp;&nbsp;
Previous
</button>
<button mat-raised-button [routerLink]="['../']">
Cancel
</button>
<button mat-raised-button color="primary" (click)="submit.emit()">
Submit
</button>
</div>
@@ -0,0 +1,36 @@
h2, h3, h4 {
margin: 0;
font-weight: 500;
}
span {
margin: 0.5em 0;
}
mat-divider {
margin: 0 0 0.5em 0;
}
.margin-t {
margin-top: 2em;
}
.family-member {
h3 {
margin: 1% auto;
}
p {
line-height: 30px;
margin-left: 2%;
}
}
.address {
h3 {
margin: 1% auto;
}
p {
line-height: 30px;
margin-left: 2%;
}
}
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ClientPreviewStepComponent } from './client-preview-step.component';
describe('ClientPreviewStepComponent', () => {
let component: ClientPreviewStepComponent;
let fixture: ComponentFixture<ClientPreviewStepComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ClientPreviewStepComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ClientPreviewStepComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,44 @@
/** Angular Imports */
import { Component, Output, EventEmitter, Input } from '@angular/core';
/**
* Client Preview Step Component
*/
@Component({
selector: 'mifosx-client-preview-step',
templateUrl: './client-preview-step.component.html',
styleUrls: ['./client-preview-step.component.scss']
})
export class ClientPreviewStepComponent {
/** Client Address field configuration */
@Input() clientAddressFieldConfig: any;
/** Client Template */
@Input() clientTemplate: any;
/** Client Object */
@Input() client: any;
/** Form submission event */
@Output() submit = new EventEmitter();
constructor() { }
/**
* Utilized in address preview.
* Find pipe doesn't work with accordian.
* @param {any} fieldName Field Name
* @param {any} fieldId Field Id
*/
getSelectedValue(fieldName: any, fieldId: any) {
return (this.clientTemplate.address[fieldName].find((fieldObj: any) => fieldObj.id === fieldId));
}
/**
* Utilized in address preview to check if field is enabled in configuration.
* @param {any} fieldName Field Name
*/
isFieldEnabled(fieldName: any) {
return (this.clientAddressFieldConfig.find((fieldObj: any) => fieldObj.field === fieldName)).is_enabled;
}
}
+11 -1
View File
@@ -23,6 +23,7 @@ import { ClientActionsComponent } from './clients-view/client-actions/client-act
import { ViewChargeComponent } from './clients-view/charges/view-charge/view-charge.component';
import { ClientPayChargesComponent } from './clients-view/charges/client-pay-charges/client-pay-charges.component';
import { EditClientComponent } from './edit-client/edit-client.component';
import { CreateClientComponent } from './create-client/create-client.component';
/** Custom Resolvers */
import { ClientViewResolver } from './common-resolvers/client-view.resolver';
@@ -55,7 +56,16 @@ const routes: Routes = [
children: [
{
path: '',
component: ClientsComponent,
component: ClientsComponent
},
{
path: 'create',
data: { title: extract('Create Client'), breadcrumb: 'Create Client', routeParamBreadcrumb: false },
component: CreateClientComponent,
resolve: {
clientAddressFieldConfig: ClientAddressFieldConfigurationResolver,
clientTemplate: ClientTemplateResolver
}
},
{
path: ':clientId',
+2 -3
View File
@@ -8,10 +8,10 @@
</mat-checkbox>
</div>
<div class="action-button" fxLayoutGap="25px">
<button mat-raised-button color="primary" [routerLink]="['./import']">
<button mat-raised-button color="primary" [routerLink]="['import']">
<fa-icon icon="download"></fa-icon>&nbsp;&nbsp;Import Client
</button>
<button mat-raised-button color="primary" [routerLink]="['./add']">
<button mat-raised-button color="primary" [routerLink]="['create']">
<fa-icon icon="plus"></fa-icon>&nbsp;&nbsp;Create Client
</button>
</div>
@@ -65,7 +65,6 @@
<td mat-cell *matCellDef="let row"> {{row.officeName}} </td>
</ng-container>
<!-- ID Column -->
<ng-container matColumnDef="staff">
<th mat-header-cell *matHeaderCellDef mat-sort-header='staffName'> StaffName </th>
+15 -2
View File
@@ -50,6 +50,12 @@ import { DeleteSignatureDialogComponent } from './clients-view/custom-dialogs/de
import { UploadImageDialogComponent } from './clients-view/custom-dialogs/upload-image-dialog/upload-image-dialog.component';
import { ClientScreenReportsComponent } from './clients-view/client-actions/client-screen-reports/client-screen-reports.component';
import { EditClientComponent } from './edit-client/edit-client.component';
import { CreateClientComponent } from './create-client/create-client.component';
import { ClientGeneralStepComponent } from './client-stepper/client-general-step/client-general-step.component';
import { ClientFamilyMembersStepComponent } from './client-stepper/client-family-members-step/client-family-members-step.component';
import { ClientPreviewStepComponent } from './client-stepper/client-preview-step/client-preview-step.component';
import { ClientAddressStepComponent } from './client-stepper/client-address-step/client-address-step.component';
import { ClientFamilyMemberDialogComponent } from './client-stepper/client-family-members-step/client-family-member-dialog/client-family-member-dialog.component';
/**
@@ -105,7 +111,13 @@ import { EditClientComponent } from './edit-client/edit-client.component';
DeleteSignatureDialogComponent,
UploadImageDialogComponent,
ClientScreenReportsComponent,
EditClientComponent
EditClientComponent,
CreateClientComponent,
ClientGeneralStepComponent,
ClientFamilyMembersStepComponent,
ClientPreviewStepComponent,
ClientAddressStepComponent,
ClientFamilyMemberDialogComponent
],
entryComponents: [
UploadDocumentDialogComponent,
@@ -114,7 +126,8 @@ import { EditClientComponent } from './edit-client/edit-client.component';
ViewSignatureDialogComponent,
UploadSignatureDialogComponent,
DeleteSignatureDialogComponent,
UploadImageDialogComponent
UploadImageDialogComponent,
ClientFamilyMemberDialogComponent
],
providers: [DatePipe]
+4
View File
@@ -45,6 +45,10 @@ export class ClientsService {
return this.http.get(`/clients/${clientId}`);
}
createClient(client: any) {
return this.http.post(`/clients`, client);
}
updateClient(clientId: string, client: any) {
return this.http.put(`/clients/${clientId}`, client);
}
@@ -0,0 +1,68 @@
<div class="container">
<mat-horizontal-stepper class="mat-elevation-z8" labelPosition="bottom" #clientStepper>
<ng-template matStepperIcon="number">
<fa-icon icon="pencil-alt" size="sm"></fa-icon>
</ng-template>
<ng-template matStepperIcon="edit">
<fa-icon icon="pencil-alt" size="sm"></fa-icon>
</ng-template>
<ng-template matStepperIcon="done">
<fa-icon icon="check" size="sm"></fa-icon>
</ng-template>
<ng-template matStepperIcon="error">
<fa-icon icon="exclamation-triangle" size="lg"></fa-icon>
</ng-template>
<ng-template matStepperIcon="preview">
<fa-icon icon="eye" size="sm"></fa-icon>
</ng-template>
<mat-step [stepControl]="clientGeneralForm">
<ng-template matStepLabel>GENERAL</ng-template>
<mifosx-client-general-step [clientTemplate]="clientTemplate"></mifosx-client-general-step>
</mat-step>
<mat-step>
<ng-template matStepLabel>FAMILY MEMBERS</ng-template>
<mifosx-client-family-members-step [clientTemplate]="clientTemplate"></mifosx-client-family-members-step>
</mat-step>
<mat-step>
<ng-template matStepLabel>ADDRESS</ng-template>
<mifosx-client-address-step
[clientTemplate]="clientTemplate"
[clientAddressFieldConfig]="clientAddressFieldConfig"
>
</mifosx-client-address-step>
</mat-step>
<mat-step *ngIf="clientGeneralForm.valid" completed>
<ng-template matStepLabel>PREVIEW</ng-template>
<mifosx-client-preview-step
[clientTemplate]="clientTemplate"
[clientAddressFieldConfig]="clientAddressFieldConfig"
[client]="client" (submit)="submit()"
>
</mifosx-client-preview-step>
</mat-step>
</mat-horizontal-stepper>
</div>
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CreateClientComponent } from './create-client.component';
describe('CreateClientComponent', () => {
let component: CreateClientComponent;
let fixture: ComponentFixture<CreateClientComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CreateClientComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CreateClientComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,84 @@
/** Angular Imports */
import { Component, ViewChild } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
/** Custom Services */
import { ClientsService } from '../clients.service';
/** Custom Components */
import { ClientGeneralStepComponent } from '../client-stepper/client-general-step/client-general-step.component';
import { ClientFamilyMembersStepComponent } from '../client-stepper/client-family-members-step/client-family-members-step.component';
import { ClientAddressStepComponent } from '../client-stepper/client-address-step/client-address-step.component';
/**
* Create Client Component.
*/
@Component({
selector: 'mifosx-create-client',
templateUrl: './create-client.component.html',
styleUrls: ['./create-client.component.scss']
})
export class CreateClientComponent {
/** Client General Step */
@ViewChild(ClientGeneralStepComponent) clientGeneralStep: ClientGeneralStepComponent;
/** Client Family Members Step */
@ViewChild(ClientFamilyMembersStepComponent) clientFamilyMembersStep: ClientFamilyMembersStepComponent;
/** Client Address Step */
@ViewChild(ClientAddressStepComponent) clientAddressStep: ClientAddressStepComponent;
/** Client Template */
clientTemplate: any;
/** Client Address Field Config */
clientAddressFieldConfig: any;
/**
* Fetches client and address template from `resolve`
* @param {ActivatedRoute} route Activated Route
* @param {Router} router Router
* @param {ClientsService} clientsService Clients Service
*/
constructor(private route: ActivatedRoute,
private router: Router,
private clientsService: ClientsService) {
this.route.data.subscribe((data: { clientTemplate: any, clientAddressFieldConfig: any }) => {
this.clientTemplate = data.clientTemplate;
this.clientAddressFieldConfig = data.clientAddressFieldConfig;
});
}
/**
* Retrieves general information about client.
*/
get clientGeneralForm() {
return this.clientGeneralStep.createClientForm;
}
/**
* Retrieves the client object
*/
get client() {
return {
...this.clientGeneralStep.clientGeneralDetails,
...this.clientFamilyMembersStep.familyMembers,
...this.clientAddressStep.address
};
}
/**
* Submits the create client form.
*/
submit() {
const locale = 'en';
const dateFormat = 'dd MMMM yyyy';
// TODO: Update once language and date settings are setup
const clientData = {
...this.client,
dateFormat,
locale
};
this.clientsService.createClient(clientData).subscribe((response: any) => {
this.router.navigate(['../', response.resourceId], { relativeTo: this.route });
});
}
}