feat: client extra fields + KYC home-visit & video-KYC dialogs

- Add Education Level, Degree/Qualification, Religion, Social Category dropdowns (individual clients) with dependent filtering on the create/edit/preview/view flows
- Add Agriculture Income & Non-Agriculture Income fields under Financial & Classification
- Gate Save with a Verify Client alert dialog when sanctions screening is not yet performed
- Trim Office Name and CKYC Number columns from clients list; widen table to fill container
- Remove DIP/Screening log IDs from preview and personal-data tabs
- Rename "Branch Visit" to "Home Visit" across KYC management
- Wire Take Action: open dedicated dialogs for Schedule Home Visit (officer + datetime) and Send Video KYC link (channels + officer + 48h expiry + session preview)
- Migrate Signatory and Guardian image dialogs to inject() for prefer-inject lint rule

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Aditya
2026-04-27 16:36:58 +05:30
co-authored by Claude Opus 4.7
parent d72e969dc3
commit 52e3395345
15 changed files with 4021 additions and 997 deletions
@@ -42,7 +42,9 @@
<mat-select-trigger> <mat-select-trigger>
@if (createClientForm.get('legalFormId')?.value) { @if (createClientForm.get('legalFormId')?.value) {
<span class="legal-form-option"> <span class="legal-form-option">
<mat-icon class="legal-form-option-icon">{{ createClientForm.get('legalFormId')?.value === 1 ? 'person' : 'business' }}</mat-icon> <mat-icon class="legal-form-option-icon">{{
createClientForm.get('legalFormId')?.value === 1 ? 'person' : 'business'
}}</mat-icon>
{{ getSelectedLegalFormLabel() }} {{ getSelectedLegalFormLabel() }}
</span> </span>
} }
@@ -67,12 +69,12 @@
@if (getSelectedNationality().flagType === 'india') { @if (getSelectedNationality().flagType === 'india') {
<span class="nat-flag"> <span class="nat-flag">
<svg viewBox="0 0 36 24" class="nat-flag-svg"> <svg viewBox="0 0 36 24" class="nat-flag-svg">
<rect width="36" height="8" fill="#FF9933"/> <rect width="36" height="8" fill="#FF9933" />
<rect y="8" width="36" height="8" fill="#FFFFFF"/> <rect y="8" width="36" height="8" fill="#FFFFFF" />
<rect y="16" width="36" height="8" fill="#138808"/> <rect y="16" width="36" height="8" fill="#138808" />
<circle cx="18" cy="12" r="3" fill="#000080"/> <circle cx="18" cy="12" r="3" fill="#000080" />
<circle cx="18" cy="12" r="2.5" fill="#FFFFFF"/> <circle cx="18" cy="12" r="2.5" fill="#FFFFFF" />
<circle cx="18" cy="12" r="0.7" fill="#000080"/> <circle cx="18" cy="12" r="0.7" fill="#000080" />
</svg> </svg>
</span> </span>
} @else { } @else {
@@ -88,12 +90,12 @@
@if (nat.flagType === 'india') { @if (nat.flagType === 'india') {
<span class="nat-flag"> <span class="nat-flag">
<svg viewBox="0 0 36 24" class="nat-flag-svg"> <svg viewBox="0 0 36 24" class="nat-flag-svg">
<rect width="36" height="8" fill="#FF9933"/> <rect width="36" height="8" fill="#FF9933" />
<rect y="8" width="36" height="8" fill="#FFFFFF"/> <rect y="8" width="36" height="8" fill="#FFFFFF" />
<rect y="16" width="36" height="8" fill="#138808"/> <rect y="16" width="36" height="8" fill="#138808" />
<circle cx="18" cy="12" r="3" fill="#000080"/> <circle cx="18" cy="12" r="3" fill="#000080" />
<circle cx="18" cy="12" r="2.5" fill="#FFFFFF"/> <circle cx="18" cy="12" r="2.5" fill="#FFFFFF" />
<circle cx="18" cy="12" r="0.7" fill="#000080"/> <circle cx="18" cy="12" r="0.7" fill="#000080" />
</svg> </svg>
</span> </span>
} @else { } @else {
@@ -129,7 +131,10 @@
@if (createClientForm.get('fullname')) { @if (createClientForm.get('fullname')) {
<mat-form-field appearance="outline" class="form-field full-width"> <mat-form-field appearance="outline" class="form-field full-width">
<mat-label> <mat-label>
{{ 'labels.inputs.' + getDateLabel(createClientForm.get('legalFormId')?.value, ['Name', 'Entity Name']) | translate }} {{
'labels.inputs.' + getDateLabel(createClientForm.get('legalFormId')?.value, ['Name', 'Entity Name'])
| translate
}}
</mat-label> </mat-label>
<input matInput required formControlName="fullname" /> <input matInput required formControlName="fullname" />
@if (createClientForm.controls.fullname.hasError('required')) { @if (createClientForm.controls.fullname.hasError('required')) {
@@ -150,30 +155,39 @@
</div> </div>
<div class="verify-inline"> <div class="verify-inline">
@if (!sanctionsVerified && !sanctionsBlocked) { @if (!sanctionsVerified && !sanctionsBlocked) {
<button mat-stroked-button color="primary" type="button" class="verify-name-btn" <button
mat-stroked-button
color="primary"
type="button"
class="verify-name-btn"
[disabled]="readOnly" [disabled]="readOnly"
(click)="verifySanctions()"> (click)="verifySanctions()"
>
<fa-icon icon="shield-alt" class="verify-name-icon"></fa-icon> <fa-icon icon="shield-alt" class="verify-name-icon"></fa-icon>
Verify Entity Verify Entity
</button> </button>
} }
@if (sanctionsVerified) { @if (sanctionsVerified) {
<span class="sanctions-citation">RBI KYC Master Direction (2016) — Section 51 &amp; PMLA 2002</span> <span class="sanctions-citation">RBI KYC Master Direction (2016) — Section 51 &amp; PMLA 2002</span>
<span class="verify-status verified"> <span class="verify-status verified"> <fa-icon icon="check-circle"></fa-icon> Verified </span>
<fa-icon icon="check-circle"></fa-icon> Verified <mat-icon
</span> class="sanctions-info-icon"
<mat-icon class="sanctions-info-icon"
matTooltip="All customer names must be verified against the UN Security Council Consolidated List before account opening and periodically thereafter. Positive matches must be escalated to the Principal Officer immediately." matTooltip="All customer names must be verified against the UN Security Council Consolidated List before account opening and periodically thereafter. Positive matches must be escalated to the Principal Officer immediately."
matTooltipClass="sanctions-tooltip">info</mat-icon> matTooltipClass="sanctions-tooltip"
>info</mat-icon
>
} }
@if (sanctionsBlocked) { @if (sanctionsBlocked) {
<span class="sanctions-citation">RBI KYC Master Direction (2016) — Section 51 &amp; PMLA 2002</span> <span class="sanctions-citation">RBI KYC Master Direction (2016) — Section 51 &amp; PMLA 2002</span>
<span class="verify-status blocked"> <span class="verify-status blocked">
<fa-icon icon="exclamation-triangle"></fa-icon> Sanctions Match <fa-icon icon="exclamation-triangle"></fa-icon> Sanctions Match
</span> </span>
<mat-icon class="sanctions-info-icon blocked-info" <mat-icon
class="sanctions-info-icon blocked-info"
matTooltip="All customer names must be verified against the UN Security Council Consolidated List before account opening and periodically thereafter. Positive matches must be escalated to the Principal Officer immediately." matTooltip="All customer names must be verified against the UN Security Council Consolidated List before account opening and periodically thereafter. Positive matches must be escalated to the Principal Officer immediately."
matTooltipClass="sanctions-tooltip">info</mat-icon> matTooltipClass="sanctions-tooltip"
>info</mat-icon
>
} }
</div> </div>
</div> </div>
@@ -181,7 +195,9 @@
} }
<!-- Person: Salutation + First / Middle / Last Name --> <!-- Person: Salutation + First / Middle / Last Name -->
@if (createClientForm.get('firstname') || createClientForm.get('middlename') || createClientForm.get('lastname')) { @if (
createClientForm.get('firstname') || createClientForm.get('middlename') || createClientForm.get('lastname')
) {
<div class="name-fields-row"> <div class="name-fields-row">
@if (createClientForm.get('salutationId') && salutationOptions?.length > 0) { @if (createClientForm.get('salutationId') && salutationOptions?.length > 0) {
<mat-form-field appearance="outline" class="name-field salutation-field-wide"> <mat-form-field appearance="outline" class="name-field salutation-field-wide">
@@ -213,7 +229,10 @@
<mat-label>{{ 'labels.inputs.Middle Name' | translate }}</mat-label> <mat-label>{{ 'labels.inputs.Middle Name' | translate }}</mat-label>
<span matPrefix class="field-prefix-icon"><mat-icon>person_outline</mat-icon></span> <span matPrefix class="field-prefix-icon"><mat-icon>person_outline</mat-icon></span>
<input matInput formControlName="middlename" /> <input matInput formControlName="middlename" />
@if (createClientForm.controls.middlename?.hasError('pattern') && createClientForm.controls.middlename?.touched) { @if (
createClientForm.controls.middlename?.hasError('pattern') &&
createClientForm.controls.middlename?.touched
) {
<mat-error>Only letters, spaces, dots and hyphens allowed</mat-error> <mat-error>Only letters, spaces, dots and hyphens allowed</mat-error>
} }
</mat-form-field> </mat-form-field>
@@ -243,30 +262,39 @@
</div> </div>
<div class="verify-inline"> <div class="verify-inline">
@if (!sanctionsVerified && !sanctionsBlocked) { @if (!sanctionsVerified && !sanctionsBlocked) {
<button mat-stroked-button color="primary" type="button" class="verify-name-btn" <button
mat-stroked-button
color="primary"
type="button"
class="verify-name-btn"
[disabled]="readOnly" [disabled]="readOnly"
(click)="verifySanctions()"> (click)="verifySanctions()"
>
<fa-icon icon="shield-alt" class="verify-name-icon"></fa-icon> <fa-icon icon="shield-alt" class="verify-name-icon"></fa-icon>
Verify Client Verify Client
</button> </button>
} }
@if (sanctionsVerified) { @if (sanctionsVerified) {
<span class="sanctions-citation">RBI KYC Master Direction (2016) — Section 51 &amp; PMLA 2002</span> <span class="sanctions-citation">RBI KYC Master Direction (2016) — Section 51 &amp; PMLA 2002</span>
<span class="verify-status verified"> <span class="verify-status verified"> <fa-icon icon="check-circle"></fa-icon> Verified </span>
<fa-icon icon="check-circle"></fa-icon> Verified <mat-icon
</span> class="sanctions-info-icon"
<mat-icon class="sanctions-info-icon"
matTooltip="All customer names must be verified against the UN Security Council Consolidated List before account opening and periodically thereafter. Positive matches must be escalated to the Principal Officer immediately." matTooltip="All customer names must be verified against the UN Security Council Consolidated List before account opening and periodically thereafter. Positive matches must be escalated to the Principal Officer immediately."
matTooltipClass="sanctions-tooltip">info</mat-icon> matTooltipClass="sanctions-tooltip"
>info</mat-icon
>
} }
@if (sanctionsBlocked) { @if (sanctionsBlocked) {
<span class="sanctions-citation">RBI KYC Master Direction (2016) — Section 51 &amp; PMLA 2002</span> <span class="sanctions-citation">RBI KYC Master Direction (2016) — Section 51 &amp; PMLA 2002</span>
<span class="verify-status blocked"> <span class="verify-status blocked">
<fa-icon icon="exclamation-triangle"></fa-icon> Sanctions Match <fa-icon icon="exclamation-triangle"></fa-icon> Sanctions Match
</span> </span>
<mat-icon class="sanctions-info-icon blocked-info" <mat-icon
class="sanctions-info-icon blocked-info"
matTooltip="All customer names must be verified against the UN Security Council Consolidated List before account opening and periodically thereafter. Positive matches must be escalated to the Principal Officer immediately." matTooltip="All customer names must be verified against the UN Security Council Consolidated List before account opening and periodically thereafter. Positive matches must be escalated to the Principal Officer immediately."
matTooltipClass="sanctions-tooltip">info</mat-icon> matTooltipClass="sanctions-tooltip"
>info</mat-icon
>
} }
</div> </div>
</div> </div>
@@ -278,10 +306,18 @@
<mat-form-field appearance="outline" class="form-field" (click)="dateOfBirthDatePicker.open()"> <mat-form-field appearance="outline" class="form-field" (click)="dateOfBirthDatePicker.open()">
<mat-label>Date of Birth</mat-label> <mat-label>Date of Birth</mat-label>
<span matPrefix class="field-prefix-icon"><mat-icon>calendar_today</mat-icon></span> <span matPrefix class="field-prefix-icon"><mat-icon>calendar_today</mat-icon></span>
<input matInput required [max]="maxDate" [matDatepicker]="dateOfBirthDatePicker" formControlName="dateOfBirth" /> <input
matInput
required
[max]="maxDate"
[matDatepicker]="dateOfBirthDatePicker"
formControlName="dateOfBirth"
/>
<mat-datepicker-toggle matSuffix [for]="dateOfBirthDatePicker"></mat-datepicker-toggle> <mat-datepicker-toggle matSuffix [for]="dateOfBirthDatePicker"></mat-datepicker-toggle>
<mat-datepicker #dateOfBirthDatePicker></mat-datepicker> <mat-datepicker #dateOfBirthDatePicker></mat-datepicker>
@if (createClientForm.get('dateOfBirth')?.hasError('required') && createClientForm.get('dateOfBirth')?.touched) { @if (
createClientForm.get('dateOfBirth')?.hasError('required') && createClientForm.get('dateOfBirth')?.touched
) {
<mat-error>Date of Birth is <strong>required</strong></mat-error> <mat-error>Date of Birth is <strong>required</strong></mat-error>
} }
</mat-form-field> </mat-form-field>
@@ -324,7 +360,6 @@
} }
</div> </div>
} }
</div> </div>
<!-- Minor / Guardian Mapping (mandatory if age < 10) --> <!-- Minor / Guardian Mapping (mandatory if age < 10) -->
@@ -341,9 +376,15 @@
</div> </div>
<div class="guardian-section__header-right"> <div class="guardian-section__header-right">
<div class="guardian-section__age-chips"> <div class="guardian-section__age-chips">
<span class="guardian-age-chip"><strong>{{ ageBreakdown.years }}</strong> yrs</span> <span class="guardian-age-chip"
<span class="guardian-age-chip"><strong>{{ ageBreakdown.months }}</strong> mos</span> ><strong>{{ ageBreakdown.years }}</strong> yrs</span
<span class="guardian-age-chip"><strong>{{ ageBreakdown.days }}</strong> days</span> >
<span class="guardian-age-chip"
><strong>{{ ageBreakdown.months }}</strong> mos</span
>
<span class="guardian-age-chip"
><strong>{{ ageBreakdown.days }}</strong> days</span
>
<span class="guardian-minor-badge">MINOR</span> <span class="guardian-minor-badge">MINOR</span>
</div> </div>
@if (!selectedGuardian && !guardianLinking) { @if (!selectedGuardian && !guardianLinking) {
@@ -357,13 +398,14 @@
<!-- Body --> <!-- Body -->
<div class="guardian-section__body" formGroupName="guardianDetails"> <div class="guardian-section__body" formGroupName="guardianDetails">
<!-- Empty state: no guardian linked, not linking --> <!-- Empty state: no guardian linked, not linking -->
@if (!selectedGuardian && !guardianLinking) { @if (!selectedGuardian && !guardianLinking) {
<div class="guardian-empty guardian-empty--required"> <div class="guardian-empty guardian-empty--required">
<mat-icon class="guardian-empty__icon">person_off</mat-icon> <mat-icon class="guardian-empty__icon">person_off</mat-icon>
<div class="guardian-empty__content"> <div class="guardian-empty__content">
<span class="guardian-empty__text">No guardian linked yet. Click <strong>"Link Guardian"</strong> to search and add.</span> <span class="guardian-empty__text"
>No guardian linked yet. Click <strong>"Link Guardian"</strong> to search and add.</span
>
<span class="guardian-empty__required">Guardian is mandatory for minor accounts</span> <span class="guardian-empty__required">Guardian is mandatory for minor accounts</span>
</div> </div>
</div> </div>
@@ -377,22 +419,33 @@
<mat-select required formControlName="guardianRelation"> <mat-select required formControlName="guardianRelation">
<mat-option value="FATHER"><mat-icon class="icon-option-icon">man</mat-icon> Father</mat-option> <mat-option value="FATHER"><mat-icon class="icon-option-icon">man</mat-icon> Father</mat-option>
<mat-option value="MOTHER"><mat-icon class="icon-option-icon">woman</mat-icon> Mother</mat-option> <mat-option value="MOTHER"><mat-icon class="icon-option-icon">woman</mat-icon> Mother</mat-option>
<mat-option value="LEGAL_GUARDIAN"><mat-icon class="icon-option-icon">gavel</mat-icon> Legal Guardian</mat-option> <mat-option value="LEGAL_GUARDIAN"
<mat-option value="GRANDPARENT"><mat-icon class="icon-option-icon">elderly</mat-icon> Grandparent</mat-option> ><mat-icon class="icon-option-icon">gavel</mat-icon> Legal Guardian</mat-option
>
<mat-option value="GRANDPARENT"
><mat-icon class="icon-option-icon">elderly</mat-icon> Grandparent</mat-option
>
<mat-option value="OTHER"><mat-icon class="icon-option-icon">group</mat-icon> Other</mat-option> <mat-option value="OTHER"><mat-icon class="icon-option-icon">group</mat-icon> Other</mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<div class="guardian-search-wrapper guardian-search-col"> <div class="guardian-search-wrapper guardian-search-col">
<mat-form-field appearance="outline" class="guardian-search-field" <mat-form-field
[class.guardian-search-disabled]="!createClientForm.get('guardianDetails.guardianRelation')?.value"> appearance="outline"
class="guardian-search-field"
[class.guardian-search-disabled]="!createClientForm.get('guardianDetails.guardianRelation')?.value"
>
<mat-label>Search Guardian by Name or Client ID</mat-label> <mat-label>Search Guardian by Name or Client ID</mat-label>
<span matPrefix class="field-prefix-icon"><mat-icon>person_search</mat-icon></span> <span matPrefix class="field-prefix-icon"><mat-icon>person_search</mat-icon></span>
<input matInput [value]="guardianSearchText" <input
matInput
[value]="guardianSearchText"
(input)="onGuardianSearch($any($event.target).value)" (input)="onGuardianSearch($any($event.target).value)"
(blur)="dismissGuardianSearch()" (blur)="dismissGuardianSearch()"
[disabled]="!createClientForm.get('guardianDetails.guardianRelation')?.value" [disabled]="!createClientForm.get('guardianDetails.guardianRelation')?.value"
placeholder="Select relationship first, then search..." autocomplete="off" /> placeholder="Select relationship first, then search..."
autocomplete="off"
/>
</mat-form-field> </mat-form-field>
@if (guardianSearching) { @if (guardianSearching) {
@@ -413,11 +466,17 @@
<div class="guardian-result-item__info"> <div class="guardian-result-item__info">
<span class="guardian-result-item__name">{{ result.displayName }}</span> <span class="guardian-result-item__name">{{ result.displayName }}</span>
<span class="guardian-result-item__meta"> <span class="guardian-result-item__meta">
ID: {{ result.id }} &middot; A/C: {{ result.accountNumber }} &middot; {{ result.officeName }}@if (result.dateOfBirth) { &middot; DOB: {{ result.dateOfBirth | dateFormat }} }@if (result.fatherSpouseName) { &middot; {{ result.fatherSpouseName }} } ID: {{ result.id }} &middot; A/C: {{ result.accountNumber }} &middot;
{{ result.officeName }}
@if (result.dateOfBirth) {
&middot; DOB: {{ result.dateOfBirth | dateFormat }}
}
@if (result.fatherSpouseName) {
&middot; {{ result.fatherSpouseName }}
}
</span> </span>
</div> </div>
<span class="guardian-result-item__status" <span class="guardian-result-item__status" [class.active]="result.status?.value === 'Active'">
[class.active]="result.status?.value === 'Active'">
{{ result.status?.value }} {{ result.status?.value }}
</span> </span>
</div> </div>
@@ -429,7 +488,13 @@
} }
</div> </div>
<button mat-icon-button type="button" class="guardian-cancel-link" (click)="guardianLinking = false" matTooltip="Cancel"> <button
mat-icon-button
type="button"
class="guardian-cancel-link"
(click)="guardianLinking = false"
matTooltip="Cancel"
>
<mat-icon>close</mat-icon> <mat-icon>close</mat-icon>
</button> </button>
</div> </div>
@@ -453,8 +518,12 @@
<span class="guardian-row__col guardian-row__col--photo"> <span class="guardian-row__col guardian-row__col--photo">
<div class="guardian-row__avatar" [class.has-photo]="selectedGuardian.profileImageUrl"> <div class="guardian-row__avatar" [class.has-photo]="selectedGuardian.profileImageUrl">
@if (selectedGuardian.profileImageUrl) { @if (selectedGuardian.profileImageUrl) {
<img [src]="selectedGuardian.profileImageUrl" alt="Photo" class="avatar-photo guardian-row__zoomable" <img
(click)="zoomGuardianImage(selectedGuardian.profileImageUrl, 'Guardian Photo')" /> [src]="selectedGuardian.profileImageUrl"
alt="Photo"
class="avatar-photo guardian-row__zoomable"
(click)="zoomGuardianImage(selectedGuardian.profileImageUrl, 'Guardian Photo')"
/>
} @else { } @else {
<fa-icon icon="user-shield"></fa-icon> <fa-icon icon="user-shield"></fa-icon>
} }
@@ -470,26 +539,40 @@
</div> </div>
</span> </span>
<span class="guardian-row__col guardian-row__col--rel"> <span class="guardian-row__col guardian-row__col--rel">
<span class="guardian-row__rel-badge">{{ createClientForm.get('guardianDetails.guardianRelation')?.value || '-' }}</span> <span class="guardian-row__rel-badge">{{
createClientForm.get('guardianDetails.guardianRelation')?.value || '-'
}}</span>
</span> </span>
<span class="guardian-row__col guardian-row__col--cif"> <span class="guardian-row__col guardian-row__col--cif">
<span class="guardian-row__mono">{{ selectedGuardian.accountNo || selectedGuardian.accountNumber || '-' }}</span> <span class="guardian-row__mono">{{
selectedGuardian.accountNo || selectedGuardian.accountNumber || '-'
}}</span>
</span> </span>
<span class="guardian-row__col guardian-row__col--status"> <span class="guardian-row__col guardian-row__col--status">
<span class="guardian-row__status-badge" <span
[class.guardian-row__status-badge--active]="selectedGuardian.status?.value === 'Active'"> class="guardian-row__status-badge"
[class.guardian-row__status-badge--active]="selectedGuardian.status?.value === 'Active'"
>
{{ selectedGuardian.status?.value || '-' }} {{ selectedGuardian.status?.value || '-' }}
</span> </span>
</span> </span>
<span class="guardian-row__col guardian-row__col--actions"> <span class="guardian-row__col guardian-row__col--actions">
<button mat-icon-button type="button" class="guardian-action-btn" <button
mat-icon-button
type="button"
class="guardian-action-btn"
(click)="guardianExpanded = !guardianExpanded" (click)="guardianExpanded = !guardianExpanded"
[matTooltip]="guardianExpanded ? 'Collapse' : 'View Details'"> [matTooltip]="guardianExpanded ? 'Collapse' : 'View Details'"
>
<mat-icon>{{ guardianExpanded ? 'expand_less' : 'visibility' }}</mat-icon> <mat-icon>{{ guardianExpanded ? 'expand_less' : 'visibility' }}</mat-icon>
</button> </button>
<button mat-icon-button type="button" class="guardian-action-btn guardian-action-btn--delete" <button
mat-icon-button
type="button"
class="guardian-action-btn guardian-action-btn--delete"
(click)="removeGuardian()" (click)="removeGuardian()"
matTooltip="Delete"> matTooltip="Delete"
>
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>
</button> </button>
</span> </span>
@@ -506,16 +589,22 @@
@if (selectedGuardian.panNumber) { @if (selectedGuardian.panNumber) {
<div class="guardian-expand__item"> <div class="guardian-expand__item">
<span class="guardian-expand__label">PAN</span> <span class="guardian-expand__label">PAN</span>
<span class="guardian-expand__value">{{ selectedGuardian.panNumber }} <span class="guardian-expand__value"
@if (selectedGuardian.panVerified) { <mat-icon class="verify-tick-sm">check_circle</mat-icon> } >{{ selectedGuardian.panNumber }}
@if (selectedGuardian.panVerified) {
<mat-icon class="verify-tick-sm">check_circle</mat-icon>
}
</span> </span>
</div> </div>
} }
@if (selectedGuardian.aadhaarMasked) { @if (selectedGuardian.aadhaarMasked) {
<div class="guardian-expand__item"> <div class="guardian-expand__item">
<span class="guardian-expand__label">Aadhaar</span> <span class="guardian-expand__label">Aadhaar</span>
<span class="guardian-expand__value">{{ selectedGuardian.aadhaarMasked }} <span class="guardian-expand__value"
@if (selectedGuardian.aadhaarVerified) { <mat-icon class="verify-tick-sm">check_circle</mat-icon> } >{{ selectedGuardian.aadhaarMasked }}
@if (selectedGuardian.aadhaarVerified) {
<mat-icon class="verify-tick-sm">check_circle</mat-icon>
}
</span> </span>
</div> </div>
} }
@@ -535,8 +624,12 @@
@if (selectedGuardian.signatureUrl) { @if (selectedGuardian.signatureUrl) {
<div class="guardian-expand__sig"> <div class="guardian-expand__sig">
<span class="guardian-expand__label">Specimen Signature</span> <span class="guardian-expand__label">Specimen Signature</span>
<img [src]="selectedGuardian.signatureUrl" alt="Signature" class="guardian-expand__sig-img guardian-row__zoomable" <img
(click)="zoomGuardianImage(selectedGuardian.signatureUrl, 'Specimen Signature')" /> [src]="selectedGuardian.signatureUrl"
alt="Signature"
class="guardian-expand__sig-img guardian-row__zoomable"
(click)="zoomGuardianImage(selectedGuardian.signatureUrl, 'Specimen Signature')"
/>
</div> </div>
} }
</div> </div>
@@ -549,7 +642,7 @@
<!-- Non-Individual Details --> <!-- Non-Individual Details -->
@if (createClientForm.get('clientNonPersonDetails')) { @if (createClientForm.get('clientNonPersonDetails')) {
<div class="form-grid" formGroupName="clientNonPersonDetails" style="margin-top: 16px;"> <div class="form-grid" formGroupName="clientNonPersonDetails" style="margin-top: 16px">
<mat-form-field appearance="outline" class="form-field"> <mat-form-field appearance="outline" class="form-field">
<mat-label>Constitution Type</mat-label> <mat-label>Constitution Type</mat-label>
<mat-select formControlName="constitutionId" required> <mat-select formControlName="constitutionId" required>
@@ -607,7 +700,10 @@
</h4> </h4>
<div class="minor-info-bar"> <div class="minor-info-bar">
<fa-icon icon="info-circle" class="minor-info-icon"></fa-icon> <fa-icon icon="info-circle" class="minor-info-icon"></fa-icon>
<span>Select registered clients as authorized signatories — as per Board Resolution / Partnership Deed. Max 10.</span> <span
>Select registered clients as authorized signatories — as per Board Resolution / Partnership Deed. Max
10.</span
>
</div> </div>
<!-- Signatory List Table --> <!-- Signatory List Table -->
@@ -638,27 +734,45 @@
<span class="sig-table__col sig-table__col--name"> <span class="sig-table__col sig-table__col--name">
<strong>{{ sig.displayName }}</strong> <strong>{{ sig.displayName }}</strong>
@if (sig._loading) { @if (sig._loading) {
<mat-progress-spinner diameter="14" mode="indeterminate" class="sig-inline-spinner"></mat-progress-spinner> <mat-progress-spinner
diameter="14"
mode="indeterminate"
class="sig-inline-spinner"
></mat-progress-spinner>
} }
</span> </span>
<span class="sig-table__col sig-table__col--id">{{ sig.cifNumber || sig.id }}</span> <span class="sig-table__col sig-table__col--id">{{ sig.cifNumber || sig.id }}</span>
<span class="sig-table__col sig-table__col--pan"> <span class="sig-table__col sig-table__col--pan">
{{ sig.panNumber || '-' }} {{ sig.panNumber || '-' }}
@if (sig.panVerified) { <mat-icon class="verify-tick-sm">check_circle</mat-icon> } @if (sig.panVerified) {
<mat-icon class="verify-tick-sm">check_circle</mat-icon>
}
</span> </span>
<span class="sig-table__col sig-table__col--din"> <span class="sig-table__col sig-table__col--din">
@if (sig.dinNumber) { @if (sig.dinNumber) {
{{ sig.dinNumber }} {{ sig.dinNumber }}
@if (sig.dinVerified) { <mat-icon class="verify-tick-sm">check_circle</mat-icon> } @if (sig.dinVerified) {
@if (!sig.dinVerified) { <mat-icon class="verify-cross-sm">cancel</mat-icon> } <mat-icon class="verify-tick-sm">check_circle</mat-icon>
}
@if (!sig.dinVerified) {
<mat-icon class="verify-cross-sm">cancel</mat-icon>
}
} @else { } @else {
<button mat-stroked-button class="din-verify-btn" type="button" matTooltip="Verify DIN" (click)="openDinVerifyDialog(sig)"> <button
mat-stroked-button
class="din-verify-btn"
type="button"
matTooltip="Verify DIN"
(click)="openDinVerifyDialog(sig)"
>
<mat-icon class="din-verify-icon">badge</mat-icon> Verify DIN <mat-icon class="din-verify-icon">badge</mat-icon> Verify DIN
</button> </button>
} }
</span> </span>
<span class="sig-table__col sig-table__col--status"> <span class="sig-table__col sig-table__col--status">
<span class="sig-status-chip" [class.active]="sig.status?.value === 'Active'">{{ sig.status?.value || '-' }}</span> <span class="sig-status-chip" [class.active]="sig.status?.value === 'Active'">{{
sig.status?.value || '-'
}}</span>
</span> </span>
<span class="sig-table__col sig-table__col--actions"> <span class="sig-table__col sig-table__col--actions">
<button mat-icon-button type="button" matTooltip="View Details" (click)="viewSignatoryDetails(sig)"> <button mat-icon-button type="button" matTooltip="View Details" (click)="viewSignatoryDetails(sig)">
@@ -680,10 +794,14 @@
<mat-form-field appearance="outline" class="guardian-search-field"> <mat-form-field appearance="outline" class="guardian-search-field">
<mat-label>Add Signatory — Search by Name or Client ID</mat-label> <mat-label>Add Signatory — Search by Name or Client ID</mat-label>
<span matPrefix class="field-prefix-icon"><mat-icon>person_add</mat-icon></span> <span matPrefix class="field-prefix-icon"><mat-icon>person_add</mat-icon></span>
<input matInput [value]="signatorySearchText" <input
matInput
[value]="signatorySearchText"
(input)="onSignatorySearch($any($event.target).value)" (input)="onSignatorySearch($any($event.target).value)"
(blur)="dismissSignatorySearch()" (blur)="dismissSignatorySearch()"
placeholder="Type name or client ID..." autocomplete="off" /> placeholder="Type name or client ID..."
autocomplete="off"
/>
</mat-form-field> </mat-form-field>
@if (signatorySearching) { @if (signatorySearching) {
<div class="guardian-results"> <div class="guardian-results">
@@ -700,9 +818,20 @@
<div class="guardian-result-item__avatar"><fa-icon icon="user"></fa-icon></div> <div class="guardian-result-item__avatar"><fa-icon icon="user"></fa-icon></div>
<div class="guardian-result-item__info"> <div class="guardian-result-item__info">
<span class="guardian-result-item__name">{{ result.displayName }}</span> <span class="guardian-result-item__name">{{ result.displayName }}</span>
<span class="guardian-result-item__meta">ID: {{ result.id }} &middot; A/C: {{ result.accountNumber }} &middot; {{ result.officeName }}@if (result.dateOfBirth) { &middot; DOB: {{ result.dateOfBirth | dateFormat }} }@if (result.fatherSpouseName) { &middot; {{ result.fatherSpouseName }} }</span> <span class="guardian-result-item__meta"
>ID: {{ result.id }} &middot; A/C: {{ result.accountNumber }} &middot;
{{ result.officeName }}
@if (result.dateOfBirth) {
&middot; DOB: {{ result.dateOfBirth | dateFormat }}
}
@if (result.fatherSpouseName) {
&middot; {{ result.fatherSpouseName }}
}
</span>
</div> </div>
<span class="guardian-result-item__status" [class.active]="result.status?.value === 'Active'">{{ result.status?.value }}</span> <span class="guardian-result-item__status" [class.active]="result.status?.value === 'Active'">{{
result.status?.value
}}</span>
</div> </div>
} }
</div> </div>
@@ -728,7 +857,6 @@
<textarea matInput formControlName="remarks" cdkTextareaAutosize cdkAutosizeMinRows="2"></textarea> <textarea matInput formControlName="remarks" cdkTextareaAutosize cdkAutosizeMinRows="2"></textarea>
</mat-form-field> </mat-form-field>
</div> </div>
} }
</div> </div>
@@ -737,9 +865,12 @@
<h4 class="section-title"> <h4 class="section-title">
<fa-icon icon="mobile-alt" class="section-icon"></fa-icon> <fa-icon icon="mobile-alt" class="section-icon"></fa-icon>
Contact Details Contact Details
<mat-icon class="dip-info-icon" <mat-icon
class="dip-info-icon"
matTooltip="Digital Intelligence Platform (DIP) refers to the fraud-monitoring and intelligence ecosystem created by the Department of Telecommunications (DoT)" matTooltip="Digital Intelligence Platform (DIP) refers to the fraud-monitoring and intelligence ecosystem created by the Department of Telecommunications (DoT)"
matTooltipPosition="above">info</mat-icon> matTooltipPosition="above"
>info</mat-icon
>
</h4> </h4>
<div class="form-grid"> <div class="form-grid">
@@ -747,21 +878,35 @@
<mat-form-field appearance="outline" class="form-field"> <mat-form-field appearance="outline" class="form-field">
<mat-label>{{ 'labels.inputs.Mobile No' | translate }}</mat-label> <mat-label>{{ 'labels.inputs.Mobile No' | translate }}</mat-label>
<span matPrefix class="field-prefix-icon"><mat-icon>phone_android</mat-icon></span> <span matPrefix class="field-prefix-icon"><mat-icon>phone_android</mat-icon></span>
<input matInput required type="text" formControlName="mobileNo" placeholder="e.g. 9876543210" maxlength="10" <input
matInput
required
type="text"
formControlName="mobileNo"
placeholder="e.g. 9876543210"
maxlength="10"
(keypress)="$event.charCode >= 48 && $event.charCode <= 57 ? true : $event.preventDefault()" (keypress)="$event.charCode >= 48 && $event.charCode <= 57 ? true : $event.preventDefault()"
(paste)="$event.preventDefault()" /> (paste)="$event.preventDefault()"
/>
@if (createClientForm.get('mobileNo')?.hasError('required') && createClientForm.get('mobileNo')?.touched) { @if (createClientForm.get('mobileNo')?.hasError('required') && createClientForm.get('mobileNo')?.touched) {
<mat-error>Mobile number is <strong>required</strong></mat-error> <mat-error>Mobile number is <strong>required</strong></mat-error>
} @else if (createClientForm.get('mobileNo')?.hasError('pattern') && createClientForm.get('mobileNo')?.touched) { } @else if (
createClientForm.get('mobileNo')?.hasError('pattern') && createClientForm.get('mobileNo')?.touched
) {
<mat-error>Enter valid 10-digit number (starts with 6-9)</mat-error> <mat-error>Enter valid 10-digit number (starts with 6-9)</mat-error>
} }
</mat-form-field> </mat-form-field>
<div class="form-field mobile-verify-row"> <div class="form-field mobile-verify-row">
@if (!mobileVerified) { @if (!mobileVerified) {
<button mat-stroked-button color="primary" type="button" class="verify-btn" <button
mat-stroked-button
color="primary"
type="button"
class="verify-btn"
[disabled]="readOnly || createClientForm.get('mobileNo')?.value?.length !== 10" [disabled]="readOnly || createClientForm.get('mobileNo')?.value?.length !== 10"
(click)="verifyWithDIP()"> (click)="verifyWithDIP()"
>
<fa-icon icon="shield-alt" class="m-r-10"></fa-icon> <fa-icon icon="shield-alt" class="m-r-10"></fa-icon>
Verify with DIP Verify with DIP
</button> </button>
@@ -774,19 +919,33 @@
<mat-form-field appearance="outline" class="form-field"> <mat-form-field appearance="outline" class="form-field">
<mat-label>Alternate Mobile No</mat-label> <mat-label>Alternate Mobile No</mat-label>
<span matPrefix class="field-prefix-icon"><mat-icon>phone_android</mat-icon></span> <span matPrefix class="field-prefix-icon"><mat-icon>phone_android</mat-icon></span>
<input matInput type="text" formControlName="alternateMobileNo" placeholder="e.g. 9876543210" maxlength="10" <input
matInput
type="text"
formControlName="alternateMobileNo"
placeholder="e.g. 9876543210"
maxlength="10"
(keypress)="$event.charCode >= 48 && $event.charCode <= 57 ? true : $event.preventDefault()" (keypress)="$event.charCode >= 48 && $event.charCode <= 57 ? true : $event.preventDefault()"
(paste)="$event.preventDefault()" /> (paste)="$event.preventDefault()"
@if (createClientForm.get('alternateMobileNo')?.hasError('pattern') && createClientForm.get('alternateMobileNo')?.touched) { />
@if (
createClientForm.get('alternateMobileNo')?.hasError('pattern') &&
createClientForm.get('alternateMobileNo')?.touched
) {
<mat-error>Enter valid 10-digit number (starts with 6-9)</mat-error> <mat-error>Enter valid 10-digit number (starts with 6-9)</mat-error>
} }
</mat-form-field> </mat-form-field>
<div class="form-field mobile-verify-row"> <div class="form-field mobile-verify-row">
@if (!alternateMobileVerified) { @if (!alternateMobileVerified) {
<button mat-stroked-button color="primary" type="button" class="verify-btn" <button
mat-stroked-button
color="primary"
type="button"
class="verify-btn"
[disabled]="readOnly || createClientForm.get('alternateMobileNo')?.value?.length !== 10" [disabled]="readOnly || createClientForm.get('alternateMobileNo')?.value?.length !== 10"
(click)="verifyAlternateDIP()"> (click)="verifyAlternateDIP()"
>
<fa-icon icon="shield-alt" class="m-r-10"></fa-icon> <fa-icon icon="shield-alt" class="m-r-10"></fa-icon>
Verify with DIP Verify with DIP
</button> </button>
@@ -800,21 +959,27 @@
<mat-label>{{ 'labels.inputs.Email Address' | translate }}</mat-label> <mat-label>{{ 'labels.inputs.Email Address' | translate }}</mat-label>
<span matPrefix class="field-prefix-icon"><mat-icon>email</mat-icon></span> <span matPrefix class="field-prefix-icon"><mat-icon>email</mat-icon></span>
<input matInput required formControlName="emailAddress" placeholder="e.g. name@example.com" /> <input matInput required formControlName="emailAddress" placeholder="e.g. name@example.com" />
@if (createClientForm.get('emailAddress')?.hasError('required') && createClientForm.get('emailAddress')?.touched) { @if (
createClientForm.get('emailAddress')?.hasError('required') && createClientForm.get('emailAddress')?.touched
) {
<mat-error>Email is <strong>required</strong></mat-error> <mat-error>Email is <strong>required</strong></mat-error>
} @else if (createClientForm.get('emailAddress')?.invalid && createClientForm.get('emailAddress')?.touched) { } @else if (createClientForm.get('emailAddress')?.invalid && createClientForm.get('emailAddress')?.touched) {
<mat-error>Enter a valid email (e.g. name&#64;example.com)</mat-error> <mat-error>Enter a valid email (e.g. name&#64;example.com)</mat-error>
} }
</mat-form-field> </mat-form-field>
@if (createClientForm.get('legalFormId')?.value === LegalFormId.PERSON && maritalStatusOptions.length > 0 && !isMinor) { @if (
createClientForm.get('legalFormId')?.value === LegalFormId.PERSON && maritalStatusOptions.length > 0 && !isMinor
) {
<mat-form-field appearance="outline" class="form-field"> <mat-form-field appearance="outline" class="form-field">
<mat-label>Marital Status</mat-label> <mat-label>Marital Status</mat-label>
<mat-select formControlName="maritalStatusId"> <mat-select formControlName="maritalStatusId">
<mat-select-trigger> <mat-select-trigger>
@if (getSelectedMaritalStatus()) { @if (getSelectedMaritalStatus()) {
<span class="icon-option"> <span class="icon-option">
<mat-icon class="icon-option-icon">{{ getMaritalStatusIcon(getSelectedMaritalStatus().name) }}</mat-icon> <mat-icon class="icon-option-icon">{{
getMaritalStatusIcon(getSelectedMaritalStatus().name)
}}</mat-icon>
{{ getSelectedMaritalStatus().name }} {{ getSelectedMaritalStatus().name }}
</span> </span>
} }
@@ -833,83 +998,249 @@
</div> </div>
</div> </div>
<!-- Section: Education & Social Details (Individual only) -->
@if (createClientForm.get('legalFormId')?.value === LegalFormId.PERSON) {
<div class="section-block">
<h4 class="section-title">
<fa-icon icon="book" class="section-icon"></fa-icon>
Education &amp; Social Details
</h4>
<div class="form-grid">
<!-- Education Level (non-minor) -->
@if (!isMinor) {
<mat-form-field appearance="outline" class="form-field">
<mat-label>Education Level</mat-label>
<mat-select formControlName="educationLevelId">
<mat-select-trigger>
@if (getSelectedEducationLevel()) {
<span class="icon-option">
<mat-icon class="icon-option-icon">{{
getEducationLevelIcon(getSelectedEducationLevel().name)
}}</mat-icon>
{{ getSelectedEducationLevel().name }}
</span>
}
</mat-select-trigger>
@for (el of educationLevelOptions; track el) {
<mat-option [value]="el.id">
<span class="icon-option">
<mat-icon class="icon-option-icon">{{ getEducationLevelIcon(el.name) }}</mat-icon>
{{ el.name }}
</span>
</mat-option>
}
</mat-select>
</mat-form-field>
<!-- Degree / Qualification (depends on Education Level) -->
<mat-form-field appearance="outline" class="form-field">
<mat-label>Degree / Qualification</mat-label>
<mat-select
formControlName="degreeQualificationId"
[placeholder]="getSelectedEducationLevel() ? 'Select Degree' : 'Select Education Level first'"
>
<mat-select-trigger>
@if (getSelectedDegreeQualification()) {
<span class="icon-option">
<mat-icon class="icon-option-icon">{{
getDegreeQualificationIcon(getSelectedDegreeQualification().name)
}}</mat-icon>
{{ getSelectedDegreeQualification().name }}
</span>
}
</mat-select-trigger>
@for (dq of filteredDegreeQualificationOptions; track dq) {
<mat-option [value]="dq.id">
<span class="icon-option">
<mat-icon class="icon-option-icon">{{ getDegreeQualificationIcon(dq.name) }}</mat-icon>
{{ dq.name }}
</span>
</mat-option>
}
@if (!getSelectedEducationLevel()) {
<mat-option disabled>
<em>Please select Education Level first</em>
</mat-option>
} @else if (filteredDegreeQualificationOptions.length === 0) {
<mat-option disabled>
<em>No options available</em>
</mat-option>
}
</mat-select>
</mat-form-field>
}
<!-- Religion -->
<mat-form-field appearance="outline" class="form-field">
<mat-label>Religion</mat-label>
<mat-select formControlName="religionId">
<mat-select-trigger>
@if (getSelectedReligion()) {
<span class="icon-option">
<mat-icon class="icon-option-icon">{{ getReligionIcon(getSelectedReligion().name) }}</mat-icon>
{{ getSelectedReligion().name }}
</span>
}
</mat-select-trigger>
@for (r of religionOptions; track r) {
<mat-option [value]="r.id">
<span class="icon-option">
<mat-icon class="icon-option-icon">{{ getReligionIcon(r.name) }}</mat-icon>
{{ r.name }}
</span>
</mat-option>
}
</mat-select>
</mat-form-field>
<!-- Social Category -->
<mat-form-field appearance="outline" class="form-field">
<mat-label>Social Category</mat-label>
<mat-select formControlName="socialCategoryId">
<mat-select-trigger>
@if (getSelectedSocialCategory()) {
<span class="icon-option">
<mat-icon class="icon-option-icon">{{
getSocialCategoryIcon(getSelectedSocialCategory().name)
}}</mat-icon>
{{ getSelectedSocialCategory().name }}
</span>
}
</mat-select-trigger>
@for (sc of socialCategoryOptions; track sc) {
<mat-option [value]="sc.id">
<span class="icon-option">
<mat-icon class="icon-option-icon">{{ getSocialCategoryIcon(sc.name) }}</mat-icon>
{{ sc.name }}
</span>
</mat-option>
}
</mat-select>
</mat-form-field>
</div>
</div>
}
<!-- Section: Financial & Classification (Individual only) --> <!-- Section: Financial & Classification (Individual only) -->
@if (createClientForm.get('legalFormId')?.value === LegalFormId.PERSON) { @if (createClientForm.get('legalFormId')?.value === LegalFormId.PERSON) {
<div class="section-block"> <div class="section-block">
<h4 class="section-title"> <h4 class="section-title">
<fa-icon icon="chart-line" class="section-icon"></fa-icon> <fa-icon icon="chart-line" class="section-icon"></fa-icon>
Financial &amp; Classification Financial &amp; Classification
</h4> </h4>
<div class="form-grid"> <div class="form-grid">
<mat-form-field appearance="outline" class="form-field">
<mat-label>{{ 'labels.inputs.Occupation Category' | translate }}</mat-label>
<mat-form-field appearance="outline" class="form-field"> <mat-select formControlName="occupationCategoryId">
<mat-label>{{ 'labels.inputs.Occupation Category' | translate }}</mat-label> <mat-select-trigger>
<mat-select formControlName="occupationCategoryId"> @if (getSelectedOccupationCategory()) {
<mat-select-trigger> <span class="occ-option">
@if (getSelectedOccupationCategory()) { <mat-icon class="occ-option-icon">{{
<span class="occ-option"> getOccupationCategoryIcon(getSelectedOccupationCategory().name)
<mat-icon class="occ-option-icon">{{ getOccupationCategoryIcon(getSelectedOccupationCategory().name) }}</mat-icon> }}</mat-icon>
{{ getSelectedOccupationCategory().name }} {{ getSelectedOccupationCategory().name }}
</span> </span>
}
</mat-select-trigger>
@for (cat of occupationCategoryOptions; track cat) {
<mat-option [value]="cat.id">
<span class="occ-option">
<mat-icon class="occ-option-icon">{{ getOccupationCategoryIcon(cat.name) }}</mat-icon>
{{ cat.name }}
</span>
</mat-option>
} }
</mat-select-trigger> </mat-select>
@for (cat of occupationCategoryOptions; track cat) { </mat-form-field>
<mat-option [value]="cat.id">
<span class="occ-option">
<mat-icon class="occ-option-icon">{{ getOccupationCategoryIcon(cat.name) }}</mat-icon>
{{ cat.name }}
</span>
</mat-option>
}
</mat-select>
</mat-form-field>
<mat-form-field appearance="outline" class="form-field"> <mat-form-field appearance="outline" class="form-field">
<mat-label>{{ 'labels.inputs.Occupation' | translate }}</mat-label> <mat-label>{{ 'labels.inputs.Occupation' | translate }}</mat-label>
<mat-select formControlName="occupationId"> <mat-select formControlName="occupationId">
<mat-select-trigger> <mat-select-trigger>
@if (getSelectedOccupation()) { @if (getSelectedOccupation()) {
<span class="occ-option"> <span class="occ-option">
<mat-icon class="occ-option-icon">{{ getOccupationIcon(getSelectedOccupation().name) }}</mat-icon> <mat-icon class="occ-option-icon">{{ getOccupationIcon(getSelectedOccupation().name) }}</mat-icon>
{{ getSelectedOccupation().name }} {{ getSelectedOccupation().name }}
</span> </span>
}
</mat-select-trigger>
@for (occ of occupationOptions; track occ) {
<mat-option [value]="occ.id">
<span class="occ-option">
<mat-icon class="occ-option-icon">{{ getOccupationIcon(occ.name) }}</mat-icon>
{{ occ.name }}
</span>
</mat-option>
} }
</mat-select-trigger> </mat-select>
@for (occ of occupationOptions; track occ) { </mat-form-field>
<mat-option [value]="occ.id">
<span class="occ-option"> <mat-form-field appearance="outline" class="form-field">
<mat-icon class="occ-option-icon">{{ getOccupationIcon(occ.name) }}</mat-icon> <mat-label>Monthly Income</mat-label>
{{ occ.name }} <span matPrefix class="rupee-prefix">&#8377;</span>
</span> <input matInput type="number" formControlName="monthlyIncome" min="0" />
</mat-option> @if (
createClientForm.get('monthlyIncome')?.hasError('pattern') && createClientForm.get('monthlyIncome')?.touched
) {
<mat-error>Enter a valid amount</mat-error>
} @else if (
createClientForm.get('monthlyIncome')?.hasError('min') && createClientForm.get('monthlyIncome')?.touched
) {
<mat-error>Amount cannot be negative</mat-error>
} }
</mat-select> </mat-form-field>
</mat-form-field>
<mat-form-field appearance="outline" class="form-field"> <mat-form-field appearance="outline" class="form-field">
<mat-label>Monthly Income</mat-label> <mat-label>Annual Turnover (Last FY)</mat-label>
<span matPrefix class="rupee-prefix">&#8377;</span> <span matPrefix class="rupee-prefix">&#8377;</span>
<input matInput type="number" formControlName="monthlyIncome" min="0" /> <input matInput type="number" formControlName="annualTurnover" min="0" />
@if (createClientForm.get('monthlyIncome')?.hasError('pattern') && createClientForm.get('monthlyIncome')?.touched) { @if (
<mat-error>Enter a valid amount</mat-error> createClientForm.get('annualTurnover')?.hasError('pattern') &&
} @else if (createClientForm.get('monthlyIncome')?.hasError('min') && createClientForm.get('monthlyIncome')?.touched) { createClientForm.get('annualTurnover')?.touched
<mat-error>Amount cannot be negative</mat-error> ) {
} <mat-error>Enter a valid amount</mat-error>
</mat-form-field> } @else if (
createClientForm.get('annualTurnover')?.hasError('min') && createClientForm.get('annualTurnover')?.touched
) {
<mat-error>Amount cannot be negative</mat-error>
}
</mat-form-field>
<mat-form-field appearance="outline" class="form-field"> <mat-form-field appearance="outline" class="form-field">
<mat-label>Annual Turnover (Last FY)</mat-label> <mat-label>Agriculture Income</mat-label>
<span matPrefix class="rupee-prefix">&#8377;</span> <span matPrefix class="rupee-prefix">&#8377;</span>
<input matInput type="number" formControlName="annualTurnover" min="0" /> <input matInput type="number" formControlName="agricultureIncome" min="0" />
@if (createClientForm.get('annualTurnover')?.hasError('pattern') && createClientForm.get('annualTurnover')?.touched) { @if (
<mat-error>Enter a valid amount</mat-error> createClientForm.get('agricultureIncome')?.hasError('pattern') &&
} @else if (createClientForm.get('annualTurnover')?.hasError('min') && createClientForm.get('annualTurnover')?.touched) { createClientForm.get('agricultureIncome')?.touched
<mat-error>Amount cannot be negative</mat-error> ) {
} <mat-error>Enter a valid amount</mat-error>
</mat-form-field> } @else if (
createClientForm.get('agricultureIncome')?.hasError('min') &&
createClientForm.get('agricultureIncome')?.touched
) {
<mat-error>Amount cannot be negative</mat-error>
}
</mat-form-field>
<mat-form-field appearance="outline" class="form-field">
<mat-label>Non-Agriculture Income</mat-label>
<span matPrefix class="rupee-prefix">&#8377;</span>
<input matInput type="number" formControlName="nonAgricultureIncome" min="0" />
@if (
createClientForm.get('nonAgricultureIncome')?.hasError('pattern') &&
createClientForm.get('nonAgricultureIncome')?.touched
) {
<mat-error>Enter a valid amount</mat-error>
} @else if (
createClientForm.get('nonAgricultureIncome')?.hasError('min') &&
createClientForm.get('nonAgricultureIncome')?.touched
) {
<mat-error>Amount cannot be negative</mat-error>
}
</mat-form-field>
</div>
</div> </div>
</div>
} }
<!-- submittedOnDate is set automatically via form default (business date) --> <!-- submittedOnDate is set automatically via form default (business date) -->
@@ -95,8 +95,8 @@ form {
background: #f0f9ff; background: #f0f9ff;
border: 1px solid #bae6fd; border: 1px solid #bae6fd;
border-radius: 6px; border-radius: 6px;
margin-top: -8px; margin-top: 12px;
margin-bottom: 4px; margin-bottom: 16px;
} }
.full-name-preview { .full-name-preview {
@@ -408,7 +408,10 @@ form {
.guardian-cancel-link { .guardian-cancel-link {
color: #94a3b8 !important; color: #94a3b8 !important;
margin-top: 8px; margin-top: 8px;
&:hover { color: #ef4444 !important; }
&:hover {
color: #ef4444 !important;
}
} }
/* ═══ Guardian Row Table ═══ */ /* ═══ Guardian Row Table ═══ */
@@ -437,7 +440,10 @@ form {
align-items: center; align-items: center;
padding: 10px 12px; padding: 10px 12px;
transition: background 0.1s; transition: background 0.1s;
&:hover { background: #f8fafc; }
&:hover {
background: #f8fafc;
}
} }
.guardian-row__col { .guardian-row__col {
@@ -445,12 +451,33 @@ form {
font-size: 13px; font-size: 13px;
color: #1e293b; color: #1e293b;
&--photo { flex: 0 0 50px; } &--photo {
&--name { flex: 1; min-width: 0; } flex: 0 0 50px;
&--rel { flex: 0 0 120px; } }
&--cif { flex: 0 0 130px; }
&--status { flex: 0 0 80px; } &--name {
&--actions { flex: 0 0 110px; display: flex; gap: 2px; justify-content: flex-end; } flex: 1;
min-width: 0;
}
&--rel {
flex: 0 0 120px;
}
&--cif {
flex: 0 0 130px;
}
&--status {
flex: 0 0 80px;
}
&--actions {
flex: 0 0 110px;
display: flex;
gap: 2px;
justify-content: flex-end;
}
} }
.guardian-row__avatar { .guardian-row__avatar {
@@ -543,14 +570,22 @@ form {
color: #64748b; color: #64748b;
} }
&:hover mat-icon { color: #1074b9; } &:hover mat-icon {
&--delete:hover mat-icon { color: #ef4444; } color: #1074b9;
}
&--delete:hover mat-icon {
color: #ef4444;
}
} }
.guardian-row__zoomable { .guardian-row__zoomable {
cursor: zoom-in; cursor: zoom-in;
transition: transform 0.15s; transition: transform 0.15s;
&:hover { transform: scale(1.05); }
&:hover {
transform: scale(1.05);
}
} }
/* Expandable detail panel */ /* Expandable detail panel */
@@ -558,12 +593,19 @@ form {
border-top: 1px solid #e2e8f0; border-top: 1px solid #e2e8f0;
background: #f8fafc; background: #f8fafc;
padding: 16px 20px; padding: 16px 20px;
animation: guardianSlideDown 0.2s ease-out; animation: guardian-slide-down 0.2s ease-out;
} }
@keyframes guardianSlideDown { @keyframes guardian-slide-down {
from { opacity: 0; max-height: 0; } from {
to { opacity: 1; max-height: 500px; } opacity: 0;
max-height: 0;
}
to {
opacity: 1;
max-height: 500px;
}
} }
.guardian-expand__grid { .guardian-expand__grid {
@@ -666,7 +708,9 @@ form {
width: 100%; width: 100%;
} }
.guardian-row__header { display: none; } .guardian-row__header {
display: none;
}
.guardian-row__data { .guardian-row__data {
flex-wrap: wrap; flex-wrap: wrap;
@@ -674,10 +718,23 @@ form {
} }
.guardian-row__col { .guardian-row__col {
&--photo { flex: 0 0 40px; } &--photo {
&--name { flex: 1 1 200px; } flex: 0 0 40px;
&--rel, &--cif, &--status { flex: 0 0 auto; } }
&--actions { flex: 0 0 auto; }
&--name {
flex: 1 1 200px;
}
&--rel,
&--cif,
&--status {
flex: 0 0 auto;
}
&--actions {
flex: 0 0 auto;
}
} }
.guardian-expand__grid { .guardian-expand__grid {
@@ -777,8 +834,13 @@ form {
transition: background 0.1s; transition: background 0.1s;
border-bottom: 1px solid #f1f5f9; border-bottom: 1px solid #f1f5f9;
&:last-child { border-bottom: none; } &:last-child {
&:hover { background: #f0f7ff; } border-bottom: none;
}
&:hover {
background: #f0f7ff;
}
} }
.guardian-result-item__avatar { .guardian-result-item__avatar {
@@ -882,7 +944,10 @@ form {
.guardian-card__remove { .guardian-card__remove {
color: #94a3b8 !important; color: #94a3b8 !important;
&:hover { color: #ef4444 !important; }
&:hover {
color: #ef4444 !important;
}
} }
.guardian-card__body { .guardian-card__body {
@@ -899,8 +964,13 @@ form {
border-bottom: 1px solid #f1f5f9; border-bottom: 1px solid #f1f5f9;
border-right: 1px solid #f1f5f9; border-right: 1px solid #f1f5f9;
&:nth-child(2n) { border-right: none; } &:nth-child(2n) {
&:nth-last-child(-n+2) { border-bottom: none; } border-right: none;
}
&:nth-last-child(-n + 2) {
border-bottom: none;
}
} }
.guardian-card__label { .guardian-card__label {
@@ -1069,10 +1139,22 @@ form {
color: #0f172a; color: #0f172a;
} }
.row-micro td { background: #ecfdf5; } .row-micro td {
.row-small td { background: #e0f2fe; } background: #ecfdf5;
.row-medium td { background: #fffbeb; } }
.row-large td { background: #f8fafc; color: #94a3b8; }
.row-small td {
background: #e0f2fe;
}
.row-medium td {
background: #fffbeb;
}
.row-large td {
background: #f8fafc;
color: #94a3b8;
}
} }
.msme-dot { .msme-dot {
@@ -1082,10 +1164,21 @@ form {
border-radius: 50%; border-radius: 50%;
margin-right: 6px; margin-right: 6px;
&.green { background: #10b981; } &.green {
&.blue { background: #0ea5e9; } background: #10b981;
&.amber { background: #f59e0b; } }
&.gray { background: #cbd5e1; }
&.blue {
background: #0ea5e9;
}
&.amber {
background: #f59e0b;
}
&.gray {
background: #cbd5e1;
}
} }
.msme-class-table-footer { .msme-class-table-footer {
@@ -1499,19 +1592,68 @@ form {
border-bottom: 1px solid #f1f5f9; border-bottom: 1px solid #f1f5f9;
transition: background 0.15s; transition: background 0.15s;
&:last-child { border-bottom: none; } &:last-child {
&:hover { background: #f8fafc; } border-bottom: none;
}
&:hover {
background: #f8fafc;
}
} }
.sig-table__col { .sig-table__col {
&--sno { flex: 0 0 32px; text-align: center; color: #94a3b8; font-size: 12px; } &--sno {
&--photo { flex: 0 0 40px; } flex: 0 0 32px;
&--name { flex: 1; font-size: 13px; display: flex; align-items: center; gap: 6px; } text-align: center;
&--id { flex: 0 0 100px; font-size: 12px; color: #64748b; } color: #94a3b8;
&--pan { flex: 0 0 120px; font-size: 12px; color: #1e293b; display: flex; align-items: center; gap: 3px; } font-size: 12px;
&--din { flex: 0 0 140px; font-size: 12px; color: #1e293b; display: flex; align-items: center; gap: 3px; } }
&--status { flex: 0 0 80px; }
&--actions { flex: 0 0 80px; display: flex; gap: 2px; } &--photo {
flex: 0 0 40px;
}
&--name {
flex: 1;
font-size: 13px;
display: flex;
align-items: center;
gap: 6px;
}
&--id {
flex: 0 0 100px;
font-size: 12px;
color: #64748b;
}
&--pan {
flex: 0 0 120px;
font-size: 12px;
color: #1e293b;
display: flex;
align-items: center;
gap: 3px;
}
&--din {
flex: 0 0 140px;
font-size: 12px;
color: #1e293b;
display: flex;
align-items: center;
gap: 3px;
}
&--status {
flex: 0 0 80px;
}
&--actions {
flex: 0 0 80px;
display: flex;
gap: 2px;
}
} }
.sig-table__avatar { .sig-table__avatar {
@@ -1526,11 +1668,21 @@ form {
font-size: 12px; font-size: 12px;
overflow: hidden; overflow: hidden;
&.has-photo { padding: 0; } &.has-photo {
.avatar-photo { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; } padding: 0;
}
.avatar-photo {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 50%;
}
} }
.sig-inline-spinner { display: inline-block; } .sig-inline-spinner {
display: inline-block;
}
.verify-tick-sm { .verify-tick-sm {
font-size: 14px !important; font-size: 14px !important;
@@ -1569,7 +1721,10 @@ form {
font-size: 11px; font-size: 11px;
font-weight: 600; font-weight: 600;
color: #94a3b8; color: #94a3b8;
&.active { color: #22c55e; }
&.active {
color: #22c55e;
}
} }
.sig-action-icon { .sig-action-icon {
@@ -1577,7 +1732,10 @@ form {
width: 18px !important; width: 18px !important;
height: 18px !important; height: 18px !important;
color: #64748b; color: #64748b;
&.remove { color: #ef4444; }
&.remove {
color: #ef4444;
}
} }
.sig-add-wrapper { .sig-add-wrapper {
File diff suppressed because it is too large Load Diff
@@ -88,14 +88,18 @@
} }
@if (client?.dateOfBirth) { @if (client?.dateOfBirth) {
<div class="preview-field"> <div class="preview-field">
<span class="preview-label">{{ client.legalFormId === LegalFormId.PERSON ? 'Date of Birth' : 'Incorporation Date' }}</span> <span class="preview-label">{{
client.legalFormId === LegalFormId.PERSON ? 'Date of Birth' : 'Incorporation Date'
}}</span>
<span class="preview-value">{{ client.dateOfBirth | dateFormat }}</span> <span class="preview-value">{{ client.dateOfBirth | dateFormat }}</span>
</div> </div>
} }
@if (client?.legalFormId === LegalFormId.PERSON && client?.genderId) { @if (client?.legalFormId === LegalFormId.PERSON && client?.genderId) {
<div class="preview-field"> <div class="preview-field">
<span class="preview-label">Gender</span> <span class="preview-label">Gender</span>
<span class="preview-value">{{ client.genderId | find: clientTemplate.genderOptions : 'id' : 'name' }}</span> <span class="preview-value">{{
client.genderId | find: clientTemplate.genderOptions : 'id' : 'name'
}}</span>
</div> </div>
} }
@if (client?.legalFormId) { @if (client?.legalFormId) {
@@ -107,13 +111,17 @@
@if (client?.officeId) { @if (client?.officeId) {
<div class="preview-field"> <div class="preview-field">
<span class="preview-label">Office</span> <span class="preview-label">Office</span>
<span class="preview-value">{{ client.officeId | find: clientTemplate.officeOptions : 'id' : 'name' }}</span> <span class="preview-value">{{
client.officeId | find: clientTemplate.officeOptions : 'id' : 'name'
}}</span>
</div> </div>
} }
@if (client?.staffId) { @if (client?.staffId) {
<div class="preview-field"> <div class="preview-field">
<span class="preview-label">Staff</span> <span class="preview-label">Staff</span>
<span class="preview-value">{{ client.staffId | find: clientTemplate.staffOptions : 'id' : 'displayName' }}</span> <span class="preview-value">{{
client.staffId | find: clientTemplate.staffOptions : 'id' : 'displayName'
}}</span>
</div> </div>
} }
@if (client?.legalFormId === LegalFormId.PERSON) { @if (client?.legalFormId === LegalFormId.PERSON) {
@@ -134,10 +142,42 @@
<span class="preview-value">{{ client.maritalStatusId | find: maritalStatusOptions : 'id' : 'name' }}</span> <span class="preview-value">{{ client.maritalStatusId | find: maritalStatusOptions : 'id' : 'name' }}</span>
</div> </div>
} }
@if (client?.educationLevelId) {
<div class="preview-field">
<span class="preview-label">Education Level</span>
<span class="preview-value">{{
client.educationLevelId | find: educationLevelOptions : 'id' : 'name'
}}</span>
</div>
}
@if (client?.degreeQualificationId) {
<div class="preview-field">
<span class="preview-label">Degree / Qualification</span>
<span class="preview-value">{{
client.degreeQualificationId | find: degreeQualificationOptions : 'id' : 'name'
}}</span>
</div>
}
@if (client?.religionId) {
<div class="preview-field">
<span class="preview-label">Religion</span>
<span class="preview-value">{{ client.religionId | find: religionOptions : 'id' : 'name' }}</span>
</div>
}
@if (client?.socialCategoryId) {
<div class="preview-field">
<span class="preview-label">Social Category</span>
<span class="preview-value">{{
client.socialCategoryId | find: socialCategoryOptions : 'id' : 'name'
}}</span>
</div>
}
@if (client?.salutationId) { @if (client?.salutationId) {
<div class="preview-field"> <div class="preview-field">
<span class="preview-label">Salutation</span> <span class="preview-label">Salutation</span>
<span class="preview-value">{{ client.salutationId | find: clientTemplate.salutationOptions : 'id' : 'name' }}</span> <span class="preview-value">{{
client.salutationId | find: clientTemplate.salutationOptions : 'id' : 'name'
}}</span>
</div> </div>
} }
@if (client?.ageYears != null) { @if (client?.ageYears != null) {
@@ -150,7 +190,11 @@
<div class="preview-field"> <div class="preview-field">
<span class="preview-label">Minor</span> <span class="preview-label">Minor</span>
<span class="preview-value"> <span class="preview-value">
<span [class]="client.isMinor ? 'preview-badge preview-badge--danger' : 'preview-badge preview-badge--verified'"> <span
[class]="
client.isMinor ? 'preview-badge preview-badge--danger' : 'preview-badge preview-badge--verified'
"
>
{{ client.isMinor ? 'Yes (Age < 10)' : 'No' }} {{ client.isMinor ? 'Yes (Age < 10)' : 'No' }}
</span> </span>
</span> </span>
@@ -172,13 +216,19 @@
@if (client.clientNonPersonDetails.constitutionId) { @if (client.clientNonPersonDetails.constitutionId) {
<div class="preview-field"> <div class="preview-field">
<span class="preview-label">Constitution</span> <span class="preview-label">Constitution</span>
<span class="preview-value">{{ client.clientNonPersonDetails.constitutionId | find: clientTemplate.clientNonPersonConstitutionOptions : 'id' : 'name' }}</span> <span class="preview-value">{{
client.clientNonPersonDetails.constitutionId
| find: clientTemplate.clientNonPersonConstitutionOptions : 'id' : 'name'
}}</span>
</div> </div>
} }
@if (client.clientNonPersonDetails.mainBusinessLineId) { @if (client.clientNonPersonDetails.mainBusinessLineId) {
<div class="preview-field"> <div class="preview-field">
<span class="preview-label">Main Business Line</span> <span class="preview-label">Main Business Line</span>
<span class="preview-value">{{ client.clientNonPersonDetails.mainBusinessLineId | find: clientTemplate.clientNonPersonMainBusinessLineOptions : 'id' : 'name' }}</span> <span class="preview-value">{{
client.clientNonPersonDetails.mainBusinessLineId
| find: clientTemplate.clientNonPersonMainBusinessLineOptions : 'id' : 'name'
}}</span>
</div> </div>
} }
@if (client.clientNonPersonDetails.incorpNumber) { @if (client.clientNonPersonDetails.incorpNumber) {
@@ -215,17 +265,15 @@
<div class="preview-field"> <div class="preview-field">
<span class="preview-label">DIP Verification</span> <span class="preview-label">DIP Verification</span>
<span class="preview-value"> <span class="preview-value">
<span [class]="client?.dipVerified ? 'preview-badge preview-badge--verified' : 'preview-badge preview-badge--pending'"> <span
[class]="
client?.dipVerified ? 'preview-badge preview-badge--verified' : 'preview-badge preview-badge--pending'
"
>
{{ client?.dipVerified ? 'Verified' : 'Pending' }} {{ client?.dipVerified ? 'Verified' : 'Pending' }}
</span> </span>
</span> </span>
</div> </div>
@if (client?.dipLookupLogId) {
<div class="preview-field">
<span class="preview-label">DIP Lookup Log ID</span>
<span class="preview-value mono">{{ client.dipLookupLogId }}</span>
</div>
}
@if (client?.alternateMobileNo) { @if (client?.alternateMobileNo) {
<div class="preview-field"> <div class="preview-field">
<span class="preview-label">Alternate Mobile No</span> <span class="preview-label">Alternate Mobile No</span>
@@ -236,18 +284,18 @@
<div class="preview-field"> <div class="preview-field">
<span class="preview-label">Alternate DIP Verification</span> <span class="preview-label">Alternate DIP Verification</span>
<span class="preview-value"> <span class="preview-value">
<span [class]="client?.alternateDipVerified ? 'preview-badge preview-badge--verified' : 'preview-badge preview-badge--pending'"> <span
[class]="
client?.alternateDipVerified
? 'preview-badge preview-badge--verified'
: 'preview-badge preview-badge--pending'
"
>
{{ client?.alternateDipVerified ? 'Verified' : 'Pending' }} {{ client?.alternateDipVerified ? 'Verified' : 'Pending' }}
</span> </span>
</span> </span>
</div> </div>
} }
@if (client?.alternateDipLookupLogId) {
<div class="preview-field">
<span class="preview-label">Alternate DIP Lookup Log ID</span>
<span class="preview-value mono">{{ client.alternateDipLookupLogId }}</span>
</div>
}
@if (client?.emailAddress) { @if (client?.emailAddress) {
<div class="preview-field"> <div class="preview-field">
<span class="preview-label">Email Address</span> <span class="preview-label">Email Address</span>
@@ -257,78 +305,103 @@
<div class="preview-field"> <div class="preview-field">
<span class="preview-label">Name Verification (Sanctions)</span> <span class="preview-label">Name Verification (Sanctions)</span>
<span class="preview-value"> <span class="preview-value">
<span [class]="client?.nameVerified ? 'preview-badge preview-badge--verified' : 'preview-badge preview-badge--danger'"> <span
{{ client?.nameVerified ? 'Verified — Clear' : (client?.nameVerified === false ? 'Sanctions Match' : 'Pending') }} [class]="
client?.nameVerified ? 'preview-badge preview-badge--verified' : 'preview-badge preview-badge--danger'
"
>
{{
client?.nameVerified
? 'Verified — Clear'
: client?.nameVerified === false
? 'Sanctions Match'
: 'Pending'
}}
</span> </span>
</span> </span>
</div> </div>
@if (client?.screeningLogId) {
<div class="preview-field">
<span class="preview-label">Screening Log ID</span>
<span class="preview-value mono">{{ client.screeningLogId }}</span>
</div>
}
</div> </div>
</div> </div>
</div> </div>
<!-- Financial & Classification (Individual only) --> <!-- Financial & Classification (Individual only) -->
@if (client?.legalFormId !== 2) { @if (client?.legalFormId !== 2) {
<div class="info-card"> <div class="info-card">
<div class="card-header"> <div class="card-header">
<fa-icon icon="chart-line" class="card-icon"></fa-icon> <fa-icon icon="chart-line" class="card-icon"></fa-icon>
<span>Financial &amp; Classification</span> <span>Financial &amp; Classification</span>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="preview-grid"> <div class="preview-grid">
@if (client?.occupationCategoryId) { @if (client?.occupationCategoryId) {
<div class="preview-field"> <div class="preview-field">
<span class="preview-label">Occupation Category</span> <span class="preview-label">Occupation Category</span>
<span class="preview-value">{{ client.occupationCategoryId | find: clientTemplate.occupationCategoryOptions : 'id' : 'name' }}</span> <span class="preview-value">{{
</div> client.occupationCategoryId | find: clientTemplate.occupationCategoryOptions : 'id' : 'name'
} }}</span>
@if (client?.occupationId) { </div>
<div class="preview-field"> }
<span class="preview-label">Occupation</span> @if (client?.occupationId) {
<span class="preview-value">{{ client.occupationId | find: clientTemplate.occupationOptions : 'id' : 'name' }}</span> <div class="preview-field">
</div> <span class="preview-label">Occupation</span>
} <span class="preview-value">{{
@if (client?.clientTypeId) { client.occupationId | find: clientTemplate.occupationOptions : 'id' : 'name'
<div class="preview-field"> }}</span>
<span class="preview-label">Client Type</span> </div>
<span class="preview-value">{{ client.clientTypeId | find: clientTemplate.clientTypeOptions : 'id' : 'name' }}</span> }
</div> @if (client?.clientTypeId) {
} <div class="preview-field">
@if (client?.clientClassificationId) { <span class="preview-label">Client Type</span>
<div class="preview-field"> <span class="preview-value">{{
<span class="preview-label">Client Classification</span> client.clientTypeId | find: clientTemplate.clientTypeOptions : 'id' : 'name'
<span class="preview-value">{{ client.clientClassificationId | find: clientTemplate.clientClassificationOptions : 'id' : 'name' }}</span> }}</span>
</div> </div>
} }
@if (client?.monthlyIncome) { @if (client?.clientClassificationId) {
<div class="preview-field"> <div class="preview-field">
<span class="preview-label">Monthly Income</span> <span class="preview-label">Client Classification</span>
<span class="preview-value">₹ {{ client.monthlyIncome | number }}</span> <span class="preview-value">{{
</div> client.clientClassificationId | find: clientTemplate.clientClassificationOptions : 'id' : 'name'
} }}</span>
@if (client?.annualTurnover) { </div>
<div class="preview-field"> }
<span class="preview-label">Annual Turnover</span> @if (client?.monthlyIncome) {
<span class="preview-value">₹ {{ client.annualTurnover | number }}</span> <div class="preview-field">
</div> <span class="preview-label">Monthly Income</span>
} <span class="preview-value">₹ {{ client.monthlyIncome | number }}</span>
@if (!client?.occupationCategoryId && !client?.occupationId && !client?.clientTypeId && !client?.clientClassificationId && !client?.monthlyIncome && !client?.annualTurnover) { </div>
<div class="preview-field full-width"> }
<span class="preview-value no-data">No classification data provided</span> @if (client?.annualTurnover) {
</div> <div class="preview-field">
} <span class="preview-label">Annual Turnover</span>
<span class="preview-value">₹ {{ client.annualTurnover | number }}</span>
</div>
}
@if (
!client?.occupationCategoryId &&
!client?.occupationId &&
!client?.clientTypeId &&
!client?.clientClassificationId &&
!client?.monthlyIncome &&
!client?.annualTurnover
) {
<div class="preview-field full-width">
<span class="preview-value no-data">No classification data provided</span>
</div>
}
</div>
</div> </div>
</div> </div>
</div>
} }
<!-- KYC Details --> <!-- KYC Details -->
@if (kycData?.panNumber || kycData?.aadhaarNumberMasked || kycData?.gstinNumber || kycData?.enterprisePan || kycData?.cinNumber) { @if (
kycData?.panNumber ||
kycData?.aadhaarNumberMasked ||
kycData?.gstinNumber ||
kycData?.enterprisePan ||
kycData?.cinNumber
) {
<div class="info-card"> <div class="info-card">
<div class="card-header"> <div class="card-header">
<fa-icon icon="fingerprint" class="card-icon"></fa-icon> <fa-icon icon="fingerprint" class="card-icon"></fa-icon>
@@ -391,9 +464,13 @@
<div class="preview-field"> <div class="preview-field">
<span class="preview-label">Risk Category</span> <span class="preview-label">Risk Category</span>
<span class="preview-value"> <span class="preview-value">
@if (kycData.riskCategory === 1) { Low Risk } @if (kycData.riskCategory === 1) {
@else if (kycData.riskCategory === 2) { Medium Risk } Low Risk
@else if (kycData.riskCategory === 3) { High Risk } } @else if (kycData.riskCategory === 2) {
Medium Risk
} @else if (kycData.riskCategory === 3) {
High Risk
}
</span> </span>
</div> </div>
} }
@@ -414,7 +491,7 @@
<div class="card-header"> <div class="card-header">
<fa-icon icon="user-shield" class="card-icon"></fa-icon> <fa-icon icon="user-shield" class="card-icon"></fa-icon>
<span>Guardian Details</span> <span>Guardian Details</span>
<span class="preview-badge preview-badge--info" style="margin-left:auto">Minor</span> <span class="preview-badge preview-badge--info" style="margin-left: auto">Minor</span>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="preview-grid"> <div class="preview-grid">
@@ -450,7 +527,13 @@
<div class="preview-field"> <div class="preview-field">
<span class="preview-label">Status</span> <span class="preview-label">Status</span>
<span class="preview-value"> <span class="preview-value">
<span [class]="guardian.status?.value === 'Active' ? 'preview-badge preview-badge--verified' : 'preview-badge preview-badge--pending'"> <span
[class]="
guardian.status?.value === 'Active'
? 'preview-badge preview-badge--verified'
: 'preview-badge preview-badge--pending'
"
>
{{ guardian.status.value }} {{ guardian.status.value }}
</span> </span>
</span> </span>
@@ -524,7 +607,12 @@
</div> </div>
} }
} }
@if (!addressData.permanent?.addressLine1 && !addressData.current?.addressLine1 && !addressData.office?.addressLine1 && !addressData.correspondence?.addressLine1) { @if (
!addressData.permanent?.addressLine1 &&
!addressData.current?.addressLine1 &&
!addressData.office?.addressLine1 &&
!addressData.correspondence?.addressLine1
) {
<div class="preview-field full-width"> <div class="preview-field full-width">
<span class="preview-value no-data">No address data provided</span> <span class="preview-value no-data">No address data provided</span>
</div> </div>
@@ -563,40 +651,76 @@
<div class="card-body"> <div class="card-body">
<div class="preview-grid"> <div class="preview-grid">
@if (entityClassificationData.msme.udyamRegistrationNumber) { @if (entityClassificationData.msme.udyamRegistrationNumber) {
<div class="preview-field"><span class="preview-label">Udyam Registration No.</span><span class="preview-value mono">{{ entityClassificationData.msme.udyamRegistrationNumber }}</span></div> <div class="preview-field">
<span class="preview-label">Udyam Registration No.</span
><span class="preview-value mono">{{ entityClassificationData.msme.udyamRegistrationNumber }}</span>
</div>
} }
@if (entityClassificationData.msme.msmeClassification) { @if (entityClassificationData.msme.msmeClassification) {
<div class="preview-field"><span class="preview-label">MSME Classification</span><span class="preview-value">{{ entityClassificationData.msme.msmeClassification }}</span></div> <div class="preview-field">
<span class="preview-label">MSME Classification</span
><span class="preview-value">{{ entityClassificationData.msme.msmeClassification }}</span>
</div>
} }
@if (entityClassificationData.msme.enterpriseType) { @if (entityClassificationData.msme.enterpriseType) {
<div class="preview-field"><span class="preview-label">Enterprise Type</span><span class="preview-value">{{ entityClassificationData.msme.enterpriseType }}</span></div> <div class="preview-field">
<span class="preview-label">Enterprise Type</span
><span class="preview-value">{{ entityClassificationData.msme.enterpriseType }}</span>
</div>
} }
@if (entityClassificationData.msme.nicActivityCode) { @if (entityClassificationData.msme.nicActivityCode) {
<div class="preview-field"><span class="preview-label">NIC Activity Code</span><span class="preview-value mono">{{ entityClassificationData.msme.nicActivityCode }}</span></div> <div class="preview-field">
<span class="preview-label">NIC Activity Code</span
><span class="preview-value mono">{{ entityClassificationData.msme.nicActivityCode }}</span>
</div>
} }
@if (entityClassificationData.msme.annualTurnover) { @if (entityClassificationData.msme.annualTurnover) {
<div class="preview-field"><span class="preview-label">Annual Turnover</span><span class="preview-value">₹ {{ entityClassificationData.msme.annualTurnover }} Cr</span></div> <div class="preview-field">
<span class="preview-label">Annual Turnover</span
><span class="preview-value">₹ {{ entityClassificationData.msme.annualTurnover }} Cr</span>
</div>
} }
@if (entityClassificationData.msme.investmentPlantMachinery) { @if (entityClassificationData.msme.investmentPlantMachinery) {
<div class="preview-field"><span class="preview-label">Investment (Plant &amp; Machinery)</span><span class="preview-value">₹ {{ entityClassificationData.msme.investmentPlantMachinery }} Cr</span></div> <div class="preview-field">
<span class="preview-label">Investment (Plant &amp; Machinery)</span
><span class="preview-value">₹ {{ entityClassificationData.msme.investmentPlantMachinery }} Cr</span>
</div>
} }
@if (entityClassificationData.msme.financialYearAssessment) { @if (entityClassificationData.msme.financialYearAssessment) {
<div class="preview-field"><span class="preview-label">FY of Assessment</span><span class="preview-value">{{ entityClassificationData.msme.financialYearAssessment }}</span></div> <div class="preview-field">
<span class="preview-label">FY of Assessment</span
><span class="preview-value">{{ entityClassificationData.msme.financialYearAssessment }}</span>
</div>
} }
@if (entityClassificationData.msme.socialCategoryOwner) { @if (entityClassificationData.msme.socialCategoryOwner) {
<div class="preview-field"><span class="preview-label">Social Category</span><span class="preview-value">{{ entityClassificationData.msme.socialCategoryOwner }}</span></div> <div class="preview-field">
<span class="preview-label">Social Category</span
><span class="preview-value">{{ entityClassificationData.msme.socialCategoryOwner }}</span>
</div>
} }
@if (entityClassificationData.msme.genderPrimaryOwner) { @if (entityClassificationData.msme.genderPrimaryOwner) {
<div class="preview-field"><span class="preview-label">Gender of Owner</span><span class="preview-value">{{ entityClassificationData.msme.genderPrimaryOwner }}</span></div> <div class="preview-field">
<span class="preview-label">Gender of Owner</span
><span class="preview-value">{{ entityClassificationData.msme.genderPrimaryOwner }}</span>
</div>
} }
@if (entityClassificationData.msme.noOfEmployees) { @if (entityClassificationData.msme.noOfEmployees) {
<div class="preview-field"><span class="preview-label">No. of Employees</span><span class="preview-value">{{ entityClassificationData.msme.noOfEmployees }}</span></div> <div class="preview-field">
<span class="preview-label">No. of Employees</span
><span class="preview-value">{{ entityClassificationData.msme.noOfEmployees }}</span>
</div>
} }
@if (entityClassificationData.msme.womenLedEnterprise) { @if (entityClassificationData.msme.womenLedEnterprise) {
<div class="preview-field"><span class="preview-label">Women-led Enterprise</span><span class="preview-value"><span class="preview-badge preview-badge--verified">Yes</span></span></div> <div class="preview-field">
<span class="preview-label">Women-led Enterprise</span
><span class="preview-value"><span class="preview-badge preview-badge--verified">Yes</span></span>
</div>
} }
@if (entityClassificationData.msme.divyangPwdOwned) { @if (entityClassificationData.msme.divyangPwdOwned) {
<div class="preview-field"><span class="preview-label">Divyang (PWD) Owned</span><span class="preview-value"><span class="preview-badge preview-badge--verified">Yes</span></span></div> <div class="preview-field">
<span class="preview-label">Divyang (PWD) Owned</span
><span class="preview-value"><span class="preview-badge preview-badge--verified">Yes</span></span>
</div>
} }
</div> </div>
</div> </div>
@@ -613,19 +737,34 @@
<div class="card-body"> <div class="card-body">
<div class="preview-grid"> <div class="preview-grid">
@if (entityClassificationData.startup.dpiitRecognitionNumber) { @if (entityClassificationData.startup.dpiitRecognitionNumber) {
<div class="preview-field"><span class="preview-label">DPIIT Recognition No.</span><span class="preview-value mono">{{ entityClassificationData.startup.dpiitRecognitionNumber }}</span></div> <div class="preview-field">
<span class="preview-label">DPIIT Recognition No.</span
><span class="preview-value mono">{{ entityClassificationData.startup.dpiitRecognitionNumber }}</span>
</div>
} }
@if (entityClassificationData.startup.entityStage) { @if (entityClassificationData.startup.entityStage) {
<div class="preview-field"><span class="preview-label">Entity Stage</span><span class="preview-value">{{ entityClassificationData.startup.entityStage }}</span></div> <div class="preview-field">
<span class="preview-label">Entity Stage</span
><span class="preview-value">{{ entityClassificationData.startup.entityStage }}</span>
</div>
} }
@if (entityClassificationData.startup.sectorAsDpiit) { @if (entityClassificationData.startup.sectorAsDpiit) {
<div class="preview-field"><span class="preview-label">Sector (DPIIT)</span><span class="preview-value">{{ entityClassificationData.startup.sectorAsDpiit }}</span></div> <div class="preview-field">
<span class="preview-label">Sector (DPIIT)</span
><span class="preview-value">{{ entityClassificationData.startup.sectorAsDpiit }}</span>
</div>
} }
@if (entityClassificationData.startup.taxExemption80iac) { @if (entityClassificationData.startup.taxExemption80iac) {
<div class="preview-field"><span class="preview-label">80IAC Tax Exemption</span><span class="preview-value"><span class="preview-badge preview-badge--verified">Yes</span></span></div> <div class="preview-field">
<span class="preview-label">80IAC Tax Exemption</span
><span class="preview-value"><span class="preview-badge preview-badge--verified">Yes</span></span>
</div>
} }
@if (entityClassificationData.startup.pslLoanLimit) { @if (entityClassificationData.startup.pslLoanLimit) {
<div class="preview-field"><span class="preview-label">PSL Loan Limit</span><span class="preview-value">₹ {{ entityClassificationData.startup.pslLoanLimit | number }} Cr</span></div> <div class="preview-field">
<span class="preview-label">PSL Loan Limit</span
><span class="preview-value">₹ {{ entityClassificationData.startup.pslLoanLimit | number }} Cr</span>
</div>
} }
</div> </div>
</div> </div>
@@ -642,22 +781,40 @@
<div class="card-body"> <div class="card-body">
<div class="preview-grid"> <div class="preview-grid">
@if (entityClassificationData.mudra.mudraCategory) { @if (entityClassificationData.mudra.mudraCategory) {
<div class="preview-field"><span class="preview-label">Mudra Category</span><span class="preview-value">{{ entityClassificationData.mudra.mudraCategory }}</span></div> <div class="preview-field">
<span class="preview-label">Mudra Category</span
><span class="preview-value">{{ entityClassificationData.mudra.mudraCategory }}</span>
</div>
} }
@if (entityClassificationData.mudra.mudraLoanAccountNo) { @if (entityClassificationData.mudra.mudraLoanAccountNo) {
<div class="preview-field"><span class="preview-label">Loan Account No.</span><span class="preview-value mono">{{ entityClassificationData.mudra.mudraLoanAccountNo }}</span></div> <div class="preview-field">
<span class="preview-label">Loan Account No.</span
><span class="preview-value mono">{{ entityClassificationData.mudra.mudraLoanAccountNo }}</span>
</div>
} }
@if (entityClassificationData.mudra.sanctionedAmount) { @if (entityClassificationData.mudra.sanctionedAmount) {
<div class="preview-field"><span class="preview-label">Sanctioned Amount</span><span class="preview-value">₹ {{ entityClassificationData.mudra.sanctionedAmount | number }}</span></div> <div class="preview-field">
<span class="preview-label">Sanctioned Amount</span
><span class="preview-value">₹ {{ entityClassificationData.mudra.sanctionedAmount | number }}</span>
</div>
} }
@if (entityClassificationData.mudra.lendingInstitutionName) { @if (entityClassificationData.mudra.lendingInstitutionName) {
<div class="preview-field"><span class="preview-label">Lending Institution</span><span class="preview-value">{{ entityClassificationData.mudra.lendingInstitutionName }}</span></div> <div class="preview-field">
<span class="preview-label">Lending Institution</span
><span class="preview-value">{{ entityClassificationData.mudra.lendingInstitutionName }}</span>
</div>
} }
@if (entityClassificationData.mudra.repaymentStatus) { @if (entityClassificationData.mudra.repaymentStatus) {
<div class="preview-field"><span class="preview-label">Repayment Status</span><span class="preview-value">{{ entityClassificationData.mudra.repaymentStatus }}</span></div> <div class="preview-field">
<span class="preview-label">Repayment Status</span
><span class="preview-value">{{ entityClassificationData.mudra.repaymentStatus }}</span>
</div>
} }
@if (entityClassificationData.mudra.npaFlag) { @if (entityClassificationData.mudra.npaFlag) {
<div class="preview-field"><span class="preview-label">NPA Flag</span><span class="preview-value"><span class="preview-badge preview-badge--danger">NPA</span></span></div> <div class="preview-field">
<span class="preview-label">NPA Flag</span
><span class="preview-value"><span class="preview-badge preview-badge--danger">NPA</span></span>
</div>
} }
</div> </div>
</div> </div>
@@ -674,19 +831,36 @@
<div class="card-body"> <div class="card-body">
<div class="preview-grid"> <div class="preview-grid">
@if (entityClassificationData.eou.eouRegistrationNumber) { @if (entityClassificationData.eou.eouRegistrationNumber) {
<div class="preview-field"><span class="preview-label">EOU Registration No.</span><span class="preview-value mono">{{ entityClassificationData.eou.eouRegistrationNumber }}</span></div> <div class="preview-field">
<span class="preview-label">EOU Registration No.</span
><span class="preview-value mono">{{ entityClassificationData.eou.eouRegistrationNumber }}</span>
</div>
} }
@if (entityClassificationData.eou.registeredDcZone) { @if (entityClassificationData.eou.registeredDcZone) {
<div class="preview-field"><span class="preview-label">Registered DC Zone</span><span class="preview-value">{{ entityClassificationData.eou.registeredDcZone }}</span></div> <div class="preview-field">
<span class="preview-label">Registered DC Zone</span
><span class="preview-value">{{ entityClassificationData.eou.registeredDcZone }}</span>
</div>
} }
@if (entityClassificationData.eou.gstinEou) { @if (entityClassificationData.eou.gstinEou) {
<div class="preview-field"><span class="preview-label">GSTIN (EOU)</span><span class="preview-value mono">{{ entityClassificationData.eou.gstinEou }}</span></div> <div class="preview-field">
<span class="preview-label">GSTIN (EOU)</span
><span class="preview-value mono">{{ entityClassificationData.eou.gstinEou }}</span>
</div>
} }
@if (entityClassificationData.eou.annualExportObligation) { @if (entityClassificationData.eou.annualExportObligation) {
<div class="preview-field"><span class="preview-label">Export Obligation</span><span class="preview-value">₹ {{ entityClassificationData.eou.annualExportObligation | number }} Cr</span></div> <div class="preview-field">
<span class="preview-label">Export Obligation</span
><span class="preview-value"
>₹ {{ entityClassificationData.eou.annualExportObligation | number }} Cr</span
>
</div>
} }
@if (entityClassificationData.eou.adCategoryBankCode) { @if (entityClassificationData.eou.adCategoryBankCode) {
<div class="preview-field"><span class="preview-label">AD Bank Code</span><span class="preview-value mono">{{ entityClassificationData.eou.adCategoryBankCode }}</span></div> <div class="preview-field">
<span class="preview-label">AD Bank Code</span
><span class="preview-value mono">{{ entityClassificationData.eou.adCategoryBankCode }}</span>
</div>
} }
</div> </div>
</div> </div>
@@ -703,22 +877,40 @@
<div class="card-body"> <div class="card-body">
<div class="preview-grid"> <div class="preview-grid">
@if (entityClassificationData.importexport.iecCode) { @if (entityClassificationData.importexport.iecCode) {
<div class="preview-field"><span class="preview-label">IEC Code</span><span class="preview-value mono">{{ entityClassificationData.importexport.iecCode }}</span></div> <div class="preview-field">
<span class="preview-label">IEC Code</span
><span class="preview-value mono">{{ entityClassificationData.importexport.iecCode }}</span>
</div>
} }
@if (entityClassificationData.importexport.iecStatus) { @if (entityClassificationData.importexport.iecStatus) {
<div class="preview-field"><span class="preview-label">IEC Status</span><span class="preview-value">{{ entityClassificationData.importexport.iecStatus }}</span></div> <div class="preview-field">
<span class="preview-label">IEC Status</span
><span class="preview-value">{{ entityClassificationData.importexport.iecStatus }}</span>
</div>
} }
@if (entityClassificationData.importexport.tradeType) { @if (entityClassificationData.importexport.tradeType) {
<div class="preview-field"><span class="preview-label">Trade Type</span><span class="preview-value">{{ entityClassificationData.importexport.tradeType }}</span></div> <div class="preview-field">
<span class="preview-label">Trade Type</span
><span class="preview-value">{{ entityClassificationData.importexport.tradeType }}</span>
</div>
} }
@if (entityClassificationData.importexport.primaryHsCode) { @if (entityClassificationData.importexport.primaryHsCode) {
<div class="preview-field"><span class="preview-label">Primary HS Code</span><span class="preview-value mono">{{ entityClassificationData.importexport.primaryHsCode }}</span></div> <div class="preview-field">
<span class="preview-label">Primary HS Code</span
><span class="preview-value mono">{{ entityClassificationData.importexport.primaryHsCode }}</span>
</div>
} }
@if (entityClassificationData.importexport.eefcAccountRequired) { @if (entityClassificationData.importexport.eefcAccountRequired) {
<div class="preview-field"><span class="preview-label">EEFC Account</span><span class="preview-value"><span class="preview-badge preview-badge--verified">Required</span></span></div> <div class="preview-field">
<span class="preview-label">EEFC Account</span
><span class="preview-value"><span class="preview-badge preview-badge--verified">Required</span></span>
</div>
} }
@if (entityClassificationData.importexport.femaDeclarationObtained) { @if (entityClassificationData.importexport.femaDeclarationObtained) {
<div class="preview-field"><span class="preview-label">FEMA Declaration</span><span class="preview-value"><span class="preview-badge preview-badge--verified">Obtained</span></span></div> <div class="preview-field">
<span class="preview-label">FEMA Declaration</span
><span class="preview-value"><span class="preview-badge preview-badge--verified">Obtained</span></span>
</div>
} }
</div> </div>
</div> </div>
@@ -735,22 +927,40 @@
<div class="card-body"> <div class="card-body">
<div class="preview-grid"> <div class="preview-grid">
@if (entityClassificationData.psl.pslCategory) { @if (entityClassificationData.psl.pslCategory) {
<div class="preview-field"><span class="preview-label">PSL Category</span><span class="preview-value">{{ entityClassificationData.psl.pslCategory }}</span></div> <div class="preview-field">
<span class="preview-label">PSL Category</span
><span class="preview-value">{{ entityClassificationData.psl.pslCategory }}</span>
</div>
} }
@if (entityClassificationData.psl.pslSubCategory) { @if (entityClassificationData.psl.pslSubCategory) {
<div class="preview-field"><span class="preview-label">PSL Sub-category</span><span class="preview-value">{{ entityClassificationData.psl.pslSubCategory }}</span></div> <div class="preview-field">
<span class="preview-label">PSL Sub-category</span
><span class="preview-value">{{ entityClassificationData.psl.pslSubCategory }}</span>
</div>
} }
@if (entityClassificationData.psl.pslSanctionedLimit) { @if (entityClassificationData.psl.pslSanctionedLimit) {
<div class="preview-field"><span class="preview-label">Sanctioned Limit</span><span class="preview-value">₹ {{ entityClassificationData.psl.pslSanctionedLimit | number }}</span></div> <div class="preview-field">
<span class="preview-label">Sanctioned Limit</span
><span class="preview-value">₹ {{ entityClassificationData.psl.pslSanctionedLimit | number }}</span>
</div>
} }
@if (entityClassificationData.psl.fiddReportingCode) { @if (entityClassificationData.psl.fiddReportingCode) {
<div class="preview-field"><span class="preview-label">FIDD Reporting Code</span><span class="preview-value mono">{{ entityClassificationData.psl.fiddReportingCode }}</span></div> <div class="preview-field">
<span class="preview-label">FIDD Reporting Code</span
><span class="preview-value mono">{{ entityClassificationData.psl.fiddReportingCode }}</span>
</div>
} }
@if (entityClassificationData.psl.weakerSectionFlag) { @if (entityClassificationData.psl.weakerSectionFlag) {
<div class="preview-field"><span class="preview-label">Weaker Section</span><span class="preview-value"><span class="preview-badge preview-badge--verified">Yes</span></span></div> <div class="preview-field">
<span class="preview-label">Weaker Section</span
><span class="preview-value"><span class="preview-badge preview-badge--verified">Yes</span></span>
</div>
} }
@if (entityClassificationData.psl.womenEntrepreneurFlag) { @if (entityClassificationData.psl.womenEntrepreneurFlag) {
<div class="preview-field"><span class="preview-label">Women Entrepreneur</span><span class="preview-value"><span class="preview-badge preview-badge--verified">Yes</span></span></div> <div class="preview-field">
<span class="preview-label">Women Entrepreneur</span
><span class="preview-value"><span class="preview-badge preview-badge--verified">Yes</span></span>
</div>
} }
</div> </div>
</div> </div>
@@ -767,25 +977,46 @@
<div class="card-body"> <div class="card-body">
<div class="preview-grid"> <div class="preview-grid">
@if (entityClassificationData.nps.industrySector) { @if (entityClassificationData.nps.industrySector) {
<div class="preview-field"><span class="preview-label">Industry Sector</span><span class="preview-value">{{ entityClassificationData.nps.industrySector }}</span></div> <div class="preview-field">
<span class="preview-label">Industry Sector</span
><span class="preview-value">{{ entityClassificationData.nps.industrySector }}</span>
</div>
} }
@if (entityClassificationData.nps.nicCode) { @if (entityClassificationData.nps.nicCode) {
<div class="preview-field"><span class="preview-label">NIC Code</span><span class="preview-value mono">{{ entityClassificationData.nps.nicCode }}</span></div> <div class="preview-field">
<span class="preview-label">NIC Code</span
><span class="preview-value mono">{{ entityClassificationData.nps.nicCode }}</span>
</div>
} }
@if (entityClassificationData.nps.annualRevenueBand) { @if (entityClassificationData.nps.annualRevenueBand) {
<div class="preview-field"><span class="preview-label">Annual Revenue Band</span><span class="preview-value">{{ entityClassificationData.nps.annualRevenueBand }}</span></div> <div class="preview-field">
<span class="preview-label">Annual Revenue Band</span
><span class="preview-value">{{ entityClassificationData.nps.annualRevenueBand }}</span>
</div>
} }
@if (entityClassificationData.nps.riskCategory) { @if (entityClassificationData.nps.riskCategory) {
<div class="preview-field"><span class="preview-label">Risk Category</span><span class="preview-value">{{ entityClassificationData.nps.riskCategory }}</span></div> <div class="preview-field">
<span class="preview-label">Risk Category</span
><span class="preview-value">{{ entityClassificationData.nps.riskCategory }}</span>
</div>
} }
@if (entityClassificationData.nps.amlRiskClassification) { @if (entityClassificationData.nps.amlRiskClassification) {
<div class="preview-field"><span class="preview-label">AML Risk</span><span class="preview-value">{{ entityClassificationData.nps.amlRiskClassification }}</span></div> <div class="preview-field">
<span class="preview-label">AML Risk</span
><span class="preview-value">{{ entityClassificationData.nps.amlRiskClassification }}</span>
</div>
} }
@if (entityClassificationData.nps.pepFlag) { @if (entityClassificationData.nps.pepFlag) {
<div class="preview-field"><span class="preview-label">PEP Flag</span><span class="preview-value"><span class="preview-badge preview-badge--danger">PEP</span></span></div> <div class="preview-field">
<span class="preview-label">PEP Flag</span
><span class="preview-value"><span class="preview-badge preview-badge--danger">PEP</span></span>
</div>
} }
@if (entityClassificationData.nps.amlWatchlistMatch) { @if (entityClassificationData.nps.amlWatchlistMatch) {
<div class="preview-field"><span class="preview-label">AML Watchlist</span><span class="preview-value"><span class="preview-badge preview-badge--danger">Match Found</span></span></div> <div class="preview-field">
<span class="preview-label">AML Watchlist</span
><span class="preview-value"><span class="preview-badge preview-badge--danger">Match Found</span></span>
</div>
} }
</div> </div>
</div> </div>
@@ -793,58 +1024,64 @@
} }
} }
<!-- Family Members (Individual only) --> <!-- Family Members (Individual only) -->
@if (client?.legalFormId !== 2) { @if (client?.legalFormId !== 2) {
<div class="info-card"> <div class="info-card">
<div class="card-header"> <div class="card-header">
<fa-icon icon="users" class="card-icon"></fa-icon> <fa-icon icon="users" class="card-icon"></fa-icon>
<span>Family Members</span> <span>Family Members</span>
@if (familyMembers.length > 0) { @if (familyMembers.length > 0) {
<span class="count-badge">{{ familyMembers.length }}</span> <span class="count-badge">{{ familyMembers.length }}</span>
} }
</div> </div>
<div class="card-body"> <div class="card-body">
@if (familyMembers.length === 0) { @if (familyMembers.length === 0) {
<div class="preview-empty preview-empty--warn"> <div class="preview-empty preview-empty--warn">
<mat-icon class="preview-empty__icon">warning</mat-icon> <mat-icon class="preview-empty__icon">warning</mat-icon>
<span>No family members added. At least one is required.</span> <span>No family members added. At least one is required.</span>
</div> </div>
} @else { } @else {
<div class="fm-preview-list"> <div class="fm-preview-list">
@for (member of familyMembers; track member; let i = $index) { @for (member of familyMembers; track member; let i = $index) {
<div class="fm-preview-row"> <div class="fm-preview-row">
<div class="fm-preview-row__sno">{{ i + 1 }}</div> <div class="fm-preview-row__sno">{{ i + 1 }}</div>
<div class="fm-preview-row__avatar"> <div class="fm-preview-row__avatar">
<mat-icon>person</mat-icon> <mat-icon>person</mat-icon>
</div>
<div class="fm-preview-row__info">
<span class="fm-preview-row__name">{{ member.firstName || member.displayName }}</span>
<span class="fm-preview-row__meta">
@if (member.relationship) { {{ member.relationship }} }
@if (member.dateOfBirth) { &middot; DOB: {{ member.dateOfBirth }} }
</span>
</div>
@if (member.aadhaarNumber || member.panNumber) {
<div class="fm-preview-row__ids">
@if (member.panNumber) { <span class="fm-preview-id">PAN: {{ member.panNumber }}</span> }
@if (member.aadhaarNumber) { <span class="fm-preview-id">Aadhaar: {{ member.aadhaarNumber }}</span> }
</div> </div>
} <div class="fm-preview-row__info">
<div class="fm-preview-row__status"> <span class="fm-preview-row__name">{{ member.firstName || member.displayName }}</span>
@if (member.isDependent) { <span class="fm-preview-row__meta">
<span class="preview-badge preview-badge--info">Dependent</span> @if (member.relationship) {
} {{ member.relationship }}
@if (member.linkedClientId) { }
<span class="preview-badge preview-badge--verified">Linked</span> @if (member.dateOfBirth) {
&middot; DOB: {{ member.dateOfBirth }}
}
</span>
</div>
@if (member.aadhaarNumber || member.panNumber) {
<div class="fm-preview-row__ids">
@if (member.panNumber) {
<span class="fm-preview-id">PAN: {{ member.panNumber }}</span>
}
@if (member.aadhaarNumber) {
<span class="fm-preview-id">Aadhaar: {{ member.aadhaarNumber }}</span>
}
</div>
} }
<div class="fm-preview-row__status">
@if (member.isDependent) {
<span class="preview-badge preview-badge--info">Dependent</span>
}
@if (member.linkedClientId) {
<span class="preview-badge preview-badge--verified">Linked</span>
}
</div>
</div> </div>
</div> }
} </div>
</div> }
} </div>
</div> </div>
</div>
} }
</div> </div>
@@ -73,9 +73,22 @@ export class ClientPreviewStepComponent {
@Input() maritalStatusId: number | null = null; @Input() maritalStatusId: number | null = null;
/** Marital Status Options */ /** Marital Status Options */
@Input() maritalStatusOptions: any[] = []; @Input() maritalStatusOptions: any[] = [];
/** Education Level Options */
@Input() educationLevelOptions: any[] = [];
/** Degree / Qualification Options */
@Input() degreeQualificationOptions: any[] = [];
/** Religion Options */
@Input() religionOptions: any[] = [];
/** Social Category Options */
@Input() socialCategoryOptions: any[] = [];
/** Address tab labels */ /** Address tab labels */
addressTabs = ['permanent', 'current', 'office', 'correspondence']; addressTabs = [
'permanent',
'current',
'office',
'correspondence'
];
addressTabLabels: Record<string, string> = { addressTabLabels: Record<string, string> = {
permanent: 'Permanent Address', permanent: 'Permanent Address',
current: 'Current / Residential', current: 'Current / Residential',
@@ -7,7 +7,6 @@
--> -->
<div class="personal-data-container mat-typography"> <div class="personal-data-container mat-typography">
<!-- Personal Information --> <!-- Personal Information -->
<div class="pd-section"> <div class="pd-section">
<div class="pd-section__header"> <div class="pd-section__header">
@@ -50,13 +49,15 @@
@if (clientViewData.dateOfBirth) { @if (clientViewData.dateOfBirth) {
<div class="pd-field"> <div class="pd-field">
<div class="pd-field__label">{{ 'labels.inputs.Date of Birth' | translate }}</div> <div class="pd-field__label">{{ 'labels.inputs.Date of Birth' | translate }}</div>
<div class="pd-field__value">{{ clientViewData.dateOfBirth | dateFormat:'DD-MMMM-YYYY' }}</div> <div class="pd-field__value">{{ clientViewData.dateOfBirth | dateFormat: 'DD-MMMM-YYYY' }}</div>
</div> </div>
} }
@if (clientViewData.ageYears != null) { @if (clientViewData.ageYears != null) {
<div class="pd-field"> <div class="pd-field">
<div class="pd-field__label">Age</div> <div class="pd-field__label">Age</div>
<div class="pd-field__value">{{ clientViewData.ageYears }}y {{ clientViewData.ageMonths || 0 }}m {{ clientViewData.ageDays || 0 }}d</div> <div class="pd-field__value">
{{ clientViewData.ageYears }}y {{ clientViewData.ageMonths || 0 }}m {{ clientViewData.ageDays || 0 }}d
</div>
</div> </div>
} }
@if (clientViewData.gender) { @if (clientViewData.gender) {
@@ -71,11 +72,39 @@
<div class="pd-field__value">{{ clientViewData.maritalStatus.name }}</div> <div class="pd-field__value">{{ clientViewData.maritalStatus.name }}</div>
</div> </div>
} }
@if (clientViewData.educationLevel) {
<div class="pd-field">
<div class="pd-field__label">Education Level</div>
<div class="pd-field__value">{{ clientViewData.educationLevel.name }}</div>
</div>
}
@if (clientViewData.degreeQualification) {
<div class="pd-field">
<div class="pd-field__label">Degree / Qualification</div>
<div class="pd-field__value">{{ clientViewData.degreeQualification.name }}</div>
</div>
}
@if (clientViewData.religion) {
<div class="pd-field">
<div class="pd-field__label">Religion</div>
<div class="pd-field__value">{{ clientViewData.religion.name }}</div>
</div>
}
@if (clientViewData.socialCategory) {
<div class="pd-field">
<div class="pd-field__label">Social Category</div>
<div class="pd-field__value">{{ clientViewData.socialCategory.name }}</div>
</div>
}
@if (clientViewData.isMinor != null) { @if (clientViewData.isMinor != null) {
<div class="pd-field"> <div class="pd-field">
<div class="pd-field__label">Minor</div> <div class="pd-field__label">Minor</div>
<div class="pd-field__value"> <div class="pd-field__value">
<span class="pd-verify-badge" [class.pd-verify-badge--verified]="!clientViewData.isMinor" [class.pd-verify-badge--pending]="clientViewData.isMinor"> <span
class="pd-verify-badge"
[class.pd-verify-badge--verified]="!clientViewData.isMinor"
[class.pd-verify-badge--pending]="clientViewData.isMinor"
>
{{ clientViewData.isMinor ? 'Yes (Age < 10)' : 'No' }} {{ clientViewData.isMinor ? 'Yes (Age < 10)' : 'No' }}
</span> </span>
</div> </div>
@@ -84,7 +113,11 @@
<div class="pd-field"> <div class="pd-field">
<div class="pd-field__label">{{ 'labels.inputs.Is staff' | translate }}</div> <div class="pd-field__label">{{ 'labels.inputs.Is staff' | translate }}</div>
<div class="pd-field__value"> <div class="pd-field__value">
<span class="pd-verify-badge" [class.pd-verify-badge--verified]="clientViewData.isStaff" [class.pd-verify-badge--pending]="!clientViewData.isStaff"> <span
class="pd-verify-badge"
[class.pd-verify-badge--verified]="clientViewData.isStaff"
[class.pd-verify-badge--pending]="!clientViewData.isStaff"
>
{{ clientViewData.isStaff ? 'Yes' : 'No' }} {{ clientViewData.isStaff ? 'Yes' : 'No' }}
</span> </span>
</div> </div>
@@ -100,7 +133,7 @@
@if (clientViewData.dateOfBirth) { @if (clientViewData.dateOfBirth) {
<div class="pd-field"> <div class="pd-field">
<div class="pd-field__label">{{ 'labels.inputs.Incorporation Date' | translate }}</div> <div class="pd-field__label">{{ 'labels.inputs.Incorporation Date' | translate }}</div>
<div class="pd-field__value">{{ clientViewData.dateOfBirth | dateFormat:'DD-MM-YYYY' }}</div> <div class="pd-field__value">{{ clientViewData.dateOfBirth | dateFormat: 'DD-MM-YYYY' }}</div>
</div> </div>
} }
} }
@@ -112,7 +145,9 @@
} }
<div class="pd-field"> <div class="pd-field">
<div class="pd-field__label">Nationality</div> <div class="pd-field__label">Nationality</div>
<div class="pd-field__value">{{ clientViewData.nationalityId || clientViewData.nationality || 'Indian' }}</div> <div class="pd-field__value">
{{ clientViewData.nationalityId || clientViewData.nationality || 'Indian' }}
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -176,7 +211,9 @@
@if (clientViewData.clientNonPersonDetails.incorpValidityTillDate) { @if (clientViewData.clientNonPersonDetails.incorpValidityTillDate) {
<div class="pd-field"> <div class="pd-field">
<div class="pd-field__label">{{ 'labels.inputs.Incorporation Validity Till Date' | translate }}</div> <div class="pd-field__label">{{ 'labels.inputs.Incorporation Validity Till Date' | translate }}</div>
<div class="pd-field__value">{{ clientViewData.clientNonPersonDetails.incorpValidityTillDate | dateFormat:'DD-MM-YYYY' }}</div> <div class="pd-field__value">
{{ clientViewData.clientNonPersonDetails.incorpValidityTillDate | dateFormat: 'DD-MM-YYYY' }}
</div>
</div> </div>
} }
@if (clientViewData.clientNonPersonDetails.incorpNumber) { @if (clientViewData.clientNonPersonDetails.incorpNumber) {
@@ -221,7 +258,9 @@
<div class="pd-field"> <div class="pd-field">
<div class="pd-field__label">{{ 'labels.inputs.Email' | translate }}</div> <div class="pd-field__label">{{ 'labels.inputs.Email' | translate }}</div>
<div class="pd-field__value"> <div class="pd-field__value">
<span class="pd-contact-icon"><i class="fa fa-envelope-o"></i> {{ clientViewData.emailAddress || '-' }}</span> <span class="pd-contact-icon"
><i class="fa fa-envelope-o"></i> {{ clientViewData.emailAddress || '-' }}</span
>
</div> </div>
</div> </div>
</div> </div>
@@ -240,15 +279,19 @@
<div class="pd-field"> <div class="pd-field">
<div class="pd-field__label">Mobile DIP Status</div> <div class="pd-field__label">Mobile DIP Status</div>
<div class="pd-field__value"> <div class="pd-field__value">
@if (clientViewData.mobileVerified === true || clientViewData.mobileVerified === 'true' || clientViewData.mobileVerified === 1 @if (
|| clientViewData.dipVerified === true || clientViewData.dipVerified === 'true' || clientViewData.dipVerified === 1) { clientViewData.mobileVerified === true ||
clientViewData.mobileVerified === 'true' ||
clientViewData.mobileVerified === 1 ||
clientViewData.dipVerified === true ||
clientViewData.dipVerified === 'true' ||
clientViewData.dipVerified === 1
) {
<span class="pd-verify-badge pd-verify-badge--verified"> <span class="pd-verify-badge pd-verify-badge--verified">
<i class="fa fa-check-circle"></i> Verified <i class="fa fa-check-circle"></i> Verified
</span> </span>
} @else { } @else {
<span class="pd-verify-badge pd-verify-badge--pending"> <span class="pd-verify-badge pd-verify-badge--pending"> <i class="fa fa-clock-o"></i> Pending </span>
<i class="fa fa-clock-o"></i> Pending
</span>
} }
</div> </div>
</div> </div>
@@ -258,26 +301,21 @@
<div class="pd-field__value monospace">{{ formatDateTime(clientViewData.dipTimestamp) }}</div> <div class="pd-field__value monospace">{{ formatDateTime(clientViewData.dipTimestamp) }}</div>
</div> </div>
} }
@if (clientViewData.dipLookupLogId) {
<div class="pd-field">
<div class="pd-field__label">DIP Lookup Log ID</div>
<div class="pd-field__value monospace">{{ clientViewData.dipLookupLogId }}</div>
</div>
}
<!-- Alternate Mobile DIP --> <!-- Alternate Mobile DIP -->
@if (clientViewData.alternateMobileNo) { @if (clientViewData.alternateMobileNo) {
<div class="pd-field"> <div class="pd-field">
<div class="pd-field__label">Alternate Mobile DIP Status</div> <div class="pd-field__label">Alternate Mobile DIP Status</div>
<div class="pd-field__value"> <div class="pd-field__value">
@if (clientViewData.alternateDipVerified === true || clientViewData.alternateDipVerified === 'true' || clientViewData.alternateDipVerified === 1) { @if (
clientViewData.alternateDipVerified === true ||
clientViewData.alternateDipVerified === 'true' ||
clientViewData.alternateDipVerified === 1
) {
<span class="pd-verify-badge pd-verify-badge--verified"> <span class="pd-verify-badge pd-verify-badge--verified">
<i class="fa fa-check-circle"></i> Verified <i class="fa fa-check-circle"></i> Verified
</span> </span>
} @else { } @else {
<span class="pd-verify-badge pd-verify-badge--pending"> <span class="pd-verify-badge pd-verify-badge--pending"> <i class="fa fa-clock-o"></i> Pending </span>
<i class="fa fa-clock-o"></i> Pending
</span>
} }
</div> </div>
</div> </div>
@@ -287,93 +325,91 @@
<div class="pd-field__value monospace">{{ formatDateTime(clientViewData.alternateDipTimestamp) }}</div> <div class="pd-field__value monospace">{{ formatDateTime(clientViewData.alternateDipTimestamp) }}</div>
</div> </div>
} }
@if (clientViewData.alternateDipLookupLogId) {
<div class="pd-field">
<div class="pd-field__label">Alternate DIP Log ID</div>
<div class="pd-field__value monospace">{{ clientViewData.alternateDipLookupLogId }}</div>
</div>
}
} }
<!-- Sanctions / Name Screening --> <!-- Sanctions / Name Screening -->
<div class="pd-field"> <div class="pd-field">
<div class="pd-field__label">Name Screening (Sanctions)</div> <div class="pd-field__label">Name Screening (Sanctions)</div>
<div class="pd-field__value"> <div class="pd-field__value">
@if (clientViewData.nameVerified === true || clientViewData.nameVerified === 'true' || clientViewData.nameVerified === 1) { @if (
clientViewData.nameVerified === true ||
clientViewData.nameVerified === 'true' ||
clientViewData.nameVerified === 1
) {
<span class="pd-verify-badge pd-verify-badge--verified"><i class="fa fa-check-circle"></i> Clear</span> <span class="pd-verify-badge pd-verify-badge--verified"><i class="fa fa-check-circle"></i> Clear</span>
} @else if (clientViewData.nameVerified === false || clientViewData.nameVerified === 'false' || clientViewData.nameVerified === 0) { } @else if (
<span class="pd-verify-badge pd-verify-badge--danger"><i class="fa fa-exclamation-triangle"></i> Match Found</span> clientViewData.nameVerified === false ||
clientViewData.nameVerified === 'false' ||
clientViewData.nameVerified === 0
) {
<span class="pd-verify-badge pd-verify-badge--danger"
><i class="fa fa-exclamation-triangle"></i> Match Found</span
>
} @else { } @else {
<span class="pd-verify-badge pd-verify-badge--pending"><i class="fa fa-clock-o"></i> Pending</span> <span class="pd-verify-badge pd-verify-badge--pending"><i class="fa fa-clock-o"></i> Pending</span>
} }
</div> </div>
</div> </div>
@if (clientViewData.screeningLogId) {
<div class="pd-field">
<div class="pd-field__label">Screening Log ID</div>
<div class="pd-field__value monospace">{{ clientViewData.screeningLogId }}</div>
</div>
}
</div> </div>
</div> </div>
</div> </div>
<!-- Financial & Classification (Individual only) --> <!-- Financial & Classification (Individual only) -->
@if (clientViewData?.legalForm?.id !== 2) { @if (clientViewData?.legalForm?.id !== 2) {
<div class="pd-section"> <div class="pd-section">
<div class="pd-section__header"> <div class="pd-section__header">
<i class="fa fa-chart-line"></i> <i class="fa fa-chart-line"></i>
<h3>Financial &amp; Classification</h3> <h3>Financial &amp; Classification</h3>
</div> </div>
<div class="pd-section__body"> <div class="pd-section__body">
<div class="pd-grid"> <div class="pd-grid">
<div class="pd-field"> <div class="pd-field">
<div class="pd-field__label">Monthly Income</div> <div class="pd-field__label">Monthly Income</div>
<div class="pd-field__value"> <div class="pd-field__value">
@if (clientViewData.monthlyIncome != null && clientViewData.monthlyIncome !== '') { @if (clientViewData.monthlyIncome != null && clientViewData.monthlyIncome !== '') {
<span class="monospace">&#8377; {{ clientViewData.monthlyIncome | number }}</span> <span class="monospace">&#8377; {{ clientViewData.monthlyIncome | number }}</span>
} @else { } @else {
- -
} }
</div>
</div> </div>
<div class="pd-field">
<div class="pd-field__label">Annual Turnover (Last FY)</div>
<div class="pd-field__value">
@if (clientViewData.annualTurnover != null && clientViewData.annualTurnover !== '') {
<span class="monospace">&#8377; {{ clientViewData.annualTurnover | number }}</span>
} @else {
-
}
</div>
</div>
@if (clientViewData.clientType) {
<div class="pd-field">
<div class="pd-field__label">{{ 'labels.inputs.Client Type' | translate }}</div>
<div class="pd-field__value">{{ clientViewData.clientType.name }}</div>
</div>
}
@if (clientViewData.clientClassification) {
<div class="pd-field">
<div class="pd-field__label">{{ 'labels.inputs.Client Classification' | translate }}</div>
<div class="pd-field__value">{{ clientViewData.clientClassification.name }}</div>
</div>
}
@if (clientViewData.occupationCategory) {
<div class="pd-field">
<div class="pd-field__label">{{ 'labels.inputs.Occupation Category' | translate }}</div>
<div class="pd-field__value">{{ clientViewData.occupationCategory.name }}</div>
</div>
}
@if (clientViewData.occupation) {
<div class="pd-field">
<div class="pd-field__label">{{ 'labels.inputs.Occupation' | translate }}</div>
<div class="pd-field__value">{{ clientViewData.occupation.name }}</div>
</div>
}
</div> </div>
<div class="pd-field">
<div class="pd-field__label">Annual Turnover (Last FY)</div>
<div class="pd-field__value">
@if (clientViewData.annualTurnover != null && clientViewData.annualTurnover !== '') {
<span class="monospace">&#8377; {{ clientViewData.annualTurnover | number }}</span>
} @else {
-
}
</div>
</div>
@if (clientViewData.clientType) {
<div class="pd-field">
<div class="pd-field__label">{{ 'labels.inputs.Client Type' | translate }}</div>
<div class="pd-field__value">{{ clientViewData.clientType.name }}</div>
</div>
}
@if (clientViewData.clientClassification) {
<div class="pd-field">
<div class="pd-field__label">{{ 'labels.inputs.Client Classification' | translate }}</div>
<div class="pd-field__value">{{ clientViewData.clientClassification.name }}</div>
</div>
}
@if (clientViewData.occupationCategory) {
<div class="pd-field">
<div class="pd-field__label">{{ 'labels.inputs.Occupation Category' | translate }}</div>
<div class="pd-field__value">{{ clientViewData.occupationCategory.name }}</div>
</div>
}
@if (clientViewData.occupation) {
<div class="pd-field">
<div class="pd-field__label">{{ 'labels.inputs.Occupation' | translate }}</div>
<div class="pd-field__value">{{ clientViewData.occupation.name }}</div>
</div>
}
</div> </div>
</div> </div>
</div>
} }
<!-- Guardian Details (Minor) --> <!-- Guardian Details (Minor) -->
@@ -436,19 +472,19 @@
@if (clientViewData.timeline?.submittedOnDate) { @if (clientViewData.timeline?.submittedOnDate) {
<div class="pd-field"> <div class="pd-field">
<div class="pd-field__label">{{ 'labels.inputs.Submitted On' | translate }}</div> <div class="pd-field__label">{{ 'labels.inputs.Submitted On' | translate }}</div>
<div class="pd-field__value">{{ clientViewData.timeline.submittedOnDate | dateFormat:'DD-MM-YYYY' }}</div> <div class="pd-field__value">{{ clientViewData.timeline.submittedOnDate | dateFormat: 'DD-MM-YYYY' }}</div>
</div> </div>
} }
@if (clientViewData.activationDate) { @if (clientViewData.activationDate) {
<div class="pd-field"> <div class="pd-field">
<div class="pd-field__label">{{ 'labels.inputs.Activation Date' | translate }}</div> <div class="pd-field__label">{{ 'labels.inputs.Activation Date' | translate }}</div>
<div class="pd-field__value">{{ clientViewData.activationDate | dateFormat:'DD-MM-YYYY' }}</div> <div class="pd-field__value">{{ clientViewData.activationDate | dateFormat: 'DD-MM-YYYY' }}</div>
</div> </div>
} }
@if (clientViewData.timeline?.closedOnDate) { @if (clientViewData.timeline?.closedOnDate) {
<div class="pd-field"> <div class="pd-field">
<div class="pd-field__label">{{ 'labels.inputs.Closed Date' | translate }}</div> <div class="pd-field__label">{{ 'labels.inputs.Closed Date' | translate }}</div>
<div class="pd-field__value">{{ clientViewData.timeline.closedOnDate | dateFormat:'DD-MM-YYYY' }}</div> <div class="pd-field__value">{{ clientViewData.timeline.closedOnDate | dateFormat: 'DD-MM-YYYY' }}</div>
</div> </div>
} }
</div> </div>
@@ -467,10 +503,14 @@
<div class="pd-field"> <div class="pd-field">
<div class="pd-field__label">{{ 'labels.inputs.Client Status' | translate }}</div> <div class="pd-field__label">{{ 'labels.inputs.Client Status' | translate }}</div>
<div class="pd-field__value"> <div class="pd-field__value">
<span class="pd-field__value status-badge" <span
class="pd-field__value status-badge"
[class.status-active]="clientViewData.status.value === 'Active'" [class.status-active]="clientViewData.status.value === 'Active'"
[class.status-closed]="clientViewData.status.value === 'Closed'" [class.status-closed]="clientViewData.status.value === 'Closed'"
[class.status-pending]="clientViewData.status.value !== 'Active' && clientViewData.status.value !== 'Closed'"> [class.status-pending]="
clientViewData.status.value !== 'Active' && clientViewData.status.value !== 'Closed'
"
>
{{ 'labels.status.' + clientViewData.status.value | translate }} {{ 'labels.status.' + clientViewData.status.value | translate }}
</span> </span>
</div> </div>
@@ -485,5 +525,4 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
+8 -21
View File
@@ -74,7 +74,6 @@
<mat-option value="pending">CKYC Pending</mat-option> <mat-option value="pending">CKYC Pending</mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>
@@ -89,7 +88,9 @@
<ng-container matColumnDef="displayName"> <ng-container matColumnDef="displayName">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.name' | translate }}</th> <th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.name' | translate }}</th>
<td mat-cell *matCellDef="let row" class="cell-link"> <td mat-cell *matCellDef="let row" class="cell-link">
<mat-icon class="row-legal-icon" [ngClass]="row.legalForm?.id === 2 ? 'entity' : 'person'">{{ row.legalForm?.id === 2 ? 'business' : 'person' }}</mat-icon> <mat-icon class="row-legal-icon" [ngClass]="row.legalForm?.id === 2 ? 'entity' : 'person'">{{
row.legalForm?.id === 2 ? 'business' : 'person'
}}</mat-icon>
{{ hideClientData ? maskName(row.displayName) : row.displayName }} {{ hideClientData ? maskName(row.displayName) : row.displayName }}
@if (row._ckycDone) { @if (row._ckycDone) {
<mat-icon class="row-ckyc-icon" matTooltip="CKYC Done">verified</mat-icon> <mat-icon class="row-ckyc-icon" matTooltip="CKYC Done">verified</mat-icon>
@@ -111,18 +112,6 @@
<td mat-cell *matCellDef="let row" class="cell-mono">{{ row.cifNumber }}</td> <td mat-cell *matCellDef="let row" class="cell-mono">{{ row.cifNumber }}</td>
</ng-container> </ng-container>
<!-- CKYC Number Column -->
<ng-container matColumnDef="ckycNumber">
<th mat-header-cell *matHeaderCellDef>CKYC Number</th>
<td mat-cell *matCellDef="let row" class="cell-mono">
@if (row._ckycNumber) {
{{ row._ckycNumber }}
} @else {
<span class="ckyc-pending">—</span>
}
</td>
</ng-container>
<!-- Legal Form Column --> <!-- Legal Form Column -->
<ng-container matColumnDef="legalForm"> <ng-container matColumnDef="legalForm">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Legal Form' | translate }}</th> <th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Legal Form' | translate }}</th>
@@ -149,7 +138,11 @@
<td mat-cell *matCellDef="let row"> <td mat-cell *matCellDef="let row">
<div class="profile-steps"> <div class="profile-steps">
@for (step of getProfileSteps(row); track step.label) { @for (step of getProfileSteps(row); track step.label) {
<span class="step-dot" [class.done]="step.done" [matTooltip]="step.label + (step.done ? ' ✓' : ' — Pending')"> <span
class="step-dot"
[class.done]="step.done"
[matTooltip]="step.label + (step.done ? ' ✓' : ' — Pending')"
>
{{ step.label.charAt(0) }} {{ step.label.charAt(0) }}
</span> </span>
} }
@@ -157,12 +150,6 @@
</td> </td>
</ng-container> </ng-container>
<!-- Office Column -->
<ng-container matColumnDef="officeName">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Office Name' | translate }}</th>
<td mat-cell *matCellDef="let row">{{ row.officeName }}</td>
</ng-container>
<!-- Actions Column --> <!-- Actions Column -->
<ng-container matColumnDef="actions"> <ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Actions' | translate }}</th> <th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Actions' | translate }}</th>
+30 -29
View File
@@ -90,11 +90,9 @@ export class ClientsComponent implements OnInit {
'displayName', 'displayName',
'accountNumber', 'accountNumber',
'cifNumber', 'cifNumber',
'ckycNumber',
'legalForm', 'legalForm',
'status', 'status',
'profileCompletion', 'profileCompletion',
'officeName',
'actions' 'actions'
]; ];
dataSource: MatTableDataSource<any> = new MatTableDataSource(); dataSource: MatTableDataSource<any> = new MatTableDataSource();
@@ -167,34 +165,37 @@ export class ClientsComponent implements OnInit {
// Use individual subscriptions instead of forkJoin to avoid EMPTY observable issues // Use individual subscriptions instead of forkJoin to avoid EMPTY observable issues
clients.forEach((client, i) => { clients.forEach((client, i) => {
this.clientService.getClientData(client.id?.toString()).pipe( this.clientService
catchError(() => of(null)) .getClientData(client.id?.toString())
).subscribe((data: any) => { .pipe(catchError(() => of(null)))
if (data?.legalForm) { .subscribe((data: any) => {
clients[i].legalForm = data.legalForm; if (data?.legalForm) {
this.applyClientFilters(); clients[i].legalForm = data.legalForm;
} this.applyClientFilters();
}); }
});
this.clientService.getClientKyc(client.id?.toString()).pipe( this.clientService
catchError(() => of(null)) .getClientKyc(client.id?.toString())
).subscribe((kyc: any) => { .pipe(catchError(() => of(null)))
if (kyc) { .subscribe((kyc: any) => {
clients[i]._kycDone = kyc.kycStatus === 300; if (kyc) {
clients[i]._ckycDone = !!kyc.ckycNumber; clients[i]._kycDone = kyc.kycStatus === 300;
clients[i]._ckycNumber = kyc.ckycNumber || ''; clients[i]._ckycDone = !!kyc.ckycNumber;
this.applyClientFilters(); clients[i]._ckycNumber = kyc.ckycNumber || '';
} this.applyClientFilters();
}); }
});
this.clientService.getClientAddressData(client.id?.toString()).pipe( this.clientService
catchError(() => of(null)) .getClientAddressData(client.id?.toString())
).subscribe((addr: any) => { .pipe(catchError(() => of(null)))
if (addr && Array.isArray(addr) && addr.length > 0) { .subscribe((addr: any) => {
clients[i]._addressDone = true; if (addr && Array.isArray(addr) && addr.length > 0) {
this.applyClientFilters(); clients[i]._addressDone = true;
} this.applyClientFilters();
}); }
});
}); });
} }
@@ -255,7 +256,7 @@ export class ClientsComponent implements OnInit {
} }
getCompletedCount(row: any): number { getCompletedCount(row: any): number {
return this.getProfileSteps(row).filter(s => s.done).length; return this.getProfileSteps(row).filter((s) => s.done).length;
} }
private resetPaginator() { private resetPaginator() {
@@ -12,7 +12,12 @@
<h2>{{ 'labels.heading.Create Client' | translate }}</h2> <h2>{{ 'labels.heading.Create Client' | translate }}</h2>
</div> </div>
<mat-tab-group class="client-tabs" animationDuration="200ms" [preserveContent]="true" [(selectedIndex)]="selectedTabIndex"> <mat-tab-group
class="client-tabs"
animationDuration="200ms"
[preserveContent]="true"
[(selectedIndex)]="selectedTabIndex"
>
<!-- Tab 1: General Details --> <!-- Tab 1: General Details -->
<mat-tab> <mat-tab>
<ng-template mat-tab-label> <ng-template mat-tab-label>
@@ -62,7 +67,8 @@
</div> </div>
} }
<div class="tab-section"> <div class="tab-section">
<mifosx-client-kyc-step #clientKyc <mifosx-client-kyc-step
#clientKyc
[isNonIndividual]="legalFormType === 2" [isNonIndividual]="legalFormType === 2"
[clientId]="createdClientId" [clientId]="createdClientId"
[constitutionMeta]="clientGeneralStep?.selectedConstitution" [constitutionMeta]="clientGeneralStep?.selectedConstitution"
@@ -127,13 +133,18 @@
<mat-icon>edit</mat-icon> Edit <mat-icon>edit</mat-icon> Edit
</button> </button>
} @else { } @else {
<button mat-stroked-button class="tab-mode-btn tab-mode-btn--cancel" (click)="cancelTabEdit(addressTabIdx)"> <button
mat-stroked-button
class="tab-mode-btn tab-mode-btn--cancel"
(click)="cancelTabEdit(addressTabIdx)"
>
<mat-icon>close</mat-icon> Cancel <mat-icon>close</mat-icon> Cancel
</button> </button>
} }
</div> </div>
} }
<mifosx-client-india-address-step #clientIndiaAddress <mifosx-client-india-address-step
#clientIndiaAddress
[readOnly]="isTabReadOnly(addressTabIdx)" [readOnly]="isTabReadOnly(addressTabIdx)"
></mifosx-client-india-address-step> ></mifosx-client-india-address-step>
</div> </div>
@@ -155,13 +166,18 @@
<mat-icon>edit</mat-icon> Edit <mat-icon>edit</mat-icon> Edit
</button> </button>
} @else { } @else {
<button mat-stroked-button class="tab-mode-btn tab-mode-btn--cancel" (click)="cancelTabEdit(familyTabIdx)"> <button
mat-stroked-button
class="tab-mode-btn tab-mode-btn--cancel"
(click)="cancelTabEdit(familyTabIdx)"
>
<mat-icon>close</mat-icon> Cancel <mat-icon>close</mat-icon> Cancel
</button> </button>
} }
</div> </div>
} }
<mifosx-client-family-members-step #clientFamilyMembers <mifosx-client-family-members-step
#clientFamilyMembers
[clientTemplate]="clientTemplate" [clientTemplate]="clientTemplate"
[clientId]="createdClientId" [clientId]="createdClientId"
[maritalStatusId]="clientGeneralStep?.createClientForm?.get('maritalStatusId')?.value" [maritalStatusId]="clientGeneralStep?.createClientForm?.get('maritalStatusId')?.value"
@@ -191,6 +207,10 @@
[familyMembers]="clientFamilyMembersStep?.clientFamilyMembers || []" [familyMembers]="clientFamilyMembersStep?.clientFamilyMembers || []"
[maritalStatusId]="clientGeneralStep?.createClientForm?.get('maritalStatusId')?.value" [maritalStatusId]="clientGeneralStep?.createClientForm?.get('maritalStatusId')?.value"
[maritalStatusOptions]="clientGeneralStep?.maritalStatusOptions || []" [maritalStatusOptions]="clientGeneralStep?.maritalStatusOptions || []"
[educationLevelOptions]="clientGeneralStep?.educationLevelOptions || []"
[degreeQualificationOptions]="clientGeneralStep?.degreeQualificationOptions || []"
[religionOptions]="clientGeneralStep?.religionOptions || []"
[socialCategoryOptions]="clientGeneralStep?.socialCategoryOptions || []"
(submitEvent)="submit()" (submitEvent)="submit()"
></mifosx-client-preview-step> ></mifosx-client-preview-step>
</div> </div>
@@ -220,9 +240,19 @@
} }
@if (selectedTabIndex < totalTabs - 1) { @if (selectedTabIndex < totalTabs - 1) {
<div class="save-next-wrapper"> <div class="save-next-wrapper">
<button mat-raised-button color="primary" class="save-next-btn" (click)="onNext()" [disabled]="savingClient || (selectedTabIndex === 0 && clientGeneralStep?.sanctionsBlocked)"> <button
mat-raised-button
color="primary"
class="save-next-btn"
(click)="onNext()"
[disabled]="savingClient || (selectedTabIndex === 0 && clientGeneralStep?.sanctionsBlocked)"
>
@if (savingClient) { @if (savingClient) {
<mat-progress-spinner diameter="18" mode="indeterminate" class="m-r-10 save-spinner"></mat-progress-spinner> <mat-progress-spinner
diameter="18"
mode="indeterminate"
class="m-r-10 save-spinner"
></mat-progress-spinner>
Saving... Saving...
} @else { } @else {
<mat-icon class="m-r-6">save</mat-icon> <mat-icon class="m-r-6">save</mat-icon>
@@ -198,6 +198,26 @@ export class CreateClientComponent {
return; return;
} }
// Sanctions verification gate — Verify Client must be clicked before save
if (!this.clientGeneralStep?.sanctionsVerified) {
this.dialog
.open(VerifyClientAlertDialogComponent, {
width: '420px',
autoFocus: false
})
.afterClosed()
.subscribe(() => {
setTimeout(() => {
const verifyBtn = document.querySelector('.verify-name-btn') as HTMLElement;
if (verifyBtn) {
verifyBtn.scrollIntoView({ behavior: 'smooth', block: 'center' });
verifyBtn.focus();
}
}, 100);
});
return;
}
// Show confirmation dialog // Show confirmation dialog
const dialogRef = this.dialog.open(SaveConfirmDialogComponent, { const dialogRef = this.dialog.open(SaveConfirmDialogComponent, {
width: '440px', width: '440px',
@@ -236,10 +256,14 @@ export class CreateClientComponent {
} }
// Upload photo & signature // Upload photo & signature
if (this.clientKycStep?.photographFile) { if (this.clientKycStep?.photographFile) {
this.clientsService.uploadClientProfileImage(clientId, this.clientKycStep.photographFile).subscribe({ error: () => {} }); this.clientsService
.uploadClientProfileImage(clientId, this.clientKycStep.photographFile)
.subscribe({ error: () => {} });
} }
if (this.clientKycStep?.signatureFile) { if (this.clientKycStep?.signatureFile) {
this.clientsService.uploadClientSignatureImage(clientId, this.clientKycStep.signatureFile).subscribe({ error: () => {} }); this.clientsService
.uploadClientSignatureImage(clientId, this.clientKycStep.signatureFile)
.subscribe({ error: () => {} });
} }
return; return;
} }
@@ -368,16 +392,27 @@ export class CreateClientComponent {
* Finalize: upload photo/signature, save KYC, save addresses, then navigate. * Finalize: upload photo/signature, save KYC, save addresses, then navigate.
*/ */
private finalizeClient(clientId: string) { private finalizeClient(clientId: string) {
const navigateToClient = () => this.router.navigate(['../', clientId], { relativeTo: this.route }); const navigateToClient = () =>
this.router.navigate(
[
'../',
clientId
],
{ relativeTo: this.route }
);
// Upload photo if selected // Upload photo if selected
if (this.clientKycStep?.photographFile) { if (this.clientKycStep?.photographFile) {
this.clientsService.uploadClientProfileImage(clientId, this.clientKycStep.photographFile).subscribe({ error: () => {} }); this.clientsService
.uploadClientProfileImage(clientId, this.clientKycStep.photographFile)
.subscribe({ error: () => {} });
} }
// Upload signature if selected // Upload signature if selected
if (this.clientKycStep?.signatureFile) { if (this.clientKycStep?.signatureFile) {
this.clientsService.uploadClientSignatureImage(clientId, this.clientKycStep.signatureFile).subscribe({ error: () => {} }); this.clientsService
.uploadClientSignatureImage(clientId, this.clientKycStep.signatureFile)
.subscribe({ error: () => {} });
} }
// Save addresses from India address step // Save addresses from India address step
@@ -409,16 +444,36 @@ export class CreateClientComponent {
const addressData = this.clientIndiaAddressStep.addressData; const addressData = this.clientIndiaAddressStep.addressData;
// Map tab names to Fineract address type names (as defined in address template) // Map tab names to Fineract address type names (as defined in address template)
const typeNameMap: Record<string, string[]> = { const typeNameMap: Record<string, string[]> = {
permanent: ['Permanent', 'Permanent Address'], permanent: [
current: ['Residential', 'Current', 'Current / Residential', 'Communication'], 'Permanent',
office: ['Office', 'Office / Work', 'Business', 'Work'], 'Permanent Address'
correspondence: ['Correspondence', 'Mailing', 'Postal'] ],
current: [
'Residential',
'Current',
'Current / Residential',
'Communication'
],
office: [
'Office',
'Office / Work',
'Business',
'Work'
],
correspondence: [
'Correspondence',
'Mailing',
'Postal'
]
}; };
this.clientsService.getClientAddressTemplate().subscribe({ this.clientsService.getClientAddressTemplate().subscribe({
next: (template: any) => { next: (template: any) => {
const typeOptions = template?.addressTypeIdOptions || []; const typeOptions = template?.addressTypeIdOptions || [];
for (const [tab, addr] of Object.entries(addressData)) { for (const [
tab,
addr
] of Object.entries(addressData)) {
if (!addr || !(addr as any).addressLine1) continue; if (!addr || !(addr as any).addressLine1) continue;
// Find matching address type ID // Find matching address type ID
@@ -448,8 +503,8 @@ export class CreateClientComponent {
// Try to resolve state to stateProvinceId // Try to resolve state to stateProvinceId
if ((addr as any).state && template?.stateProvinceIdOptions?.length) { if ((addr as any).state && template?.stateProvinceIdOptions?.length) {
const stateMatch = template.stateProvinceIdOptions.find((s: any) => const stateMatch = template.stateProvinceIdOptions.find(
s.name?.toLowerCase() === (addr as any).state?.toLowerCase() (s: any) => s.name?.toLowerCase() === (addr as any).state?.toLowerCase()
); );
if (stateMatch) payload.stateProvinceId = stateMatch.id; if (stateMatch) payload.stateProvinceId = stateMatch.id;
} }
@@ -535,34 +590,228 @@ export class CreateClientComponent {
</div> </div>
</div> </div>
`, `,
styles: [` styles: [
.scd { background: #fff; border-radius: 12px; overflow: hidden; } `
.scd-header { .scd {
display: flex; align-items: center; gap: 14px; background: #fff;
padding: 20px 24px 16px; border-radius: 12px;
} overflow: hidden;
.scd-header__icon-wrap { }
width: 44px; height: 44px; border-radius: 12px; .scd-header {
background: #eff6ff; display: flex; align-items: center; justify-content: center; display: flex;
} align-items: center;
.scd-header__icon { color: #1074b9; font-size: 24px; width: 24px; height: 24px; } gap: 14px;
.scd-header__title { margin: 0; font-size: 17px; font-weight: 700; color: #0f172a; } padding: 20px 24px 16px;
.scd-header__sub { margin: 2px 0 0; font-size: 12px; color: #64748b; } }
.scd-body { padding: 0 24px 20px; } .scd-header__icon-wrap {
.scd-body p { margin: 0 0 14px; font-size: 14px; color: #374151; line-height: 1.5; } width: 44px;
.scd-info { height: 44px;
display: flex; align-items: flex-start; gap: 8px; border-radius: 12px;
padding: 10px 14px; background: #f0f9ff; border: 1px solid #bae6fd; background: #eff6ff;
border-radius: 8px; font-size: 12px; color: #0c4a6e; display: flex;
} align-items: center;
.scd-info__icon { font-size: 16px !important; width: 16px !important; height: 16px !important; color: #0284c7; flex-shrink: 0; margin-top: 1px; } justify-content: center;
.scd-actions { }
display: flex; justify-content: flex-end; gap: 10px; .scd-header__icon {
padding: 14px 24px; border-top: 1px solid #f1f5f9; background: #f8fafc; color: #1074b9;
} font-size: 24px;
.scd-cancel, .scd-confirm { display: flex !important; align-items: center; gap: 6px; font-size: 13px !important; height: 38px !important; border-radius: 8px !important; } width: 24px;
.scd-confirm { font-weight: 600 !important; } height: 24px;
`], }
imports: [MatIcon, MatDialogClose] .scd-header__title {
margin: 0;
font-size: 17px;
font-weight: 700;
color: #0f172a;
}
.scd-header__sub {
margin: 2px 0 0;
font-size: 12px;
color: #64748b;
}
.scd-body {
padding: 0 24px 20px;
}
.scd-body p {
margin: 0 0 14px;
font-size: 14px;
color: #374151;
line-height: 1.5;
}
.scd-info {
display: flex;
align-items: flex-start;
gap: 8px;
padding: 10px 14px;
background: #f0f9ff;
border: 1px solid #bae6fd;
border-radius: 8px;
font-size: 12px;
color: #0c4a6e;
}
.scd-info__icon {
font-size: 16px !important;
width: 16px !important;
height: 16px !important;
color: #0284c7;
flex-shrink: 0;
margin-top: 1px;
}
.scd-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
padding: 14px 24px;
border-top: 1px solid #f1f5f9;
background: #f8fafc;
}
.scd-cancel,
.scd-confirm {
display: flex !important;
align-items: center;
gap: 6px;
font-size: 13px !important;
height: 38px !important;
border-radius: 8px !important;
}
.scd-confirm {
font-weight: 600 !important;
}
`
],
imports: [
MatIcon,
MatDialogClose
]
}) })
export class SaveConfirmDialogComponent {} export class SaveConfirmDialogComponent {}
/**
* Verify Client Required Alert Dialog
*/
@Component({
selector: 'mifosx-verify-client-alert-dialog',
template: `
<div class="vcad">
<div class="vcad-header">
<div class="vcad-header__icon-wrap">
<mat-icon class="vcad-header__icon">shield</mat-icon>
</div>
<div>
<h3 class="vcad-header__title">Verification Required</h3>
<p class="vcad-header__sub">Client name must be screened before saving</p>
</div>
</div>
<div class="vcad-body">
<p>
Please click <strong>Verify Client</strong> to screen the client name against the UN Security Council
Consolidated Sanctions List before proceeding.
</p>
<div class="vcad-info">
<mat-icon class="vcad-info__icon">gavel</mat-icon>
<span
>RBI KYC Master Direction (2016) — Section 51 &amp; PMLA 2002. Sanctions screening is mandatory before
account opening.</span
>
</div>
</div>
<div class="vcad-actions">
<button mat-flat-button color="primary" [mat-dialog-close]="true" class="vcad-ok">
<mat-icon>check</mat-icon>
OK, Verify Now
</button>
</div>
</div>
`,
styles: [
`
.vcad {
background: #fff;
border-radius: 12px;
overflow: hidden;
}
.vcad-header {
display: flex;
align-items: center;
gap: 14px;
padding: 20px 24px 16px;
}
.vcad-header__icon-wrap {
width: 44px;
height: 44px;
border-radius: 12px;
background: #fef3c7;
display: flex;
align-items: center;
justify-content: center;
}
.vcad-header__icon {
color: #d97706;
font-size: 24px;
width: 24px;
height: 24px;
}
.vcad-header__title {
margin: 0;
font-size: 17px;
font-weight: 700;
color: #0f172a;
}
.vcad-header__sub {
margin: 2px 0 0;
font-size: 12px;
color: #64748b;
}
.vcad-body {
padding: 0 24px 20px;
}
.vcad-body p {
margin: 0 0 14px;
font-size: 14px;
color: #374151;
line-height: 1.5;
}
.vcad-info {
display: flex;
align-items: flex-start;
gap: 8px;
padding: 10px 14px;
background: #fff7ed;
border: 1px solid #fed7aa;
border-radius: 8px;
font-size: 12px;
color: #7c2d12;
}
.vcad-info__icon {
font-size: 16px !important;
width: 16px !important;
height: 16px !important;
color: #d97706;
flex-shrink: 0;
margin-top: 1px;
}
.vcad-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
padding: 14px 24px;
border-top: 1px solid #f1f5f9;
background: #f8fafc;
}
.vcad-ok {
display: flex !important;
align-items: center;
gap: 6px;
font-size: 13px !important;
height: 38px !important;
border-radius: 8px !important;
font-weight: 600 !important;
}
`
],
imports: [
MatIcon,
MatDialogClose
]
})
export class VerifyClientAlertDialogComponent {}
@@ -180,6 +180,12 @@
[addressData]="clientIndiaAddressStep?.addressData" [addressData]="clientIndiaAddressStep?.addressData"
[guardian]="clientGeneralStep?.selectedGuardian" [guardian]="clientGeneralStep?.selectedGuardian"
[guardianRelation]="clientGeneralStep?.createClientForm?.get('guardianDetails.guardianRelation')?.value || ''" [guardianRelation]="clientGeneralStep?.createClientForm?.get('guardianDetails.guardianRelation')?.value || ''"
[maritalStatusId]="clientGeneralStep?.createClientForm?.get('maritalStatusId')?.value"
[maritalStatusOptions]="clientGeneralStep?.maritalStatusOptions || []"
[educationLevelOptions]="clientGeneralStep?.educationLevelOptions || []"
[degreeQualificationOptions]="clientGeneralStep?.degreeQualificationOptions || []"
[religionOptions]="clientGeneralStep?.religionOptions || []"
[socialCategoryOptions]="clientGeneralStep?.socialCategoryOptions || []"
(submitEvent)="submit()" (submitEvent)="submit()"
></mifosx-client-preview-step> ></mifosx-client-preview-step>
</div> </div>
@@ -77,15 +77,24 @@
<div class="km-filters"> <div class="km-filters">
<select class="km-fsel"> <select class="km-fsel">
<option>All Risk</option><option>Low</option><option>Mid</option><option>High</option> <option>All Risk</option>
<option>Low</option>
<option>Mid</option>
<option>High</option>
</select> </select>
<select class="km-fsel"> <select class="km-fsel">
<option>All States</option><option>Alert Window</option><option>Debit Freeze</option> <option>All States</option>
<option>Inoperative</option><option>Dormant</option> <option>Alert Window</option>
<option>Debit Freeze</option>
<option>Inoperative</option>
<option>Dormant</option>
</select> </select>
<select class="km-fsel"> <select class="km-fsel">
<option>All Notices</option><option>Notice 1 Pending</option><option>Notice 2 Pending</option> <option>All Notices</option>
<option>Notice 3 Pending</option><option>All 3 Sent</option> <option>Notice 1 Pending</option>
<option>Notice 2 Pending</option>
<option>Notice 3 Pending</option>
<option>All 3 Sent</option>
</select> </select>
<input class="km-finp" type="text" placeholder="Search name or account..." /> <input class="km-finp" type="text" placeholder="Search name or account..." />
<button class="km-btn km-btn-t" (click)="toast('Filters applied')">Apply</button> <button class="km-btn km-btn-t" (click)="toast('Filters applied')">Apply</button>
@@ -100,8 +109,15 @@
<table class="km-tbl"> <table class="km-tbl">
<thead> <thead>
<tr> <tr>
<th>Customer</th><th>Risk</th><th>Expiry Date</th><th>Days Left</th> <th>Customer</th>
<th>Notices</th><th>State</th><th>Pending Action</th><th>Next Stage</th><th></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> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -111,7 +127,9 @@
<div class="km-an">{{ row.name }}</div> <div class="km-an">{{ row.name }}</div>
<div class="km-ai">{{ row.account }}</div> <div class="km-ai">{{ row.account }}</div>
</td> </td>
<td><span class="km-b" [ngClass]="riskClass(row.risk)">{{ row.riskLabel.split(' ')[0] }}</span></td> <td>
<span class="km-b" [ngClass]="riskClass(row.risk)">{{ row.riskLabel.split(' ')[0] }}</span>
</td>
<td>{{ row.expiry }}</td> <td>{{ row.expiry }}</td>
<td> <td>
<div class="km-pbar"> <div class="km-pbar">
@@ -125,10 +143,18 @@
</div> </div>
</div> </div>
</td> </td>
<td><span class="km-b km-b-bl">{{ row.noticesSent }}/3</span></td> <td>
<td><span class="km-sp" [ngClass]="stateClass(row.state)">{{ stateLabel(row.state) }}</span></td> <span class="km-b km-b-bl">{{ row.noticesSent }}/3</span>
<td><span class="km-b km-b-am">{{ row.noticePending }}</span></td> </td>
<td><span class="km-b km-b-or">{{ row.nextStage }}</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> <td>
<button class="km-btn km-btn-ot" (click)="$event.stopPropagation(); openModal(row.id)">View</button> <button class="km-btn km-btn-ot" (click)="$event.stopPropagation(); openModal(row.id)">View</button>
</td> </td>
@@ -165,7 +191,10 @@
<div class="km-cb"> <div class="km-cb">
<div class="km-ntl"> <div class="km-ntl">
<div class="km-nt"> <div class="km-nt">
<div class="km-nd-col"><div class="km-nd km-nd1">1</div><div class="km-nl"></div></div> <div class="km-nd-col">
<div class="km-nd km-nd1">1</div>
<div class="km-nl"></div>
</div>
<div> <div>
<div class="km-nh">Notice 1 — Alert window opens</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-ns">31 March 2027 · Auto-scheduled · <span class="km-ok">✓ Delivered</span></div>
@@ -176,7 +205,10 @@
</div> </div>
</div> </div>
<div class="km-nt"> <div class="km-nt">
<div class="km-nd-col"><div class="km-nd km-nd2">2</div><div class="km-nl"></div></div> <div class="km-nd-col">
<div class="km-nd km-nd2">2</div>
<div class="km-nl"></div>
</div>
<div> <div>
<div class="km-nh">Notice 2 — Follow-up</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-ns">10 April 2027 · +10 days · <span class="km-pending">⏳ Pending</span></div>
@@ -189,7 +221,10 @@
</div> </div>
</div> </div>
<div class="km-nt"> <div class="km-nt">
<div class="km-nd-col"><div class="km-nd km-nd3">3</div><div class="km-nl"></div></div> <div class="km-nd-col">
<div class="km-nd km-nd3">3</div>
<div class="km-nl"></div>
</div>
<div> <div>
<div class="km-nh">Notice 3 — Final notice</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-ns">20 April 2027 · +20 days · <span class="km-fail">✗ Not dispatched</span></div>
@@ -198,16 +233,23 @@
<span class="km-cp km-cp-mail">Email</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-ivr">IVR</span>
<span class="km-cp km-cp-post">Reg. Post</span> <span class="km-cp km-cp-post">Reg. Post</span>
<span class="km-cp km-cp-vis">Branch Visit</span> <span class="km-cp km-cp-vis">Home Visit</span>
</div> </div>
<div class="km-row-actions"> <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-t" (click)="toast('Notice 3 scheduled for 20 Apr 2027')">
<button class="km-btn km-btn-ot" (click)="toast('Video KYC link added to Notice 3')">+ Video KYC Link</button> 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>
</div> </div>
<div class="km-nt"> <div class="km-nt">
<div class="km-nd-col"><div class="km-nd km-ndf">!</div><div class="km-nl"></div></div> <div class="km-nd-col">
<div class="km-nd km-ndf">!</div>
<div class="km-nl"></div>
</div>
<div> <div>
<div class="km-nh km-warn">Debit Freeze — if no action</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-ns">30 April 2027 · Auto-triggered on expiry</div>
@@ -219,7 +261,10 @@
</div> </div>
</div> </div>
<div class="km-nt"> <div class="km-nt">
<div class="km-nd-col"><div class="km-nd km-ndi">!</div><div class="km-nl"></div></div> <div class="km-nd-col">
<div class="km-nd km-ndi">!</div>
<div class="km-nl"></div>
</div>
<div> <div>
<div class="km-nh km-warn-o">Inoperative — +60 days</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-ns">30 June 2027 · No compliance</div>
@@ -270,7 +315,7 @@
</div> </div>
<div class="km-channel" (click)="toast('Branch visit toggled')"> <div class="km-channel" (click)="toast('Branch visit toggled')">
<div class="km-channel-i">🏦</div> <div class="km-channel-i">🏦</div>
<div class="km-channel-n">Branch Visit</div> <div class="km-channel-n">Home Visit</div>
<div class="km-channel-d">Manual</div> <div class="km-channel-d">Manual</div>
</div> </div>
<div class="km-channel" (click)="toast('Video KYC toggled')"> <div class="km-channel" (click)="toast('Video KYC toggled')">
@@ -287,17 +332,28 @@
<div class="km-ct">India Post Notice</div> <div class="km-ct">India Post Notice</div>
</div> </div>
<div class="km-cb"> <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-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-ml">Dispatch Date</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> <div class="km-mv">10 April 2027</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 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> </div>
<div class="km-card"> <div class="km-card">
<div class="km-ch"> <div class="km-ch">
<div class="km-ci km-ci-t"><fa-icon icon="building"></fa-icon></div> <div class="km-ci km-ci-t"><fa-icon icon="building"></fa-icon></div>
<div class="km-ct">Branch Visit</div> <div class="km-ct">Home Visit</div>
</div> </div>
<div class="km-cb"> <div class="km-cb">
<div class="km-ml">Scheduled Date</div> <div class="km-ml">Scheduled Date</div>
@@ -334,15 +390,26 @@
<div class="km-filters"> <div class="km-filters">
<select class="km-fsel"> <select class="km-fsel">
<option>All Channels</option><option>SMS</option><option>Email</option><option>IVR</option> <option>All Channels</option>
<option>Reg. Post</option><option>Branch Visit</option><option>Video KYC</option> <option>SMS</option>
<option>Email</option>
<option>IVR</option>
<option>Reg. Post</option>
<option>Home Visit</option>
<option>Video KYC</option>
</select> </select>
<select class="km-fsel"> <select class="km-fsel">
<option>All Statuses</option><option>Delivered</option><option>In Transit</option> <option>All Statuses</option>
<option>Pending</option><option>Failed</option> <option>Delivered</option>
<option>In Transit</option>
<option>Pending</option>
<option>Failed</option>
</select> </select>
<select class="km-fsel"> <select class="km-fsel">
<option>All Notice Nos.</option><option>Notice 1</option><option>Notice 2</option><option>Notice 3</option> <option>All Notice Nos.</option>
<option>Notice 1</option>
<option>Notice 2</option>
<option>Notice 3</option>
</select> </select>
<input class="km-finp" type="text" placeholder="Search account, name or reference..." /> <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> <button class="km-btn km-btn-t" (click)="toast('Filters applied')">Apply</button>
@@ -357,15 +424,24 @@
<table class="km-tbl"> <table class="km-tbl">
<thead> <thead>
<tr> <tr>
<th>#</th><th>Account / Customer</th><th>Notice</th><th>Channel</th> <th>#</th>
<th>Dispatched</th><th>Reference / Docket</th><th>Delivery</th> <th>Account / Customer</th>
<th>Client Action</th><th>Officer</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> </tr>
</thead> </thead>
<tbody> <tbody>
<tr (click)="openModal('rajesh')"> <tr (click)="openModal('rajesh')">
<td class="km-num">001</td> <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>
<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><span class="km-b km-b-bl">Notice 1</span></td>
<td>SMS + Email</td> <td>SMS + Email</td>
<td>31 Mar 2027</td> <td>31 Mar 2027</td>
@@ -376,7 +452,10 @@
</tr> </tr>
<tr (click)="openModal('rajesh')"> <tr (click)="openModal('rajesh')">
<td class="km-num">002</td> <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>
<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><span class="km-b km-b-am">Notice 2</span></td>
<td>Reg. Post + IVR</td> <td>Reg. Post + IVR</td>
<td>10 Apr 2027</td> <td>10 Apr 2027</td>
@@ -387,7 +466,10 @@
</tr> </tr>
<tr (click)="openModal('priya')"> <tr (click)="openModal('priya')">
<td class="km-num">003</td> <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>
<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><span class="km-b km-b-bl">Notice 1</span></td>
<td>SMS + Email</td> <td>SMS + Email</td>
<td>10 Mar 2027</td> <td>10 Mar 2027</td>
@@ -398,7 +480,10 @@
</tr> </tr>
<tr (click)="openModal('priya')"> <tr (click)="openModal('priya')">
<td class="km-num">004</td> <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>
<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><span class="km-b km-b-am">Notice 2</span></td>
<td>Reg. Post</td> <td>Reg. Post</td>
<td>20 Mar 2027</td> <td>20 Mar 2027</td>
@@ -409,9 +494,12 @@
</tr> </tr>
<tr (click)="openModal('priya')"> <tr (click)="openModal('priya')">
<td class="km-num">005</td> <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>
<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><span class="km-b km-b-or">Notice 3</span></td>
<td>Branch Visit</td> <td>Home Visit</td>
<td>30 Mar 2027</td> <td>30 Mar 2027</td>
<td><span class="km-ref">VIS-BPL-20270330-012</span></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-ok">✓ Done</span></td>
@@ -420,7 +508,10 @@
</tr> </tr>
<tr (click)="openModal('suresh')"> <tr (click)="openModal('suresh')">
<td class="km-num">006</td> <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>
<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><span class="km-b km-b-or">Notice 3</span></td>
<td>Video KYC</td> <td>Video KYC</td>
<td>5 Feb 2027</td> <td>5 Feb 2027</td>
@@ -454,19 +545,29 @@
<div class="km-sc-row"> <div class="km-sc-row">
<div class="km-sc km-sc-ac" (click)="toast('Filtering: Active')"> <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 class="km-sc-l">Active</div>
<div class="km-sc-n">1,847</div>
<div class="km-sc-d">KYC valid</div>
</div> </div>
<div class="km-sc km-sc-al" (click)="toast('Filtering: Alert window')"> <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 &lt; 30d</div> <div class="km-sc-l">Alert Window</div>
<div class="km-sc-n">47</div>
<div class="km-sc-d">Expiring &lt; 30d</div>
</div> </div>
<div class="km-sc km-sc-fr" (click)="toast('Filtering: Debit Freeze')"> <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 class="km-sc-l">Debit Freeze</div>
<div class="km-sc-n">23</div>
<div class="km-sc-d">Debits blocked</div>
</div> </div>
<div class="km-sc km-sc-in" (click)="toast('Filtering: Inoperative')"> <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 class="km-sc-l">Inoperative</div>
<div class="km-sc-n">8</div>
<div class="km-sc-d">All txns blocked</div>
</div> </div>
<div class="km-sc km-sc-do" (click)="toast('Filtering: Dormant')"> <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 class="km-sc-l">Dormant</div>
<div class="km-sc-n">3</div>
<div class="km-sc-d">RBI reportable</div>
</div> </div>
</div> </div>
@@ -478,20 +579,28 @@
</div> </div>
<div class="km-cb-flush"> <div class="km-cb-flush">
<div class="km-rule"> <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-h">
<div class="km-rule-d">KYC expires + 3 notices sent + no response → debits blocked, credits allowed</div> <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"> <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">Window: 31 Mar – 30 Apr</span>
<span class="km-b km-b-gy">3/3 notices</span> <span class="km-b km-b-gy">3/3 notices</span>
</div> </div>
</div> </div>
<div class="km-rule"> <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-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-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 class="km-rule-tags"><span class="km-b km-b-gy">Apr 30 – Jun 30</span></div>
</div> </div>
<div class="km-rule km-rule-last"> <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-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-d">+150 days from expiry → dormant, RBI Dormant Account reporting triggered</div>
<div class="km-rule-tags"> <div class="km-rule-tags">
<span class="km-b km-b-gy">Jun 30 – Sep 30</span> <span class="km-b km-b-gy">Jun 30 – Sep 30</span>
@@ -533,7 +642,9 @@
</div> </div>
<div class="km-warn-box"> <div class="km-warn-box">
<div class="km-warn-t">⚠ Maker-Checker Required</div> <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 class="km-warn-d">
All manual changes require second officer approval and are logged in audit inventory.
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -547,35 +658,64 @@
</div> </div>
<table class="km-tbl"> <table class="km-tbl">
<thead> <thead>
<tr><th>Account</th><th>From</th><th>To</th><th>Trigger</th><th>Date</th><th>By</th><th>Action</th></tr> <tr>
<th>Account</th>
<th>From</th>
<th>To</th>
<th>Trigger</th>
<th>Date</th>
<th>By</th>
<th>Action</th>
</tr>
</thead> </thead>
<tbody> <tbody>
<tr (click)="openModal('priya')"> <tr (click)="openModal('priya')">
<td><div class="km-an">Priya Malhotra</div><div class="km-ai">ACC-BPL-2021-00231</div></td> <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-ac">Active</span></td>
<td><span class="km-sp km-sp-fr">Debit Freeze</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><span class="km-b km-b-am">Auto — KYC Expired</span></td>
<td>10 Apr 2027</td> <td>10 Apr 2027</td>
<td class="km-small">Scheduler</td> <td class="km-small">Scheduler</td>
<td><button class="km-btn km-btn-ot" (click)="$event.stopPropagation(); toast('Unfreeze initiated')">Unfreeze</button></td> <td>
<button class="km-btn km-btn-ot" (click)="$event.stopPropagation(); toast('Unfreeze initiated')">
Unfreeze
</button>
</td>
</tr> </tr>
<tr (click)="openModal('suresh')"> <tr (click)="openModal('suresh')">
<td><div class="km-an">Suresh Patel</div><div class="km-ai">ACC-BPL-2019-00112</div></td> <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-fr">Debit Freeze</span></td>
<td><span class="km-sp km-sp-in">Inoperative</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><span class="km-b km-b-or">Auto — 60d elapsed</span></td>
<td>15 May 2027</td> <td>15 May 2027</td>
<td class="km-small">Scheduler</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> <td>
<button class="km-btn km-btn-g" (click)="$event.stopPropagation(); toast('Override needs approval')">
Override
</button>
</td>
</tr> </tr>
<tr (click)="openModal('kavitha')"> <tr (click)="openModal('kavitha')">
<td><div class="km-an">Kavitha Nair</div><div class="km-ai">ACC-BPL-2018-00088</div></td> <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-in">Inoperative</span></td>
<td><span class="km-sp km-sp-do">Dormant</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><span class="km-b km-b-re">Auto — 150d elapsed</span></td>
<td>1 Jun 2027</td> <td>1 Jun 2027</td>
<td class="km-small">Scheduler</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> <td>
<button class="km-btn km-btn-g" (click)="$event.stopPropagation(); toast('Reversal needs RM + RBI')">
Reverse
</button>
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@@ -600,11 +740,21 @@
</div> </div>
<div class="km-mbody"> <div class="km-mbody">
<div class="km-mtab-bar"> <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 === 'overview'" (click)="showMTab('overview')">
<button class="km-mtab" [class.active]="modalTab === 'timeline'" (click)="showMTab('timeline')">Alert Timeline</button> Overview
<button class="km-mtab" [class.active]="modalTab === 'notices'" (click)="showMTab('notices')">Notices Sent</button> </button>
<button class="km-mtab" [class.active]="modalTab === 'evidence'" (click)="showMTab('evidence')">Evidence</button> <button class="km-mtab" [class.active]="modalTab === 'timeline'" (click)="showMTab('timeline')">
<button class="km-mtab" [class.active]="modalTab === 'action'" (click)="showMTab('action')">Take Action</button> 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> </div>
<!-- OVERVIEW --> <!-- OVERVIEW -->
@@ -623,36 +773,86 @@
<div class="km-nb-icon">⚡</div> <div class="km-nb-icon">⚡</div>
<div> <div>
<div class="km-nb-title">{{ stateLabel(current.state) }} — {{ current.daysLeft }} days to expiry</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 class="km-nb-sub">
Next pending: <strong>{{ current.noticePending }}</strong
>. Next stage: <strong>{{ current.nextStage }}</strong
>.
</div>
</div> </div>
</div> </div>
<div class="km-mstat-row"> <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"><div class="km-mstat-n">{{ noticesPending(current) }}</div><div class="km-mstat-l">Notices Pending</div></div> <div class="km-mstat-n">{{ current.noticesSent }}</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-l">Notices Sent</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-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>
<div class="km-r2"> <div class="km-r2">
<div> <div>
<div class="km-slbl">KYC Information</div> <div class="km-slbl">KYC Information</div>
<div class="km-mg"> <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-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-ml">PAN</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-mv km-mono">{{ current.kyc.pan }}</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>
<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-mi"><div class="km-ml">Expiry Date</div><div class="km-mv km-red">{{ current.expiry }}</div></div> <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>
<div> <div>
<div class="km-slbl">Contact Details</div> <div class="km-slbl">Contact Details</div>
<div class="km-mg"> <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-mi"><div class="km-ml">Email</div><div class="km-mv km-small">{{ current.contact.email }}</div></div> <div class="km-ml">Mobile</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-mv">{{ current.contact.mobile }}</div>
<div class="km-mi"><div class="km-ml">Branch</div><div class="km-mv">{{ current.contact.branch }}</div></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> </div>
</div> </div>
@@ -688,22 +888,44 @@
<div class="km-slbl">All Notices Dispatched</div> <div class="km-slbl">All Notices Dispatched</div>
<table class="km-tbl"> <table class="km-tbl">
<thead> <thead>
<tr><th>#</th><th>Notice</th><th>Channel</th><th>Date</th><th>Reference</th><th>Delivery</th><th>Response</th></tr> <tr>
<th>#</th>
<th>Notice</th>
<th>Channel</th>
<th>Date</th>
<th>Reference</th>
<th>Delivery</th>
<th>Response</th>
</tr>
</thead> </thead>
<tbody> <tbody>
@for (n of current.notices; track n.ref; let i = $index) { @for (n of current.notices; track n.ref; let i = $index) {
<tr> <tr>
<td class="km-num">{{ i + 1 }}</td> <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>
<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 class="km-small">{{ n.channel }}</td>
<td>{{ n.date }}</td> <td>{{ n.date }}</td>
<td><span class="km-ref">{{ n.ref }}</span></td> <td>
<td><span class="km-del" [ngClass]="deliveryClass(n.delivery)">{{ n.delivery }}</span></td> <span class="km-ref">{{ n.ref }}</span>
<td><span class="km-del" [ngClass]="responseClass(n.response)">{{ n.response }}</span></td> </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> </tr>
} }
@if (noticesPending(current) > 0) { @if (noticesPending(current) > 0) {
<tr><td colspan="7" class="km-pending-row">⏳ {{ noticesPending(current) }} notice(s) still pending dispatch</td></tr> <tr>
<td colspan="7" class="km-pending-row">
⏳ {{ noticesPending(current) }} notice(s) still pending dispatch
</td>
</tr>
} }
</tbody> </tbody>
</table> </table>
@@ -718,28 +940,55 @@
<div class="km-empty-s">Evidence is captured during branch visits or video KYC sessions.</div> <div class="km-empty-s">Evidence is captured during branch visits or video KYC sessions.</div>
</div> </div>
} @else { } @else {
<div class="km-slbl">{{ current.evidence.type === 'visit' ? 'Branch Visit Evidence' : 'Video KYC Session Log' }}</div> <div class="km-slbl">
{{ current.evidence.type === 'visit' ? 'Home Visit Evidence' : 'Video KYC Session Log' }}
</div>
<div class="km-r2 km-mb"> <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-mi"><div class="km-ml">Officer / Agent</div><div class="km-mv">{{ current.evidence.officer }}</div></div> <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') { @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-mi"><div class="km-ml">Location</div><div class="km-mv km-small">{{ current.evidence.location }}</div></div> <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-mi"><div class="km-ml">Outcome</div><div class="km-mv"><span class="km-del km-d-fa">No Compliance</span></div></div> <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> </div>
@if (current.evidence.type === 'visit') { @if (current.evidence.type === 'visit') {
<div class="km-slbl">Map Placeholder</div> <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-map">
{{ current.evidence.gps }}
<div class="km-small">{{ current.evidence.location }}</div>
</div>
<div class="km-slbl">Photo Evidence</div> <div class="km-slbl">Photo Evidence</div>
<div class="km-photo">Visit photo uploaded · Tap to view</div> <div class="km-photo">Visit photo uploaded · Tap to view</div>
} }
<div class="km-slbl">Officer Notes</div> <div class="km-slbl">Officer Notes</div>
<div class="km-notes">{{ current.evidence.outcome }}</div> <div class="km-notes">{{ current.evidence.outcome }}</div>
<div class="km-row-actions"> <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-t" (click)="toast('Downloading evidence package...')">
<button class="km-btn km-btn-g" (click)="toast('Sharing with RBI audit team...')">Share for Audit</button> Download Package
</button>
<button class="km-btn km-btn-g" (click)="toast('Sharing with RBI audit team...')">
Share for Audit
</button>
</div> </div>
} }
} }
@@ -754,7 +1003,7 @@
<div class="km-action-t">{{ action }}</div> <div class="km-action-t">{{ action }}</div>
<div class="km-action-d">Initiate via available channels</div> <div class="km-action-d">Initiate via available channels</div>
</div> </div>
<button class="km-btn km-btn-t" (click)="toast(action + ' initiated for ' + current.name)">Take Action</button> <button class="km-btn km-btn-t" (click)="takeAction(action)">Take Action</button>
</div> </div>
} }
</div> </div>
@@ -764,6 +1013,158 @@
</div> </div>
} }
<!-- HOME VISIT SCHEDULE DIALOG -->
@if (homeVisitOpen && current) {
<div class="km-overlay" (click)="closeHomeVisitDialog()">
<div class="km-modal hv-modal" (click)="$event.stopPropagation()">
<div class="km-mh">
<div class="km-mh-av">{{ current.av }}</div>
<div>
<div class="km-mh-name">Schedule Home Visit</div>
<div class="km-mh-meta">{{ current.name }} · {{ current.account }}</div>
</div>
<div class="km-mclose" (click)="closeHomeVisitDialog()">✕</div>
</div>
<div class="km-mbody hv-body">
<div class="hv-addr">
<div class="hv-addr-label">Visit Address</div>
<div class="hv-addr-name">{{ current.name }}</div>
<div class="hv-addr-text">{{ current.contact.address }}</div>
<div class="hv-addr-branch">Branch: {{ current.contact.branch }}</div>
</div>
<div class="hv-row">
<label class="hv-label">Bank Officer<span class="hv-req">*</span></label>
<select class="hv-input" [(ngModel)]="homeVisitOfficer">
<option value="" disabled>Select an officer…</option>
@for (officer of bankOfficers; track officer) {
<option [value]="officer">{{ officer }}</option>
}
</select>
</div>
<div class="hv-row">
<label class="hv-label">Visit Date &amp; Time<span class="hv-req">*</span></label>
<input type="datetime-local" class="hv-input" [(ngModel)]="homeVisitDateTime" />
</div>
<div class="hv-row">
<label class="hv-label">Notes (optional)</label>
<textarea
class="hv-input hv-textarea"
rows="2"
placeholder="Any specific instructions for the officer…"
[(ngModel)]="homeVisitNote"
></textarea>
</div>
</div>
<div class="hv-footer">
<button class="km-btn" (click)="closeHomeVisitDialog()">Cancel</button>
<button
class="km-btn km-btn-t"
[disabled]="!homeVisitOfficer || !homeVisitDateTime"
(click)="saveHomeVisit()"
>
Save Visit
</button>
</div>
</div>
</div>
}
<!-- VIDEO KYC LINK DIALOG -->
@if (vkycOpen && current) {
<div class="km-overlay" (click)="closeVkycDialog()">
<div class="km-modal hv-modal" (click)="$event.stopPropagation()">
<div class="km-mh">
<div class="km-mh-av">{{ current.av }}</div>
<div>
<div class="km-mh-name">Send Video KYC Link</div>
<div class="km-mh-meta">{{ current.name }} · {{ current.account }}</div>
</div>
<div class="km-mclose" (click)="closeVkycDialog()">✕</div>
</div>
<div class="km-mbody hv-body">
<div class="hv-addr">
<div class="hv-addr-label">Customer Contact</div>
<div class="hv-addr-name">{{ current.name }}</div>
<div class="hv-addr-text">
<fa-icon icon="mobile-alt" class="vkyc-ic"></fa-icon> {{ current.contact.mobile }}
</div>
<div class="hv-addr-text">
<fa-icon icon="envelope" class="vkyc-ic"></fa-icon> {{ current.contact.email }}
</div>
</div>
<div class="hv-row">
<label class="hv-label">Send Via<span class="hv-req">*</span></label>
<div class="vkyc-chans">
<label class="vkyc-chan">
<input type="checkbox" [(ngModel)]="vkycChannels.sms" />
<span>SMS</span>
</label>
<label class="vkyc-chan">
<input type="checkbox" [(ngModel)]="vkycChannels.email" />
<span>Email</span>
</label>
<label class="vkyc-chan">
<input type="checkbox" [(ngModel)]="vkycChannels.whatsapp" />
<span>WhatsApp</span>
</label>
</div>
</div>
<div class="hv-row">
<label class="hv-label">Assign KYC Officer<span class="hv-req">*</span></label>
<select class="hv-input" [(ngModel)]="vkycOfficer">
<option value="" disabled>Select an officer…</option>
@for (officer of bankOfficers; track officer) {
<option [value]="officer">{{ officer }}</option>
}
</select>
</div>
<div class="hv-row">
<label class="hv-label">Link Expiry<span class="hv-req">*</span></label>
<input type="datetime-local" class="hv-input" [(ngModel)]="vkycExpiry" />
<div class="vkyc-hint">Default: 48 hours (RBI Video-based KYC norms)</div>
</div>
<div class="hv-row">
<label class="hv-label">Session Link Preview</label>
<div class="vkyc-link">
<span class="vkyc-link-text">{{ vkycLink }}</span>
</div>
</div>
<div class="hv-row">
<label class="hv-label">Custom Message (optional)</label>
<textarea
class="hv-input hv-textarea"
rows="2"
placeholder="e.g. Please complete your Video KYC at your earliest convenience…"
[(ngModel)]="vkycMessage"
></textarea>
</div>
</div>
<div class="hv-footer">
<button class="km-btn" (click)="closeVkycDialog()">Cancel</button>
<button
class="km-btn km-btn-t"
[disabled]="!vkycOfficer || !vkycExpiry || vkycSelectedChannels.length === 0"
(click)="saveVkyc()"
>
Send Link
</button>
</div>
</div>
</div>
}
<!-- TOAST --> <!-- TOAST -->
<div class="km-toast" [class.show]="toastVisible">{{ toastMsg }}</div> <div class="km-toast" [class.show]="toastVisible">{{ toastMsg }}</div>
</div> </div>
@@ -34,7 +34,11 @@
--km-g900: #0f172a; --km-g900: #0f172a;
display: block; display: block;
font-family: 'DM Sans', system-ui, -apple-system, sans-serif; font-family:
'DM Sans',
system-ui,
-apple-system,
sans-serif;
background: var(--km-g50); background: var(--km-g50);
color: var(--km-g800); color: var(--km-g800);
font-size: 14px; font-size: 14px;
@@ -130,7 +134,7 @@
background: #fff; background: #fff;
border: 1px solid var(--km-g200); border: 1px solid var(--km-g200);
border-radius: 12px; border-radius: 12px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); box-shadow: 0 1px 3px rgb(0 0 0 / 8%);
} }
.km-ch { .km-ch {
@@ -172,11 +176,30 @@
flex-shrink: 0; flex-shrink: 0;
} }
.km-ci-t { background: var(--km-teal-l); color: var(--km-teal); } .km-ci-t {
.km-ci-a { background: var(--km-amber-l); color: var(--km-amber); } background: var(--km-teal-l);
.km-ci-r { background: var(--km-red-l); color: var(--km-red); } color: var(--km-teal);
.km-ci-b { background: var(--km-blue-l); color: var(--km-blue); } }
.km-ci-g { background: var(--km-green-l); color: var(--km-green); }
.km-ci-a {
background: var(--km-amber-l);
color: var(--km-amber);
}
.km-ci-r {
background: var(--km-red-l);
color: var(--km-red);
}
.km-ci-b {
background: var(--km-blue-l);
color: var(--km-blue);
}
.km-ci-g {
background: var(--km-green-l);
color: var(--km-green);
}
/* Badges */ /* Badges */
.km-b { .km-b {
@@ -189,17 +212,53 @@
font-weight: 500; font-weight: 500;
} }
.km-b-gr { background: var(--km-green-l); color: var(--km-green); } .km-b-gr {
.km-b-am { background: var(--km-amber-l); color: var(--km-amber); } background: var(--km-green-l);
.km-b-re { background: var(--km-red-l); color: var(--km-red); } color: var(--km-green);
.km-b-or { background: var(--km-orange-l); color: var(--km-orange); } }
.km-b-bl { background: var(--km-blue-l); color: var(--km-blue); }
.km-b-te { background: var(--km-teal-l); color: var(--km-teal); }
.km-b-gy { background: var(--km-g100); color: var(--km-g600); }
.km-b-nv { background: #e8edf5; color: var(--km-navy); }
.km-ml-auto { margin-left: auto; } .km-b-am {
.km-ml-12 { margin-left: 12px; } background: var(--km-amber-l);
color: var(--km-amber);
}
.km-b-re {
background: var(--km-red-l);
color: var(--km-red);
}
.km-b-or {
background: var(--km-orange-l);
color: var(--km-orange);
}
.km-b-bl {
background: var(--km-blue-l);
color: var(--km-blue);
}
.km-b-te {
background: var(--km-teal-l);
color: var(--km-teal);
}
.km-b-gy {
background: var(--km-g100);
color: var(--km-g600);
}
.km-b-nv {
background: #e8edf5;
color: var(--km-navy);
}
.km-ml-auto {
margin-left: auto;
}
.km-ml-12 {
margin-left: 12px;
}
/* Buttons */ /* Buttons */
.km-btn { .km-btn {
@@ -216,14 +275,57 @@
transition: all 0.15s; transition: all 0.15s;
} }
.km-btn-t { background: var(--km-teal); border-color: var(--km-teal); color: #fff; &:hover { opacity: 0.9; } } .km-btn-t {
.km-btn-ot { background: #fff; border-color: var(--km-teal); color: var(--km-teal); &:hover { background: var(--km-teal-l); } } background: var(--km-teal);
.km-btn-n { background: var(--km-navy); border-color: var(--km-navy); color: #fff; } border-color: var(--km-teal);
.km-btn-a { background: var(--km-amber); border-color: var(--km-amber); color: #fff; } color: #fff;
.km-btn-r { background: var(--km-red); border-color: var(--km-red); color: #fff; }
.km-btn-g { background: #fff; border-color: var(--km-g300); color: var(--km-g600); &:hover { background: var(--km-g50); } }
.km-w { width: 100%; } &:hover {
opacity: 0.9;
}
}
.km-btn-ot {
background: #fff;
border-color: var(--km-teal);
color: var(--km-teal);
&:hover {
background: var(--km-teal-l);
}
}
.km-btn-n {
background: var(--km-navy);
border-color: var(--km-navy);
color: #fff;
}
.km-btn-a {
background: var(--km-amber);
border-color: var(--km-amber);
color: #fff;
}
.km-btn-r {
background: var(--km-red);
border-color: var(--km-red);
color: #fff;
}
.km-btn-g {
background: #fff;
border-color: var(--km-g300);
color: var(--km-g600);
&:hover {
background: var(--km-g50);
}
}
.km-w {
width: 100%;
}
/* Stat row */ /* Stat row */
.km-stat-row { .km-stat-row {
@@ -238,12 +340,12 @@
border: 1px solid var(--km-g200); border: 1px solid var(--km-g200);
border-radius: 12px; border-radius: 12px;
padding: 14px; padding: 14px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); box-shadow: 0 1px 3px rgb(0 0 0 / 8%);
cursor: pointer; cursor: pointer;
transition: all 0.15s; transition: all 0.15s;
&:hover { &:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); box-shadow: 0 4px 12px rgb(0 0 0 / 10%);
} }
} }
@@ -370,11 +472,30 @@
font-weight: 600; font-weight: 600;
} }
.km-sp-ac { background: var(--km-green-l); color: var(--km-green); } .km-sp-ac {
.km-sp-fr { background: var(--km-amber-l); color: var(--km-amber); } background: var(--km-green-l);
.km-sp-in { background: var(--km-orange-l); color: var(--km-orange); } color: var(--km-green);
.km-sp-do { background: var(--km-red-l); color: var(--km-red); } }
.km-sp-al { background: #fef9c3; color: #92400e; }
.km-sp-fr {
background: var(--km-amber-l);
color: var(--km-amber);
}
.km-sp-in {
background: var(--km-orange-l);
color: var(--km-orange);
}
.km-sp-do {
background: var(--km-red-l);
color: var(--km-red);
}
.km-sp-al {
background: #fef9c3;
color: #92400e;
}
/* Progress bar */ /* Progress bar */
.km-pbar { .km-pbar {
@@ -424,10 +545,25 @@
font-weight: 600; font-weight: 600;
} }
.km-d-ok { background: var(--km-green-l); color: var(--km-green); } .km-d-ok {
.km-d-pe { background: var(--km-amber-l); color: var(--km-amber); } background: var(--km-green-l);
.km-d-fa { background: var(--km-red-l); color: var(--km-red); } color: var(--km-green);
.km-d-tr { background: var(--km-blue-l); color: var(--km-blue); } }
.km-d-pe {
background: var(--km-amber-l);
color: var(--km-amber);
}
.km-d-fa {
background: var(--km-red-l);
color: var(--km-red);
}
.km-d-tr {
background: var(--km-blue-l);
color: var(--km-blue);
}
/* Pagination */ /* Pagination */
.km-pagination { .km-pagination {
@@ -489,12 +625,41 @@
flex-shrink: 0; flex-shrink: 0;
} }
.km-nd1 { background: var(--km-blue-l); border-color: #93c5fd; color: var(--km-blue); } .km-nd1 {
.km-nd2 { background: var(--km-amber-l); border-color: #fcd34d; color: var(--km-amber); } background: var(--km-blue-l);
.km-nd3 { background: var(--km-orange-l); border-color: #fdba74; color: var(--km-orange); } border-color: #93c5fd;
.km-ndf { background: var(--km-red-l); border-color: #fca5a5; color: var(--km-red); } color: var(--km-blue);
.km-ndi { background: #fee2e2; border-color: #f87171; color: #991b1b; } }
.km-ndd { background: var(--km-g100); border-color: var(--km-g300); color: var(--km-g500); }
.km-nd2 {
background: var(--km-amber-l);
border-color: #fcd34d;
color: var(--km-amber);
}
.km-nd3 {
background: var(--km-orange-l);
border-color: #fdba74;
color: var(--km-orange);
}
.km-ndf {
background: var(--km-red-l);
border-color: #fca5a5;
color: var(--km-red);
}
.km-ndi {
background: #fee2e2;
border-color: #f87171;
color: #991b1b;
}
.km-ndd {
background: var(--km-g100);
border-color: var(--km-g300);
color: var(--km-g500);
}
.km-nl { .km-nl {
width: 1px; width: 1px;
@@ -509,9 +674,13 @@
color: var(--km-g800); color: var(--km-g800);
} }
.km-warn { color: var(--km-amber); } .km-warn {
.km-warn-o { color: var(--km-orange); } color: var(--km-amber);
.km-warn-d { color: var(--km-g600); } }
.km-warn-o {
color: var(--km-orange);
}
.km-ns { .km-ns {
font-size: 11px; font-size: 11px;
@@ -520,9 +689,20 @@
margin-bottom: 5px; margin-bottom: 5px;
} }
.km-ok { color: var(--km-green); font-weight: 600; } .km-ok {
.km-pending { color: var(--km-amber); font-weight: 600; } color: var(--km-green);
.km-fail { color: var(--km-red); font-weight: 600; } font-weight: 600;
}
.km-pending {
color: var(--km-amber);
font-weight: 600;
}
.km-fail {
color: var(--km-red);
font-weight: 600;
}
.km-ntags { .km-ntags {
display: flex; display: flex;
@@ -541,12 +721,41 @@
border: 1px solid; border: 1px solid;
} }
.km-cp-sms { background: #eff6ff; color: #1d4ed8; border-color: #93c5fd; } .km-cp-sms {
.km-cp-mail { background: #f0fdf4; color: #15803d; border-color: #86efac; } background: #eff6ff;
.km-cp-ivr { background: #faf5ff; color: #7c3aed; border-color: #c4b5fd; } color: #1d4ed8;
.km-cp-post { background: #fff7ed; color: #c2410c; border-color: #fdba74; } border-color: #93c5fd;
.km-cp-vis { background: var(--km-teal-l); color: var(--km-teal); border-color: #5eead4; } }
.km-cp-vid { background: #ede9fe; color: #5b21b6; border-color: #a78bfa; }
.km-cp-mail {
background: #f0fdf4;
color: #15803d;
border-color: #86efac;
}
.km-cp-ivr {
background: #faf5ff;
color: #7c3aed;
border-color: #c4b5fd;
}
.km-cp-post {
background: #fff7ed;
color: #c2410c;
border-color: #fdba74;
}
.km-cp-vis {
background: var(--km-teal-l);
color: var(--km-teal);
border-color: #5eead4;
}
.km-cp-vid {
background: #ede9fe;
color: #5b21b6;
border-color: #a78bfa;
}
.km-row-actions { .km-row-actions {
margin-top: 8px; margin-top: 8px;
@@ -574,9 +783,20 @@
background: var(--km-teal-l); background: var(--km-teal-l);
} }
.km-channel-i { font-size: 16px; margin-bottom: 3px; } .km-channel-i {
.km-channel-n { font-size: 11px; font-weight: 600; } font-size: 16px;
.km-channel-d { font-size: 10px; color: var(--km-g500); } margin-bottom: 3px;
}
.km-channel-n {
font-size: 11px;
font-weight: 600;
}
.km-channel-d {
font-size: 10px;
color: var(--km-g500);
}
/* Meta items */ /* Meta items */
.km-mi { .km-mi {
@@ -618,15 +838,79 @@
transition: all 0.15s; transition: all 0.15s;
&:hover { &:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); box-shadow: 0 4px 12px rgb(0 0 0 / 10%);
} }
} }
.km-sc-ac { border-color: #86efac; background: var(--km-green-l); .km-sc-l, .km-sc-d { color: #166534; } .km-sc-n { color: var(--km-green); } } .km-sc-ac {
.km-sc-fr { border-color: #fcd34d; background: var(--km-amber-l); .km-sc-l, .km-sc-d { color: #92400e; } .km-sc-n { color: var(--km-amber); } } border-color: #86efac;
.km-sc-in { border-color: #fdba74; background: var(--km-orange-l); .km-sc-l, .km-sc-d { color: #7c2d12; } .km-sc-n { color: var(--km-orange); } } background: var(--km-green-l);
.km-sc-do { border-color: #fca5a5; background: var(--km-red-l); .km-sc-l, .km-sc-d { color: #7f1d1d; } .km-sc-n { color: var(--km-red); } }
.km-sc-al { border-color: #fde047; background: #fef9c3; .km-sc-l, .km-sc-d { color: #713f12; } .km-sc-n { color: #92400e; } } .km-sc-l,
.km-sc-d {
color: #166534;
}
.km-sc-n {
color: var(--km-green);
}
}
.km-sc-fr {
border-color: #fcd34d;
background: var(--km-amber-l);
.km-sc-l,
.km-sc-d {
color: #92400e;
}
.km-sc-n {
color: var(--km-amber);
}
}
.km-sc-in {
border-color: #fdba74;
background: var(--km-orange-l);
.km-sc-l,
.km-sc-d {
color: #7c2d12;
}
.km-sc-n {
color: var(--km-orange);
}
}
.km-sc-do {
border-color: #fca5a5;
background: var(--km-red-l);
.km-sc-l,
.km-sc-d {
color: #7f1d1d;
}
.km-sc-n {
color: var(--km-red);
}
}
.km-sc-al {
border-color: #fde047;
background: #fef9c3;
.km-sc-l,
.km-sc-d {
color: #713f12;
}
.km-sc-n {
color: #92400e;
}
}
.km-sc-l { .km-sc-l {
font-size: 10px; font-size: 10px;
@@ -719,7 +1003,7 @@
.km-overlay { .km-overlay {
position: fixed; position: fixed;
inset: 0; inset: 0;
background: rgba(0, 0, 0, 0.45); background: rgb(0 0 0 / 45%);
z-index: 1200; z-index: 1200;
display: flex; display: flex;
align-items: center; align-items: center;
@@ -735,7 +1019,7 @@
max-height: 90vh; max-height: 90vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2); box-shadow: 0 20px 60px rgb(0 0 0 / 20%);
} }
.km-mh { .km-mh {
@@ -787,7 +1071,9 @@
margin-left: auto; margin-left: auto;
flex-shrink: 0; flex-shrink: 0;
&:hover { background: var(--km-g100); } &:hover {
background: var(--km-g100);
}
} }
.km-mbody { .km-mbody {
@@ -863,7 +1149,9 @@
} }
} }
.km-ps-done::after { background: var(--km-teal); } .km-ps-done::after {
background: var(--km-teal);
}
.km-ps-dot { .km-ps-dot {
width: 28px; width: 28px;
@@ -881,9 +1169,23 @@
position: relative; position: relative;
} }
.km-ps-done .km-ps-dot { background: var(--km-teal); border-color: var(--km-teal); color: #fff; } .km-ps-done .km-ps-dot {
.km-ps-current .km-ps-dot { background: #fff; border-color: var(--km-amber); color: var(--km-amber); } background: var(--km-teal);
.km-ps-danger .km-ps-dot { background: #fff; border-color: var(--km-red); color: var(--km-red); } border-color: var(--km-teal);
color: #fff;
}
.km-ps-current .km-ps-dot {
background: #fff;
border-color: var(--km-amber);
color: var(--km-amber);
}
.km-ps-danger .km-ps-dot {
background: #fff;
border-color: var(--km-red);
color: var(--km-red);
}
.km-ps-label { .km-ps-label {
font-size: 10px; font-size: 10px;
@@ -893,9 +1195,19 @@
max-width: 70px; max-width: 70px;
} }
.km-ps-done .km-ps-label { color: var(--km-teal); } .km-ps-done .km-ps-label {
.km-ps-current .km-ps-label { color: var(--km-amber); font-weight: 600; } color: var(--km-teal);
.km-ps-danger .km-ps-label { color: var(--km-red); font-weight: 600; } }
.km-ps-current .km-ps-label {
color: var(--km-amber);
font-weight: 600;
}
.km-ps-danger .km-ps-label {
color: var(--km-red);
font-weight: 600;
}
/* Next action banner */ /* Next action banner */
.km-next-banner { .km-next-banner {
@@ -908,15 +1220,45 @@
gap: 12px; gap: 12px;
} }
.km-nb-alert { background: var(--km-amber-l); border-color: #fcd34d; } .km-nb-alert {
.km-nb-urgent { background: var(--km-orange-l); border-color: #fdba74; } background: var(--km-amber-l);
.km-nb-freeze { background: var(--km-amber-l); border-color: #fcd34d; } border-color: #fcd34d;
.km-nb-inop { background: var(--km-orange-l); border-color: #fdba74; } }
.km-nb-dormant { background: var(--km-red-l); border-color: #fca5a5; }
.km-nb-icon { font-size: 22px; flex-shrink: 0; } .km-nb-urgent {
.km-nb-title { font-size: 13px; font-weight: 700; margin-bottom: 2px; } background: var(--km-orange-l);
.km-nb-sub { font-size: 12px; } border-color: #fdba74;
}
.km-nb-freeze {
background: var(--km-amber-l);
border-color: #fcd34d;
}
.km-nb-inop {
background: var(--km-orange-l);
border-color: #fdba74;
}
.km-nb-dormant {
background: var(--km-red-l);
border-color: #fca5a5;
}
.km-nb-icon {
font-size: 22px;
flex-shrink: 0;
}
.km-nb-title {
font-size: 13px;
font-weight: 700;
margin-bottom: 2px;
}
.km-nb-sub {
font-size: 12px;
}
/* Modal stat strip */ /* Modal stat strip */
.km-mstat-row { .km-mstat-row {
@@ -969,7 +1311,9 @@
padding: 12px 0; padding: 12px 0;
border-bottom: 1px solid var(--km-g100); border-bottom: 1px solid var(--km-g100);
&:last-child { border-bottom: none; } &:last-child {
border-bottom: none;
}
} }
.km-mti-dot { .km-mti-dot {
@@ -985,14 +1329,36 @@
flex-shrink: 0; flex-shrink: 0;
} }
.km-mti-done { background: var(--km-green-l); border-color: var(--km-green); color: var(--km-green); } .km-mti-done {
.km-mti-pend { background: var(--km-amber-l); border-color: var(--km-amber); color: var(--km-amber); } background: var(--km-green-l);
border-color: var(--km-green);
color: var(--km-green);
}
.km-mti-text { font-size: 13px; font-weight: 500; } .km-mti-pend {
.km-done { color: var(--km-g700); } background: var(--km-amber-l);
.km-pend { color: var(--km-amber); } border-color: var(--km-amber);
color: var(--km-amber);
}
.km-mti-sub { font-size: 11px; color: var(--km-g400); margin-top: 2px; } .km-mti-text {
font-size: 13px;
font-weight: 500;
}
.km-done {
color: var(--km-g700);
}
.km-pend {
color: var(--km-amber);
}
.km-mti-sub {
font-size: 11px;
color: var(--km-g400);
margin-top: 2px;
}
.km-pending-row { .km-pending-row {
text-align: center; text-align: center;
@@ -1009,8 +1375,15 @@
color: var(--km-g400); color: var(--km-g400);
} }
.km-empty-i { font-size: 28px; margin-bottom: 8px; } .km-empty-i {
.km-empty-s { font-size: 12px; margin-top: 4px; } font-size: 28px;
margin-bottom: 8px;
}
.km-empty-s {
font-size: 12px;
margin-top: 4px;
}
/* Evidence */ /* Evidence */
.km-map { .km-map {
@@ -1069,8 +1442,15 @@
gap: 12px; gap: 12px;
} }
.km-action-t { font-size: 13px; font-weight: 600; } .km-action-t {
.km-action-d { font-size: 12px; color: var(--km-g500); } font-size: 13px;
font-weight: 600;
}
.km-action-d {
font-size: 12px;
color: var(--km-g500);
}
/* Toast */ /* Toast */
.km-toast { .km-toast {
@@ -1094,12 +1474,178 @@
} }
} }
@media (max-width: 900px) { @media (width <= 900px) {
.km-stat-row, .km-stat-row,
.km-sc-row { grid-template-columns: repeat(2, 1fr); } .km-sc-row {
grid-template-columns: repeat(2, 1fr);
}
.km-r2, .km-r2,
.km-notif-grid { grid-template-columns: 1fr; } .km-notif-grid {
.km-mstat-row { grid-template-columns: repeat(2, 1fr); } grid-template-columns: 1fr;
.km-prog-steps { flex-wrap: wrap; gap: 10px; } }
.km-ps::after { display: none; }
.km-mstat-row {
grid-template-columns: repeat(2, 1fr);
}
.km-prog-steps {
flex-wrap: wrap;
gap: 10px;
}
.km-ps::after {
display: none;
}
}
/* Home Visit Schedule Dialog */
.hv-modal {
width: 520px;
}
.hv-body {
padding: 18px 22px;
display: flex;
flex-direction: column;
gap: 14px;
overflow: auto;
}
.hv-addr {
background: #f8fafc;
border: 1px solid var(--km-g200);
border-radius: 10px;
padding: 12px 14px;
}
.hv-addr-label {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.06em;
color: #64748b;
margin-bottom: 4px;
}
.hv-addr-name {
font-size: 14px;
font-weight: 600;
color: #0f172a;
margin-bottom: 2px;
}
.hv-addr-text {
font-size: 13px;
color: #334155;
line-height: 1.4;
}
.hv-addr-branch {
margin-top: 6px;
font-size: 12px;
color: #64748b;
}
.hv-row {
display: flex;
flex-direction: column;
gap: 6px;
}
.hv-label {
font-size: 12px;
font-weight: 600;
color: #334155;
}
.hv-req {
color: #dc2626;
margin-left: 2px;
}
.hv-input {
width: 100%;
padding: 9px 12px;
border: 1px solid var(--km-g200);
border-radius: 8px;
font-size: 13px;
color: #0f172a;
background: #fff;
font-family: inherit;
transition:
border-color 0.15s,
box-shadow 0.15s;
}
.hv-input:focus {
outline: none;
border-color: var(--km-teal);
box-shadow: 0 0 0 3px rgb(20 184 166 / 15%);
}
.hv-textarea {
resize: vertical;
min-height: 60px;
}
.hv-footer {
padding: 14px 22px;
border-top: 1px solid var(--km-g200);
display: flex;
justify-content: flex-end;
gap: 10px;
flex-shrink: 0;
}
.km-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* Video KYC Dialog */
.vkyc-ic {
color: var(--km-teal);
margin-right: 6px;
font-size: 12px;
}
.vkyc-chans {
display: flex;
gap: 18px;
flex-wrap: wrap;
}
.vkyc-chan {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 13px;
color: #334155;
cursor: pointer;
user-select: none;
}
.vkyc-chan input {
accent-color: var(--km-teal);
width: 15px;
height: 15px;
cursor: pointer;
}
.vkyc-hint {
font-size: 11px;
color: #64748b;
margin-top: 2px;
}
.vkyc-link {
background: #f1f5f9;
border: 1px dashed #cbd5e1;
border-radius: 8px;
padding: 9px 12px;
font-family: 'Roboto Mono', 'DM Mono', monospace;
font-size: 12px;
color: #0f172a;
word-break: break-all;
} }
@@ -89,6 +89,38 @@ export class KycManagementComponent {
toastVisible = false; toastVisible = false;
private toastTimer: any; private toastTimer: any;
/** Home Visit scheduling dialog */
homeVisitOpen = false;
homeVisitOfficer = '';
homeVisitDateTime = '';
homeVisitNote = '';
bankOfficers = [
'Amit Sharma — Branch Officer (Bhopal Main)',
'Neha Verma — KYC Officer (Bhopal Main)',
'Ravi Kulkarni — Field Officer (Bhopal Zone-2)',
'Sunita Patel — Compliance Officer (Bhopal Main)',
'Manoj Tiwari — Field Officer (Indore Zone-1)'
];
scheduledHomeVisits: { client: string; address: string; officer: string; dateTime: string; note: string }[] = [];
/** Video KYC link dialog */
vkycOpen = false;
vkycOfficer = '';
vkycExpiry = '';
vkycChannels = { sms: true, email: true, whatsapp: false };
vkycMessage = '';
vkycSessionId = '';
sentVkycLinks: {
client: string;
mobile: string;
email: string;
officer: string;
expiry: string;
channels: string[];
sessionId: string;
link: string;
}[] = [];
clients: Record<string, KmClient> = { clients: Record<string, KmClient> = {
rajesh: { rajesh: {
id: 'rajesh', id: 'rajesh',
@@ -119,14 +151,35 @@ export class KycManagementComponent {
branch: 'Bhopal Main' branch: 'Bhopal Main'
}, },
notices: [ 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' } 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, noticesSent: 2,
noticePending: 'Notice 3', noticePending: 'Notice 3',
nextStage: 'Debit Freeze', nextStage: 'Debit Freeze',
pendingActions: ['Send Notice 3 (due 20 Apr 2027)', 'Schedule Branch Visit', 'Send Video KYC link'], pendingActions: [
actionTaken: ['Notice 1 delivered', 'Notice 2 dispatched via Reg. Post'], 'Send Notice 3 (due 20 Apr 2027)',
'Schedule Home Visit',
'Send Video KYC link'
],
actionTaken: [
'Notice 1 delivered',
'Notice 2 dispatched via Reg. Post'
],
evidence: null evidence: null
}, },
priya: { priya: {
@@ -158,15 +211,45 @@ export class KycManagementComponent {
branch: 'Bhopal Main' branch: 'Bhopal Main'
}, },
notices: [ 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: 'N1',
{ no: 'N3', channel: 'Branch Visit', date: '30 Mar 2027', ref: 'VIS-BPL-20270330-012', delivery: 'Visit Done', response: 'Refused' } 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: 'Home Visit',
date: '30 Mar 2027',
ref: 'VIS-BPL-20270330-012',
delivery: 'Visit Done',
response: 'Refused'
}
], ],
noticesSent: 3, noticesSent: 3,
noticePending: 'None — all 3 sent', noticePending: 'None — all 3 sent',
nextStage: 'Inoperative by 30 Jun 2027', nextStage: 'Inoperative by 30 Jun 2027',
pendingActions: ['Account in Debit Freeze', 'Inoperative transition due 30 Jun 2027', 'Maker-checker unfreeze pending'], pendingActions: [
actionTaken: ['Notice 1 delivered', 'Notice 2 delivered via Reg. Post', 'Notice 3 — Branch visit done, client refused', 'Debit Freeze applied 10 Apr 2027'], '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: { evidence: {
type: 'visit', type: 'visit',
date: '30 Mar 2027', date: '30 Mar 2027',
@@ -206,15 +289,46 @@ export class KycManagementComponent {
branch: 'Bhopal South' branch: 'Bhopal South'
}, },
notices: [ 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: 'N1',
{ no: 'N3', channel: 'Video KYC', date: '5 Mar 2027', ref: 'VKYC-2027-0305-007', delivery: 'No Response', response: 'No Action' } 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, noticesSent: 3,
noticePending: 'None', noticePending: 'None',
nextStage: 'Dormant by 30 Sep 2027', nextStage: 'Dormant by 30 Sep 2027',
pendingActions: ['Account Inoperative — all txns blocked', 'Dormant transition due 30 Sep 2027', 'RBI Dormant Account reporting'], pendingActions: [
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'], '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: { evidence: {
type: 'vkyc', type: 'vkyc',
date: '5 Mar 2027', date: '5 Mar 2027',
@@ -252,22 +366,54 @@ export class KycManagementComponent {
branch: 'Bhopal East' branch: 'Bhopal East'
}, },
notices: [ 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: 'N1',
{ no: 'N3', channel: 'Branch Visit', date: '22 Dec 2026', ref: 'VIS-BPL-20261222-019', delivery: 'Not Found', response: 'Not Found at Address' } 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: 'Home Visit',
date: '22 Dec 2026',
ref: 'VIS-BPL-20261222-019',
delivery: 'Not Found',
response: 'Not Found at Address'
}
], ],
noticesSent: 3, noticesSent: 3,
noticePending: 'None', noticePending: 'None',
nextStage: 'Dormant (Final) — RBI Reporting', nextStage: 'Dormant (Final) — RBI Reporting',
pendingActions: ['RBI Dormant Account Report submission', 'Branch Manager approval for any reversal', 'Regional Manager sign-off required'], pendingActions: [
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'], '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: { evidence: {
type: 'visit', type: 'visit',
date: '22 Dec 2026', date: '22 Dec 2026',
officer: 'Rekha Sharma', officer: 'Rekha Sharma',
gps: '23.2491° N, 77.4384° E', gps: '23.2491° N, 77.4384° E',
location: 'Awadhpuri, Bhopal', location: 'Awadhpuri, Bhopal',
outcome: 'Client not found at registered address. Neighbours confirmed relocation. Address verification required.', outcome:
'Client not found at registered address. Neighbours confirmed relocation. Address verification required.',
docket: 'VIS-BPL-20261222-019' docket: 'VIS-BPL-20261222-019'
} }
}, },
@@ -300,12 +446,23 @@ export class KycManagementComponent {
branch: 'Bhopal Main' branch: 'Bhopal Main'
}, },
notices: [ notices: [
{ no: 'N1', channel: 'SMS + Email', date: '5 Apr 2027', ref: 'MSG-2027-0405-011', delivery: 'Delivered', response: 'No Action' } {
no: 'N1',
channel: 'SMS + Email',
date: '5 Apr 2027',
ref: 'MSG-2027-0405-011',
delivery: 'Delivered',
response: 'No Action'
}
], ],
noticesSent: 1, noticesSent: 1,
noticePending: 'Notice 2 & 3', noticePending: 'Notice 2 & 3',
nextStage: 'Debit Freeze if no action', 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'], 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'], actionTaken: ['Notice 1 delivered 5 Apr 2027'],
evidence: null evidence: null
}, },
@@ -338,14 +495,35 @@ export class KycManagementComponent {
branch: 'Bhopal North' branch: 'Bhopal North'
}, },
notices: [ 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' } 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, noticesSent: 2,
noticePending: 'URGENT: Notice 3', noticePending: 'URGENT: Notice 3',
nextStage: 'Debit Freeze in 3 days', nextStage: 'Debit Freeze in 3 days',
pendingActions: ['URGENT: Send Notice 3 NOW', 'Schedule Branch Visit immediately', 'Consider Video KYC as last resort'], pendingActions: [
actionTaken: ['Notice 1 delivered', 'Notice 2 delivered via Reg. Post + IVR'], 'URGENT: Send Notice 3 NOW',
'Schedule Home Visit immediately',
'Consider Video KYC as last resort'
],
actionTaken: [
'Notice 1 delivered',
'Notice 2 delivered via Reg. Post + IVR'
],
evidence: null evidence: null
} }
}; };
@@ -384,6 +562,108 @@ export class KycManagementComponent {
this.toastTimer = setTimeout(() => (this.toastVisible = false), 2800); this.toastTimer = setTimeout(() => (this.toastVisible = false), 2800);
} }
/**
* Routes a "Take Action" click. Opens the Home Visit dialog if the action is
* a home-visit scheduling action; otherwise falls back to a toast confirmation.
*/
takeAction(action: string): void {
if (!this.current) return;
const lower = action.toLowerCase();
if (lower.includes('home visit')) {
this.openHomeVisitDialog();
return;
}
if (lower.includes('video kyc')) {
this.openVkycDialog();
return;
}
this.toast(action + ' initiated for ' + this.current.name);
}
openHomeVisitDialog(): void {
this.homeVisitOfficer = '';
this.homeVisitNote = '';
// Default to tomorrow at 10:00 AM (datetime-local format: YYYY-MM-DDTHH:mm)
const t = new Date();
t.setDate(t.getDate() + 1);
t.setHours(10, 0, 0, 0);
const pad = (n: number) => n.toString().padStart(2, '0');
this.homeVisitDateTime = `${t.getFullYear()}-${pad(t.getMonth() + 1)}-${pad(t.getDate())}T${pad(t.getHours())}:${pad(t.getMinutes())}`;
this.homeVisitOpen = true;
}
closeHomeVisitDialog(): void {
this.homeVisitOpen = false;
}
saveHomeVisit(): void {
if (!this.current || !this.homeVisitOfficer || !this.homeVisitDateTime) return;
this.scheduledHomeVisits.push({
client: this.current.name,
address: this.current.contact.address,
officer: this.homeVisitOfficer,
dateTime: this.homeVisitDateTime,
note: this.homeVisitNote
});
const when = new Date(this.homeVisitDateTime).toLocaleString('en-IN', {
day: '2-digit',
month: 'short',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
this.toast(`Home Visit scheduled for ${this.current.name} on ${when}`);
this.homeVisitOpen = false;
}
openVkycDialog(): void {
this.vkycOfficer = '';
this.vkycMessage = '';
this.vkycChannels = { sms: true, email: true, whatsapp: false };
// Default: link expires 48 hours from now (RBI Video-based KYC norms)
const t = new Date();
t.setHours(t.getHours() + 48);
const pad = (n: number) => n.toString().padStart(2, '0');
this.vkycExpiry = `${t.getFullYear()}-${pad(t.getMonth() + 1)}-${pad(t.getDate())}T${pad(t.getHours())}:${pad(t.getMinutes())}`;
// Generate a session id (token preview only — server would issue the real one)
this.vkycSessionId = 'VKYC-' + Math.random().toString(36).slice(2, 10).toUpperCase();
this.vkycOpen = true;
}
closeVkycDialog(): void {
this.vkycOpen = false;
}
get vkycSelectedChannels(): string[] {
const out: string[] = [];
if (this.vkycChannels.sms) out.push('SMS');
if (this.vkycChannels.email) out.push('Email');
if (this.vkycChannels.whatsapp) out.push('WhatsApp');
return out;
}
get vkycLink(): string {
return `https://vkyc.bank.example/session/${this.vkycSessionId}`;
}
saveVkyc(): void {
if (!this.current || !this.vkycOfficer || !this.vkycExpiry) return;
const channels = this.vkycSelectedChannels;
if (channels.length === 0) return;
this.sentVkycLinks.push({
client: this.current.name,
mobile: this.current.contact.mobile,
email: this.current.contact.email,
officer: this.vkycOfficer,
expiry: this.vkycExpiry,
channels,
sessionId: this.vkycSessionId,
link: this.vkycLink
});
this.toast(`Video KYC link sent to ${this.current.name} via ${channels.join(' + ')}`);
this.vkycOpen = false;
}
/** Compliance journey stepper state for a given client */ /** Compliance journey stepper state for a given client */
getStepperState(c: KmClient): { label: string; state: 'done' | 'pending' | 'current' | 'danger' }[] { getStepperState(c: KmClient): { label: string; state: 'done' | 'pending' | 'current' | 'danger' }[] {
return [ return [
@@ -393,19 +673,20 @@ export class KycManagementComponent {
{ {
label: 'Notice 3', label: 'Notice 3',
state: state:
c.noticesSent >= 3 c.noticesSent >= 3 ? 'done' : c.noticesSent >= 2 ? (c.state === 'urgent' ? 'danger' : 'current') : 'pending'
? 'done'
: c.noticesSent >= 2
? c.state === 'urgent'
? 'danger'
: 'current'
: 'pending'
}, },
{ {
label: 'Debit Freeze', label: 'Debit Freeze',
state: ['freeze', 'inop', 'dormant'].includes(c.state) ? 'done' : c.noticesSent >= 3 ? 'danger' : 'pending' state: [
'freeze',
'inop',
'dormant'
].includes(c.state) ? 'done' : c.noticesSent >= 3 ? 'danger' : 'pending'
}, },
{ label: 'Inoperative', state: ['inop', 'dormant'].includes(c.state) ? 'done' : 'pending' }, { label: 'Inoperative', state: [
'inop',
'dormant'
].includes(c.state) ? 'done' : 'pending' },
{ label: 'Dormant', state: c.state === 'dormant' ? 'danger' : 'pending' } { label: 'Dormant', state: c.state === 'dormant' ? 'danger' : 'pending' }
]; ];
} }
@@ -418,26 +699,30 @@ export class KycManagementComponent {
stateLabel(state: string): string { stateLabel(state: string): string {
return ( return (
{ (
alert: 'Alert Window', {
freeze: 'Debit Freeze', alert: 'Alert Window',
inop: 'Inoperative', freeze: 'Debit Freeze',
dormant: 'Dormant', inop: 'Inoperative',
urgent: 'Urgent Alert' dormant: 'Dormant',
} as Record<string, string> urgent: 'Urgent Alert'
)[state] || state; } as Record<string, string>
)[state] || state
);
} }
stateClass(state: string): string { stateClass(state: string): string {
return ( return (
{ (
alert: 'sp-al', {
freeze: 'sp-fr', alert: 'sp-al',
inop: 'sp-in', freeze: 'sp-fr',
dormant: 'sp-do', inop: 'sp-in',
urgent: 'sp-fr' dormant: 'sp-do',
} as Record<string, string> urgent: 'sp-fr'
)[state] || 'sp-al'; } as Record<string, string>
)[state] || 'sp-al'
);
} }
riskClass(risk: string): string { riskClass(risk: string): string {
+2
View File
@@ -390,6 +390,7 @@ $mono: 'Roboto Mono', 'DM Mono', monospace;
color: $text-muted; color: $text-muted;
border-bottom: 1px solid $border; border-bottom: 1px solid $border;
padding: 0 12px; padding: 0 12px;
white-space: nowrap;
} }
// Rows // Rows
@@ -421,6 +422,7 @@ $mono: 'Roboto Mono', 'DM Mono', monospace;
font-weight: 400; font-weight: 400;
color: $text; color: $text;
padding: 0 12px; padding: 0 12px;
white-space: nowrap;
font-family: font-family:
Inter, Inter,
Roboto, Roboto,