KYC management: stage-wise notices, pagination, RD base chart
This commit is contained in:
@@ -90,6 +90,21 @@
|
||||
<mifosx-long-text textValue="{{ element.productName }}" chars="35"></mifosx-long-text>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="Branch">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Branch' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
@if (element.officeName) {
|
||||
<span [class]="branchChipClass(element)" [title]="branchChipTitle(element)">
|
||||
<i class="fa fa-map-marker"></i>{{ element.officeName }}
|
||||
@if (isHomeBranch(element)) {
|
||||
<span class="branch-chip__home">Home</span>
|
||||
}
|
||||
</span>
|
||||
} @else {
|
||||
—
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="Last Active">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Last Active' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let element">{{ element.lastActiveTransactionDate | dateFormat }}</td>
|
||||
@@ -203,6 +218,21 @@
|
||||
<mifosx-long-text textValue="{{ element.productName }}" chars="35"></mifosx-long-text>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="Branch">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Branch' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
@if (element.officeName) {
|
||||
<span [class]="branchChipClass(element)" [title]="branchChipTitle(element)">
|
||||
<i class="fa fa-map-marker"></i>{{ element.officeName }}
|
||||
@if (isHomeBranch(element)) {
|
||||
<span class="branch-chip__home">Home</span>
|
||||
}
|
||||
</span>
|
||||
} @else {
|
||||
—
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="Closed Date">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Closed Date' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let element">{{ element.timeline.closedOnDate | dateFormat }}</td>
|
||||
@@ -262,6 +292,21 @@
|
||||
<mifosx-long-text textValue="{{ element.productName }}" chars="35"></mifosx-long-text>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="Branch">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Branch' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
@if (element.officeName) {
|
||||
<span [class]="branchChipClass(element)" [title]="branchChipTitle(element)">
|
||||
<i class="fa fa-map-marker"></i>{{ element.officeName }}
|
||||
@if (isHomeBranch(element)) {
|
||||
<span class="branch-chip__home">Home</span>
|
||||
}
|
||||
</span>
|
||||
} @else {
|
||||
—
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="Last Active">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Last Active' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let element">{{ element.lastActiveTransactionDate | dateFormat }}</td>
|
||||
@@ -452,6 +497,21 @@
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Fixed Deposit Product' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let element">{{ element.productName }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="Branch">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Branch' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
@if (element.officeName) {
|
||||
<span [class]="branchChipClass(element)" [title]="branchChipTitle(element)">
|
||||
<i class="fa fa-map-marker"></i>{{ element.officeName }}
|
||||
@if (isHomeBranch(element)) {
|
||||
<span class="branch-chip__home">Home</span>
|
||||
}
|
||||
</span>
|
||||
} @else {
|
||||
—
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="Last Active">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Last Active' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let element">{{ element.lastActiveTransactionDate | dateFormat }}</td>
|
||||
@@ -551,6 +611,21 @@
|
||||
<mifosx-long-text textValue="{{ element.productName }}" chars="35"></mifosx-long-text>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="Branch">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Branch' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
@if (element.officeName) {
|
||||
<span [class]="branchChipClass(element)" [title]="branchChipTitle(element)">
|
||||
<i class="fa fa-map-marker"></i>{{ element.officeName }}
|
||||
@if (isHomeBranch(element)) {
|
||||
<span class="branch-chip__home">Home</span>
|
||||
}
|
||||
</span>
|
||||
} @else {
|
||||
—
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="Closed Date">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Closed Date' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let element">{{ element.timeline.closedOnDate | dateFormat }}</td>
|
||||
@@ -611,10 +686,27 @@
|
||||
<mifosx-long-text textValue="{{ element.productName }}" chars="35"></mifosx-long-text>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="Branch">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Branch' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
@if (element.officeName) {
|
||||
<span [class]="branchChipClass(element)" [title]="branchChipTitle(element)">
|
||||
<i class="fa fa-map-marker"></i>{{ element.officeName }}
|
||||
@if (isHomeBranch(element)) {
|
||||
<span class="branch-chip__home">Home</span>
|
||||
}
|
||||
</span>
|
||||
} @else {
|
||||
—
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="Status">
|
||||
<th mat-header-cell *matHeaderCellDef>Status</th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<span class="rd-status" [class.rd-status--ended]="element.timeline?.closedOnDate">{{ rdStatusLabel(element) }}</span>
|
||||
<span class="rd-status" [class.rd-status--ended]="element.timeline?.closedOnDate">{{
|
||||
rdStatusLabel(element)
|
||||
}}</span>
|
||||
@if (element.timeline?.closedOnDate) {
|
||||
<div class="rd-status__date">on {{ element.timeline.closedOnDate | dateFormat }}</div>
|
||||
}
|
||||
|
||||
@@ -440,3 +440,66 @@ table[mat-table] {
|
||||
font-size: 11px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
/*
|
||||
* Branch an account is held at, as a pill coloured by where the signed-in user works: green for their own branch, red for
|
||||
* any other. When the customer banks at several other branches the reds run from light to dark, one shade per branch.
|
||||
*/
|
||||
.branch-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 3px 10px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid transparent;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
|
||||
.fa {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&--mine {
|
||||
background: #dcfce7;
|
||||
color: #166534;
|
||||
border-color: #4ade80;
|
||||
}
|
||||
|
||||
&--other-0 {
|
||||
background: #fee2e2;
|
||||
color: #991b1b;
|
||||
border-color: #fca5a5;
|
||||
}
|
||||
|
||||
&--other-1 {
|
||||
background: #fca5a5;
|
||||
color: #7f1d1d;
|
||||
border-color: #f87171;
|
||||
}
|
||||
|
||||
&--other-2 {
|
||||
background: #dc2626;
|
||||
color: #fff;
|
||||
border-color: #b91c1c;
|
||||
}
|
||||
|
||||
&--other-3 {
|
||||
background: #991b1b;
|
||||
color: #fff;
|
||||
border-color: #7f1d1d;
|
||||
}
|
||||
}
|
||||
|
||||
.branch-chip__home {
|
||||
padding: 0 6px;
|
||||
border-radius: 8px;
|
||||
|
||||
/* fixed colours: the chip text turns white on the dark reds, which would vanish against this light tag */
|
||||
background: rgb(255 255 255 / 85%);
|
||||
color: #1f2937;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import { DateFormatPipe } from '../../../pipes/date-format.pipe';
|
||||
import { FormatNumberPipe } from '../../../pipes/format-number.pipe';
|
||||
import { CurrencyPipe } from '@angular/common';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
import { AuthenticationService } from '../../../core/authentication/authentication.service';
|
||||
import { ReportsService } from 'app/reports/reports.service';
|
||||
import { SettingsService } from 'app/settings/settings.service';
|
||||
import { Subject } from 'rxjs';
|
||||
@@ -151,6 +152,7 @@ export class GeneralTabComponent implements OnDestroy {
|
||||
private router = inject(Router);
|
||||
private reportsService = inject(ReportsService);
|
||||
private settingsService = inject(SettingsService);
|
||||
private authenticationService = inject(AuthenticationService);
|
||||
|
||||
/** Open Loan Accounts Columns */
|
||||
openLoansColumns: string[] = [
|
||||
@@ -177,6 +179,7 @@ export class GeneralTabComponent implements OnDestroy {
|
||||
openSavingsColumns: string[] = [
|
||||
'Account No',
|
||||
'Saving Account',
|
||||
'Branch',
|
||||
'Last Active',
|
||||
'Balance',
|
||||
'Actions'
|
||||
@@ -185,12 +188,14 @@ export class GeneralTabComponent implements OnDestroy {
|
||||
closedSavingsColumns: string[] = [
|
||||
'Account No',
|
||||
'Saving Account',
|
||||
'Branch',
|
||||
'Closed Date'
|
||||
];
|
||||
/** Closed Recurring Deposit Accounts Columns - with how each one ended, for tracking. */
|
||||
recurringColumns: string[] = [
|
||||
'Account No',
|
||||
'Saving Account',
|
||||
'Branch',
|
||||
'Status',
|
||||
'Last Active',
|
||||
'Balance',
|
||||
@@ -336,12 +341,76 @@ export class GeneralTabComponent implements OnDestroy {
|
||||
/** Client Id */
|
||||
clientid: any;
|
||||
|
||||
/** The customer's own (home) branch, to tell it from branches the customer banks at elsewhere. */
|
||||
clientOfficeId: number | null = null;
|
||||
|
||||
/** How many red shades the branch chips have (general-tab.component.scss, .branch-chip--other-0 ... -3), light to dark. */
|
||||
private static readonly OTHER_BRANCH_SHADES = 4;
|
||||
|
||||
/** The branch the signed-in user works at: accounts held here read green, every other branch red. */
|
||||
private get myOfficeId(): number | null {
|
||||
const officeId = this.authenticationService.getCredentials()?.officeId;
|
||||
return officeId == null ? null : Number(officeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* The other branches this customer banks at, lightest to darkest. Ordered by branch id so the shades stay put between
|
||||
* visits; computed over every deposit account on the page so one branch has the same shade in every table.
|
||||
*/
|
||||
private otherBranchIds(): number[] {
|
||||
const mine = this.myOfficeId;
|
||||
const ids = new Set<number>();
|
||||
for (const account of [
|
||||
...this.savingAccounts,
|
||||
...this.currentAccounts
|
||||
]) {
|
||||
const officeId = Number(account?.officeId);
|
||||
if (Number.isFinite(officeId) && officeId !== mine) {
|
||||
ids.add(officeId);
|
||||
}
|
||||
}
|
||||
return [...ids].sort((a, b) => a - b);
|
||||
}
|
||||
|
||||
/** Green for an account at the signed-in user's branch; red for any other branch, a different shade for each. */
|
||||
branchChipClass(account: any): string {
|
||||
const officeId = Number(account?.officeId);
|
||||
if (officeId === this.myOfficeId) {
|
||||
return 'branch-chip branch-chip--mine';
|
||||
}
|
||||
const others = this.otherBranchIds();
|
||||
const rank = Math.max(others.indexOf(officeId), 0);
|
||||
const last = GeneralTabComponent.OTHER_BRANCH_SHADES - 1;
|
||||
// one other branch gets a mid red, several are spread from the lightest to the darkest
|
||||
const shade = others.length <= 1 ? 1 : Math.round((rank * last) / (others.length - 1));
|
||||
return `branch-chip branch-chip--other-${shade}`;
|
||||
}
|
||||
|
||||
branchChipTitle(account: any): string {
|
||||
const parts = [account?.officeName];
|
||||
if (Number(account?.officeId) === this.myOfficeId) {
|
||||
parts.push('your branch');
|
||||
}
|
||||
if (this.isHomeBranch(account)) {
|
||||
parts.push('customer home branch');
|
||||
}
|
||||
return parts.join(' - ');
|
||||
}
|
||||
|
||||
isHomeBranch(account: any): boolean {
|
||||
return this.clientOfficeId != null && Number(account?.officeId) === this.clientOfficeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ActivatedRoute} route Activated Route
|
||||
* @param {ClientsService} clientService Clients Service
|
||||
* @param {Router} router Router
|
||||
*/
|
||||
constructor() {
|
||||
// The customer's record is resolved on the parent route, not on this tab's own route data.
|
||||
this.route.parent?.data.subscribe((data: any) => {
|
||||
this.clientOfficeId = data?.clientViewData?.officeId ?? null;
|
||||
});
|
||||
this.route.data.subscribe(
|
||||
(data: { clientAccountsData: any; clientChargesData: any; clientSummary: any; clientCollateralData: any }) => {
|
||||
this.clientAccountData = data.clientAccountsData;
|
||||
|
||||
Reference in New Issue
Block a user