257 lines
12 KiB
HTML
257 lines
12 KiB
HTML
<!--
|
||
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="rdd-container">
|
||
<!-- Header -->
|
||
<div class="rdd-header">
|
||
<div class="rdd-header__left">
|
||
<div class="rdd-header__icon">
|
||
<fa-icon icon="piggy-bank"></fa-icon>
|
||
</div>
|
||
<div>
|
||
<h2 class="rdd-header__title">RD Dashboard</h2>
|
||
<p class="rdd-header__sub">
|
||
All running Recurring Deposits as of <strong>{{ businessDate || '—' }}</strong> (business date). Click any
|
||
card to see those RDs in the list below.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<button class="rdd-btn rdd-btn--slim" (click)="load()" [disabled]="loading">
|
||
<fa-icon icon="sync-alt" [class.fa-spin]="loading"></fa-icon>
|
||
<span>{{ loading ? 'Loading...' : 'Reload' }}</span>
|
||
</button>
|
||
</div>
|
||
|
||
@if (errorMessage) {
|
||
<div class="rdd-alert">
|
||
<fa-icon icon="exclamation-triangle"></fa-icon>
|
||
<span>{{ errorMessage }}</span>
|
||
</div>
|
||
}
|
||
|
||
<!-- 1. Summary -->
|
||
<section class="rdd-section">
|
||
<h3 class="rdd-section__title">RD Book at a Glance</h3>
|
||
<div class="rdd-cards">
|
||
<button class="rdd-card rdd-card--success" [class.rdd-card--active]="stageFilter === 'REGULAR'" (click)="showStage('REGULAR')">
|
||
<span class="rdd-card__label"><fa-icon icon="chart-line"></fa-icon> Active RDs</span>
|
||
<span class="rdd-card__val">{{ summary.activeCount }}</span>
|
||
<span class="rdd-card__sub">Balance ₹{{ summary.activeBalance | number: '1.2-2' }}</span>
|
||
</button>
|
||
|
||
<button class="rdd-card rdd-card--info" [class.rdd-card--active]="stageFilter === 'MATURED'" (click)="showStage('MATURED')">
|
||
<span class="rdd-card__label"><fa-icon icon="calendar-check"></fa-icon> Matured – Close Pending</span>
|
||
<span class="rdd-card__val">{{ summary.maturedCount }}</span>
|
||
<span class="rdd-card__sub">To pay ₹{{ summary.maturedAmount | number: '1.2-2' }}</span>
|
||
</button>
|
||
|
||
<div class="rdd-card rdd-card--neutral rdd-card--static">
|
||
<span class="rdd-card__label"><fa-icon icon="check-circle"></fa-icon> Closed This Month</span>
|
||
<span class="rdd-card__val">{{ maturedClosedThisMonth + prematureClosedThisMonth }}</span>
|
||
<span class="rdd-card__sub">On maturity {{ maturedClosedThisMonth }} · Premature {{ prematureClosedThisMonth }}</span>
|
||
</div>
|
||
|
||
<div class="rdd-card rdd-card--neutral rdd-card--static">
|
||
<span class="rdd-card__label"><fa-icon icon="hand-holding-usd"></fa-icon> This Month's Installments</span>
|
||
<span class="rdd-card__val">{{ summary.collectionPercent }}%</span>
|
||
<div class="rdd-progress">
|
||
<div class="rdd-progress__bar" [style.width.%]="summary.collectionPercent"></div>
|
||
</div>
|
||
<span class="rdd-card__sub"
|
||
>Received ₹{{ summary.receivedThisMonth | number: '1.0-0' }} of ₹{{
|
||
summary.dueThisMonth | number: '1.0-0'
|
||
}}</span
|
||
>
|
||
</div>
|
||
|
||
<button class="rdd-card rdd-card--danger" [class.rdd-card--active]="extraFilter === 'overdue'" (click)="showExtra('overdue')">
|
||
<span class="rdd-card__label"><fa-icon icon="exclamation-circle"></fa-icon> Overdue RDs</span>
|
||
<span class="rdd-card__val">{{ summary.overdueCount }}</span>
|
||
<span class="rdd-card__sub">Pending ₹{{ summary.overdueAmount | number: '1.2-2' }}</span>
|
||
</button>
|
||
|
||
<button class="rdd-card rdd-card--warn" [class.rdd-card--active]="extraFilter === 'lien'" (click)="showExtra('lien')">
|
||
<span class="rdd-card__label"><fa-icon icon="lock"></fa-icon> Under Lien (Loan)</span>
|
||
<span class="rdd-card__val">{{ summary.lienCount }}</span>
|
||
<span class="rdd-card__sub">Lien ₹{{ summary.lienAmount | number: '1.2-2' }}</span>
|
||
</button>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 2. Installment health -->
|
||
<section class="rdd-section">
|
||
<h3 class="rdd-section__title">Installment Health</h3>
|
||
<div class="rdd-health">
|
||
<div class="rdd-tiles">
|
||
<button class="rdd-tile" [class.rdd-tile--active]="stageFilter === 'DUE_TODAY'" (click)="showStage('DUE_TODAY')">
|
||
<span class="rdd-tile__dot rdd-tile__dot--neutral"></span>
|
||
<span class="rdd-tile__label">Due today</span>
|
||
<span class="rdd-tile__val">{{ summary.dueTodayCount }}</span>
|
||
<span class="rdd-tile__sub">{{ summary.dueTodayPaidCount }} paid · {{ summary.dueTodayCount - summary.dueTodayPaidCount }} pending</span>
|
||
</button>
|
||
<button class="rdd-tile" [class.rdd-tile--active]="stageFilter === 'IN_GRACE'" (click)="showStage('IN_GRACE')">
|
||
<span class="rdd-tile__dot rdd-tile__dot--warn"></span>
|
||
<span class="rdd-tile__label">Late, in grace</span>
|
||
<span class="rdd-tile__val">{{ summary.inGraceCount }}</span>
|
||
<span class="rdd-tile__sub">₹{{ summary.inGraceAmount | number: '1.0-0' }} · no penalty yet</span>
|
||
</button>
|
||
<button class="rdd-tile" [class.rdd-tile--active]="stageFilter === 'PAST_GRACE'" (click)="showStage('PAST_GRACE')">
|
||
<span class="rdd-tile__dot rdd-tile__dot--danger"></span>
|
||
<span class="rdd-tile__label">Past grace</span>
|
||
<span class="rdd-tile__val">{{ summary.pastGraceCount }}</span>
|
||
<span class="rdd-tile__sub">₹{{ summary.pastGraceAmount | number: '1.0-0' }} · penalty applies</span>
|
||
</button>
|
||
<div class="rdd-tile rdd-tile--static">
|
||
<span class="rdd-tile__dot rdd-tile__dot--success"></span>
|
||
<span class="rdd-tile__label">Penalty income</span>
|
||
<span class="rdd-tile__val">₹{{ penaltyIncomeThisMonth | number: '1.0-0' }}</span>
|
||
<span class="rdd-tile__sub">This month · ₹{{ penaltyIncomeTotal | number: '1.0-0' }} in all</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="rdd-ageing">
|
||
<div class="rdd-ageing__title">How late are overdue RDs?</div>
|
||
@for (b of ageingBuckets; track b.key) {
|
||
<button class="rdd-ageing__row" [class.rdd-ageing__row--active]="extraFilter === b.key" (click)="showExtra(b.key)">
|
||
<span class="rdd-ageing__label">{{ b.label }}</span>
|
||
<span class="rdd-ageing__track">
|
||
<span class="rdd-ageing__bar" [style.width.%]="ageingWidth(b.key)"></span>
|
||
</span>
|
||
<span class="rdd-ageing__count">{{ summary.ageing[b.key] || 0 }}</span>
|
||
</button>
|
||
}
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 3. Action list -->
|
||
<section class="rdd-section">
|
||
<h3 class="rdd-section__title">RD List – What to Do Next</h3>
|
||
<div class="rdd-toolbar">
|
||
<div class="rdd-search">
|
||
<fa-icon icon="search" class="rdd-search__icon"></fa-icon>
|
||
<input class="rdd-search__input" type="text" placeholder="Account no. or client name" [(ngModel)]="search" />
|
||
</div>
|
||
<select class="rdd-select" [(ngModel)]="officeFilter">
|
||
<option [ngValue]="''">All branches</option>
|
||
@for (o of offices; track o.id) {
|
||
<option [ngValue]="o.id">{{ o.name }}</option>
|
||
}
|
||
</select>
|
||
<select class="rdd-select" [(ngModel)]="productFilter">
|
||
<option [ngValue]="''">All products</option>
|
||
@for (p of products; track p.id) {
|
||
<option [ngValue]="p.id">{{ p.name }}</option>
|
||
}
|
||
</select>
|
||
<select class="rdd-select" [(ngModel)]="stageFilter" (ngModelChange)="extraFilter = ''">
|
||
<option [ngValue]="''">All statuses</option>
|
||
@for (k of stageKeys; track k) {
|
||
<option [ngValue]="k">{{ stages[k].label }}</option>
|
||
}
|
||
</select>
|
||
@if (anyFilter) {
|
||
<button class="rdd-btn rdd-btn--slim" (click)="resetFilters()"><fa-icon icon="undo"></fa-icon> Clear</button>
|
||
}
|
||
<span class="rdd-toolbar__spacer"></span>
|
||
<span class="rdd-count"
|
||
><strong>{{ visibleRows.length }}</strong> of {{ rows.length }} RDs</span
|
||
>
|
||
</div>
|
||
|
||
@if (loading) {
|
||
<div class="rdd-state"><fa-icon icon="spinner" [class.fa-spin]="true"></fa-icon> Loading RDs...</div>
|
||
} @else if (visibleRows.length === 0) {
|
||
<div class="rdd-state">
|
||
<fa-icon icon="inbox"></fa-icon>
|
||
{{ rows.length ? 'No RDs match these filters.' : 'No running Recurring Deposits.' }}
|
||
</div>
|
||
} @else {
|
||
<div class="rdd-table-card">
|
||
<table class="rdd-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Account</th>
|
||
<th>Client</th>
|
||
<th>Product</th>
|
||
<th class="text-right">Installment</th>
|
||
<th>Next Due</th>
|
||
<th class="text-right">Overdue</th>
|
||
<th>Maturity</th>
|
||
<th>Status</th>
|
||
<th class="text-center">Auto Debit</th>
|
||
<th class="text-right">Lien</th>
|
||
<th class="text-center">Action</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
@for (r of visibleRows; track r.id) {
|
||
<tr class="rdd-row" (click)="openAccount(r)">
|
||
<td>
|
||
<span class="rdd-mono">{{ r.accountNo }}</span>
|
||
<div class="rdd-sub">{{ r.officeName }}</div>
|
||
</td>
|
||
<td class="rdd-strong">{{ r.clientName }}</td>
|
||
<td class="rdd-muted">{{ r.productName }}</td>
|
||
<td class="text-right">
|
||
<span class="rdd-mono">₹{{ r.installmentAmount | number: '1.2-2' }}</span>
|
||
<div class="rdd-sub">{{ r.paidInstallments }} of {{ r.totalInstallments }} paid</div>
|
||
</td>
|
||
<td>
|
||
<span class="rdd-mono">{{ r.nextDueDate || '—' }}</span>
|
||
<div class="rdd-sub" [class.rdd-sub--danger]="r.daysLate > 0">{{ dueText(r) }}</div>
|
||
</td>
|
||
<td class="text-right">
|
||
@if (r.overdueInstallments > 0) {
|
||
<span class="rdd-mono rdd-danger">₹{{ r.overdueAmount | number: '1.2-2' }}</span>
|
||
<div class="rdd-sub">
|
||
{{ r.overdueInstallments }} installment{{ r.overdueInstallments === 1 ? '' : 's' }}
|
||
</div>
|
||
} @else {
|
||
<span class="rdd-muted">—</span>
|
||
}
|
||
</td>
|
||
<td>
|
||
<span class="rdd-mono">{{ r.maturityDate || '—' }}</span>
|
||
<div class="rdd-sub">{{ maturityText(r) }}</div>
|
||
</td>
|
||
<td>
|
||
<span [class]="stageClass(r.stage)" [title]="r.meta.hint">{{ r.meta.label }}</span>
|
||
@if (r.penaltyCount > 0) {
|
||
<div class="rdd-sub">Penalty ₹{{ r.penaltyAmount | number: '1.0-0' }} ({{ r.penaltyCount }}x)</div>
|
||
}
|
||
</td>
|
||
<td class="text-center">
|
||
@if (r.autoDebit) {
|
||
<span class="rdd-yes" [title]="'From ' + (r.linkedAccountNo || 'linked savings')">On</span>
|
||
} @else {
|
||
<span class="rdd-muted">Off</span>
|
||
}
|
||
</td>
|
||
<td class="text-right">
|
||
@if (r.lienAmount > 0) {
|
||
<span class="rdd-mono">₹{{ r.lienAmount | number: '1.0-0' }}</span>
|
||
} @else {
|
||
<span class="rdd-muted">—</span>
|
||
}
|
||
</td>
|
||
<td class="text-center rdd-actions" (click)="$event.stopPropagation()">
|
||
@if (r.stage === 'MATURED') {
|
||
<button class="rdd-btn rdd-btn--primary rdd-btn--xs" (click)="closeAccount(r)">Close</button>
|
||
}
|
||
<button class="rdd-btn rdd-btn--xs" (click)="openAccount(r)">View</button>
|
||
</td>
|
||
</tr>
|
||
}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
}
|
||
</section>
|
||
</div>
|