feat: KYC re-verify UX, family/nominee tab gating, savings dialogs, cfcfrms shell
- KYC tab: fix Verify (PAN) and Send OTP (Aadhaar) being disabled in edit mode; unmask Aadhaar input and show on-file masked value as hint; resolve kycId from id/kycId/resourceId fallbacks. - Client stepper/view: add familyAllowed / nomineeAllowed predicates + CanActivate route guards. Family tab hidden for non-individuals, Nominee tab hidden for non-proprietor entities. Family-members step accepts a nomineeOnly input for proprietorship clients. Add/Edit/Delete buttons inside family-members and nominee tabs gated by canAdd. - cfcfrms: add cfcfrms-shell wrapper, restyle complaint/fraud/mule/escalate pages and dashboard. - Savings: identity-verify and transaction-confirm dialogs, manage / activate / add-charge flows, account preview styling. - Tasks, core shell, account-nominees, icons: minor style and behaviour updates. 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
bf3e26a0d0
commit
b149fef72e
+67
-47
@@ -6,53 +6,73 @@
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<div class="container mat-elevation-z8">
|
||||
<mat-card>
|
||||
<form [formGroup]="activateSavingsAccountForm" (ngSubmit)="submit()">
|
||||
<mat-card-content>
|
||||
<div class="layout-column">
|
||||
<mat-form-field class="flex-fill" (click)="activatedOnDatePicker.open()">
|
||||
<mat-label>{{ 'labels.inputs.Activated On Date' | translate }}</mat-label>
|
||||
<input
|
||||
matInput
|
||||
[min]="minDate"
|
||||
[max]="maxDate"
|
||||
[matDatepicker]="activatedOnDatePicker"
|
||||
required
|
||||
formControlName="activatedOnDate"
|
||||
/>
|
||||
<mat-datepicker-toggle matSuffix [for]="activatedOnDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #activatedOnDatePicker></mat-datepicker>
|
||||
@if (activateSavingsAccountForm.controls.activatedOnDate.hasError('required')) {
|
||||
<mat-error>
|
||||
{{ 'labels.inputs.Activated On Date' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<div class="act-savings">
|
||||
<!-- Hero -->
|
||||
<div class="act-savings__hero">
|
||||
<div class="act-savings__hero-icon">
|
||||
<fa-icon icon="check-circle"></fa-icon>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="act-savings__title">Activate Savings Account</h2>
|
||||
<p class="act-savings__sub">Set the activation date and opening balance to bring this account live.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<mat-form-field class="flex-fill">
|
||||
<mat-label>Opening Balance</mat-label>
|
||||
<input matInput type="number" [min]="minRequiredOpeningBalance ?? 0" formControlName="openingBalance" />
|
||||
<span matPrefix>₹ </span>
|
||||
@if (minRequiredOpeningBalance) {
|
||||
<mat-hint>Minimum required: ₹{{ minRequiredOpeningBalance }}</mat-hint>
|
||||
}
|
||||
@if (activateSavingsAccountForm.controls.openingBalance.hasError('min')) {
|
||||
<mat-error>Opening balance must be at least ₹{{ minRequiredOpeningBalance ?? 0 }}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
<!-- Form card -->
|
||||
<form class="act-savings__card" [formGroup]="activateSavingsAccountForm" (ngSubmit)="submit()">
|
||||
<div class="act-savings__card-head">
|
||||
<fa-icon icon="info-circle" class="act-savings__card-ico"></fa-icon>
|
||||
<h3 class="act-savings__card-title">Activation Details</h3>
|
||||
</div>
|
||||
|
||||
<mat-card-actions class="layout-row align-center gap-5px responsive-column">
|
||||
<button type="button" mat-raised-button [routerLink]="['../../transactions']">
|
||||
{{ 'labels.buttons.Cancel' | translate }}
|
||||
</button>
|
||||
<button mat-raised-button color="primary" [disabled]="!activateSavingsAccountForm.valid">
|
||||
{{ 'labels.buttons.Confirm' | translate }}
|
||||
</button>
|
||||
</mat-card-actions>
|
||||
</form>
|
||||
</mat-card>
|
||||
<div class="act-savings__card-body">
|
||||
<mat-form-field appearance="outline" class="act-savings__field" (click)="activatedOnDatePicker.open()">
|
||||
<mat-label>{{ 'labels.inputs.Activated On Date' | translate }}</mat-label>
|
||||
<input
|
||||
matInput
|
||||
[min]="minDate"
|
||||
[max]="maxDate"
|
||||
[matDatepicker]="activatedOnDatePicker"
|
||||
required
|
||||
formControlName="activatedOnDate"
|
||||
/>
|
||||
<mat-datepicker-toggle matSuffix [for]="activatedOnDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #activatedOnDatePicker></mat-datepicker>
|
||||
@if (activateSavingsAccountForm.controls.activatedOnDate.hasError('required')) {
|
||||
<mat-error>
|
||||
{{ 'labels.inputs.Activated On Date' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="act-savings__field">
|
||||
<mat-label>Opening Balance</mat-label>
|
||||
<input matInput type="number" [min]="minRequiredOpeningBalance ?? 0" formControlName="openingBalance" />
|
||||
<span matPrefix class="act-savings__prefix">₹ </span>
|
||||
@if (minRequiredOpeningBalance) {
|
||||
<mat-hint>Minimum required: ₹{{ minRequiredOpeningBalance }}</mat-hint>
|
||||
}
|
||||
@if (activateSavingsAccountForm.controls.openingBalance.hasError('min')) {
|
||||
<mat-error>Opening balance must be at least ₹{{ minRequiredOpeningBalance ?? 0 }}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="act-savings__actions">
|
||||
<button type="button" mat-stroked-button class="act-savings__cancel" [routerLink]="['../../transactions']">
|
||||
<fa-icon icon="times" class="m-r-6"></fa-icon>
|
||||
{{ 'labels.buttons.Cancel' | translate }}
|
||||
</button>
|
||||
<button
|
||||
mat-raised-button
|
||||
type="submit"
|
||||
class="act-savings__confirm"
|
||||
[disabled]="!activateSavingsAccountForm.valid"
|
||||
>
|
||||
<fa-icon icon="check" class="m-r-6"></fa-icon>
|
||||
{{ 'labels.buttons.Confirm' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
+186
-2
@@ -6,6 +6,190 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
.container {
|
||||
max-width: 37rem;
|
||||
$navy: #0d1b2a;
|
||||
$blue: #1e88e5;
|
||||
$blue-d: #1e40af;
|
||||
$green: #10b981;
|
||||
$green-d: #047857;
|
||||
$border: #e2e8f0;
|
||||
$soft: #f8fafc;
|
||||
|
||||
.act-savings {
|
||||
max-width: 720px;
|
||||
margin: 0 auto;
|
||||
padding: 20px 24px;
|
||||
|
||||
/* ─── Hero ───────────────────────────────────────────────────────── */
|
||||
&__hero {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
&__hero-icon {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 12px;
|
||||
background: linear-gradient(135deg, $green 0%, $green-d 100%);
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
box-shadow: 0 4px 14px -4px rgb(4 120 87 / 45%);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__title {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 800;
|
||||
color: $navy;
|
||||
line-height: 1.15;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
&__sub {
|
||||
margin: 4px 0 0;
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
max-width: 540px;
|
||||
}
|
||||
|
||||
/* ─── Form card ─────────────────────────────────────────────────── */
|
||||
&__card {
|
||||
background: #fff;
|
||||
border: 1px solid $border;
|
||||
border-radius: 14px;
|
||||
box-shadow: 0 1px 3px rgb(15 23 42 / 4%);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__card-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 13px 20px;
|
||||
background: $soft;
|
||||
border-bottom: 1px solid $border;
|
||||
}
|
||||
|
||||
&__card-ico {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 8px;
|
||||
background: linear-gradient(135deg, $blue 0%, $blue-d 100%);
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&__card-title {
|
||||
margin: 0;
|
||||
font-size: 13.5px;
|
||||
font-weight: 700;
|
||||
color: $navy;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
&__card-body {
|
||||
padding: 22px 22px 4px;
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
/* Form field tweaks — slightly more compact density. */
|
||||
&__field {
|
||||
width: 100%;
|
||||
|
||||
::ng-deep .mat-mdc-text-field-wrapper {
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
::ng-deep .mat-mdc-form-field-infix {
|
||||
min-height: 48px;
|
||||
padding-top: 12px !important;
|
||||
padding-bottom: 12px !important;
|
||||
}
|
||||
|
||||
::ng-deep .mdc-notched-outline__leading,
|
||||
::ng-deep .mdc-notched-outline__notch,
|
||||
::ng-deep .mdc-notched-outline__trailing {
|
||||
border-color: #cbd5e1 !important;
|
||||
}
|
||||
|
||||
::ng-deep &.mat-focused {
|
||||
.mdc-notched-outline__leading,
|
||||
.mdc-notched-outline__notch,
|
||||
.mdc-notched-outline__trailing {
|
||||
border-color: $blue-d !important;
|
||||
border-width: 2px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__prefix {
|
||||
color: #64748b;
|
||||
font-weight: 600;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
/* ─── Action row ────────────────────────────────────────────────── */
|
||||
&__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
padding: 14px 20px 18px;
|
||||
border-top: 1px solid #f1f5f9;
|
||||
background: #fff;
|
||||
|
||||
button {
|
||||
height: 42px !important;
|
||||
border-radius: 10px !important;
|
||||
font-weight: 600 !important;
|
||||
padding: 0 22px !important;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
}
|
||||
|
||||
&__cancel {
|
||||
color: #475569 !important;
|
||||
border-color: $border !important;
|
||||
}
|
||||
|
||||
&__confirm {
|
||||
background: linear-gradient(135deg, $green 0%, $green-d 100%) !important;
|
||||
color: #fff !important;
|
||||
box-shadow: 0 4px 12px -2px rgb(4 120 87 / 35%) !important;
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.55;
|
||||
filter: grayscale(0.3);
|
||||
background: linear-gradient(135deg, #9ca3af 0%, #6b7280 100%) !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.m-r-6 {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
@media (width <= 600px) {
|
||||
.act-savings {
|
||||
padding: 14px;
|
||||
|
||||
&__actions {
|
||||
flex-direction: column-reverse;
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -11,6 +11,7 @@ import { Component, OnInit, Input, inject } from '@angular/core';
|
||||
import { UntypedFormGroup, UntypedFormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
|
||||
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
||||
import { Dates } from 'app/core/utils/dates';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
|
||||
/** Custom Services */
|
||||
import { SavingsService } from 'app/savings/savings.service';
|
||||
@@ -19,13 +20,17 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
|
||||
/**
|
||||
* Activate Savings Account Component
|
||||
*
|
||||
* Nexora redesign: gradient green hero + section card with form fields and
|
||||
* a primary/secondary action row.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-activate-savings-account',
|
||||
templateUrl: './activate-savings-account.component.html',
|
||||
styleUrls: ['./activate-savings-account.component.scss'],
|
||||
imports: [
|
||||
...STANDALONE_SHARED_IMPORTS
|
||||
...STANDALONE_SHARED_IMPORTS,
|
||||
FaIconComponent
|
||||
]
|
||||
})
|
||||
export class ActivateSavingsAccountComponent implements OnInit {
|
||||
|
||||
Reference in New Issue
Block a user