feat: add clients view page

- add client view page
- add general tab 
- add status lookup pipe to store styles based on status
- add accounts filter pipe to filter accounts
- add status related colors in _color.scss

Fixes: #216
This commit is contained in:
Jivjyot Singh
2019-12-01 23:20:51 +03:00
committed by Abhay Chawla
parent 61606b09e9
commit 1916e67062
30 changed files with 2423 additions and 1419 deletions
@@ -0,0 +1,13 @@
@import "~@angular/material/theming";
@mixin clients-view-component-theme($theme) {
$primary: map-get($theme, primary);
mifosx-clients-view {
.client-card {
.header {
background-color: mat-color($primary, 500);
}
}
}
}
@@ -0,0 +1,52 @@
<mat-card class="client-card">
<mat-card-header fxLayout="column" class="header">
<mat-card-title-group class="header-title-group">
<!-- change this -->
<div class="profile-image-container">
<div>
<img mat-card-md-image class="profile-image" [src]="(clientImage)?clientImage:'../../../assets/images/user_placeholder.png'">
</div>
</div>
<div class="mat-typography client-card-title">
<mat-card-title>
<h3>
Client Name : {{clientViewData.displayName}}
</h3>
</mat-card-title>
<mat-card-subtitle>
<p>
Client #:{{clientViewData.accountNo}} | External
Id: {{clientViewData.externalId}} | Staff:{{clientViewData.staffName}}<br />
Activation Date :
{{(clientViewData.activationDate)?(clientViewData.activationDate|date) :'Not Activated'}}<br />
Member Of : <span *ngFor="let group of clientViewData.groups">
<a>{{group.name}}</a>
&nbsp;
</span>
<span *ngIf="!clientViewData.groups">Unassigned</span>
<br />
</p>
</mat-card-subtitle>
</div>
</mat-card-title-group>
<mat-card-actions class="client-actions">
<button mat-raised-button>Edit</button>
<button mat-raised-button>+ New Loan</button>
<button mat-raised-button>+ New Savings</button>
<button mat-raised-button>+ Deposit</button>
<button mat-raised-button [matMenuTriggerFor]="More">More</button>
<mat-menu #More="matMenu">
<button mat-menu-item>Item 1</button>
<button mat-menu-item>Item 2</button>
</mat-menu>
</mat-card-actions>
</mat-card-header>
<mat-card-content>
<nav mat-tab-nav-bar>
<a mat-tab-link [routerLink]="['./general']" routerLinkActive active="true">
General
</a>
</nav>
<router-outlet></router-outlet>
</mat-card-content>
</mat-card>
@@ -0,0 +1,29 @@
.client-card {
margin: 0 auto;
max-width: 80rem;
width: 90%;
padding: 0;
.header {
padding: 1%;
.header-title-group {
.client-card-title {
color: white;
width: 90%;
p {
color: white;
}
}
}
.profile-image-container {
margin: 1%;
.profile-image {
object-fit: cover;
border-radius: 20px;
}
}
.client-actions {
align-self: flex-end;
margin: 0 1%;
}
}
}
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ClientsViewComponent } from './clients-view.component';
describe('ClientsViewComponent', () => {
let component: ClientsViewComponent;
let fixture: ComponentFixture<ClientsViewComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ClientsViewComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ClientsViewComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,31 @@
/** Angular Imports */
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { DomSanitizer } from '@angular/platform-browser';
/** Custom Services */
import { ClientsService } from '../clients.service';
@Component({
selector: 'mifosx-clients-view',
templateUrl: './clients-view.component.html',
styleUrls: ['./clients-view.component.scss']
})
export class ClientsViewComponent implements OnInit {
clientViewData: any;
clientImage: any;
constructor(private route: ActivatedRoute,
private clientsService: ClientsService,
private _sanitizer: DomSanitizer) {
this.route.data.subscribe((data: { clientViewData: any }) => {
this.clientViewData = data.clientViewData;
});
}
ngOnInit() {
this.clientsService.getClientProfileImage(this.clientViewData.id).subscribe(base64Image => {
this.clientImage = this._sanitizer.bypassSecurityTrustResourceUrl(base64Image);
});
}
}
@@ -0,0 +1,363 @@
<div class="tab-container mat-typography">
<h3>Performance History</h3>
<div fxLayout="row" fxLayoutGap="32px" class="performance-history-container">
<p>
No. Of Loan Cycles :{{clientSummary.loanCycle}} <br />
No. of Active Loans :{{clientSummary.activeLoans}} <br />
Last Loan Amount :{{clientSummary.lastLoanAmount}} <br />
</p>
<p>
No. of Active Savings :{{clientSummary.activeSavings}} <br />
Total Savings :{{clientSummary.totalSavings}} <br />
</p>
</div>
<!-- Upcoming Charges -->
<h3>Upcoming Charges</h3>
<button mat-raised-button class="f-right" color="primary">Charges Overview</button>
<table mat-table [dataSource]="upcomingCharges" class="mat-elevation-z1">
<ng-container matColumnDef="Name">
<th mat-header-cell *matHeaderCellDef> Name </th>
<td mat-cell *matCellDef="let element"> <i class="fa fa-stop"
[ngClass]="(!(element.isWaived || element.isPaid))|statusLookup"></i>
{{element.name}} </td>
</ng-container>
<ng-container matColumnDef="Due as of">
<th mat-header-cell *matHeaderCellDef> Due as of </th>
<td mat-cell *matCellDef="let element"> {{element.dueDate|date}} </td>
</ng-container>
<ng-container matColumnDef="Due">
<th mat-header-cell *matHeaderCellDef> Due </th>
<td mat-cell *matCellDef="let element"> {{element.amount}} </td>
</ng-container>
<ng-container matColumnDef="Paid">
<th mat-header-cell *matHeaderCellDef>Paid </th>
<td mat-cell *matCellDef="let element"> {{element.amountPaid}} </td>
</ng-container>
<ng-container matColumnDef="Waived">
<th mat-header-cell *matHeaderCellDef> Waived </th>
<td mat-cell *matCellDef="let element"> {{element.amountWaived}} </td>
</ng-container>
<ng-container matColumnDef="Outstanding">
<th mat-header-cell *matHeaderCellDef> Outstanding </th>
<td mat-cell *matCellDef="let element"> {{element.amountOutstanding}} </td>
</ng-container>
<ng-container matColumnDef="Actions">
<th mat-header-cell *matHeaderCellDef> Actions </th>
<td mat-cell *matCellDef="let element">
<button class="account-action-button" mat-raised-button color="primary">
<i class="fa fa-dollar"></i>
</button>
<button class="account-action-button" mat-raised-button color="primary">
<i class="fa fa-flag"></i>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="upcomingChargesColumns"></tr>
<tr mat-row *matRowDef="let row; columns: upcomingChargesColumns;"></tr>
</table>
<!-- loans overview table -->
<h3>Loan Accounts</h3>
<button mat-raised-button class="f-right" color="primary"
(click)="toggleLoanAccountsOverview()">{{showClosedLoanAccounts?'View Active Accounts':'View Closed Accounts'}}</button>
<table *ngIf="!showClosedLoanAccounts" mat-table [dataSource]="loanAccounts|accountsFilter:'loan'"
class="mat-elevation-z1">
<ng-container matColumnDef="Account No">
<th mat-header-cell *matHeaderCellDef> Account No. </th>
<td mat-cell *matCellDef="let element"> <i class="fa fa-stop"
[ngClass]="element.inArrears?'status-active-overdue':(element.status.code|statusLookup)"></i>
{{element.accountNo}}
</td>
</ng-container>
<ng-container matColumnDef="Loan Account">
<th mat-header-cell *matHeaderCellDef> Loan Account </th>
<td mat-cell *matCellDef="let element"> {{element.productName}} </td>
</ng-container>
<ng-container matColumnDef="Original Loan">
<th mat-header-cell *matHeaderCellDef> Original Loan </th>
<td mat-cell *matCellDef="let element"> {{element.lastActiveTransactionDate | date}} </td>
</ng-container>
<ng-container matColumnDef="Loan Balance">
<th mat-header-cell *matHeaderCellDef>Loan Balance </th>
<td mat-cell *matCellDef="let element"> {{element.loanBalance}} </td>
</ng-container>
<ng-container matColumnDef="Amount Paid">
<th mat-header-cell *matHeaderCellDef> Amount Paid </th>
<td mat-cell *matCellDef="let element"> {{element.accountBalance}} </td>
</ng-container>
<ng-container matColumnDef="Type">
<th mat-header-cell *matHeaderCellDef> Type </th>
<td mat-cell *matCellDef="let element">
<i class="fa fa-large" [ngClass]="(element.loanType.value=== 'Individual')?'fa-user':'fa-group'"></i>
</td>
</ng-container>
<ng-container matColumnDef="Actions">
<th mat-header-cell *matHeaderCellDef> Actions </th>
<td mat-cell *matCellDef="let element">
<button *ngIf="element.status.active" class="account-action-button" mat-raised-button color="primary">
<i class="fa fa-dollar"></i>
</button>
<button *ngIf="element.status.pendingApproval" class="account-action-button" mat-raised-button color="primary">
<i class="fa fa-check"></i>
</button>
<button *ngIf="!element.status.pendingApproval && !element.status.active && !element.status.overpaid"
class="account-action-button" mat-raised-button color="primary">
<i class="fa fa-flag"></i>
</button>
<button *ngIf="!element.status.pendingApproval && !element.status.active && element.status.overpaid"
class="account-action-button" mat-raised-button color="primary">
<i class="fa fa-exchange"></i>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="openLoansColumns"></tr>
<tr mat-row *matRowDef="let row; columns: openLoansColumns;"></tr>
</table>
<!-- Closed Loan Accounts -->
<table *ngIf="showClosedLoanAccounts" mat-table [dataSource]="loanAccounts|accountsFilter:'loan':'closed'"
class="mat-elevation-z1">
<ng-container matColumnDef="Account No">
<th mat-header-cell *matHeaderCellDef> Account No. </th>
<td mat-cell *matCellDef="let element"> <i class="fa fa-stop"
[ngClass]="element.inArrears?'status-active-overdue':(element.status.code|statusLookup)"></i>
{{element.accountNo}}
</td>
</ng-container>
<ng-container matColumnDef="Loan Account">
<th mat-header-cell *matHeaderCellDef> Loan Account </th>
<td mat-cell *matCellDef="let element"> {{element.productName}} </td>
</ng-container>
<ng-container matColumnDef="Original Loan">
<th mat-header-cell *matHeaderCellDef> Original Loan </th>
<td mat-cell *matCellDef="let element"> {{element.lastActiveTransactionDate | date}} </td>
</ng-container>
<ng-container matColumnDef="Loan Balance">
<th mat-header-cell *matHeaderCellDef>Loan Balance </th>
<td mat-cell *matCellDef="let element"> {{element.loanBalance}} </td>
</ng-container>
<ng-container matColumnDef="Amount Paid">
<th mat-header-cell *matHeaderCellDef> Amount Paid </th>
<td mat-cell *matCellDef="let element"> {{element.accountBalance}} </td>
</ng-container>
<ng-container matColumnDef="Type">
<th mat-header-cell *matHeaderCellDef> Type </th>
<td mat-cell *matCellDef="let element">
<i class="fa fa-large" [ngClass]="(element.loanType.value=== 'Individual')?'fa-user':'fa-group'"></i>
</td>
</ng-container>
<ng-container matColumnDef="Closed Date">
<th mat-header-cell *matHeaderCellDef> Closed Date </th>
<td mat-cell *matCellDef="let element">{{element.timeline.closedOnDate | date}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="closedLoansColumns"></tr>
<tr mat-row *matRowDef="let row; columns: closedLoansColumns;"></tr>
</table>
<!-- Saving overview Table -->
<h3>Saving Accounts</h3>
<button mat-raised-button class="f-right" color="primary"
(click)="toggleSavingAccountsOverview()">{{showClosedSavingAccounts?'View Active Accounts':'View Closed Accounts'}}</button>
<table *ngIf="!showClosedSavingAccounts" mat-table [dataSource]="savingAccounts|accountsFilter:'saving'"
class="mat-elevation-z1">
<ng-container matColumnDef="Account No">
<th mat-header-cell *matHeaderCellDef> Account No. </th>
<td mat-cell *matCellDef="let element"> <i class="fa fa-stop" [ngClass]="element.status.code|statusLookup"></i>
{{element.accountNo}} </td>
</ng-container>
<ng-container matColumnDef="Saving Account">
<th mat-header-cell *matHeaderCellDef> Saving Account </th>
<td mat-cell *matCellDef="let element"> {{element.productName}} </td>
</ng-container>
<ng-container matColumnDef="Last Active">
<th mat-header-cell *matHeaderCellDef> Last Active </th>
<td mat-cell *matCellDef="let element"> {{element.lastActiveTransactionDate | date}} </td>
</ng-container>
<ng-container matColumnDef="Balance">
<th mat-header-cell *matHeaderCellDef> Balance </th>
<td mat-cell *matCellDef="let element"> {{element.accountBalance}} </td>
</ng-container>
<ng-container matColumnDef="Actions">
<th mat-header-cell *matHeaderCellDef> Actions </th>
<td mat-cell *matCellDef="let element">
<button *ngIf="element.status.active" class="account-action-button" mat-raised-button color="primary">
<i class="fa fa-arrow-up"></i>
</button>
<button *ngIf="element.status.active" class="account-action-button" mat-raised-button color="primary">
<i class="fa fa-arrow-down"></i>
</button>
<button *ngIf="element.status.submittedAndPendingApproval" class="account-action-button" mat-raised-button
color="primary">
<i class="fa fa-check"></i>
</button>
<button *ngIf="element.status.submittedAndPendingApproval" class="account-action-button" mat-raised-button
color="primary">
<i class="fa fa-check"></i>
</button>
<button *ngIf="!element.status.submittedAndPendingApproval && !element.status.active"
class="account-action-button" mat-raised-button color="primary">
<i class="fa fa-undo"></i>
</button>
<button *ngIf="!element.status.submittedAndPendingApproval && !element.status.active"
class="account-action-button" mat-raised-button color="primary">
<i class="fa fa-check-circle"></i>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="openSavingsColumns"></tr>
<tr mat-row *matRowDef="let row; columns: openSavingsColumns;"></tr>
</table>
<!-- Closed Saving Accounts -->
<table *ngIf="showClosedSavingAccounts" mat-table [dataSource]="savingAccounts|accountsFilter:'saving':'closed'"
class="mat-elevation-z1">
<ng-container matColumnDef="Account No">
<th mat-header-cell *matHeaderCellDef> Account No. </th>
<td mat-cell *matCellDef="let element"> <i class="fa fa-stop" [ngClass]="element.status.code|statusLookup"></i>
{{element.accountNo}} </td>
</ng-container>
<ng-container matColumnDef="Saving Account">
<th mat-header-cell *matHeaderCellDef> Saving Account </th>
<td mat-cell *matCellDef="let element"> {{element.productName}} </td>
</ng-container>
<ng-container matColumnDef="Closed Date">
<th mat-header-cell *matHeaderCellDef> Closed Date </th>
<td mat-cell *matCellDef="let element">{{element.timeline.closedOnDate | date}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="closedSavingsColumns"></tr>
<tr mat-row *matRowDef="let row; columns: closedSavingsColumns;"></tr>
</table>
<!-- Shares overview Table -->
<h3>Shares Accounts</h3>
<button mat-raised-button class="f-right" color="primary"
(click)="toggleShareAccountsOverview()">{{showClosedSavingAccounts?'View Active Accounts':'View Closed Accounts'}}</button>
<table *ngIf="!showClosedShareAccounts" mat-table [dataSource]="shareAccounts|accountsFilter:'share'"
class="mat-elevation-z1">
<ng-container matColumnDef="Account No">
<th mat-header-cell *matHeaderCellDef> Account No. </th>
<td mat-cell *matCellDef="let element"> <i class="fa fa-stop" [ngClass]="element.status.code|statusLookup"></i>
{{element.accountNo}} </td>
</ng-container>
<ng-container matColumnDef="Share Account">
<th mat-header-cell *matHeaderCellDef> Share Account </th>
<td mat-cell *matCellDef="let element"> {{element.productName}} </td>
</ng-container>
<ng-container matColumnDef="Approved Shares">
<th mat-header-cell *matHeaderCellDef> Approved Shares </th>
<td mat-cell *matCellDef="let element"> {{element.totalApprovedShares}} </td>
</ng-container>
<ng-container matColumnDef="Pending For Approval Shares">
<th mat-header-cell *matHeaderCellDef> Pending For Approval Shares </th>
<td mat-cell *matCellDef="let element"> {{element.totalPendingForApprovalShares}} </td>
</ng-container>
<ng-container matColumnDef="Actions">
<th mat-header-cell *matHeaderCellDef> Actions </th>
<td mat-cell *matCellDef="let element">
<button *ngIf="element.status.submittedAndPendingApproval" class="account-action-button" mat-raised-button
color="primary">
<i class="fa fa-check"></i>
</button>
<button *ngIf="!element.status.submittedAndPendingApproval && !element.status.active"
class="account-action-button" mat-raised-button color="primary">
<i class="fa fa-undo"></i>
</button>
<button *ngIf="!element.status.submittedAndPendingApproval && !element.status.active"
class="account-action-button" mat-raised-button color="primary">
<i class="fa fa-check-circle"></i>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="openSharesColumns"></tr>
<tr mat-row *matRowDef="let row; columns: openSharesColumns;"></tr>
</table>
<!-- Closed Share Accounts -->
<table *ngIf="showClosedShareAccounts" mat-table [dataSource]="shareAccounts|accountsFilter:'share':'closed'"
class="mat-elevation-z1">
<ng-container matColumnDef="Account No">
<th mat-header-cell *matHeaderCellDef> Account No. </th>
<td mat-cell *matCellDef="let element"> <i class="fa fa-stop" [ngClass]="element.status.code|statusLookup"></i>
{{element.accountNo}} </td>
</ng-container>
<ng-container matColumnDef="Share Account">
<th mat-header-cell *matHeaderCellDef> Share Account </th>
<td mat-cell *matCellDef="let element"> {{element.productName}} </td>
</ng-container>
<ng-container matColumnDef="Approved Shares">
<th mat-header-cell *matHeaderCellDef> Approved Shares </th>
<td mat-cell *matCellDef="let element"> {{element.totalApprovedShares}} </td>
</ng-container>
<ng-container matColumnDef="Pending For Approval Shares">
<th mat-header-cell *matHeaderCellDef> Pending For Approval Shares </th>
<td mat-cell *matCellDef="let element"> {{element.totalPendingForApprovalShares}} </td>
</ng-container>
<ng-container matColumnDef="Closed Date">
<th mat-header-cell *matHeaderCellDef> Closed Date </th>
<td mat-cell *matCellDef="let element">{{element.timeline.closedOnDate | date}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="closedSharesColumns"></tr>
<tr mat-row *matRowDef="let row; columns: closedSharesColumns;"></tr>
</table>
</div>
@@ -0,0 +1,22 @@
.tab-container {
padding: 1%;
margin: 1%;
h3{
margin:1% auto;
}
.performance-history-container {
border: 1px solid;
padding: 1%;
}
table {
width: 100%;
.account-action-button{
min-width: 26px;
padding: 0 6px;
margin: 4px;
line-height: 25px;
}
}
}
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { GeneralTabComponent } from './general-tab.component';
describe('GeneralTabComponent', () => {
let component: GeneralTabComponent;
let fixture: ComponentFixture<GeneralTabComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ GeneralTabComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(GeneralTabComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,60 @@
/** Angular Imports */
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { DatePipe } from '@angular/common';
@Component({
selector: 'mifosx-general-tab',
templateUrl: './general-tab.component.html',
styleUrls: ['./general-tab.component.scss']
})
export class GeneralTabComponent implements OnInit {
openLoansColumns: string[] = ['Account No', 'Loan Account', 'Original Loan', 'Loan Balance', 'Amount Paid', 'Type', 'Actions'];
closedLoansColumns: string[] = ['Account No', 'Loan Account', 'Original Loan', 'Loan Balance', 'Amount Paid', 'Type', 'Closed Date'];
openSavingsColumns: string[] = ['Account No', 'Saving Account', 'Last Active', 'Balance', 'Actions'];
closedSavingsColumns: string[] = ['Account No', 'Saving Account', 'Closed Date'];
openSharesColumns: string[] = ['Account No', 'Share Account', 'Approved Shares', 'Pending For Approval Shares', 'Actions'];
closedSharesColumns: string[] = ['Account No', 'Share Account', 'Approved Shares', 'Pending For Approval Shares', 'Closed Date'];
upcomingChargesColumns: string[] = ['Name', 'Due as of', 'Due', 'Paid', 'Waived', 'Outstanding', 'Actions'];
clientAccountData: any;
loanAccounts: any;
savingAccounts: any;
shareAccounts: any;
upcomingCharges: any;
clientSummary: any;
showClosedLoanAccounts = false;
showClosedSavingAccounts = false;
showClosedShareAccounts = false;
constructor(
private route: ActivatedRoute,
private router: Router,
private datePipe: DatePipe,
) {
this.route.data.subscribe((data: { clientAccountsData: any, clientChargesData: any, clientSummary: any }) => {
this.clientAccountData = data.clientAccountsData;
this.savingAccounts = data.clientAccountsData.savingsAccounts;
this.loanAccounts = data.clientAccountsData.loanAccounts;
this.shareAccounts = data.clientAccountsData.shareAccounts;
this.upcomingCharges = data.clientChargesData.pageItems;
this.clientSummary = data.clientSummary[0];
});
}
ngOnInit() {
}
toggleLoanAccountsOverview() {
this.showClosedLoanAccounts = !this.showClosedLoanAccounts;
}
toggleSavingAccountsOverview() {
this.showClosedSavingAccounts = !this.showClosedSavingAccounts;
}
toggleShareAccountsOverview() {
this.showClosedShareAccounts = !this.showClosedShareAccounts;
}
}