feat: letter-mgmt module + digital UPI/IMPS/ATM-POS reports + header redesign
letter-mgmt (new module): - Notice page: overdue loan list with filters, KPI strip, bulk actions, call disposition logging (Promise-to-Pay, Not Reachable, etc.), Speed Post dispatch dialog with consignment + post-office tracking, PDF preview + download, call-contact dialog with primary / alt / family / nominee numbers and per-contact disposition history. - Certificates: Loan Interest, Share, Balance, Form 60 (no-PAN flow), Form 15G/H (auto age-based), Hypothecation, NOC - each with shared preview dialog (six tone variants) and jsPDF generator using a common letterhead / summary-grid / signature-seal helper. digital: - IMPS module: report (P2A/P2P/Aadhaar tabs) + entry form with MMID/mobile lookup, NPCI switch resolve-to-A/C+IFSC BANL flow, sender autocomplete, hide sender field for P2P/Aadhaar, charges + 18% GST + waive checkbox, per-sender today-only history dialog. - UPI report with app-mix tab and full transaction filtering. - ATM/POS/ECOM hub with three ticket cards routing to today/status/cycle. - NEFT/RTGS entry: sender-account autocomplete, charges + GST + waive, field reorder (sender before amount), NEFT cap / RTGS floor validation, History button opening per-sender today's transactions. - NEFT/RTGS report: horizontal-bar trend leaderboard replacing tall bars. shell + theme: - Header redesigned with IBM Plex Sans 12/500 typography, 11-column grid per row, top-accent hover indicator, bordered cells via grid-gap. - Live KPI ticker below menu (CASH, TXN TODAY, CLEARING IN/OUT, NPA, CASA, CD RATIO, RBI CIRC, REPO) with marquee scroll, pause-on-hover, brand-blue gradient, amber bottom strip, pulsing red LIVE chip. - Sidebar 'KYC Management' -> 'KYC & ReKYC Management'. - Letter Mgmt / certificate menu items wired with green icon hints for items that have a working page. access-control: - MFA card layout compacted; method header inlines back button + title. icons: - Register faTruck/TruckFast, faEllipsisV/H, faCommentDots. - Add IBM Plex Sans Google Fonts import to index.html. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
4322f37c6d
commit
325228c514
+139
-110
@@ -6,121 +6,150 @@
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<div class="container">
|
||||
<mat-card>
|
||||
<form [formGroup]="clientChargeForm" (ngSubmit)="submit()">
|
||||
<mat-card-content>
|
||||
<div class="layout-column">
|
||||
<mat-form-field>
|
||||
<mat-label>{{ 'labels.inputs.Charge' | translate }}</mat-label>
|
||||
<mat-select required formControlName="chargeId">
|
||||
@for (clientCharge of clientChargeOptions; track clientCharge) {
|
||||
<mat-option [value]="clientCharge.id">
|
||||
{{ clientCharge.name + ' (' + clientCharge.currency.name + ')' }}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
@if (clientChargeForm.controls.chargeId.hasError('required')) {
|
||||
<div class="ca-root">
|
||||
<div class="ca-hero">
|
||||
<div class="ca-hero-icon"><fa-icon icon="receipt"></fa-icon></div>
|
||||
<div class="ca-hero-text">
|
||||
<div class="ca-hero-eyebrow">Client Action</div>
|
||||
<h2 class="ca-hero-title">{{ 'labels.buttons.Add Charge' | translate }}</h2>
|
||||
<p class="ca-hero-sub">
|
||||
Attach a one-time fee or a recurring charge to this client. The charge can be linked to a specific account or
|
||||
held at the client level for ad-hoc collection.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form [formGroup]="clientChargeForm" (ngSubmit)="submit()" class="ca-card">
|
||||
<div class="ca-card-body">
|
||||
<div class="ca-form-row">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>{{ 'labels.inputs.Charge' | translate }}</mat-label>
|
||||
<mat-select required formControlName="chargeId">
|
||||
@for (clientCharge of clientChargeOptions; track clientCharge) {
|
||||
<mat-option [value]="clientCharge.id">{{
|
||||
clientCharge.name + ' (' + clientCharge.currency.name + ')'
|
||||
}}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
@if (clientChargeForm.controls.chargeId.hasError('required')) {
|
||||
<mat-error>
|
||||
{{ 'labels.inputs.Charge' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
@if (chargeDetails) {
|
||||
<div class="ca-form-row">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>{{ 'labels.inputs.Amount' | translate }}</mat-label>
|
||||
<input type="number" required matInput formControlName="amount" />
|
||||
@if (clientChargeForm.controls.amount.hasError('required')) {
|
||||
<mat-error>
|
||||
{{ 'labels.inputs.Charge' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
{{ 'labels.inputs.Amount' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
|
||||
@if (chargeDetails) {
|
||||
<div class="layout-column">
|
||||
<mat-form-field>
|
||||
<mat-label>{{ 'labels.inputs.Amount' | translate }}</mat-label>
|
||||
<input type="number" required matInput formControlName="amount" />
|
||||
@if (clientChargeForm.controls.amount.hasError('required')) {
|
||||
<mat-error>
|
||||
{{ 'labels.inputs.Amount' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<mat-label>{{ 'labels.inputs.Charge Calculation' | translate }}</mat-label>
|
||||
<mat-select formControlName="chargeCalculationType">
|
||||
@for (chargeCalculation of chargeDetails.chargeCalculationTypeOptions; track chargeCalculation) {
|
||||
<mat-option [value]="chargeCalculation.id">
|
||||
{{ chargeCalculation.value }}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<mat-label>{{ 'labels.inputs.Charge time type' | translate }}</mat-label>
|
||||
<mat-select formControlName="chargeTimeType">
|
||||
@for (chargeTime of chargeDetails.chargeTimeTypeOptions; track chargeTime) {
|
||||
<mat-option [value]="chargeTime.id">
|
||||
{{ chargeTime.value }}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
@if (clientChargeForm.contains('dueDate')) {
|
||||
<mat-form-field (click)="dueDatePicker.open()">
|
||||
<mat-label>{{ 'labels.inputs.Due for collection on' | translate }}</mat-label>
|
||||
<input
|
||||
matInput
|
||||
[min]="minDate"
|
||||
[max]="maxDate"
|
||||
[matDatepicker]="dueDatePicker"
|
||||
required
|
||||
formControlName="dueDate"
|
||||
/>
|
||||
<mat-datepicker-toggle matSuffix [for]="dueDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #dueDatePicker></mat-datepicker>
|
||||
@if (clientChargeForm.controls.dueDate.hasError('required')) {
|
||||
<mat-error>
|
||||
{{ 'labels.inputs.Due for collection on' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
}
|
||||
@if (clientChargeForm.contains('feeOnMonthDay')) {
|
||||
<mat-form-field (click)="feeOnMonthDayPicker.open()">
|
||||
<mat-label>{{ 'labels.inputs.Due On' | translate }}</mat-label>
|
||||
<input
|
||||
matInput
|
||||
[min]="minDate"
|
||||
[max]="maxDate"
|
||||
[matDatepicker]="feeOnMonthDayPicker"
|
||||
required
|
||||
formControlName="feeOnMonthDay"
|
||||
/>
|
||||
<mat-datepicker-toggle matSuffix [for]="feeOnMonthDayPicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #feeOnMonthDayPicker></mat-datepicker>
|
||||
@if (clientChargeForm.controls.feeOnMonthDay.hasError('required')) {
|
||||
<mat-error>
|
||||
{{ 'labels.inputs.Due Date' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
}
|
||||
@if (clientChargeForm.contains('feeInterval')) {
|
||||
<mat-form-field>
|
||||
<mat-label>{{ 'labels.inputs.Repeats Every' | translate }}</mat-label>
|
||||
<input matInput formControlName="feeInterval" />
|
||||
</mat-form-field>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<mat-card-actions class="layout-row layout-xs-column layout-align-center gap-5px">
|
||||
<button type="button" mat-raised-button [routerLink]="['../../']">
|
||||
{{ 'labels.buttons.Cancel' | translate }}
|
||||
</button>
|
||||
<button mat-raised-button color="primary" [disabled]="!clientChargeForm.valid">
|
||||
{{ 'labels.buttons.Submit' | translate }}
|
||||
</button>
|
||||
</mat-card-actions>
|
||||
</mat-card-content>
|
||||
</form>
|
||||
</mat-card>
|
||||
<div class="ca-form-row">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>{{ 'labels.inputs.Charge Calculation' | translate }}</mat-label>
|
||||
<mat-select formControlName="chargeCalculationType">
|
||||
@for (chargeCalculation of chargeDetails.chargeCalculationTypeOptions; track chargeCalculation) {
|
||||
<mat-option [value]="chargeCalculation.id">{{ chargeCalculation.value }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="ca-form-row">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>{{ 'labels.inputs.Charge time type' | translate }}</mat-label>
|
||||
<mat-select formControlName="chargeTimeType">
|
||||
@for (chargeTime of chargeDetails.chargeTimeTypeOptions; track chargeTime) {
|
||||
<mat-option [value]="chargeTime.id">{{ chargeTime.value }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
@if (clientChargeForm.contains('dueDate')) {
|
||||
<div class="ca-form-row">
|
||||
<mat-form-field appearance="outline" (click)="dueDatePicker.open()">
|
||||
<mat-label>{{ 'labels.inputs.Due for collection on' | translate }}</mat-label>
|
||||
<input
|
||||
matInput
|
||||
[min]="minDate"
|
||||
[max]="maxDate"
|
||||
[matDatepicker]="dueDatePicker"
|
||||
required
|
||||
formControlName="dueDate"
|
||||
/>
|
||||
<mat-datepicker-toggle matSuffix [for]="dueDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #dueDatePicker></mat-datepicker>
|
||||
@if (clientChargeForm.controls.dueDate.hasError('required')) {
|
||||
<mat-error>
|
||||
{{ 'labels.inputs.Due for collection on' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (clientChargeForm.contains('feeOnMonthDay')) {
|
||||
<div class="ca-form-row">
|
||||
<mat-form-field appearance="outline" (click)="feeOnMonthDayPicker.open()">
|
||||
<mat-label>{{ 'labels.inputs.Due On' | translate }}</mat-label>
|
||||
<input
|
||||
matInput
|
||||
[min]="minDate"
|
||||
[max]="maxDate"
|
||||
[matDatepicker]="feeOnMonthDayPicker"
|
||||
required
|
||||
formControlName="feeOnMonthDay"
|
||||
/>
|
||||
<mat-datepicker-toggle matSuffix [for]="feeOnMonthDayPicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #feeOnMonthDayPicker></mat-datepicker>
|
||||
@if (clientChargeForm.controls.feeOnMonthDay.hasError('required')) {
|
||||
<mat-error>
|
||||
{{ 'labels.inputs.Due Date' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (clientChargeForm.contains('feeInterval')) {
|
||||
<div class="ca-form-row">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>{{ 'labels.inputs.Repeats Every' | translate }}</mat-label>
|
||||
<input matInput formControlName="feeInterval" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="ca-notice">
|
||||
<fa-icon icon="info-circle"></fa-icon>
|
||||
<span>
|
||||
The calculation type and timing determine when this charge is auto-applied (e.g. monthly fee, annual
|
||||
membership, ad-hoc). Verify the amount and due date before submitting.
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="ca-actions">
|
||||
<button type="button" class="ca-btn ca-btn--cancel" [routerLink]="['../../']">
|
||||
<fa-icon icon="times"></fa-icon><span>{{ 'labels.buttons.Cancel' | translate }}</span>
|
||||
</button>
|
||||
<button type="submit" class="ca-btn ca-btn--confirm" [disabled]="!clientChargeForm.valid">
|
||||
<fa-icon icon="check"></fa-icon><span>{{ 'labels.buttons.Submit' | translate }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
+8
-2
@@ -6,6 +6,12 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
.container {
|
||||
width: 37rem;
|
||||
@use 'assets/styles/client-action' as ca;
|
||||
|
||||
@include ca.client-action-styles;
|
||||
|
||||
/* Add Charge — amber accent (financial / fee) */
|
||||
:host {
|
||||
--ca-accent: #b45309;
|
||||
--ca-accent-l: #fef3c7;
|
||||
}
|
||||
|
||||
+3
-1
@@ -21,6 +21,7 @@ import { Router, ActivatedRoute, RouterLink } from '@angular/router';
|
||||
import { ClientsService } from 'app/clients/clients.service';
|
||||
import { Dates } from 'app/core/utils/dates';
|
||||
import { SettingsService } from 'app/settings/settings.service';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
|
||||
/**
|
||||
@@ -31,7 +32,8 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
templateUrl: './add-client-charge.component.html',
|
||||
styleUrls: ['./add-client-charge.component.scss'],
|
||||
imports: [
|
||||
...STANDALONE_SHARED_IMPORTS
|
||||
...STANDALONE_SHARED_IMPORTS,
|
||||
FaIconComponent
|
||||
]
|
||||
})
|
||||
export class AddClientChargeComponent implements OnInit {
|
||||
|
||||
Reference in New Issue
Block a user