@if (activeSection === 'alerts') {
KYC Alert Dashboard
{{ expiringRows.length }}
Expiring in 30 days
Re-KYC falls due soon
{{ expiryTotals.grace }}
Overdue — Grace Period
KYC lapsed, still operative
{{ expiryTotals.partialFreeze }}
Debit Freeze
Debits blocked
{{ expiryTotals.inoperative }}
Inoperative
KYC lapsed {{ expiryPolicy.inoperativeDays }}d+
{{ dormancyTotals.inactive }}
Inactive
No customer txn {{ dormancyPolicy.inactiveDays }}d
{{ dormancyTotals.dormant }}
Dormant
No customer txn {{ dormancyPolicy.dormantDays }}d
{{ dormancyTotals.escheat }}
Escheat (DEAF)
RBI DEAF remittance
@if (alertFiltersActive) {
{{ alertFilter ? alertLabels[alertFilter] : 'Matching accounts' }}
{{ alertList.length }} accounts · Reset to close
@for (r of alertList; track $index) { } @empty { }
Customer Account Branch State Date Days Balance Next stage Action
{{ r.name }}
{{ r.account }} {{ r.branch }} {{ r.stage }} {{ r.since | date: 'dd MMM yyyy' }} {{ r.days }} {{ r.daysLabel }} {{ r.balance === null ? '—' : '₹' + (r.balance | number: '1.2-2') }}
{{ r.next }}
@if (r.nextDate) {
{{ r.nextDate | date: 'dd MMM yyyy' }}
}
@if (r.stageKey === 'ESCHEAT') { } @else { }
{{ statusLoading ? 'Loading…' : 'No account matches these filters.' }}
}
Full reports
Acknowledge outreach, record DEAF remittance and refund, branch and balance filters, Excel export
Rule What happens
Re-KYC overdue, first {{ expiryPolicy.graceDays }} days Nothing is restricted; reminders go out
Re-KYC overdue, after {{ expiryPolicy.graceDays }} days Debits stopped; credits still accepted
Re-KYC overdue, after {{ expiryPolicy.inoperativeDays }} days Marked inoperative; only {{ alwaysAllowed.join(', ') }} still go through
No customer transaction for {{ dormancyPolicy.inactiveDays }} days Inactive — flagged for the branch, nothing restricted
No customer transaction for {{ dormancyPolicy.dormantDays }} days Dormant / inoperative — debits stopped; revived by re-KYC
No customer transaction for {{ dormancyPolicy.escheatDays }} days Escheat — balance remitted to RBI DEAF
@if (showExpiryReport) {
} @if (showDormancyReport) {
}
} @if (activeSection === 'notif') {
Notification Scheduler
{{ schedulerRows.length }}
Customers in scope
Expiring in 30 days + lapsed
{{ schedulerDueCount }}
Next notice due
SMS, Email or WhatsApp to send
{{ schedulerNeverContacted }}
Never contacted
No notice on record
{{ schedSelected.size }}
Selected
Ready to dispatch
Notice queue — sent in the background
Waiting {{ queueCounts['PENDING'] + queueCounts['SENDING'] }} · Sent {{ queueCounts['SENT'] }} · Failed {{ queueCounts['FAILED'] }} · Cancelled {{ queueCounts['CANCELLED'] }}
@if (queueError) {
{{ queueError }}
} @for (g of queueGroups; track g.date) { } @empty { }
Date Customers Notices Waiting Sent Failed Cancelled Action
{{ g.date | date: 'dd MMM yyyy' }} {{ g.customers }} {{ g.rows.length }} {{ g.waiting }} {{ g.sent }} {{ g.failed }} {{ g.cancelled }}
{{ queueLoading ? 'Loading…' : 'Nothing has been queued yet. Select customers on the Alert Dashboard and use Notify selected.' }}
Escalation ladder — one channel at a time
The next rung is the first one not yet done. Call and visit rungs are recorded from Notify.
@for (s of schedulerRows; track s.row.clientId) { } @empty { }
Customer KYC Ladder Next step Last contact Action
@if (s.next?.sends) { }
{{ s.row.clientName }}
{{ s.row.accountNo }}
{{ s.row.expiryDate | date: 'dd MMM yyyy' }}
{{ s.row.daysOverdue < 0 ? -s.row.daysOverdue + ' days left' : s.row.daysOverdue + ' days overdue' }}
@for (st of s.steps; track st.label) { {{ st.state === 'done' ? '✓ ' : '' }}{{ st.label }} }
{{ s.next ? s.next.label : 'Ladder complete' }} {{ s.row.lastContactDate ? (s.row.lastContactDate | date: 'dd MMM yyyy') : 'Never' }} @if (s.next?.sends) { } @else if (s.next) { }
{{ statusLoading ? 'Loading…' : 'No customer is expiring or lapsed.' }}
} @if (activeSection === 'audit') {
Audit Inventory
Notices sent, day by day
{{ auditDays.length }} day(s) · {{ auditNoticeTotal }} notice(s) · click a day to see who was sent what
@if (auditError) {
{{ auditError }}
} @for (d of auditDays; track d.date) { } @empty { }
Date Customers Notices Delivered Failed No contact detail Recorded (call / visit) Action
{{ d.date | date: 'dd MMM yyyy' }} {{ d.customers }} {{ d.notices }} {{ d.delivered }} {{ d.failed }} {{ d.noContact + d.notConfigured }} {{ d.recorded }}
{{ auditLoading ? 'Loading…' : 'No notice matches these filters.' }}
} @if (activeSection === 'exceptions') {
KYC Exceptions Dashboard
{{ exceptionRows.length }}
Pending Exceptions
Immediate attention
{{ notYetContactedCount }}
Not Yet Contacted
Action required
{{ panMismatchesCount }}
PAN Mismatches
Requires physical review
{{ aadhaarFailuresCount }}
Aadhaar Failures
Re-KYC OTP needed
Exceptions Register
@if (activeExceptionSubTab === 'pending') { @if (pendingExceptions.length === 0) {
🎉
All exceptions resolved!
There are no active client KYC exceptions on record.
} @else { @for (c of pendingExceptions; track c.id) { }
Client / Account Branch Exception Reason & Date Risk Contact Status Contact Logs Actions
{{ c.name }}
{{ c.account }}
{{ c.contact.branch }}
{{ c.exceptionReason }}
Opened: {{ c.exceptionDate }}
{{ c.riskLabel }} @if (c.contactHistory && c.contactHistory.length > 0) { Contacted
Last: {{ c.contactHistory[c.contactHistory.length - 1].date }}
} @else { Not Contacted }
@if (c.contactHistory && c.contactHistory.length > 0) {
@for (log of c.contactHistory; track $index) {
{{ log.date }} ({{ log.action }}): {{ log.notes }} — {{ log.officer }} @if (log.platformSent) { @if (log.delivered) { Sent {{ log.destinationMasked }} } @else { Not sent } }
}
} @else { No logs recorded }
} } @if (activeExceptionSubTab === 'resolved') { @if (resolvedExceptions.length === 0) {
📋
No resolved exceptions
Resolved KYC exceptions will appear here once verification is completed.
} @else { @for (c of resolvedExceptions; track c.id) { }
Client / Account Branch Exception Reason & Resolution Risk Contact Logs Status
{{ c.name }}
{{ c.account }}
{{ c.contact.branch }}
{{ c.exceptionReason }}
Resolved On: {{ c.exceptionResolvedOn }}
{{ c.riskLabel }} @if (c.contactHistory && c.contactHistory.length > 0) {
@for (log of c.contactHistory; track $index) {
{{ log.date }} ({{ log.action }}): {{ log.notes }} — {{ log.officer }} @if (log.platformSent) { @if (log.delivered) { Sent {{ log.destinationMasked }} } @else { Not sent } }
}
} @else { No logs recorded }
Exception Resolved
} }
}
@if (auditDay) {
Notices sent on {{ auditDay | date: 'dd MMM yyyy' }}
{{ auditDayTotal }} notice(s) · click a row for the message the customer received
✕
@if (auditDayError) {
{{ auditDayError }}
} @for (r of auditDayRows; track r.id; let i = $index) { @if (auditExpanded === r.id) { } } @empty { }
# Customer / account Channel Time Sent to Reference Delivery Officer
{{ auditDayPage * auditPageSize + i + 1 }}
{{ r.clientName }}
{{ r.accountNo }}
{{ auditChannelLabels[r.channel] || r.channel }} {{ r.contactedAt | date: 'HH:mm' }} {{ r.destinationMasked || '—' }} {{ r.providerReference || '—' }} {{ auditStatusLabels[r.deliveryStatus] || r.deliveryStatus }} {{ r.officerName || '—' }}
Note
{{ r.notes }}
@if (r.messageBody) {
Message sent to the customer
{{ r.messageBody }}
} @if (r.failureReason) {
Failure reason
{{ r.failureReason }}
}
{{ auditDayLoading ? 'Loading…' : 'No notice matches these filters on this day.' }}
} @if (queueDetail; as d) {
Notices queued on {{ d.date | date: 'dd MMM yyyy' }}
{{ d.customers }} customer(s) · {{ d.rows.length }} notice(s) — Waiting {{ d.waiting }} · Sent {{ d.sent }} · Failed {{ d.failed }} · Cancelled {{ d.cancelled }}
✕
@for (q of d.rows; track q.id; let i = $index) { }
# Customer Account Notice Channel Queued Send at Status Result Action
{{ i + 1 }}
{{ q.clientName }}
{{ q.accountNo }} {{ q.stageLabel || (q.noticeType === 'DORMANCY' ? 'Account notice' : 'Re-KYC notice') }} {{ q.channel }} {{ q.createdAt | date: 'HH:mm' }} {{ q.scheduledFor | date: 'dd MMM, HH:mm' }} {{ q.status === 'PENDING' ? 'Waiting' : q.status === 'SENDING' ? 'Sending' : q.status === 'SENT' ? 'Sent' : q.status === 'FAILED' ? 'Failed' : 'Cancelled' }} @if (q.attempts > 1 && q.status === 'PENDING') {
retry {{ q.attempts }}
}
{{ q.resultMessage || '—' }} @if (q.status === 'PENDING') { } @else if (q.status === 'FAILED') { }
} @if (bulkOpen) {
Notify selected customers
{{ bulkDone && bulkQueued ? bulkQueued.queued : bulkTargets.length }} customers
✕
@if (!bulkDone && !bulkSending) {
A notice is sent to each customer once, even if several of their accounts are ticked.
@if (bulkError) {
{{ bulkError }}
} @for (g of bulkNoticeSummary; track g.label) {
{{ g.count }} customer(s): {{ g.label }} — worded for the stage each one is at.
}
@if (bulkTargets.length > bulkLimit) {
{{ bulkTargets.length }} customers selected — send at most {{ bulkLimit }} at a time.
} } @else { @if (bulkSending) {
Queuing…
} @else if (bulkQueued) {
{{ bulkQueued.queued }} notice(s) queued — they are sent in the background. @if (bulkQueued.skipped) { {{ bulkQueued.skipped }} already waiting in the queue, so not queued again. } Follow them in the Notification Scheduler tab.
} }
} @if (schedOpen) {
Dispatch next notice
{{ schedDone && schedQueued ? schedQueued.queued : schedTargets.length }} customers
✕
@if (!schedDone && !schedSending) { @for (t of schedTargets; track t.row.clientId) { }
Customer Goes out as
{{ t.row.clientName }}
{{ t.row.accountNo }}
{{ t.next?.label }}
@if (schedError) {
{{ schedError }}
} @if (schedTargets.length > bulkLimit) {
Send at most {{ bulkLimit }} at a time.
} } @else { @if (schedSending) {
Queuing…
} @else if (schedQueued) {
{{ schedQueued.queued }} notice(s) queued — they are sent in the background and appear in the queue below. @if (schedQueued.skipped) { {{ schedQueued.skipped }} already waiting, so not queued again. }
} }
} @if (contactDialogOpen && current) {
{{ current.av }}
Log Customer Contact
{{ current.name }} · {{ current.account }}
✕
Customer Contact Info
{{ current.name }}
{{ current.contact.mobile }}
{{ current.contact.email }}
@if (selectedChannelSends) {
A KYC notice will be sent to the customer when you save.
} @else {
Records an action you already took. Nothing is sent to the customer.
} @if (selectedChannelSends && contactPreview) {
{{ contactPreview.subject }} {{ contactPreview.body }}
} @if (selectedChannelUnavailable) {
This customer has no {{ selectedChannel?.requires === 'email' ? 'email address' : 'mobile number' }} on file — choose a different channel.
}
@if (contactError) { }
} @if (resolveDialogOpen && current) {
{{ current.av }}
Resolve KYC Exception
{{ current.name }} · {{ current.account }}
✕
Pending Reason: {{ current.exceptionReason }} @if (current.exceptionRemarks) {
"{{ current.exceptionRemarks }}"
}
@if (showPanResolution) {
@if (resolvePanActive) {
@if (resolvePanNumber && resolvePanNumber.length !== 10) {
PAN must be exactly 10 characters.
}
}
} @if (showAadhaarResolution) {
@if (resolveAadhaarActive) {
@if (resolveAadhaarNumber && resolveAadhaarNumber.length !== 12) {
Aadhaar must be exactly 12 digits.
}
}
} @if (showGeneralResolution) {
@if (resolveGeneralActive) {
}
}
}
{{ toastMsg }}