feat: Client & CKYC UI improvements — filters, KYC management, image crop+merge
- Clients list: CKYC icon, CKYC Number column, status/legal form/CKYC filters - CKYC: SFTP upload logs, date formatting, modal z-index fix, non-dismissible dialogs - KYC Management: new standalone module with alert dashboard, notification scheduler, audit inventory, account status - Image cropper: ngx-image-cropper integration for front+back document merge into single PNG - Sidebar: KYC Management menu entry after CKYC Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
79e455c4ed
commit
d72e969dc3
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright since 2025 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
|
||||
import { Route } from '../core/route/route.service';
|
||||
import { KycManagementComponent } from './kyc-management.component';
|
||||
|
||||
const routes: Routes = [
|
||||
Route.withShell([
|
||||
{
|
||||
path: 'kyc-management',
|
||||
data: { title: 'KYC Management', breadcrumb: 'KYC Management' },
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: KycManagementComponent
|
||||
}
|
||||
]
|
||||
}
|
||||
])
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class KycManagementRoutingModule {}
|
||||
@@ -0,0 +1,769 @@
|
||||
<!--
|
||||
Copyright since 2025 Mifos Initiative
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<div class="km-root">
|
||||
<!-- Inner tabs -->
|
||||
<div class="km-inner-nav">
|
||||
<button class="km-itab" [class.active]="activeSection === 'alerts'" (click)="showSection('alerts')">
|
||||
<fa-icon icon="bell"></fa-icon>
|
||||
Alert Dashboard
|
||||
<span class="km-cnt km-cnt--red">81</span>
|
||||
</button>
|
||||
<button class="km-itab" [class.active]="activeSection === 'notif'" (click)="showSection('notif')">
|
||||
<fa-icon icon="paper-plane"></fa-icon>
|
||||
Notification Scheduler
|
||||
<span class="km-cnt km-cnt--amb">3</span>
|
||||
</button>
|
||||
<button class="km-itab" [class.active]="activeSection === 'audit'" (click)="showSection('audit')">
|
||||
<fa-icon icon="clipboard-list"></fa-icon>
|
||||
Audit Inventory
|
||||
<span class="km-cnt">12</span>
|
||||
</button>
|
||||
<button class="km-itab" [class.active]="activeSection === 'status'" (click)="showSection('status')">
|
||||
<fa-icon icon="lock"></fa-icon>
|
||||
Account Status
|
||||
<span class="km-cnt km-cnt--red">34</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="km-main">
|
||||
<!-- ALERT DASHBOARD -->
|
||||
@if (activeSection === 'alerts') {
|
||||
<div class="km-page">
|
||||
<div class="km-page-head">
|
||||
<span class="km-page-title">KYC Alert Dashboard</span>
|
||||
<div class="km-actions">
|
||||
<button class="km-btn km-btn-g" (click)="toast('Exporting alert report...')">
|
||||
<fa-icon icon="download"></fa-icon> Export
|
||||
</button>
|
||||
<button class="km-btn km-btn-t" (click)="toast('Running scheduler...')">
|
||||
<fa-icon icon="play"></fa-icon> Run Scheduler
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="km-stat-row">
|
||||
<div class="km-stat" (click)="toast('Filtering: Expiring in 30 days')">
|
||||
<div class="km-stat-n" style="color: var(--km-amber)">47</div>
|
||||
<div class="km-stat-l">Expiring in 30 days</div>
|
||||
<div class="km-stat-t" style="color: var(--km-amber)">▲ 12 this month</div>
|
||||
</div>
|
||||
<div class="km-stat" (click)="toast('Filtering: Debit frozen')">
|
||||
<div class="km-stat-n" style="color: var(--km-orange)">23</div>
|
||||
<div class="km-stat-l">Debit Frozen</div>
|
||||
<div class="km-stat-t" style="color: var(--km-orange)">Action required</div>
|
||||
</div>
|
||||
<div class="km-stat" (click)="toast('Filtering: Inoperative')">
|
||||
<div class="km-stat-n" style="color: var(--km-red)">8</div>
|
||||
<div class="km-stat-l">Inoperative</div>
|
||||
<div class="km-stat-t" style="color: var(--km-red)">Escalating</div>
|
||||
</div>
|
||||
<div class="km-stat" (click)="toast('Filtering: Dormant')">
|
||||
<div class="km-stat-n" style="color: var(--km-g600)">3</div>
|
||||
<div class="km-stat-l">Dormant</div>
|
||||
<div class="km-stat-t" style="color: var(--km-g500)">RBI reportable</div>
|
||||
</div>
|
||||
<div class="km-stat" (click)="toast('Filtering: Notices sent today')">
|
||||
<div class="km-stat-n" style="color: var(--km-blue)">18</div>
|
||||
<div class="km-stat-l">Notices Today</div>
|
||||
<div class="km-stat-t" style="color: var(--km-blue)">SMS + Email</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="km-filters">
|
||||
<select class="km-fsel">
|
||||
<option>All Risk</option><option>Low</option><option>Mid</option><option>High</option>
|
||||
</select>
|
||||
<select class="km-fsel">
|
||||
<option>All States</option><option>Alert Window</option><option>Debit Freeze</option>
|
||||
<option>Inoperative</option><option>Dormant</option>
|
||||
</select>
|
||||
<select class="km-fsel">
|
||||
<option>All Notices</option><option>Notice 1 Pending</option><option>Notice 2 Pending</option>
|
||||
<option>Notice 3 Pending</option><option>All 3 Sent</option>
|
||||
</select>
|
||||
<input class="km-finp" type="text" placeholder="Search name or account..." />
|
||||
<button class="km-btn km-btn-t" (click)="toast('Filters applied')">Apply</button>
|
||||
</div>
|
||||
|
||||
<div class="km-card">
|
||||
<div class="km-ch">
|
||||
<div class="km-ci km-ci-a"><fa-icon icon="bell"></fa-icon></div>
|
||||
<div class="km-ct">Accounts Requiring Action</div>
|
||||
<span class="km-ch-hint">Showing {{ alertRows.length }} of 81 · Click row for client details</span>
|
||||
</div>
|
||||
<table class="km-tbl">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Customer</th><th>Risk</th><th>Expiry Date</th><th>Days Left</th>
|
||||
<th>Notices</th><th>State</th><th>Pending Action</th><th>Next Stage</th><th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (row of alertRows; track row.id) {
|
||||
<tr (click)="openModal(row.id)">
|
||||
<td>
|
||||
<div class="km-an">{{ row.name }}</div>
|
||||
<div class="km-ai">{{ row.account }}</div>
|
||||
</td>
|
||||
<td><span class="km-b" [ngClass]="riskClass(row.risk)">{{ row.riskLabel.split(' ')[0] }}</span></td>
|
||||
<td>{{ row.expiry }}</td>
|
||||
<td>
|
||||
<div class="km-pbar">
|
||||
<span class="km-pbar-n">{{ row.daysLeft > 0 ? row.daysLeft : '—' }}</span>
|
||||
<div class="km-pbar-bg">
|
||||
<div
|
||||
class="km-pbar-fill"
|
||||
[style.width.%]="barWidth(row)"
|
||||
[style.background]="barColor(row)"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="km-b km-b-bl">{{ row.noticesSent }}/3</span></td>
|
||||
<td><span class="km-sp" [ngClass]="stateClass(row.state)">{{ stateLabel(row.state) }}</span></td>
|
||||
<td><span class="km-b km-b-am">{{ row.noticePending }}</span></td>
|
||||
<td><span class="km-b km-b-or">{{ row.nextStage }}</span></td>
|
||||
<td>
|
||||
<button class="km-btn km-btn-ot" (click)="$event.stopPropagation(); openModal(row.id)">View</button>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- NOTIFICATION SCHEDULER -->
|
||||
@if (activeSection === 'notif') {
|
||||
<div class="km-page">
|
||||
<div class="km-page-head">
|
||||
<span class="km-page-title">Notification Scheduler</span>
|
||||
<div class="km-actions">
|
||||
<button class="km-btn km-btn-g" (click)="toast('Viewing scheduler history...')">
|
||||
<fa-icon icon="history"></fa-icon> History
|
||||
</button>
|
||||
<button class="km-btn km-btn-t" (click)="toast('Dispatching selected notices...')">
|
||||
<fa-icon icon="paper-plane"></fa-icon> Dispatch Now
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="km-notif-grid">
|
||||
<div class="km-card">
|
||||
<div class="km-ch">
|
||||
<div class="km-ci km-ci-a"><fa-icon icon="calendar-alt"></fa-icon></div>
|
||||
<div class="km-ct">3-Notice Dispatch Timeline</div>
|
||||
<span class="km-ch-hint">Expiry: 30 Apr 2027 · Window: 31 Mar – 30 Apr</span>
|
||||
</div>
|
||||
<div class="km-cb">
|
||||
<div class="km-ntl">
|
||||
<div class="km-nt">
|
||||
<div class="km-nd-col"><div class="km-nd km-nd1">1</div><div class="km-nl"></div></div>
|
||||
<div>
|
||||
<div class="km-nh">Notice 1 — Alert window opens</div>
|
||||
<div class="km-ns">31 March 2027 · Auto-scheduled · <span class="km-ok">✓ Delivered</span></div>
|
||||
<div class="km-ntags">
|
||||
<span class="km-cp km-cp-sms">SMS</span>
|
||||
<span class="km-cp km-cp-mail">Email</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="km-nt">
|
||||
<div class="km-nd-col"><div class="km-nd km-nd2">2</div><div class="km-nl"></div></div>
|
||||
<div>
|
||||
<div class="km-nh">Notice 2 — Follow-up</div>
|
||||
<div class="km-ns">10 April 2027 · +10 days · <span class="km-pending">⏳ Pending</span></div>
|
||||
<div class="km-ntags">
|
||||
<span class="km-cp km-cp-sms">SMS</span>
|
||||
<span class="km-cp km-cp-mail">Email</span>
|
||||
<span class="km-cp km-cp-ivr">IVR Call</span>
|
||||
<span class="km-cp km-cp-post">Reg. Post</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="km-nt">
|
||||
<div class="km-nd-col"><div class="km-nd km-nd3">3</div><div class="km-nl"></div></div>
|
||||
<div>
|
||||
<div class="km-nh">Notice 3 — Final notice</div>
|
||||
<div class="km-ns">20 April 2027 · +20 days · <span class="km-fail">✗ Not dispatched</span></div>
|
||||
<div class="km-ntags">
|
||||
<span class="km-cp km-cp-sms">SMS</span>
|
||||
<span class="km-cp km-cp-mail">Email</span>
|
||||
<span class="km-cp km-cp-ivr">IVR</span>
|
||||
<span class="km-cp km-cp-post">Reg. Post</span>
|
||||
<span class="km-cp km-cp-vis">Branch Visit</span>
|
||||
</div>
|
||||
<div class="km-row-actions">
|
||||
<button class="km-btn km-btn-t" (click)="toast('Notice 3 scheduled for 20 Apr 2027')">Schedule Notice 3</button>
|
||||
<button class="km-btn km-btn-ot" (click)="toast('Video KYC link added to Notice 3')">+ Video KYC Link</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="km-nt">
|
||||
<div class="km-nd-col"><div class="km-nd km-ndf">!</div><div class="km-nl"></div></div>
|
||||
<div>
|
||||
<div class="km-nh km-warn">Debit Freeze — if no action</div>
|
||||
<div class="km-ns">30 April 2027 · Auto-triggered on expiry</div>
|
||||
<div class="km-ntags">
|
||||
<span class="km-b km-b-am">Auto-triggered</span>
|
||||
<span class="km-b km-b-gy">Credits allowed</span>
|
||||
<span class="km-b km-b-gy">Debits blocked</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="km-nt">
|
||||
<div class="km-nd-col"><div class="km-nd km-ndi">!</div><div class="km-nl"></div></div>
|
||||
<div>
|
||||
<div class="km-nh km-warn-o">Inoperative — +60 days</div>
|
||||
<div class="km-ns">30 June 2027 · No compliance</div>
|
||||
<div class="km-ntags"><span class="km-b km-b-or">All txns blocked</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="km-nt km-nt-last">
|
||||
<div class="km-nd-col"><div class="km-nd km-ndd">!</div></div>
|
||||
<div>
|
||||
<div class="km-nh km-warn-d">Dormant — +150 days</div>
|
||||
<div class="km-ns">30 September 2027</div>
|
||||
<div class="km-ntags">
|
||||
<span class="km-b km-b-gy">Dormant</span>
|
||||
<span class="km-b km-b-re">RBI reporting</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="km-side">
|
||||
<div class="km-card">
|
||||
<div class="km-ch">
|
||||
<div class="km-ci km-ci-b"><fa-icon icon="broadcast-tower"></fa-icon></div>
|
||||
<div class="km-ct">Channels — Notice 3</div>
|
||||
</div>
|
||||
<div class="km-cb km-channels">
|
||||
<div class="km-channel km-channel-on" (click)="toast('SMS toggled')">
|
||||
<div class="km-channel-i">📱</div>
|
||||
<div class="km-channel-n">SMS</div>
|
||||
<div class="km-channel-d">Registered mobile</div>
|
||||
</div>
|
||||
<div class="km-channel km-channel-on" (click)="toast('Email toggled')">
|
||||
<div class="km-channel-i">📧</div>
|
||||
<div class="km-channel-n">Email</div>
|
||||
<div class="km-channel-d">Registered email</div>
|
||||
</div>
|
||||
<div class="km-channel km-channel-on" (click)="toast('IVR toggled')">
|
||||
<div class="km-channel-i">📞</div>
|
||||
<div class="km-channel-n">IVR Call</div>
|
||||
<div class="km-channel-d">Outbound IVR</div>
|
||||
</div>
|
||||
<div class="km-channel km-channel-on" (click)="toast('Reg Post toggled')">
|
||||
<div class="km-channel-i">📮</div>
|
||||
<div class="km-channel-n">Reg. Post</div>
|
||||
<div class="km-channel-d">India Post</div>
|
||||
</div>
|
||||
<div class="km-channel" (click)="toast('Branch visit toggled')">
|
||||
<div class="km-channel-i">🏦</div>
|
||||
<div class="km-channel-n">Branch Visit</div>
|
||||
<div class="km-channel-d">Manual</div>
|
||||
</div>
|
||||
<div class="km-channel" (click)="toast('Video KYC toggled')">
|
||||
<div class="km-channel-i">📹</div>
|
||||
<div class="km-channel-n">Video KYC</div>
|
||||
<div class="km-channel-d">V-CIP app</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="km-card">
|
||||
<div class="km-ch">
|
||||
<div class="km-ci km-ci-b"><fa-icon icon="envelope"></fa-icon></div>
|
||||
<div class="km-ct">India Post Notice</div>
|
||||
</div>
|
||||
<div class="km-cb">
|
||||
<div class="km-mi"><div class="km-ml">Dispatch Date</div><div class="km-mv">10 April 2027</div></div>
|
||||
<div class="km-mi"><div class="km-ml">Reg. Post Docket No.</div><div class="km-mv km-mono">RP-BPL-20270410-443</div></div>
|
||||
<div class="km-mi"><div class="km-ml">Status</div><div class="km-mv"><span class="km-b km-b-bl">In Transit</span></div></div>
|
||||
<button class="km-btn km-btn-g km-w" (click)="toast('Tracking RP-BPL-20270410-443 on India Post...')">Track Delivery</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="km-card">
|
||||
<div class="km-ch">
|
||||
<div class="km-ci km-ci-t"><fa-icon icon="building"></fa-icon></div>
|
||||
<div class="km-ct">Branch Visit</div>
|
||||
</div>
|
||||
<div class="km-cb">
|
||||
<div class="km-ml">Scheduled Date</div>
|
||||
<input type="date" class="km-finp km-w" value="2027-04-20" />
|
||||
<div class="km-ml" style="margin-top: 8px">Officer</div>
|
||||
<select class="km-fsel km-w">
|
||||
<option>Anupam Mishra (BPL Main)</option>
|
||||
<option>Rekha Sharma (BPL South)</option>
|
||||
</select>
|
||||
<button class="km-btn km-btn-t km-w" (click)="toast('Branch visit scheduled — Anupam Mishra')">
|
||||
Schedule Visit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- AUDIT INVENTORY -->
|
||||
@if (activeSection === 'audit') {
|
||||
<div class="km-page">
|
||||
<div class="km-page-head">
|
||||
<span class="km-page-title">Audit Inventory</span>
|
||||
<div class="km-actions">
|
||||
<button class="km-btn km-btn-g" (click)="toast('Exporting CSV...')">
|
||||
<fa-icon icon="download"></fa-icon> Export CSV
|
||||
</button>
|
||||
<button class="km-btn km-btn-n" (click)="toast('Generating RBI audit report...')">
|
||||
<fa-icon icon="file-alt"></fa-icon> RBI Report
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="km-filters">
|
||||
<select class="km-fsel">
|
||||
<option>All Channels</option><option>SMS</option><option>Email</option><option>IVR</option>
|
||||
<option>Reg. Post</option><option>Branch Visit</option><option>Video KYC</option>
|
||||
</select>
|
||||
<select class="km-fsel">
|
||||
<option>All Statuses</option><option>Delivered</option><option>In Transit</option>
|
||||
<option>Pending</option><option>Failed</option>
|
||||
</select>
|
||||
<select class="km-fsel">
|
||||
<option>All Notice Nos.</option><option>Notice 1</option><option>Notice 2</option><option>Notice 3</option>
|
||||
</select>
|
||||
<input class="km-finp" type="text" placeholder="Search account, name or reference..." />
|
||||
<button class="km-btn km-btn-t" (click)="toast('Filters applied')">Apply</button>
|
||||
</div>
|
||||
|
||||
<div class="km-card">
|
||||
<div class="km-ch">
|
||||
<div class="km-ci km-ci-t"><fa-icon icon="clipboard-list"></fa-icon></div>
|
||||
<div class="km-ct">KYC Notice Log</div>
|
||||
<span class="km-ch-hint">Click row for client details</span>
|
||||
</div>
|
||||
<table class="km-tbl">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th><th>Account / Customer</th><th>Notice</th><th>Channel</th>
|
||||
<th>Dispatched</th><th>Reference / Docket</th><th>Delivery</th>
|
||||
<th>Client Action</th><th>Officer</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr (click)="openModal('rajesh')">
|
||||
<td class="km-num">001</td>
|
||||
<td><div class="km-an">Rajesh K. Sharma</div><div class="km-ai">ACC-BPL-2024-00847</div></td>
|
||||
<td><span class="km-b km-b-bl">Notice 1</span></td>
|
||||
<td>SMS + Email</td>
|
||||
<td>31 Mar 2027</td>
|
||||
<td><span class="km-ref">MSG-2027-0331-001</span></td>
|
||||
<td><span class="km-del km-d-ok">✓ Delivered</span></td>
|
||||
<td><span class="km-del km-d-pe">Awaited</span></td>
|
||||
<td class="km-small">Scheduler</td>
|
||||
</tr>
|
||||
<tr (click)="openModal('rajesh')">
|
||||
<td class="km-num">002</td>
|
||||
<td><div class="km-an">Rajesh K. Sharma</div><div class="km-ai">ACC-BPL-2024-00847</div></td>
|
||||
<td><span class="km-b km-b-am">Notice 2</span></td>
|
||||
<td>Reg. Post + IVR</td>
|
||||
<td>10 Apr 2027</td>
|
||||
<td><span class="km-ref">RP-BPL-20270410-443</span></td>
|
||||
<td><span class="km-del km-d-tr">In Transit</span></td>
|
||||
<td><span class="km-del km-d-pe">Awaited</span></td>
|
||||
<td class="km-small">Scheduler</td>
|
||||
</tr>
|
||||
<tr (click)="openModal('priya')">
|
||||
<td class="km-num">003</td>
|
||||
<td><div class="km-an">Priya Malhotra</div><div class="km-ai">ACC-BPL-2021-00231</div></td>
|
||||
<td><span class="km-b km-b-bl">Notice 1</span></td>
|
||||
<td>SMS + Email</td>
|
||||
<td>10 Mar 2027</td>
|
||||
<td><span class="km-ref">MSG-2027-0310-003</span></td>
|
||||
<td><span class="km-del km-d-ok">✓ Delivered</span></td>
|
||||
<td><span class="km-del km-d-fa">No Response</span></td>
|
||||
<td class="km-small">Scheduler</td>
|
||||
</tr>
|
||||
<tr (click)="openModal('priya')">
|
||||
<td class="km-num">004</td>
|
||||
<td><div class="km-an">Priya Malhotra</div><div class="km-ai">ACC-BPL-2021-00231</div></td>
|
||||
<td><span class="km-b km-b-am">Notice 2</span></td>
|
||||
<td>Reg. Post</td>
|
||||
<td>20 Mar 2027</td>
|
||||
<td><span class="km-ref">RP-BPL-20270320-091</span></td>
|
||||
<td><span class="km-del km-d-ok">✓ Delivered</span></td>
|
||||
<td><span class="km-del km-d-fa">No Response</span></td>
|
||||
<td class="km-small">Scheduler</td>
|
||||
</tr>
|
||||
<tr (click)="openModal('priya')">
|
||||
<td class="km-num">005</td>
|
||||
<td><div class="km-an">Priya Malhotra</div><div class="km-ai">ACC-BPL-2021-00231</div></td>
|
||||
<td><span class="km-b km-b-or">Notice 3</span></td>
|
||||
<td>Branch Visit</td>
|
||||
<td>30 Mar 2027</td>
|
||||
<td><span class="km-ref">VIS-BPL-20270330-012</span></td>
|
||||
<td><span class="km-del km-d-ok">✓ Done</span></td>
|
||||
<td><span class="km-del km-d-fa">Refused</span></td>
|
||||
<td class="km-small">A. Mishra</td>
|
||||
</tr>
|
||||
<tr (click)="openModal('suresh')">
|
||||
<td class="km-num">006</td>
|
||||
<td><div class="km-an">Suresh Patel</div><div class="km-ai">ACC-BPL-2019-00112</div></td>
|
||||
<td><span class="km-b km-b-or">Notice 3</span></td>
|
||||
<td>Video KYC</td>
|
||||
<td>5 Feb 2027</td>
|
||||
<td><span class="km-ref">VKYC-2027-0205-007</span></td>
|
||||
<td><span class="km-del km-d-fa">No Response</span></td>
|
||||
<td><span class="km-del km-d-fa">No Action</span></td>
|
||||
<td class="km-small">System</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="km-pagination">
|
||||
<span class="km-ch-hint">Showing 6 of 12</span>
|
||||
<div class="km-actions">
|
||||
<button class="km-btn km-btn-g">← Prev</button>
|
||||
<button class="km-btn km-btn-t" (click)="toast('Loading next page...')">Next →</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- ACCOUNT STATUS -->
|
||||
@if (activeSection === 'status') {
|
||||
<div class="km-page">
|
||||
<div class="km-page-head">
|
||||
<span class="km-page-title">Account Status Management</span>
|
||||
<button class="km-btn km-btn-n" (click)="toast('Running state transitions...')">
|
||||
<fa-icon icon="play"></fa-icon> Run Transitions
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="km-sc-row">
|
||||
<div class="km-sc km-sc-ac" (click)="toast('Filtering: Active')">
|
||||
<div class="km-sc-l">Active</div><div class="km-sc-n">1,847</div><div class="km-sc-d">KYC valid</div>
|
||||
</div>
|
||||
<div class="km-sc km-sc-al" (click)="toast('Filtering: Alert window')">
|
||||
<div class="km-sc-l">Alert Window</div><div class="km-sc-n">47</div><div class="km-sc-d">Expiring < 30d</div>
|
||||
</div>
|
||||
<div class="km-sc km-sc-fr" (click)="toast('Filtering: Debit Freeze')">
|
||||
<div class="km-sc-l">Debit Freeze</div><div class="km-sc-n">23</div><div class="km-sc-d">Debits blocked</div>
|
||||
</div>
|
||||
<div class="km-sc km-sc-in" (click)="toast('Filtering: Inoperative')">
|
||||
<div class="km-sc-l">Inoperative</div><div class="km-sc-n">8</div><div class="km-sc-d">All txns blocked</div>
|
||||
</div>
|
||||
<div class="km-sc km-sc-do" (click)="toast('Filtering: Dormant')">
|
||||
<div class="km-sc-l">Dormant</div><div class="km-sc-n">3</div><div class="km-sc-d">RBI reportable</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="km-r2">
|
||||
<div class="km-card">
|
||||
<div class="km-ch">
|
||||
<div class="km-ci km-ci-a"><fa-icon icon="cog"></fa-icon></div>
|
||||
<div class="km-ct">Auto-Transition Rules</div>
|
||||
</div>
|
||||
<div class="km-cb-flush">
|
||||
<div class="km-rule">
|
||||
<div class="km-rule-h"><span class="km-rule-t">Active → Debit Freeze</span><span class="km-b km-b-gr">Enabled</span></div>
|
||||
<div class="km-rule-d">KYC expires + 3 notices sent + no response → debits blocked, credits allowed</div>
|
||||
<div class="km-rule-tags">
|
||||
<span class="km-b km-b-gy">Window: 31 Mar – 30 Apr</span>
|
||||
<span class="km-b km-b-gy">3/3 notices</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="km-rule">
|
||||
<div class="km-rule-h"><span class="km-rule-t">Debit Freeze → Inoperative</span><span class="km-b km-b-gr">Enabled</span></div>
|
||||
<div class="km-rule-d">+60 days from expiry, still unresolved → all transactions blocked</div>
|
||||
<div class="km-rule-tags"><span class="km-b km-b-gy">Apr 30 – Jun 30</span></div>
|
||||
</div>
|
||||
<div class="km-rule km-rule-last">
|
||||
<div class="km-rule-h"><span class="km-rule-t">Inoperative → Dormant</span><span class="km-b km-b-gr">Enabled</span></div>
|
||||
<div class="km-rule-d">+150 days from expiry → dormant, RBI Dormant Account reporting triggered</div>
|
||||
<div class="km-rule-tags">
|
||||
<span class="km-b km-b-gy">Jun 30 – Sep 30</span>
|
||||
<span class="km-b km-b-re">RBI notification</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="km-card">
|
||||
<div class="km-ch">
|
||||
<div class="km-ci km-ci-r"><fa-icon icon="user-shield"></fa-icon></div>
|
||||
<div class="km-ct">Manual Override</div>
|
||||
<span class="km-b km-b-am km-ml-auto">Maker-Checker</span>
|
||||
</div>
|
||||
<div class="km-cb">
|
||||
<div class="km-ml">Account</div>
|
||||
<select class="km-fsel km-w">
|
||||
<option>ACC-BPL-2021-00231 — Priya Malhotra</option>
|
||||
<option>ACC-BPL-2019-00112 — Suresh Patel</option>
|
||||
</select>
|
||||
<div class="km-ml" style="margin-top: 10px">Current State</div>
|
||||
<div><span class="km-sp km-sp-fr">Debit Freeze</span></div>
|
||||
<div class="km-ml" style="margin-top: 10px">Override Action</div>
|
||||
<select class="km-fsel km-w">
|
||||
<option>Select action...</option>
|
||||
<option>Unfreeze — KYC Renewed</option>
|
||||
<option>Unfreeze — Manager Override</option>
|
||||
<option>Mark Inoperative</option>
|
||||
<option>Mark Dormant</option>
|
||||
</select>
|
||||
<div class="km-ml" style="margin-top: 10px">Reason</div>
|
||||
<textarea class="km-finp km-w km-ta" rows="2" placeholder="Reason for override..."></textarea>
|
||||
<div class="km-row-actions">
|
||||
<button class="km-btn km-btn-t" (click)="toast('Override submitted for checker approval')">
|
||||
Submit for Approval
|
||||
</button>
|
||||
<button class="km-btn km-btn-g" (click)="toast('Cleared')">Clear</button>
|
||||
</div>
|
||||
<div class="km-warn-box">
|
||||
<div class="km-warn-t">⚠ Maker-Checker Required</div>
|
||||
<div class="km-warn-d">All manual changes require second officer approval and are logged in audit inventory.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="km-card km-mt">
|
||||
<div class="km-ch">
|
||||
<div class="km-ci km-ci-t"><fa-icon icon="exchange-alt"></fa-icon></div>
|
||||
<div class="km-ct">Recent State Transitions</div>
|
||||
<span class="km-ch-hint">Click row for client details</span>
|
||||
</div>
|
||||
<table class="km-tbl">
|
||||
<thead>
|
||||
<tr><th>Account</th><th>From</th><th>To</th><th>Trigger</th><th>Date</th><th>By</th><th>Action</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr (click)="openModal('priya')">
|
||||
<td><div class="km-an">Priya Malhotra</div><div class="km-ai">ACC-BPL-2021-00231</div></td>
|
||||
<td><span class="km-sp km-sp-ac">Active</span></td>
|
||||
<td><span class="km-sp km-sp-fr">Debit Freeze</span></td>
|
||||
<td><span class="km-b km-b-am">Auto — KYC Expired</span></td>
|
||||
<td>10 Apr 2027</td>
|
||||
<td class="km-small">Scheduler</td>
|
||||
<td><button class="km-btn km-btn-ot" (click)="$event.stopPropagation(); toast('Unfreeze initiated')">Unfreeze</button></td>
|
||||
</tr>
|
||||
<tr (click)="openModal('suresh')">
|
||||
<td><div class="km-an">Suresh Patel</div><div class="km-ai">ACC-BPL-2019-00112</div></td>
|
||||
<td><span class="km-sp km-sp-fr">Debit Freeze</span></td>
|
||||
<td><span class="km-sp km-sp-in">Inoperative</span></td>
|
||||
<td><span class="km-b km-b-or">Auto — 60d elapsed</span></td>
|
||||
<td>15 May 2027</td>
|
||||
<td class="km-small">Scheduler</td>
|
||||
<td><button class="km-btn km-btn-g" (click)="$event.stopPropagation(); toast('Override needs approval')">Override</button></td>
|
||||
</tr>
|
||||
<tr (click)="openModal('kavitha')">
|
||||
<td><div class="km-an">Kavitha Nair</div><div class="km-ai">ACC-BPL-2018-00088</div></td>
|
||||
<td><span class="km-sp km-sp-in">Inoperative</span></td>
|
||||
<td><span class="km-sp km-sp-do">Dormant</span></td>
|
||||
<td><span class="km-b km-b-re">Auto — 150d elapsed</span></td>
|
||||
<td>1 Jun 2027</td>
|
||||
<td class="km-small">Scheduler</td>
|
||||
<td><button class="km-btn km-btn-g" (click)="$event.stopPropagation(); toast('Reversal needs RM + RBI')">Reverse</button></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- CLIENT DETAIL MODAL -->
|
||||
@if (modalOpen && current) {
|
||||
<div class="km-overlay" (click)="closeModal()">
|
||||
<div class="km-modal" (click)="$event.stopPropagation()">
|
||||
<div class="km-mh">
|
||||
<div class="km-mh-av">{{ current.av }}</div>
|
||||
<div>
|
||||
<div class="km-mh-name">{{ current.name }}</div>
|
||||
<div class="km-mh-meta">{{ current.meta }}</div>
|
||||
</div>
|
||||
<span class="km-sp km-ml-12" [ngClass]="stateClass(current.state)">{{ stateLabel(current.state) }}</span>
|
||||
<span class="km-b" [ngClass]="riskClass(current.risk)">{{ current.riskLabel }}</span>
|
||||
<div class="km-mclose" (click)="closeModal()">✕</div>
|
||||
</div>
|
||||
<div class="km-mbody">
|
||||
<div class="km-mtab-bar">
|
||||
<button class="km-mtab" [class.active]="modalTab === 'overview'" (click)="showMTab('overview')">Overview</button>
|
||||
<button class="km-mtab" [class.active]="modalTab === 'timeline'" (click)="showMTab('timeline')">Alert Timeline</button>
|
||||
<button class="km-mtab" [class.active]="modalTab === 'notices'" (click)="showMTab('notices')">Notices Sent</button>
|
||||
<button class="km-mtab" [class.active]="modalTab === 'evidence'" (click)="showMTab('evidence')">Evidence</button>
|
||||
<button class="km-mtab" [class.active]="modalTab === 'action'" (click)="showMTab('action')">Take Action</button>
|
||||
</div>
|
||||
|
||||
<!-- OVERVIEW -->
|
||||
@if (modalTab === 'overview') {
|
||||
<div class="km-slbl">Compliance Journey</div>
|
||||
<div class="km-prog-steps">
|
||||
@for (step of getStepperState(current); track step.label) {
|
||||
<div class="km-ps" [ngClass]="'km-ps-' + step.state">
|
||||
<div class="km-ps-dot">{{ stepIcon(step.state) }}</div>
|
||||
<div class="km-ps-label">{{ step.label }}</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="km-next-banner" [ngClass]="'km-nb-' + current.state">
|
||||
<div class="km-nb-icon">⚡</div>
|
||||
<div>
|
||||
<div class="km-nb-title">{{ stateLabel(current.state) }} — {{ current.daysLeft }} days to expiry</div>
|
||||
<div class="km-nb-sub">Next pending: <strong>{{ current.noticePending }}</strong>. Next stage: <strong>{{ current.nextStage }}</strong>.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="km-mstat-row">
|
||||
<div class="km-mstat"><div class="km-mstat-n">{{ current.noticesSent }}</div><div class="km-mstat-l">Notices Sent</div></div>
|
||||
<div class="km-mstat"><div class="km-mstat-n">{{ noticesPending(current) }}</div><div class="km-mstat-l">Notices Pending</div></div>
|
||||
<div class="km-mstat"><div class="km-mstat-n">{{ current.actionTaken.length }}</div><div class="km-mstat-l">Actions Taken</div></div>
|
||||
<div class="km-mstat"><div class="km-mstat-n">{{ current.pendingActions.length }}</div><div class="km-mstat-l">Pending Actions</div></div>
|
||||
</div>
|
||||
|
||||
<div class="km-r2">
|
||||
<div>
|
||||
<div class="km-slbl">KYC Information</div>
|
||||
<div class="km-mg">
|
||||
<div class="km-mi"><div class="km-ml">PAN</div><div class="km-mv km-mono">{{ current.kyc.pan }}</div></div>
|
||||
<div class="km-mi"><div class="km-ml">PAN Status</div><div class="km-mv"><span class="km-b km-b-gr">{{ current.kyc.panStatus }}</span></div></div>
|
||||
<div class="km-mi"><div class="km-ml">Aadhaar</div><div class="km-mv km-mono">{{ current.kyc.aadhaar }}</div></div>
|
||||
<div class="km-mi"><div class="km-ml">Aadhaar Status</div><div class="km-mv"><span class="km-b km-b-gr">{{ current.kyc.aadhaarStatus }}</span></div></div>
|
||||
<div class="km-mi"><div class="km-ml">KYC Validity</div><div class="km-mv">{{ current.kyc.validity }}</div></div>
|
||||
<div class="km-mi"><div class="km-ml">Expiry Date</div><div class="km-mv km-red">{{ current.expiry }}</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="km-slbl">Contact Details</div>
|
||||
<div class="km-mg">
|
||||
<div class="km-mi"><div class="km-ml">Mobile</div><div class="km-mv">{{ current.contact.mobile }}</div></div>
|
||||
<div class="km-mi"><div class="km-ml">Email</div><div class="km-mv km-small">{{ current.contact.email }}</div></div>
|
||||
<div class="km-mi km-mi-span"><div class="km-ml">Address</div><div class="km-mv km-small">{{ current.contact.address }}</div></div>
|
||||
<div class="km-mi"><div class="km-ml">Branch</div><div class="km-mv">{{ current.contact.branch }}</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- TIMELINE -->
|
||||
@if (modalTab === 'timeline') {
|
||||
<div class="km-slbl">Notice & Escalation Timeline</div>
|
||||
<div class="km-mtl">
|
||||
@for (item of current.actionTaken; track $index) {
|
||||
<div class="km-mti">
|
||||
<div class="km-mti-dot km-mti-done">✓</div>
|
||||
<div>
|
||||
<div class="km-mti-text km-done">{{ item }}</div>
|
||||
<div class="km-mti-sub">Completed</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@for (item of current.pendingActions; track $index) {
|
||||
<div class="km-mti">
|
||||
<div class="km-mti-dot km-mti-pend">!</div>
|
||||
<div>
|
||||
<div class="km-mti-text km-pend">{{ item }}</div>
|
||||
<div class="km-mti-sub">Pending action</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- NOTICES -->
|
||||
@if (modalTab === 'notices') {
|
||||
<div class="km-slbl">All Notices Dispatched</div>
|
||||
<table class="km-tbl">
|
||||
<thead>
|
||||
<tr><th>#</th><th>Notice</th><th>Channel</th><th>Date</th><th>Reference</th><th>Delivery</th><th>Response</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (n of current.notices; track n.ref; let i = $index) {
|
||||
<tr>
|
||||
<td class="km-num">{{ i + 1 }}</td>
|
||||
<td><span class="km-b" [ngClass]="i === 0 ? 'km-b-bl' : i === 1 ? 'km-b-am' : 'km-b-or'">{{ n.no }}</span></td>
|
||||
<td class="km-small">{{ n.channel }}</td>
|
||||
<td>{{ n.date }}</td>
|
||||
<td><span class="km-ref">{{ n.ref }}</span></td>
|
||||
<td><span class="km-del" [ngClass]="deliveryClass(n.delivery)">{{ n.delivery }}</span></td>
|
||||
<td><span class="km-del" [ngClass]="responseClass(n.response)">{{ n.response }}</span></td>
|
||||
</tr>
|
||||
}
|
||||
@if (noticesPending(current) > 0) {
|
||||
<tr><td colspan="7" class="km-pending-row">⏳ {{ noticesPending(current) }} notice(s) still pending dispatch</td></tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
|
||||
<!-- EVIDENCE -->
|
||||
@if (modalTab === 'evidence') {
|
||||
@if (!current.evidence) {
|
||||
<div class="km-empty">
|
||||
<div class="km-empty-i">📄</div>
|
||||
<div>No evidence collected yet for this client.</div>
|
||||
<div class="km-empty-s">Evidence is captured during branch visits or video KYC sessions.</div>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="km-slbl">{{ current.evidence.type === 'visit' ? 'Branch Visit Evidence' : 'Video KYC Session Log' }}</div>
|
||||
<div class="km-r2 km-mb">
|
||||
<div class="km-mi"><div class="km-ml">Date</div><div class="km-mv">{{ current.evidence.date }}</div></div>
|
||||
<div class="km-mi"><div class="km-ml">Officer / Agent</div><div class="km-mv">{{ current.evidence.officer }}</div></div>
|
||||
@if (current.evidence.type === 'visit') {
|
||||
<div class="km-mi"><div class="km-ml">GPS Coordinates</div><div class="km-mv km-mono">{{ current.evidence.gps }}</div></div>
|
||||
<div class="km-mi"><div class="km-ml">Location</div><div class="km-mv km-small">{{ current.evidence.location }}</div></div>
|
||||
}
|
||||
<div class="km-mi"><div class="km-ml">Reference / Docket</div><div class="km-mv km-mono">{{ current.evidence.docket || current.evidence.ref }}</div></div>
|
||||
<div class="km-mi"><div class="km-ml">Outcome</div><div class="km-mv"><span class="km-del km-d-fa">No Compliance</span></div></div>
|
||||
</div>
|
||||
@if (current.evidence.type === 'visit') {
|
||||
<div class="km-slbl">Map Placeholder</div>
|
||||
<div class="km-map">{{ current.evidence.gps }}<div class="km-small">{{ current.evidence.location }}</div></div>
|
||||
<div class="km-slbl">Photo Evidence</div>
|
||||
<div class="km-photo">Visit photo uploaded · Tap to view</div>
|
||||
}
|
||||
<div class="km-slbl">Officer Notes</div>
|
||||
<div class="km-notes">{{ current.evidence.outcome }}</div>
|
||||
<div class="km-row-actions">
|
||||
<button class="km-btn km-btn-t" (click)="toast('Downloading evidence package...')">Download Package</button>
|
||||
<button class="km-btn km-btn-g" (click)="toast('Sharing with RBI audit team...')">Share for Audit</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
<!-- ACTION -->
|
||||
@if (modalTab === 'action') {
|
||||
<div class="km-slbl">Available Actions</div>
|
||||
<div class="km-action-list">
|
||||
@for (action of current.pendingActions; track action) {
|
||||
<div class="km-action-row">
|
||||
<div>
|
||||
<div class="km-action-t">{{ action }}</div>
|
||||
<div class="km-action-d">Initiate via available channels</div>
|
||||
</div>
|
||||
<button class="km-btn km-btn-t" (click)="toast(action + ' initiated for ' + current.name)">Take Action</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- TOAST -->
|
||||
<div class="km-toast" [class.show]="toastVisible">{{ toastMsg }}</div>
|
||||
</div>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,485 @@
|
||||
/**
|
||||
* Copyright since 2025 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
|
||||
interface KmNotice {
|
||||
no: string;
|
||||
channel: string;
|
||||
date: string;
|
||||
ref: string;
|
||||
delivery: string;
|
||||
response: string;
|
||||
}
|
||||
|
||||
interface KmEvidence {
|
||||
type: 'visit' | 'vkyc';
|
||||
date: string;
|
||||
officer: string;
|
||||
gps?: string;
|
||||
location?: string;
|
||||
ref?: string;
|
||||
outcome: string;
|
||||
docket?: string;
|
||||
}
|
||||
|
||||
interface KmClient {
|
||||
id: string;
|
||||
av: string;
|
||||
name: string;
|
||||
meta: string;
|
||||
state: 'alert' | 'freeze' | 'inop' | 'dormant' | 'urgent';
|
||||
risk: 'low' | 'mid' | 'high';
|
||||
riskLabel: string;
|
||||
expiry: string;
|
||||
daysLeft: number;
|
||||
account: string;
|
||||
kyc: {
|
||||
type: string;
|
||||
initiated: string;
|
||||
lastVerified: string;
|
||||
validity: string;
|
||||
pan: string;
|
||||
aadhaar: string;
|
||||
panStatus: string;
|
||||
aadhaarStatus: string;
|
||||
panLink: string;
|
||||
};
|
||||
contact: {
|
||||
mobile: string;
|
||||
email: string;
|
||||
address: string;
|
||||
branch: string;
|
||||
};
|
||||
notices: KmNotice[];
|
||||
noticesSent: number;
|
||||
noticePending: string;
|
||||
nextStage: string;
|
||||
pendingActions: string[];
|
||||
actionTaken: string[];
|
||||
evidence: KmEvidence | null;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'mifosx-kyc-management',
|
||||
templateUrl: './kyc-management.component.html',
|
||||
styleUrls: ['./kyc-management.component.scss'],
|
||||
imports: [
|
||||
...STANDALONE_SHARED_IMPORTS,
|
||||
FormsModule,
|
||||
FaIconComponent
|
||||
]
|
||||
})
|
||||
export class KycManagementComponent {
|
||||
activeSection: 'alerts' | 'notif' | 'audit' | 'status' = 'alerts';
|
||||
|
||||
modalOpen = false;
|
||||
modalTab: 'overview' | 'timeline' | 'notices' | 'evidence' | 'action' = 'overview';
|
||||
current: KmClient | null = null;
|
||||
|
||||
toastMsg = '';
|
||||
toastVisible = false;
|
||||
private toastTimer: any;
|
||||
|
||||
clients: Record<string, KmClient> = {
|
||||
rajesh: {
|
||||
id: 'rajesh',
|
||||
av: 'RS',
|
||||
name: 'Rajesh Kumar Sharma',
|
||||
meta: 'ACC-BPL-2024-00847 · Savings A/c · Bhopal Main',
|
||||
account: 'ACC-BPL-2024-00847',
|
||||
state: 'alert',
|
||||
risk: 'low',
|
||||
riskLabel: 'Low Risk',
|
||||
expiry: '30 Apr 2027',
|
||||
daysLeft: 15,
|
||||
kyc: {
|
||||
type: 'Individual',
|
||||
initiated: '4 Apr 2026',
|
||||
lastVerified: '4 Apr 2026',
|
||||
validity: '10 Years',
|
||||
pan: 'AQRPV8345E',
|
||||
aadhaar: 'XXXXXXXX6039',
|
||||
panStatus: 'Verified',
|
||||
aadhaarStatus: 'Verified',
|
||||
panLink: 'Linked'
|
||||
},
|
||||
contact: {
|
||||
mobile: '+91-XXXXX-99041',
|
||||
email: 'r****@gmail.com',
|
||||
address: 'House No. 42, Sector 5, Bhopal MP 462001',
|
||||
branch: 'Bhopal Main'
|
||||
},
|
||||
notices: [
|
||||
{ no: 'N1', channel: 'SMS + Email', date: '31 Mar 2027', ref: 'MSG-2027-0331-001', delivery: 'Delivered', response: 'No Action' },
|
||||
{ no: 'N2', channel: 'Reg. Post + IVR', date: '10 Apr 2027', ref: 'RP-BPL-20270410-443', delivery: 'In Transit', response: 'Pending' }
|
||||
],
|
||||
noticesSent: 2,
|
||||
noticePending: 'Notice 3',
|
||||
nextStage: 'Debit Freeze',
|
||||
pendingActions: ['Send Notice 3 (due 20 Apr 2027)', 'Schedule Branch Visit', 'Send Video KYC link'],
|
||||
actionTaken: ['Notice 1 delivered', 'Notice 2 dispatched via Reg. Post'],
|
||||
evidence: null
|
||||
},
|
||||
priya: {
|
||||
id: 'priya',
|
||||
av: 'PM',
|
||||
name: 'Priya Malhotra',
|
||||
meta: 'ACC-BPL-2021-00231 · Savings A/c · Bhopal Main',
|
||||
account: 'ACC-BPL-2021-00231',
|
||||
state: 'freeze',
|
||||
risk: 'mid',
|
||||
riskLabel: 'Mid Risk',
|
||||
expiry: '10 Apr 2027',
|
||||
daysLeft: 0,
|
||||
kyc: {
|
||||
type: 'Individual',
|
||||
initiated: '10 Apr 2019',
|
||||
lastVerified: '10 Apr 2019',
|
||||
validity: '8 Years',
|
||||
pan: 'BKJPV1234A',
|
||||
aadhaar: 'XXXXXXXX7721',
|
||||
panStatus: 'Verified',
|
||||
aadhaarStatus: 'Verified',
|
||||
panLink: 'Linked'
|
||||
},
|
||||
contact: {
|
||||
mobile: '+91-XXXXX-55321',
|
||||
email: 'p****@yahoo.com',
|
||||
address: 'Flat 12B, Arera Colony, Bhopal MP 462016',
|
||||
branch: 'Bhopal Main'
|
||||
},
|
||||
notices: [
|
||||
{ no: 'N1', channel: 'SMS + Email', date: '10 Mar 2027', ref: 'MSG-2027-0310-003', delivery: 'Delivered', response: 'No Response' },
|
||||
{ no: 'N2', channel: 'Reg. Post', date: '20 Mar 2027', ref: 'RP-BPL-20270320-091', delivery: 'Delivered', response: 'No Response' },
|
||||
{ no: 'N3', channel: 'Branch Visit', date: '30 Mar 2027', ref: 'VIS-BPL-20270330-012', delivery: 'Visit Done', response: 'Refused' }
|
||||
],
|
||||
noticesSent: 3,
|
||||
noticePending: 'None — all 3 sent',
|
||||
nextStage: 'Inoperative by 30 Jun 2027',
|
||||
pendingActions: ['Account in Debit Freeze', 'Inoperative transition due 30 Jun 2027', 'Maker-checker unfreeze pending'],
|
||||
actionTaken: ['Notice 1 delivered', 'Notice 2 delivered via Reg. Post', 'Notice 3 — Branch visit done, client refused', 'Debit Freeze applied 10 Apr 2027'],
|
||||
evidence: {
|
||||
type: 'visit',
|
||||
date: '30 Mar 2027',
|
||||
officer: 'Anupam Mishra',
|
||||
gps: '23.2599° N, 77.4126° E',
|
||||
location: 'Arera Colony, Bhopal',
|
||||
outcome: 'Client refused to sign self-declaration form. Informed about debit freeze implications.',
|
||||
docket: 'VIS-BPL-20270330-012'
|
||||
}
|
||||
},
|
||||
suresh: {
|
||||
id: 'suresh',
|
||||
av: 'SP',
|
||||
name: 'Suresh Patel',
|
||||
meta: 'ACC-BPL-2019-00112 · Savings A/c · Bhopal South',
|
||||
account: 'ACC-BPL-2019-00112',
|
||||
state: 'inop',
|
||||
risk: 'high',
|
||||
riskLabel: 'High Risk',
|
||||
expiry: '15 Mar 2027',
|
||||
daysLeft: 0,
|
||||
kyc: {
|
||||
type: 'Individual',
|
||||
initiated: '15 Mar 2025',
|
||||
lastVerified: '15 Mar 2025',
|
||||
validity: '2 Years',
|
||||
pan: 'CXRTS5678B',
|
||||
aadhaar: 'XXXXXXXX2211',
|
||||
panStatus: 'Verified',
|
||||
aadhaarStatus: 'Verified',
|
||||
panLink: 'Linked'
|
||||
},
|
||||
contact: {
|
||||
mobile: '+91-XXXXX-88765',
|
||||
email: 's****@gmail.com',
|
||||
address: 'Village Ratibad, Bhopal MP 462044',
|
||||
branch: 'Bhopal South'
|
||||
},
|
||||
notices: [
|
||||
{ no: 'N1', channel: 'SMS + Email', date: '13 Feb 2027', ref: 'MSG-2027-0213-006', delivery: 'Delivered', response: 'No Response' },
|
||||
{ no: 'N2', channel: 'Reg. Post + IVR', date: '23 Feb 2027', ref: 'RP-BPL-20270223-044', delivery: 'Delivered', response: 'No Response' },
|
||||
{ no: 'N3', channel: 'Video KYC', date: '5 Mar 2027', ref: 'VKYC-2027-0305-007', delivery: 'No Response', response: 'No Action' }
|
||||
],
|
||||
noticesSent: 3,
|
||||
noticePending: 'None',
|
||||
nextStage: 'Dormant by 30 Sep 2027',
|
||||
pendingActions: ['Account Inoperative — all txns blocked', 'Dormant transition due 30 Sep 2027', 'RBI Dormant Account reporting'],
|
||||
actionTaken: ['Notice 1 delivered', 'Notice 2 delivered via Reg. Post', 'Notice 3 — Video KYC invite sent (no response)', 'Debit Freeze 15 Mar 2027', 'Inoperative 15 May 2027'],
|
||||
evidence: {
|
||||
type: 'vkyc',
|
||||
date: '5 Mar 2027',
|
||||
officer: 'System',
|
||||
ref: 'VKYC-2027-0305-007',
|
||||
outcome: 'Video KYC session link sent. Client did not join within 48 hours. Session auto-expired.'
|
||||
}
|
||||
},
|
||||
kavitha: {
|
||||
id: 'kavitha',
|
||||
av: 'KN',
|
||||
name: 'Kavitha Nair',
|
||||
meta: 'ACC-BPL-2018-00088 · Savings A/c · Bhopal East',
|
||||
account: 'ACC-BPL-2018-00088',
|
||||
state: 'dormant',
|
||||
risk: 'mid',
|
||||
riskLabel: 'Mid Risk',
|
||||
expiry: '1 Jan 2027',
|
||||
daysLeft: 0,
|
||||
kyc: {
|
||||
type: 'Individual',
|
||||
initiated: '1 Jan 2019',
|
||||
lastVerified: '1 Jan 2019',
|
||||
validity: '8 Years',
|
||||
pan: 'DVJNK9012C',
|
||||
aadhaar: 'XXXXXXXX4492',
|
||||
panStatus: 'Verified',
|
||||
aadhaarStatus: 'Verified',
|
||||
panLink: 'Linked'
|
||||
},
|
||||
contact: {
|
||||
mobile: '+91-XXXXX-11092',
|
||||
email: 'k****@rediff.com',
|
||||
address: 'Plot 7, Awadhpuri, Bhopal MP 462010',
|
||||
branch: 'Bhopal East'
|
||||
},
|
||||
notices: [
|
||||
{ no: 'N1', channel: 'SMS + Email', date: '1 Dec 2026', ref: 'MSG-2026-1201-009', delivery: 'Delivered', response: 'No Response' },
|
||||
{ no: 'N2', channel: 'Reg. Post', date: '11 Dec 2026', ref: 'RP-BPL-20261211-077', delivery: 'Delivered', response: 'No Response' },
|
||||
{ no: 'N3', channel: 'Branch Visit', date: '22 Dec 2026', ref: 'VIS-BPL-20261222-019', delivery: 'Not Found', response: 'Not Found at Address' }
|
||||
],
|
||||
noticesSent: 3,
|
||||
noticePending: 'None',
|
||||
nextStage: 'Dormant (Final) — RBI Reporting',
|
||||
pendingActions: ['RBI Dormant Account Report submission', 'Branch Manager approval for any reversal', 'Regional Manager sign-off required'],
|
||||
actionTaken: ['Notice 1–3 sent, all no response', 'Debit Freeze 1 Jan 2027', 'Inoperative 2 Mar 2027', 'Dormant 1 Jun 2027', 'RBI report auto-generated'],
|
||||
evidence: {
|
||||
type: 'visit',
|
||||
date: '22 Dec 2026',
|
||||
officer: 'Rekha Sharma',
|
||||
gps: '23.2491° N, 77.4384° E',
|
||||
location: 'Awadhpuri, Bhopal',
|
||||
outcome: 'Client not found at registered address. Neighbours confirmed relocation. Address verification required.',
|
||||
docket: 'VIS-BPL-20261222-019'
|
||||
}
|
||||
},
|
||||
amit: {
|
||||
id: 'amit',
|
||||
av: 'AS',
|
||||
name: 'Amit Singh',
|
||||
meta: 'ACC-BPL-2024-01103 · Savings A/c · Bhopal Main',
|
||||
account: 'ACC-BPL-2024-01103',
|
||||
state: 'alert',
|
||||
risk: 'low',
|
||||
riskLabel: 'Low Risk',
|
||||
expiry: '5 May 2027',
|
||||
daysLeft: 20,
|
||||
kyc: {
|
||||
type: 'Individual',
|
||||
initiated: '5 May 2016',
|
||||
lastVerified: '5 May 2016',
|
||||
validity: '10 Years',
|
||||
pan: 'EKJPS4321D',
|
||||
aadhaar: 'XXXXXXXX8834',
|
||||
panStatus: 'Verified',
|
||||
aadhaarStatus: 'Verified',
|
||||
panLink: 'Linked'
|
||||
},
|
||||
contact: {
|
||||
mobile: '+91-XXXXX-22334',
|
||||
email: 'a****@gmail.com',
|
||||
address: 'C-22, Shivaji Nagar, Bhopal MP 462016',
|
||||
branch: 'Bhopal Main'
|
||||
},
|
||||
notices: [
|
||||
{ no: 'N1', channel: 'SMS + Email', date: '5 Apr 2027', ref: 'MSG-2027-0405-011', delivery: 'Delivered', response: 'No Action' }
|
||||
],
|
||||
noticesSent: 1,
|
||||
noticePending: 'Notice 2 & 3',
|
||||
nextStage: 'Debit Freeze if no action',
|
||||
pendingActions: ['Send Notice 2 (due 15 Apr 2027)', 'Send Notice 3 (due 25 Apr 2027)', 'Branch visit if needed'],
|
||||
actionTaken: ['Notice 1 delivered 5 Apr 2027'],
|
||||
evidence: null
|
||||
},
|
||||
meena: {
|
||||
id: 'meena',
|
||||
av: 'MJ',
|
||||
name: 'Meena Joshi',
|
||||
meta: 'ACC-BPL-2022-00567 · Savings A/c · Bhopal North',
|
||||
account: 'ACC-BPL-2022-00567',
|
||||
state: 'urgent',
|
||||
risk: 'high',
|
||||
riskLabel: 'High Risk',
|
||||
expiry: '28 Apr 2027',
|
||||
daysLeft: 3,
|
||||
kyc: {
|
||||
type: 'Individual',
|
||||
initiated: '28 Apr 2025',
|
||||
lastVerified: '28 Apr 2025',
|
||||
validity: '2 Years',
|
||||
pan: 'FJLMN6789E',
|
||||
aadhaar: 'XXXXXXXX3309',
|
||||
panStatus: 'Verified',
|
||||
aadhaarStatus: 'Verified',
|
||||
panLink: 'Linked'
|
||||
},
|
||||
contact: {
|
||||
mobile: '+91-XXXXX-77654',
|
||||
email: 'm****@gmail.com',
|
||||
address: 'Lane 4, Shyamla Hills, Bhopal MP 462002',
|
||||
branch: 'Bhopal North'
|
||||
},
|
||||
notices: [
|
||||
{ no: 'N1', channel: 'SMS + Email', date: '29 Mar 2027', ref: 'MSG-2027-0329-014', delivery: 'Delivered', response: 'No Action' },
|
||||
{ no: 'N2', channel: 'Reg. Post + IVR', date: '8 Apr 2027', ref: 'RP-BPL-20270408-088', delivery: 'Delivered', response: 'No Response' }
|
||||
],
|
||||
noticesSent: 2,
|
||||
noticePending: 'URGENT: Notice 3',
|
||||
nextStage: 'Debit Freeze in 3 days',
|
||||
pendingActions: ['URGENT: Send Notice 3 NOW', 'Schedule Branch Visit immediately', 'Consider Video KYC as last resort'],
|
||||
actionTaken: ['Notice 1 delivered', 'Notice 2 delivered via Reg. Post + IVR'],
|
||||
evidence: null
|
||||
}
|
||||
};
|
||||
|
||||
alertRows: KmClient[] = [];
|
||||
|
||||
ngOnInit(): void {
|
||||
this.alertRows = Object.values(this.clients);
|
||||
}
|
||||
|
||||
showSection(section: 'alerts' | 'notif' | 'audit' | 'status'): void {
|
||||
this.activeSection = section;
|
||||
}
|
||||
|
||||
openModal(id: string): void {
|
||||
const c = this.clients[id];
|
||||
if (!c) return;
|
||||
this.current = c;
|
||||
this.modalTab = 'overview';
|
||||
this.modalOpen = true;
|
||||
}
|
||||
|
||||
closeModal(): void {
|
||||
this.modalOpen = false;
|
||||
this.current = null;
|
||||
}
|
||||
|
||||
showMTab(tab: 'overview' | 'timeline' | 'notices' | 'evidence' | 'action'): void {
|
||||
this.modalTab = tab;
|
||||
}
|
||||
|
||||
toast(msg: string): void {
|
||||
this.toastMsg = msg;
|
||||
this.toastVisible = true;
|
||||
if (this.toastTimer) clearTimeout(this.toastTimer);
|
||||
this.toastTimer = setTimeout(() => (this.toastVisible = false), 2800);
|
||||
}
|
||||
|
||||
/** Compliance journey stepper state for a given client */
|
||||
getStepperState(c: KmClient): { label: string; state: 'done' | 'pending' | 'current' | 'danger' }[] {
|
||||
return [
|
||||
{ label: 'KYC Active', state: 'done' },
|
||||
{ label: 'Alert Sent', state: c.noticesSent >= 1 ? 'done' : 'pending' },
|
||||
{ label: 'Notice 2', state: c.noticesSent >= 2 ? 'done' : c.noticesSent >= 1 ? 'current' : 'pending' },
|
||||
{
|
||||
label: 'Notice 3',
|
||||
state:
|
||||
c.noticesSent >= 3
|
||||
? 'done'
|
||||
: c.noticesSent >= 2
|
||||
? c.state === 'urgent'
|
||||
? 'danger'
|
||||
: 'current'
|
||||
: 'pending'
|
||||
},
|
||||
{
|
||||
label: 'Debit Freeze',
|
||||
state: ['freeze', 'inop', 'dormant'].includes(c.state) ? 'done' : c.noticesSent >= 3 ? 'danger' : 'pending'
|
||||
},
|
||||
{ label: 'Inoperative', state: ['inop', 'dormant'].includes(c.state) ? 'done' : 'pending' },
|
||||
{ label: 'Dormant', state: c.state === 'dormant' ? 'danger' : 'pending' }
|
||||
];
|
||||
}
|
||||
|
||||
stepIcon(state: string): string {
|
||||
if (state === 'done') return '✓';
|
||||
if (state === 'danger') return '!';
|
||||
return '·';
|
||||
}
|
||||
|
||||
stateLabel(state: string): string {
|
||||
return (
|
||||
{
|
||||
alert: 'Alert Window',
|
||||
freeze: 'Debit Freeze',
|
||||
inop: 'Inoperative',
|
||||
dormant: 'Dormant',
|
||||
urgent: 'Urgent Alert'
|
||||
} as Record<string, string>
|
||||
)[state] || state;
|
||||
}
|
||||
|
||||
stateClass(state: string): string {
|
||||
return (
|
||||
{
|
||||
alert: 'sp-al',
|
||||
freeze: 'sp-fr',
|
||||
inop: 'sp-in',
|
||||
dormant: 'sp-do',
|
||||
urgent: 'sp-fr'
|
||||
} as Record<string, string>
|
||||
)[state] || 'sp-al';
|
||||
}
|
||||
|
||||
riskClass(risk: string): string {
|
||||
return ({ low: 'b-gr', mid: 'b-am', high: 'b-re' } as Record<string, string>)[risk] || 'b-gy';
|
||||
}
|
||||
|
||||
/** Notices pending count (cap at 0) */
|
||||
noticesPending(c: KmClient): number {
|
||||
return Math.max(0, 3 - c.noticesSent);
|
||||
}
|
||||
|
||||
deliveryClass(d: string): string {
|
||||
const m: Record<string, string> = {
|
||||
Delivered: 'd-ok',
|
||||
'In Transit': 'd-tr',
|
||||
'No Response': 'd-fa',
|
||||
Pending: 'd-pe',
|
||||
'Visit Done': 'd-ok',
|
||||
'Not Found': 'd-fa'
|
||||
};
|
||||
return m[d] || 'd-pe';
|
||||
}
|
||||
|
||||
barWidth(c: KmClient): number {
|
||||
if (c.daysLeft <= 0) return 100;
|
||||
return Math.max(2, Math.min(100, c.daysLeft * 3));
|
||||
}
|
||||
|
||||
barColor(c: KmClient): string {
|
||||
if (c.daysLeft <= 0) return 'var(--km-g400)';
|
||||
return c.daysLeft > 10 ? 'var(--km-amber)' : 'var(--km-red)';
|
||||
}
|
||||
|
||||
responseClass(r: string): string {
|
||||
const m: Record<string, string> = {
|
||||
'No Action': 'd-pe',
|
||||
'No Response': 'd-fa',
|
||||
Pending: 'd-pe',
|
||||
Refused: 'd-fa',
|
||||
'Not Found at Address': 'd-fa',
|
||||
Awaited: 'd-pe'
|
||||
};
|
||||
return m[r] || 'd-pe';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright since 2025 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
import { KycManagementRoutingModule } from './kyc-management-routing.module';
|
||||
import { KycManagementComponent } from './kyc-management.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
SharedModule,
|
||||
KycManagementRoutingModule,
|
||||
KycManagementComponent
|
||||
]
|
||||
})
|
||||
export class KycManagementModule {}
|
||||
Reference in New Issue
Block a user