diff --git a/src/app/clients/client-stepper/client-address-step/client-address-step.component.html b/src/app/clients/client-stepper/client-address-step/client-address-step.component.html new file mode 100644 index 000000000..b1d26e92b --- /dev/null +++ b/src/app/clients/client-stepper/client-address-step/client-address-step.component.html @@ -0,0 +1,69 @@ +
+ +

Address

+ +
+ +
+ + + + + + + + + {{ getSelectedValue('addressTypeIdOptions',address.addressTypeId)?.name }} + + + + {{ address.relationship }} + + + + + + +
+ + + +
+ +

+ Street : {{address.street}}
+ Address Line 1 : {{address.addressLine1}}
+ Address Line 2 : {{address.addressLine2}}
+ Address Line 3 : {{address.addressLine3}}
+ Town / Village : {{address.townVillage}}
+ City : {{address.city}}
+ State / Province : + {{getSelectedValue('stateProvinceIdOptions',address.stateProvinceId)?.name}}
+ Country : + {{getSelectedValue('countryIdOptions',address.countryId)?.name}}
+ Postal Code : {{address.postalCode}}
+ Active Status : {{address.isActive}}
+

+ +
+ +
+ +
+ + +
+ +
diff --git a/src/app/clients/client-stepper/client-address-step/client-address-step.component.scss b/src/app/clients/client-stepper/client-address-step/client-address-step.component.scss new file mode 100644 index 000000000..8a3cc63e4 --- /dev/null +++ b/src/app/clients/client-stepper/client-address-step/client-address-step.component.scss @@ -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; +} diff --git a/src/app/clients/client-stepper/client-address-step/client-address-step.component.spec.ts b/src/app/clients/client-stepper/client-address-step/client-address-step.component.spec.ts new file mode 100644 index 000000000..e6a0aa276 --- /dev/null +++ b/src/app/clients/client-stepper/client-address-step/client-address-step.component.spec.ts @@ -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; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ClientAddressStepComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ClientAddressStepComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/clients/client-stepper/client-address-step/client-address-step.component.ts b/src/app/clients/client-stepper/client-address-step/client-address-step.component.ts new file mode 100644 index 000000000..946525f25 --- /dev/null +++ b/src/app/clients/client-stepper/client-address-step/client-address-step.component.ts @@ -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 }; + } + +} diff --git a/src/app/clients/client-stepper/client-family-members-step/client-family-member-dialog/client-family-member-dialog.component.html b/src/app/clients/client-stepper/client-family-members-step/client-family-member-dialog/client-family-member-dialog.component.html new file mode 100644 index 000000000..c57f51a96 --- /dev/null +++ b/src/app/clients/client-stepper/client-family-members-step/client-family-member-dialog/client-family-member-dialog.component.html @@ -0,0 +1,104 @@ +

{{ data.context }} Family Member

+ +
+ +
+ + + First Name + + + First Name is required + + + + + Middle Name + + + + + Last Name + + + Last Name is required + + + + + Qualification + + + + + Age + + + Age is required + + + + Is Dependent? + + + Relationship + + + {{ relation.name }} + + + + Relationship is required + + + + + Gender + + + {{ gender.name }} + + + + Gender is required + + + + + Profession + + + {{ profession.name }} + + + + + + Marital Status + + + {{ maritalStatus.name }} + + + + + + + Date Of Birth + + + + + Date of birth is required + + + +
+ + + + + + +
diff --git a/src/app/clients/client-stepper/client-family-members-step/client-family-member-dialog/client-family-member-dialog.component.scss b/src/app/clients/client-stepper/client-family-members-step/client-family-member-dialog/client-family-member-dialog.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/clients/client-stepper/client-family-members-step/client-family-member-dialog/client-family-member-dialog.component.spec.ts b/src/app/clients/client-stepper/client-family-members-step/client-family-member-dialog/client-family-member-dialog.component.spec.ts new file mode 100644 index 000000000..d2e5fd454 --- /dev/null +++ b/src/app/clients/client-stepper/client-family-members-step/client-family-member-dialog/client-family-member-dialog.component.spec.ts @@ -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; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ClientFamilyMemberDialogComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ClientFamilyMemberDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/clients/client-stepper/client-family-members-step/client-family-member-dialog/client-family-member-dialog.component.ts b/src/app/clients/client-stepper/client-family-members-step/client-family-member-dialog/client-family-member-dialog.component.ts new file mode 100644 index 000000000..350ef0cab --- /dev/null +++ b/src/app/clients/client-stepper/client-family-members-step/client-family-member-dialog/client-family-member-dialog.component.ts @@ -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, + 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; + } + +} diff --git a/src/app/clients/client-stepper/client-family-members-step/client-family-members-step.component.html b/src/app/clients/client-stepper/client-family-members-step/client-family-members-step.component.html new file mode 100644 index 000000000..7b4e40809 --- /dev/null +++ b/src/app/clients/client-stepper/client-family-members-step/client-family-members-step.component.html @@ -0,0 +1,67 @@ +
+ +

Family Members

+ +
+ +
+ + + + + + + + + {{ member.firstName }} {{ member.middleName ? member.middleName + ' ' + member.lastName : member.lastName }} + + + + {{member.relationship}} + + + + + + +
+ + +
+ +

+ First Name : {{ member.firstName }}
+ Middle Name : {{ member.middleName }}
+ Last Name : {{ member.lastName }}
+ Qualification : {{ member.qualification }}
+ Relationship : {{ member.relationshipId | find:clientTemplate.familyMemberOptions.relationshipIdOptions:'id':'name' }}
+ Age : {{ member.age }}
+ Is Dependent : {{ member.isDependent ? 'Yes' : 'No' }}
+ Marital Status : {{ member.maritalStatusId | find:clientTemplate.familyMemberOptions.maritalStatusIdOptions:'id':'name' }}
+ Gender : {{ member.genderId | find:clientTemplate.familyMemberOptions.genderIdOptions:'id':'name' }}
+ Profession : {{member.professionId | find:clientTemplate.familyMemberOptions.professionIdOptions:'id':'name' }}
+ Date Of Birth : {{member.dateOfBirth | date}}
+

+ +
+ +
+ +
+ + +
+ +
diff --git a/src/app/clients/client-stepper/client-family-members-step/client-family-members-step.component.scss b/src/app/clients/client-stepper/client-family-members-step/client-family-members-step.component.scss new file mode 100644 index 000000000..55d812852 --- /dev/null +++ b/src/app/clients/client-stepper/client-family-members-step/client-family-members-step.component.scss @@ -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; +} diff --git a/src/app/clients/client-stepper/client-family-members-step/client-family-members-step.component.spec.ts b/src/app/clients/client-stepper/client-family-members-step/client-family-members-step.component.spec.ts new file mode 100644 index 000000000..46b67dd2b --- /dev/null +++ b/src/app/clients/client-stepper/client-family-members-step/client-family-members-step.component.spec.ts @@ -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; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ClientFamilyMembersStepComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ClientFamilyMembersStepComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/clients/client-stepper/client-family-members-step/client-family-members-step.component.ts b/src/app/clients/client-stepper/client-family-members-step/client-family-members-step.component.ts new file mode 100644 index 000000000..145a4dc90 --- /dev/null +++ b/src/app/clients/client-stepper/client-family-members-step/client-family-members-step.component.ts @@ -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 }; + } + +} diff --git a/src/app/clients/client-stepper/client-general-step/client-general-step.component.html b/src/app/clients/client-stepper/client-general-step/client-general-step.component.html new file mode 100644 index 000000000..ff745fb2e --- /dev/null +++ b/src/app/clients/client-stepper/client-general-step/client-general-step.component.html @@ -0,0 +1,215 @@ +
+ +
+ + + Office + + + {{ office.name }} + + + + Office is required + + + + + Staff + + + {{ staff.displayName }} + + + + + + Legal Form + + + {{ legalForm.value }} + + + + + + Name + + + Client name is required + + + Client name cannot begin with a special character or number + + + + + First Name + + + Client first name is required + + + Client first name cannot begin with a special character or number + + + + + Middle Name + + + Client middle name cannot begin with a special character or number + + + + + Last Name + + + Client last name is required + + + Client last name cannot begin with a special character or number + + + + + {{ createClientForm.value.legalFormId === 1 ? 'Date of Birth' : 'Incorporation Date' }} + + + + + + + External Id + + + + + Mobile No + + + + + Gender + + + {{ gender.name }} + + + + + + Is staff? + + + + Client Type + + + {{ clientType.name }} + + + + + + Client Classification + + + {{ clientClassification.name }} + + + + + + Submitted On + + + + + + + Active? + + + + Activation Date + + + + + Activation Date is required + + + + + Open Savings Account? + + + + Savings Product + + + {{ product.name }} + + + + Savings product is required + + + +
+ + + Incorporation Validity Till Date + + + + + + + Incorporation Number + + + + + Main Business Line + + + {{ business.name }} + + + + + + Constitution + + + {{ constitution.name }} + + + + + + Remarks + + + +
+ +
+ +
+ + +
+ +
diff --git a/src/app/clients/client-stepper/client-general-step/client-general-step.component.scss b/src/app/clients/client-stepper/client-general-step/client-general-step.component.scss new file mode 100644 index 000000000..efd4884ca --- /dev/null +++ b/src/app/clients/client-stepper/client-general-step/client-general-step.component.scss @@ -0,0 +1,7 @@ +.margin-v { + margin: 2em 0 0 0; +} + +.margin-t { + margin-top: 2em; +} diff --git a/src/app/clients/client-stepper/client-general-step/client-general-step.component.spec.ts b/src/app/clients/client-stepper/client-general-step/client-general-step.component.spec.ts new file mode 100644 index 000000000..39f8f7b5c --- /dev/null +++ b/src/app/clients/client-stepper/client-general-step/client-general-step.component.spec.ts @@ -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; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ClientGeneralStepComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ClientGeneralStepComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/clients/client-stepper/client-general-step/client-general-step.component.ts b/src/app/clients/client-stepper/client-general-step/client-general-step.component.ts new file mode 100644 index 000000000..006ea8602 --- /dev/null +++ b/src/app/clients/client-stepper/client-general-step/client-general-step.component.ts @@ -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; + } + +} diff --git a/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.html b/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.html new file mode 100644 index 000000000..5c84a7995 --- /dev/null +++ b/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.html @@ -0,0 +1,218 @@ +
+ +

General

+ + + +
+ Name + {{ client.fullname }} + {{ client.firstname }} {{ client.middlename ? client.middlename + ' ' + client.lastname : client.lastname }} +
+ +
+ Office + {{ client.officeId | find:clientTemplate.officeOptions:'id':'name' }} +
+ +
+ Legal Form + {{ client.legalFormId | find:clientTemplate.clientLegalFormOptions:'id':'value' }} +
+ +
+ Staff + {{ client.staffId | find:clientTemplate.staffOptions:'id':'displayName' }} +
+ +
+ {{ client.legalFormId === 1 ? 'Date of Birth' : 'Incorporation Date' }} + {{ client.dateOfBirth | date }} +
+ +
+ External Id + {{ client.externalId }} +
+ +
+ Mobile No + {{ client.mobileNo }} +
+ +
+ Client Type + {{ client.clientTypeId | find:clientTemplate.clientTypeOptions:'id':'name' }} +
+ +
+ Client Classification + {{ client.clientClassificationId | find:clientTemplate.clientClassificationOptions:'id':'name' }} +
+ +
+ Savings Product + {{ client.savingsProductId | find:clientTemplate.savingProductOptions:'id':'name' }} +
+ +
+ Submitted On Date + {{ client.submittedOnDate | date }} +
+ +
+ Active? + {{ client.active ? 'Yes' : 'No' }} +
+ +
+ Activation Date + {{ client.activationDate | date }} +
+ + + +
+ Gender + {{ client.genderId | find:clientTemplate.genderOptions:'id':'name' }} +
+ +
+ Is Staff? + {{ client.isStaff ? 'Yes' : 'No' }} +
+ +
+ + + +
+ Incorporation Validity Till Date + {{ client.clientNonPersonDetails.incorpValidityTillDate | date }} +
+ +
+ Incorportation Number + {{ client.clientNonPersonDetails.incorpNumber || 'Not Provided' }} +
+ +
+ Main Business Line + {{ client.clientNonPersonDetails.mainBusinessLineId | find:clientTemplate.clientNonPersonMainBusinessLineOptions:'id':'name' }} +
+ +
+ Constitution + {{ client.clientNonPersonDetails.constitutionId | find:clientTemplate.clientNonPersonConstitutionOptions:'id':'name'}} +
+ +
+ Remarks + {{ client.clientNonPersonDetails.remarks || 'N/A' }} +
+ +
+ +
+ +

Family Members

+ + + + + + + + + + + {{ member.firstName }} {{ member.middleName ? member.middleName + ' ' + member.lastName : member.lastName }} + + + + {{member.relationship}} + + + + + + +

+ First Name : {{ member.firstName }}
+ Middle Name : {{ member.middleName }}
+ Last Name : {{ member.lastName }}
+ Qualification : {{ member.qualification }}
+ Relationship : {{ member.relationshipId | find:clientTemplate.familyMemberOptions.relationshipIdOptions:'id':'name' }}
+ Age : {{ member.age }}
+ Is Dependent : {{ member.isDependent ? 'Yes' : 'No' }}
+ Marital Status : {{ member.maritalStatusId | find:clientTemplate.familyMemberOptions.maritalStatusIdOptions:'id':'name' }}
+ Gender : {{ member.genderId | find:clientTemplate.familyMemberOptions.genderIdOptions:'id':'name' }}
+ Profession : {{ member.professionId | find:clientTemplate.familyMemberOptions.professionIdOptions:'id':'name' }}
+ Date Of Birth : {{ member.dateOfBirth | date }}
+

+ +
+ +
+ +
+ +
+ +

Address

+ + + + + + + + + + + {{ getSelectedValue('addressTypeIdOptions',address.addressTypeId)?.name }} + + + + {{address.relationship}} + + + + + + +

+ Street : {{address.street}}
+ Address Line 1 : {{address.addressLine1}}
+ Address Line 2 : {{address.addressLine2}}
+ Address Line 3 : {{address.addressLine3}}
+ Town / Village : {{address.townVillage}}
+ City : {{address.city}}
+ State / Province : + {{getSelectedValue('stateProvinceIdOptions',address.stateProvinceId)?.name}}
+ Country : + {{getSelectedValue('countryIdOptions',address.countryId)?.name}}
+ Postal Code : {{address.postalCode}}
+ Active Status : {{address.isActive}}
+

+ +
+ +
+ +
+ +
+ +
+ + + +
diff --git a/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.scss b/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.scss new file mode 100644 index 000000000..105432c6c --- /dev/null +++ b/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.scss @@ -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%; + } +} diff --git a/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.spec.ts b/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.spec.ts new file mode 100644 index 000000000..657958194 --- /dev/null +++ b/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.spec.ts @@ -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; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ClientPreviewStepComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ClientPreviewStepComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.ts b/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.ts new file mode 100644 index 000000000..07efeece3 --- /dev/null +++ b/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.ts @@ -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; + } + +} diff --git a/src/app/clients/clients-routing.module.ts b/src/app/clients/clients-routing.module.ts index 0b3656dbc..3ec27b3da 100644 --- a/src/app/clients/clients-routing.module.ts +++ b/src/app/clients/clients-routing.module.ts @@ -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', diff --git a/src/app/clients/clients.component.html b/src/app/clients/clients.component.html index 473668a62..00120fd7b 100644 --- a/src/app/clients/clients.component.html +++ b/src/app/clients/clients.component.html @@ -8,10 +8,10 @@
- -
@@ -65,7 +65,6 @@ {{row.officeName}} - StaffName diff --git a/src/app/clients/clients.module.ts b/src/app/clients/clients.module.ts index 38cd4a6c7..660e9c645 100644 --- a/src/app/clients/clients.module.ts +++ b/src/app/clients/clients.module.ts @@ -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] diff --git a/src/app/clients/clients.service.ts b/src/app/clients/clients.service.ts index 8cdfeaf2c..874751d4a 100644 --- a/src/app/clients/clients.service.ts +++ b/src/app/clients/clients.service.ts @@ -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); } diff --git a/src/app/clients/create-client/create-client.component.html b/src/app/clients/create-client/create-client.component.html new file mode 100644 index 000000000..e46d83844 --- /dev/null +++ b/src/app/clients/create-client/create-client.component.html @@ -0,0 +1,68 @@ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + GENERAL + + + + + + + + FAMILY MEMBERS + + + + + + + + ADDRESS + + + + + + + + + PREVIEW + + + + + + + + +
diff --git a/src/app/clients/create-client/create-client.component.scss b/src/app/clients/create-client/create-client.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/clients/create-client/create-client.component.spec.ts b/src/app/clients/create-client/create-client.component.spec.ts new file mode 100644 index 000000000..9a8598793 --- /dev/null +++ b/src/app/clients/create-client/create-client.component.spec.ts @@ -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; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ CreateClientComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CreateClientComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/clients/create-client/create-client.component.ts b/src/app/clients/create-client/create-client.component.ts new file mode 100644 index 000000000..bd3553280 --- /dev/null +++ b/src/app/clients/create-client/create-client.component.ts @@ -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 }); + }); + } + +}