feat(election): add guarantor status section to nomination-apply page
Shows whether the selected nominee is a guarantor on any loan, with loan health indicator, borrower details, savings account, guaranteed/released/remaining amounts, and loan timeline. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
3d9b0480c6
commit
71552d6875
@@ -8,8 +8,6 @@
|
|||||||
|
|
||||||
.candidate-board {
|
.candidate-board {
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
max-width: 1300px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
|
|||||||
@@ -30,8 +30,7 @@ $shadow-md: 0 4px 20px rgb(74 108 247 / 13%);
|
|||||||
}
|
}
|
||||||
|
|
||||||
.config-inner {
|
.config-inner {
|
||||||
max-width: 860px;
|
// layout wrapper
|
||||||
margin: 0 auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Page header ────────────────────────────────────────────────────────────
|
// ── Page header ────────────────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -8,8 +8,6 @@
|
|||||||
|
|
||||||
.election-dashboard {
|
.election-dashboard {
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
max-width: 1400px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
|
|||||||
@@ -8,8 +8,6 @@
|
|||||||
|
|
||||||
.election-results {
|
.election-results {
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
max-width: 1000px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
|
|||||||
@@ -28,10 +28,17 @@
|
|||||||
<mat-stepper orientation="horizontal" #stepper linear>
|
<mat-stepper orientation="horizontal" #stepper linear>
|
||||||
<!-- ── Step 1: Personal Profile ── -->
|
<!-- ── Step 1: Personal Profile ── -->
|
||||||
<mat-step [stepControl]="profileForm" label="Personal Profile">
|
<mat-step [stepControl]="profileForm" label="Personal Profile">
|
||||||
<form [formGroup]="profileForm" class="step-form">
|
<form [formGroup]="profileForm" class="step-form step-form--split">
|
||||||
|
<div class="step1-layout" #layoutRef [style.gridTemplateColumns]="leftPanelWidth + '% 8px 1fr'">
|
||||||
|
<!-- ── LEFT: Lookup panel ── -->
|
||||||
|
<div class="step1-left">
|
||||||
|
<div class="step1-left-scroll">
|
||||||
<!-- Member Search -->
|
<!-- Member Search -->
|
||||||
<div class="member-search-box">
|
<div class="member-search-box">
|
||||||
<div class="section-label"><mat-icon>manage_search</mat-icon> Search Account Holder</div>
|
<div class="section-label"><mat-icon>manage_search</mat-icon> Search Account Holder</div>
|
||||||
|
|
||||||
|
@if (!selectedMemberId) {
|
||||||
|
<!-- Search input — only shown when no member selected -->
|
||||||
<div class="search-row">
|
<div class="search-row">
|
||||||
<mat-form-field appearance="outline" class="search-field">
|
<mat-form-field appearance="outline" class="search-field">
|
||||||
<mat-label>Type member name or account number</mat-label>
|
<mat-label>Type member name or account number</mat-label>
|
||||||
@@ -49,13 +56,17 @@
|
|||||||
>
|
>
|
||||||
@if (searchLoading) {
|
@if (searchLoading) {
|
||||||
<mat-option disabled>
|
<mat-option disabled>
|
||||||
<mat-spinner diameter="18" style="display: inline-block; margin-right: 8px"></mat-spinner>
|
<mat-spinner
|
||||||
|
diameter="18"
|
||||||
|
style="display: inline-block; margin-right: 8px"
|
||||||
|
></mat-spinner>
|
||||||
Searching...
|
Searching...
|
||||||
</mat-option>
|
</mat-option>
|
||||||
}
|
}
|
||||||
@for (client of filteredClients; track client.id) {
|
@for (client of filteredClients; track client.id) {
|
||||||
<mat-option [value]="client">
|
<mat-option [value]="client">
|
||||||
<mat-icon style="font-size: 15px; vertical-align: middle; color: #4a6cf7; margin-right: 4px"
|
<mat-icon
|
||||||
|
style="font-size: 15px; vertical-align: middle; color: #4a6cf7; margin-right: 4px"
|
||||||
>person</mat-icon
|
>person</mat-icon
|
||||||
>
|
>
|
||||||
<strong>{{ client.displayName }}</strong>
|
<strong>{{ client.displayName }}</strong>
|
||||||
@@ -76,8 +87,12 @@
|
|||||||
</mat-autocomplete>
|
</mat-autocomplete>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="member-hint">
|
||||||
@if (selectedMemberId) {
|
<mat-icon>info</mat-icon>
|
||||||
|
Search and select a member to auto-fill their personal details below.
|
||||||
|
</div>
|
||||||
|
} @else {
|
||||||
|
<!-- Selected member badge with remove button -->
|
||||||
<div class="member-selected-badge">
|
<div class="member-selected-badge">
|
||||||
<mat-icon>check_circle</mat-icon>
|
<mat-icon>check_circle</mat-icon>
|
||||||
<span
|
<span
|
||||||
@@ -94,11 +109,9 @@
|
|||||||
<span style="color: #c53030"> · No active share account found</span>
|
<span style="color: #c53030"> · No active share account found</span>
|
||||||
}
|
}
|
||||||
<span> · Details auto-filled below</span>
|
<span> · Details auto-filled below</span>
|
||||||
</div>
|
<button type="button" class="member-remove-btn" (click)="clearMember()" title="Remove member">
|
||||||
} @else {
|
<mat-icon>close</mat-icon>
|
||||||
<div class="member-hint">
|
</button>
|
||||||
<mat-icon>info</mat-icon>
|
|
||||||
Search and select a member to auto-fill their personal details below.
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -106,8 +119,18 @@
|
|||||||
<!-- Family Members (shown after a member is selected) -->
|
<!-- Family Members (shown after a member is selected) -->
|
||||||
@if (selectedMemberId) {
|
@if (selectedMemberId) {
|
||||||
<div class="family-section">
|
<div class="family-section">
|
||||||
<div class="section-label"><mat-icon>group</mat-icon> Family Members</div>
|
<div class="section-label section-label--toggle" (click)="familyExpanded = !familyExpanded">
|
||||||
|
<mat-icon>group</mat-icon>
|
||||||
|
Family Members
|
||||||
|
@if (familyMembers.length > 0) {
|
||||||
|
<span class="section-count">{{ familyMembers.length }}</span>
|
||||||
|
}
|
||||||
|
<mat-icon class="section-chevron" [class.section-chevron--open]="familyExpanded"
|
||||||
|
>expand_more</mat-icon
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (familyExpanded) {
|
||||||
@if (familyLoading) {
|
@if (familyLoading) {
|
||||||
<div class="family-loading">
|
<div class="family-loading">
|
||||||
<mat-spinner diameter="18"></mat-spinner>
|
<mat-spinner diameter="18"></mat-spinner>
|
||||||
@@ -132,7 +155,9 @@
|
|||||||
<span class="family-name">{{ familyMemberDisplayName(member) }}</span>
|
<span class="family-name">{{ familyMemberDisplayName(member) }}</span>
|
||||||
|
|
||||||
@if (member.relationship || member.relationshipId?.name) {
|
@if (member.relationship || member.relationshipId?.name) {
|
||||||
<span class="family-rel">{{ member.relationship || member.relationshipId?.name }}</span>
|
<span class="family-rel">{{
|
||||||
|
member.relationship || member.relationshipId?.name
|
||||||
|
}}</span>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (isLinkedClient(member)) {
|
@if (isLinkedClient(member)) {
|
||||||
@@ -174,7 +199,12 @@
|
|||||||
<!-- Expanded: detail row + KYC -->
|
<!-- Expanded: detail row + KYC -->
|
||||||
@if (expandedFamilyCards.has(member.id)) {
|
@if (expandedFamilyCards.has(member.id)) {
|
||||||
<!-- Detail row: PAN, Aadhaar, Nationality, Mobile -->
|
<!-- Detail row: PAN, Aadhaar, Nationality, Mobile -->
|
||||||
@if (member.panNumber || member.aadhaarNumber || member.nationality || member.mobileNumber) {
|
@if (
|
||||||
|
member.panNumber ||
|
||||||
|
member.aadhaarNumber ||
|
||||||
|
member.nationality ||
|
||||||
|
member.mobileNumber
|
||||||
|
) {
|
||||||
<div class="family-detail-row">
|
<div class="family-detail-row">
|
||||||
@if (member.panNumber) {
|
@if (member.panNumber) {
|
||||||
<div class="family-detail-item">
|
<div class="family-detail-item">
|
||||||
@@ -301,7 +331,9 @@
|
|||||||
<div
|
<div
|
||||||
class="kyc-expiry-block"
|
class="kyc-expiry-block"
|
||||||
[class.kyc-expiry-block--expired]="fmExp.isExpired"
|
[class.kyc-expiry-block--expired]="fmExp.isExpired"
|
||||||
[class.kyc-expiry-block--soon]="fmExp.isExpiringSoon && !fmExp.isExpired"
|
[class.kyc-expiry-block--soon]="
|
||||||
|
fmExp.isExpiringSoon && !fmExp.isExpired
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<mat-icon>{{ fmExp.isExpired ? 'event_busy' : 'event' }}</mat-icon>
|
<mat-icon>{{ fmExp.isExpired ? 'event_busy' : 'event' }}</mat-icon>
|
||||||
<div>
|
<div>
|
||||||
@@ -338,7 +370,9 @@
|
|||||||
[class.kyc-vbadge--ok]="fmKyc.panVerified"
|
[class.kyc-vbadge--ok]="fmKyc.panVerified"
|
||||||
[class.kyc-vbadge--no]="!fmKyc.panVerified"
|
[class.kyc-vbadge--no]="!fmKyc.panVerified"
|
||||||
>
|
>
|
||||||
<mat-icon>{{ fmKyc.panVerified ? 'check_circle' : 'cancel' }}</mat-icon>
|
<mat-icon>{{
|
||||||
|
fmKyc.panVerified ? 'check_circle' : 'cancel'
|
||||||
|
}}</mat-icon>
|
||||||
{{ fmKyc.panVerified ? 'Verified' : 'Not Verified' }}
|
{{ fmKyc.panVerified ? 'Verified' : 'Not Verified' }}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
@@ -357,7 +391,9 @@
|
|||||||
[class.kyc-vbadge--ok]="fmKyc.aadhaarVerified"
|
[class.kyc-vbadge--ok]="fmKyc.aadhaarVerified"
|
||||||
[class.kyc-vbadge--no]="!fmKyc.aadhaarVerified"
|
[class.kyc-vbadge--no]="!fmKyc.aadhaarVerified"
|
||||||
>
|
>
|
||||||
<mat-icon>{{ fmKyc.aadhaarVerified ? 'check_circle' : 'cancel' }}</mat-icon>
|
<mat-icon>{{
|
||||||
|
fmKyc.aadhaarVerified ? 'check_circle' : 'cancel'
|
||||||
|
}}</mat-icon>
|
||||||
{{ fmKyc.aadhaarVerified ? 'Verified' : 'Not Verified' }}
|
{{ fmKyc.aadhaarVerified ? 'Verified' : 'Not Verified' }}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
@@ -394,7 +430,8 @@
|
|||||||
}
|
}
|
||||||
@if (fmKyc.kycDocExpiryDate) {
|
@if (fmKyc.kycDocExpiryDate) {
|
||||||
<div class="kyc-item-sub">
|
<div class="kyc-item-sub">
|
||||||
Expires: {{ parseDob(fmKyc.kycDocExpiryDate) | date: 'dd MMM yyyy' }}
|
Expires:
|
||||||
|
{{ parseDob(fmKyc.kycDocExpiryDate) | date: 'dd MMM yyyy' }}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -410,22 +447,107 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<!-- ═══ MANUAL ENTRY WARNING ═══ -->
|
||||||
|
@if (!familyLoading && manualEntryMembers.length > 0) {
|
||||||
|
<div class="manual-entry-warning">
|
||||||
|
<mat-icon>warning_amber</mat-icon>
|
||||||
|
<div class="manual-entry-warning__body">
|
||||||
|
<strong>Submission blocked</strong> —
|
||||||
|
@for (m of manualEntryMembers; track m.id; let last = $last) {
|
||||||
|
<span>
|
||||||
|
{{ familyMemberDisplayName(m)
|
||||||
|
}}{{
|
||||||
|
m.relationship || m.relationshipId?.name
|
||||||
|
? ' (' + (m.relationship || m.relationshipId?.name) + ')'
|
||||||
|
: ''
|
||||||
|
}}{{ last ? '' : ', ' }}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
{{ manualEntryMembers.length === 1 ? 'is' : 'are' }} not registered as a CBS client. Use
|
||||||
|
<strong>Create as Client</strong> below before submitting.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- ═══ ADD AS CLIENT PANEL ═══ -->
|
||||||
|
@if (!familyLoading) {
|
||||||
|
<div class="aac-panel">
|
||||||
|
<button class="aac-panel__toggle" (click)="toggleAddAsClientPanel()">
|
||||||
|
<mat-icon>person_add</mat-icon>
|
||||||
|
<span>Create New Family Member as Client</span>
|
||||||
|
<mat-icon
|
||||||
|
class="aac-panel__chevron"
|
||||||
|
[class.aac-panel__chevron--open]="showAddAsClientPanel"
|
||||||
|
>
|
||||||
|
expand_more
|
||||||
|
</mat-icon>
|
||||||
|
</button>
|
||||||
|
@if (showAddAsClientPanel) {
|
||||||
|
<div class="aac-body">
|
||||||
|
@if (addAsClientLoading) {
|
||||||
|
<div class="aac-loading">
|
||||||
|
<mat-icon>hourglass_empty</mat-icon> Loading relationships…
|
||||||
|
</div>
|
||||||
|
} @else {
|
||||||
|
<p class="aac-hint">Select the relationship you want to create as a client:</p>
|
||||||
|
<div class="aac-chips">
|
||||||
|
@for (rel of availableRelationships; track rel.id) {
|
||||||
|
<button
|
||||||
|
class="aac-chip"
|
||||||
|
[class.aac-chip--selected]="selectedNewRelationship?.id === rel.id"
|
||||||
|
(click)="selectNewRelationship(rel)"
|
||||||
|
>
|
||||||
|
{{ rel.name }}
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
@for (rel of takenRelationships; track rel.id) {
|
||||||
|
<button class="aac-chip aac-chip--taken" disabled>
|
||||||
|
<mat-icon>check_circle</mat-icon> {{ rel.name }}
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
@if (selectedNewRelationship) {
|
||||||
|
<div class="aac-create-bar">
|
||||||
|
<span class="aac-create-bar__label">
|
||||||
|
<mat-icon>person</mat-icon>
|
||||||
|
Creating a
|
||||||
|
<strong>{{ selectedNewRelationship.name }}</strong> as CBS client
|
||||||
|
</span>
|
||||||
|
<button class="aac-create-bar__btn" (click)="createClientForRelationship()">
|
||||||
|
<mat-icon>person_add</mat-icon> Create as Client
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<!-- /familyExpanded -->
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<!-- KYC Status -->
|
<!-- KYC Status -->
|
||||||
@if (selectedMemberId) {
|
@if (selectedMemberId) {
|
||||||
<div class="kyc-section">
|
<div class="kyc-section">
|
||||||
<div class="section-label">
|
<div class="section-label section-label--toggle" (click)="kycExpanded = !kycExpanded">
|
||||||
<mat-icon>verified_user</mat-icon>
|
<mat-icon>verified_user</mat-icon>
|
||||||
KYC Details
|
KYC Details
|
||||||
@if (profileForm.value.applicantName) {
|
@if (profileForm.value.applicantName) {
|
||||||
<span class="kyc-owner-chip">{{ profileForm.value.applicantName }}</span>
|
<span class="kyc-owner-chip">{{ profileForm.value.applicantName }}</span>
|
||||||
}
|
}
|
||||||
|
<mat-icon class="section-chevron" [class.section-chevron--open]="kycExpanded"
|
||||||
|
>expand_more</mat-icon
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if (kycExpanded) {
|
||||||
@if (familyLoading) {
|
@if (familyLoading) {
|
||||||
<div class="family-loading"><mat-spinner diameter="18"></mat-spinner> Loading KYC details...</div>
|
<div class="family-loading">
|
||||||
|
<mat-spinner diameter="18"></mat-spinner> Loading KYC details...
|
||||||
|
</div>
|
||||||
} @else if (!kycData) {
|
} @else if (!kycData) {
|
||||||
<div class="kyc-not-done">
|
<div class="kyc-not-done">
|
||||||
<mat-icon>warning</mat-icon>
|
<mat-icon>warning</mat-icon>
|
||||||
@@ -459,7 +581,9 @@
|
|||||||
{{ kycData.kycType === 2 ? 'Non-Individual' : 'Individual' }}
|
{{ kycData.kycType === 2 ? 'Non-Individual' : 'Individual' }}
|
||||||
</span>
|
</span>
|
||||||
@if (kycData.ckycNumber) {
|
@if (kycData.ckycNumber) {
|
||||||
<span class="ckyc-badge"> <mat-icon>task_alt</mat-icon> CKYC: {{ kycData.ckycNumber }} </span>
|
<span class="ckyc-badge">
|
||||||
|
<mat-icon>task_alt</mat-icon> CKYC: {{ kycData.ckycNumber }}
|
||||||
|
</span>
|
||||||
}
|
}
|
||||||
@if (kycData.verifiedOn) {
|
@if (kycData.verifiedOn) {
|
||||||
<span class="kyc-date-chip">
|
<span class="kyc-date-chip">
|
||||||
@@ -471,7 +595,10 @@
|
|||||||
|
|
||||||
<!-- ── Risk + Expiry banner ── -->
|
<!-- ── Risk + Expiry banner ── -->
|
||||||
<div class="kyc-risk-row">
|
<div class="kyc-risk-row">
|
||||||
<div class="kyc-risk-block" [class]="'kyc-risk-block--' + riskClass(kycData.riskCategory)">
|
<div
|
||||||
|
class="kyc-risk-block"
|
||||||
|
[class]="'kyc-risk-block--' + riskClass(kycData.riskCategory)"
|
||||||
|
>
|
||||||
<mat-icon>{{
|
<mat-icon>{{
|
||||||
kycData.riskCategory === 3
|
kycData.riskCategory === 3
|
||||||
? 'priority_high'
|
? 'priority_high'
|
||||||
@@ -532,7 +659,9 @@
|
|||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
@if (kycData.panVerifiedOn) {
|
@if (kycData.panVerifiedOn) {
|
||||||
<div class="kyc-item-sub">on {{ parseDob(kycData.panVerifiedOn) | date: 'dd MMM yyyy' }}</div>
|
<div class="kyc-item-sub">
|
||||||
|
on {{ parseDob(kycData.panVerifiedOn) | date: 'dd MMM yyyy' }}
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -563,7 +692,9 @@
|
|||||||
@if (kycData.panAadhaarSeedingStatus === 'Y') {
|
@if (kycData.panAadhaarSeedingStatus === 'Y') {
|
||||||
<span class="kyc-vbadge kyc-vbadge--ok"><mat-icon>link</mat-icon> Linked</span>
|
<span class="kyc-vbadge kyc-vbadge--ok"><mat-icon>link</mat-icon> Linked</span>
|
||||||
} @else if (kycData.panAadhaarSeedingStatus === 'N') {
|
} @else if (kycData.panAadhaarSeedingStatus === 'N') {
|
||||||
<span class="kyc-vbadge kyc-vbadge--no"><mat-icon>link_off</mat-icon> Not Linked</span>
|
<span class="kyc-vbadge kyc-vbadge--no"
|
||||||
|
><mat-icon>link_off</mat-icon> Not Linked</span
|
||||||
|
>
|
||||||
} @else if (kycData.panAadhaarSeedingStatus === 'E') {
|
} @else if (kycData.panAadhaarSeedingStatus === 'E') {
|
||||||
<span class="kyc-vbadge kyc-vbadge--na"><mat-icon>block</mat-icon> Exempt</span>
|
<span class="kyc-vbadge kyc-vbadge--na"><mat-icon>block</mat-icon> Exempt</span>
|
||||||
} @else {
|
} @else {
|
||||||
@@ -644,9 +775,438 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
<!-- /kycExpanded -->
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<!-- Eligibility Verification -->
|
||||||
|
@if (selectedMemberId && !familyLoading) {
|
||||||
|
<div class="elig-section">
|
||||||
|
<div class="section-label section-label--toggle" (click)="eligExpanded = !eligExpanded">
|
||||||
|
<mat-icon>fact_check</mat-icon>
|
||||||
|
Eligibility Verification
|
||||||
|
<span
|
||||||
|
class="section-elig-badge"
|
||||||
|
[class.section-elig-badge--ok]="isEligible"
|
||||||
|
[class.section-elig-badge--fail]="!isEligible"
|
||||||
|
>
|
||||||
|
<mat-icon>{{ isEligible ? 'check_circle' : 'warning' }}</mat-icon>
|
||||||
|
{{ isEligible ? 'Eligible' : 'Not Eligible' }}
|
||||||
|
</span>
|
||||||
|
<mat-icon class="section-chevron" [class.section-chevron--open]="eligExpanded"
|
||||||
|
>expand_more</mat-icon
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (eligExpanded) {
|
||||||
|
<!-- No Due Certificate -->
|
||||||
|
<div
|
||||||
|
class="ndc-banner"
|
||||||
|
[class.ndc-banner--eligible]="isEligible"
|
||||||
|
[class.ndc-banner--not]="!isEligible"
|
||||||
|
>
|
||||||
|
<mat-icon>{{ isEligible ? 'verified' : 'gpp_bad' }}</mat-icon>
|
||||||
|
<div class="ndc-banner__body">
|
||||||
|
<strong>No Due Certificate</strong>
|
||||||
|
<span>{{
|
||||||
|
isEligible
|
||||||
|
? 'ELIGIBLE — No overdue loans found for nominee or family members'
|
||||||
|
: 'NOT ELIGIBLE — One or more overdue loans exist'
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
@if (familyAccountsLoading) {
|
||||||
|
<mat-spinner diameter="16"></mat-spinner>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Nominee Loan Accounts -->
|
||||||
|
@if (nomineeLoanAccounts.length > 0) {
|
||||||
|
<div class="elig-sub-label"><mat-icon>account_balance</mat-icon> Nominee — Loan Accounts</div>
|
||||||
|
<div class="elig-table-wrap">
|
||||||
|
<table class="elig-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Account No</th>
|
||||||
|
<th>Product</th>
|
||||||
|
<th>Principal</th>
|
||||||
|
<th>Outstanding</th>
|
||||||
|
<th>Status</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@for (loan of nomineeLoanAccounts; track loan.id) {
|
||||||
|
<tr>
|
||||||
|
<td class="mono">{{ loan.accountNo }}</td>
|
||||||
|
<td>{{ loan.productName }}</td>
|
||||||
|
<td class="amt">
|
||||||
|
₹{{
|
||||||
|
loan.originalLoan ?? loan.summary?.principalDisbursed ?? 0 | number: '1.0-0'
|
||||||
|
}}
|
||||||
|
</td>
|
||||||
|
<td class="amt">₹{{ loan.summary?.totalOutstanding ?? 0 | number: '1.0-0' }}</td>
|
||||||
|
<td>
|
||||||
|
<span class="loan-status" [class]="'loan-status--' + loanStatusClass(loan)">{{
|
||||||
|
loanStatusLabel(loan)
|
||||||
|
}}</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
} @else {
|
||||||
|
<div class="elig-empty-row">
|
||||||
|
<mat-icon>info</mat-icon> No loan accounts found for nominee.
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- Nominee Savings Accounts -->
|
||||||
|
@if (nomineeSavingsAccounts.length > 0) {
|
||||||
|
<div class="elig-sub-label"><mat-icon>savings</mat-icon> Nominee — Savings Accounts</div>
|
||||||
|
<div class="elig-table-wrap">
|
||||||
|
<table class="elig-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Account No</th>
|
||||||
|
<th>Product</th>
|
||||||
|
<th>Balance</th>
|
||||||
|
<th>Status</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@for (sav of nomineeSavingsAccounts; track sav.id) {
|
||||||
|
<tr>
|
||||||
|
<td class="mono">{{ sav.accountNo }}</td>
|
||||||
|
<td>{{ sav.productName }}</td>
|
||||||
|
<td class="amt">₹{{ sav.accountBalance ?? sav.balance ?? 0 | number: '1.0-0' }}</td>
|
||||||
|
<td>
|
||||||
|
<span
|
||||||
|
class="loan-status"
|
||||||
|
[class]="
|
||||||
|
(sav.status?.value ?? '').toLowerCase().includes('active')
|
||||||
|
? 'loan-status--active'
|
||||||
|
: 'loan-status--closed'
|
||||||
|
"
|
||||||
|
>{{ sav.status?.value ?? '—' }}</span
|
||||||
|
>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- Family Members Accounts -->
|
||||||
|
@for (member of familyMembers; track member.id) {
|
||||||
|
@if (member.linkedClientId) {
|
||||||
|
@let fmAcc = familyMemberAccounts(member.linkedClientId);
|
||||||
|
<div class="elig-fm-header">
|
||||||
|
<mat-icon>group</mat-icon>
|
||||||
|
{{ familyMemberDisplayName(member) }}
|
||||||
|
@if (member.relationship || member.relationshipId?.name) {
|
||||||
|
<span class="elig-fm-rel">{{
|
||||||
|
member.relationship || member.relationshipId?.name
|
||||||
|
}}</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
@if (familyAccountsLoading && !fmAcc) {
|
||||||
|
<div class="elig-loading">
|
||||||
|
<mat-spinner diameter="14"></mat-spinner> Loading accounts...
|
||||||
|
</div>
|
||||||
|
} @else if (fmAcc) {
|
||||||
|
@if ((fmAcc.loanAccounts?.length ?? 0) > 0) {
|
||||||
|
<div class="elig-sub-label elig-sub-label--fm">
|
||||||
|
<mat-icon>account_balance</mat-icon> Loan Accounts
|
||||||
|
</div>
|
||||||
|
<div class="elig-table-wrap">
|
||||||
|
<table class="elig-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Account No</th>
|
||||||
|
<th>Product</th>
|
||||||
|
<th>Principal</th>
|
||||||
|
<th>Outstanding</th>
|
||||||
|
<th>Status</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@for (loan of fmAcc.loanAccounts; track loan.id) {
|
||||||
|
<tr>
|
||||||
|
<td class="mono">{{ loan.accountNo }}</td>
|
||||||
|
<td>{{ loan.productName }}</td>
|
||||||
|
<td class="amt">
|
||||||
|
₹{{
|
||||||
|
loan.originalLoan ?? loan.summary?.principalDisbursed ?? 0
|
||||||
|
| number: '1.0-0'
|
||||||
|
}}
|
||||||
|
</td>
|
||||||
|
<td class="amt">
|
||||||
|
₹{{ loan.summary?.totalOutstanding ?? 0 | number: '1.0-0' }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span
|
||||||
|
class="loan-status"
|
||||||
|
[class]="'loan-status--' + loanStatusClass(loan)"
|
||||||
|
>{{ loanStatusLabel(loan) }}</span
|
||||||
|
>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
} @else {
|
||||||
|
<div class="elig-empty-row"><mat-icon>info</mat-icon> No loan accounts.</div>
|
||||||
|
}
|
||||||
|
@if ((fmAcc.savingsAccounts?.length ?? 0) > 0) {
|
||||||
|
<div class="elig-sub-label elig-sub-label--fm">
|
||||||
|
<mat-icon>savings</mat-icon> Savings Accounts
|
||||||
|
</div>
|
||||||
|
<div class="elig-table-wrap">
|
||||||
|
<table class="elig-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Account No</th>
|
||||||
|
<th>Product</th>
|
||||||
|
<th>Balance</th>
|
||||||
|
<th>Status</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@for (sav of fmAcc.savingsAccounts; track sav.id) {
|
||||||
|
<tr>
|
||||||
|
<td class="mono">{{ sav.accountNo }}</td>
|
||||||
|
<td>{{ sav.productName }}</td>
|
||||||
|
<td class="amt">
|
||||||
|
₹{{ sav.accountBalance ?? sav.balance ?? 0 | number: '1.0-0' }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span
|
||||||
|
class="loan-status"
|
||||||
|
[class]="
|
||||||
|
(sav.status?.value ?? '').toLowerCase().includes('active')
|
||||||
|
? 'loan-status--active'
|
||||||
|
: 'loan-status--closed'
|
||||||
|
"
|
||||||
|
>{{ sav.status?.value ?? '—' }}</span
|
||||||
|
>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
} @else {
|
||||||
|
<div class="elig-empty-row"><mat-icon>info</mat-icon> No account data available.</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<!-- /eligExpanded -->
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- Guarantor Status (Is this nominee a guarantor on any loan?) -->
|
||||||
|
@if (selectedMemberId && nomineeAccounts) {
|
||||||
|
<div class="guarantors-section">
|
||||||
|
<div
|
||||||
|
class="section-label section-label--toggle"
|
||||||
|
(click)="guarantorsExpanded = !guarantorsExpanded"
|
||||||
|
>
|
||||||
|
<mat-icon>handshake</mat-icon>
|
||||||
|
Guarantor Status
|
||||||
|
@if (nomineeGuarantorAccounts.length > 0) {
|
||||||
|
<span class="section-count">{{ nomineeGuarantorAccounts.length }}</span>
|
||||||
|
}
|
||||||
|
<mat-icon class="section-chevron" [class.section-chevron--open]="guarantorsExpanded"
|
||||||
|
>expand_more</mat-icon
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (guarantorsExpanded) {
|
||||||
|
@if (nomineeGuarantorAccounts.length === 0) {
|
||||||
|
<div class="family-empty">
|
||||||
|
<mat-icon>info_outline</mat-icon>
|
||||||
|
This member is not a guarantor on any loan.
|
||||||
|
</div>
|
||||||
|
} @else {
|
||||||
|
<div class="guarantor-summary-banner">
|
||||||
|
<mat-icon>verified_user</mat-icon>
|
||||||
|
This member is acting as guarantor on
|
||||||
|
<strong
|
||||||
|
>{{ nomineeGuarantorAccounts.length }} loan{{
|
||||||
|
nomineeGuarantorAccounts.length > 1 ? 's' : ''
|
||||||
|
}}</strong
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="family-list">
|
||||||
|
@for (g of nomineeGuarantorAccounts; track g.id) {
|
||||||
|
<div class="guarantor-card">
|
||||||
|
<!-- Header: Loan No + badges -->
|
||||||
|
<div class="guarantor-card-top">
|
||||||
|
<div class="guarantor-card-left">
|
||||||
|
<mat-icon>credit_score</mat-icon>
|
||||||
|
<span class="guarantor-name">Loan #{{ g.accountNo }}</span>
|
||||||
|
@if (g.relationship) {
|
||||||
|
<span class="guarantor-rel">{{ g.relationship }}</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="guarantor-card-right">
|
||||||
|
<span class="guarantor-type-badge">{{ g.productName }}</span>
|
||||||
|
@if (g.inArrears) {
|
||||||
|
<span class="guarantor-status-badge guarantor-status-badge--deleted"
|
||||||
|
>In Arrears</span
|
||||||
|
>
|
||||||
|
} @else {
|
||||||
|
<span class="guarantor-status-badge guarantor-status-badge--active">{{
|
||||||
|
g.status?.value
|
||||||
|
}}</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Borrower name row -->
|
||||||
|
@if (g.borrowerName) {
|
||||||
|
<div class="guarantor-borrower-row">
|
||||||
|
<mat-icon>person</mat-icon>
|
||||||
|
Borrower: <strong>{{ g.borrowerName }}</strong>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- Savings A/C row -->
|
||||||
|
@if (g.savingsAccountNo) {
|
||||||
|
<div class="guarantor-savings-row">
|
||||||
|
<mat-icon>account_balance_wallet</mat-icon>
|
||||||
|
Pledged Savings A/C: <strong class="mono">{{ g.savingsAccountNo }}</strong>
|
||||||
|
@if (g.savingsBalance != null) {
|
||||||
|
<span class="guarantor-savings-balance">
|
||||||
|
· Balance: <strong>{{ g.savingsBalance | number: '1.2-2' }}</strong>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- Loan health indicator -->
|
||||||
|
@if (g.inArrears && g.totalOverdue > 0) {
|
||||||
|
<div class="guarantor-health-row guarantor-health-row--problem">
|
||||||
|
<mat-icon>warning</mat-icon>
|
||||||
|
Overdue: <strong>{{ g.totalOverdue | number: '1.2-2' }}</strong>
|
||||||
|
@if (g.overdueSinceDate) {
|
||||||
|
· Since {{ g.overdueSinceDate | date: 'dd MMM yyyy' }}
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
} @else {
|
||||||
|
<div class="guarantor-health-row guarantor-health-row--ok">
|
||||||
|
<mat-icon>check_circle</mat-icon>
|
||||||
|
No overdue — loan is in good standing
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- Amounts row -->
|
||||||
|
<div class="guarantor-funds-row">
|
||||||
|
<div class="guarantor-fund-item">
|
||||||
|
<span class="guarantor-fund-label">Loan Amount</span>
|
||||||
|
<span class="guarantor-fund-value">{{ g.originalLoan | number: '1.2-2' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="guarantor-fund-item">
|
||||||
|
<span class="guarantor-fund-label">Guaranteed</span>
|
||||||
|
<span class="guarantor-fund-value">{{ g.onHoldAmount | number: '1.2-2' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="guarantor-fund-item">
|
||||||
|
<span class="guarantor-fund-label">Released</span>
|
||||||
|
<span class="guarantor-fund-value">{{ g.amountReleased | number: '1.2-2' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="guarantor-fund-item">
|
||||||
|
<span class="guarantor-fund-label">Remaining</span>
|
||||||
|
<span
|
||||||
|
class="guarantor-fund-value"
|
||||||
|
[class.guarantor-fund-value--active]="g.amountRemaining > 0"
|
||||||
|
>
|
||||||
|
{{ g.amountRemaining | number: '1.2-2' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="guarantor-fund-item">
|
||||||
|
<span class="guarantor-fund-label">Balance</span>
|
||||||
|
<span class="guarantor-fund-value">{{ g.loanBalance | number: '1.2-2' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="guarantor-fund-item">
|
||||||
|
<span class="guarantor-fund-label">Paid</span>
|
||||||
|
<span class="guarantor-fund-value">{{ g.amountPaid | number: '1.2-2' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Loan timeline -->
|
||||||
|
<div class="guarantor-timeline">
|
||||||
|
<div class="guarantor-timeline-item">
|
||||||
|
<span class="guarantor-fund-label">Submitted</span>
|
||||||
|
<span class="guarantor-fund-value">{{
|
||||||
|
g.submittedOnDate ? (g.submittedOnDate | date: 'dd MMM yyyy') : '—'
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="guarantor-timeline-item">
|
||||||
|
<span class="guarantor-fund-label">Approved</span>
|
||||||
|
<span class="guarantor-fund-value">{{
|
||||||
|
g.approvedOnDate ? (g.approvedOnDate | date: 'dd MMM yyyy') : '—'
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="guarantor-timeline-item">
|
||||||
|
<span class="guarantor-fund-label">Disbursed</span>
|
||||||
|
<span class="guarantor-fund-value">{{
|
||||||
|
g.disbursedOnDate ? (g.disbursedOnDate | date: 'dd MMM yyyy') : '—'
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="guarantor-timeline-item">
|
||||||
|
<span class="guarantor-fund-label">Matures</span>
|
||||||
|
<span class="guarantor-fund-value">{{
|
||||||
|
g.expectedMaturityDate ? (g.expectedMaturityDate | date: 'dd MMM yyyy') : '—'
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- Spacer pushes summary card to the bottom -->
|
||||||
|
<div class="step1-left-spacer"></div>
|
||||||
|
|
||||||
|
<!-- Member summary pinned at bottom -->
|
||||||
|
@if (selectedMemberId && profileForm.value.applicantName) {
|
||||||
|
<div class="member-summary-card">
|
||||||
|
<div class="msc-avatar">{{ profileForm.value.applicantName?.charAt(0) | uppercase }}</div>
|
||||||
|
<div class="msc-info">
|
||||||
|
<div class="msc-name">{{ profileForm.value.applicantName }}</div>
|
||||||
|
<div class="msc-meta">
|
||||||
|
ID: {{ selectedMemberId }}
|
||||||
|
@if (selectedShareCount) {
|
||||||
|
· {{ selectedShareCount }} shares
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="msc-elig" [class.msc-elig--ok]="isEligible" [class.msc-elig--fail]="!isEligible">
|
||||||
|
<mat-icon>{{ isEligible ? 'check_circle' : 'warning' }}</mat-icon>
|
||||||
|
{{ isEligible ? 'Eligible' : 'Not Eligible' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<!-- /step1-left-scroll -->
|
||||||
|
</div>
|
||||||
|
<!-- /step1-left -->
|
||||||
|
|
||||||
|
<!-- ── Drag resize handle ── -->
|
||||||
|
<div class="step1-drag-handle" [class.dragging]="isDragging" (mousedown)="startDrag($event, layoutRef)">
|
||||||
|
<span class="drag-dots"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── RIGHT: Form panel ── -->
|
||||||
|
<div class="step1-right">
|
||||||
<!-- Applicant Profile -->
|
<!-- Applicant Profile -->
|
||||||
<div class="section-label"><mat-icon>person</mat-icon> Applicant Profile</div>
|
<div class="section-label"><mat-icon>person</mat-icon> Applicant Profile</div>
|
||||||
|
|
||||||
@@ -725,6 +1285,10 @@
|
|||||||
Next <mat-icon>arrow_forward</mat-icon>
|
Next <mat-icon>arrow_forward</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- /step1-right -->
|
||||||
|
</div>
|
||||||
|
<!-- /step1-layout -->
|
||||||
</form>
|
</form>
|
||||||
</mat-step>
|
</mat-step>
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -71,7 +71,43 @@ export class NominationApplyComponent implements OnInit {
|
|||||||
kycData: any = null;
|
kycData: any = null;
|
||||||
familyMemberKycMap: Record<string, any> = {};
|
familyMemberKycMap: Record<string, any> = {};
|
||||||
familyKycLoading = false;
|
familyKycLoading = false;
|
||||||
|
nomineeAccounts: any = null;
|
||||||
|
familyMemberAccountsMap: Record<string, any> = {};
|
||||||
|
familyAccountsLoading = false;
|
||||||
expandedFamilyCards = new Set<number>();
|
expandedFamilyCards = new Set<number>();
|
||||||
|
familyExpanded = true;
|
||||||
|
kycExpanded = true;
|
||||||
|
eligExpanded = true;
|
||||||
|
guarantorsExpanded = true;
|
||||||
|
|
||||||
|
// ── Resizable split panel ─────────────────────────────────────────────────
|
||||||
|
leftPanelWidth = 44;
|
||||||
|
isDragging = false;
|
||||||
|
private _dragLayout: HTMLElement | null = null;
|
||||||
|
private _onMouseMove = (e: MouseEvent) => {
|
||||||
|
if (!this._dragLayout) return;
|
||||||
|
const rect = this._dragLayout.getBoundingClientRect();
|
||||||
|
const raw = ((e.clientX - rect.left) / rect.width) * 100;
|
||||||
|
this.leftPanelWidth = Math.min(70, Math.max(25, raw));
|
||||||
|
};
|
||||||
|
private _onMouseUp = () => {
|
||||||
|
this.isDragging = false;
|
||||||
|
document.removeEventListener('mousemove', this._onMouseMove);
|
||||||
|
document.removeEventListener('mouseup', this._onMouseUp);
|
||||||
|
};
|
||||||
|
|
||||||
|
startDrag(event: MouseEvent, layoutEl: HTMLElement): void {
|
||||||
|
event.preventDefault();
|
||||||
|
this.isDragging = true;
|
||||||
|
this._dragLayout = layoutEl;
|
||||||
|
document.addEventListener('mousemove', this._onMouseMove);
|
||||||
|
document.addEventListener('mouseup', this._onMouseUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
familyMemberOptions: any = null;
|
||||||
|
showAddAsClientPanel = false;
|
||||||
|
selectedNewRelationship: any = null;
|
||||||
|
addAsClientLoading = false;
|
||||||
|
|
||||||
profileForm!: FormGroup;
|
profileForm!: FormGroup;
|
||||||
postForm!: FormGroup;
|
postForm!: FormGroup;
|
||||||
@@ -121,6 +157,59 @@ export class NominationApplyComponent implements OnInit {
|
|||||||
return !!member.linkedClientId;
|
return !!member.linkedClientId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private get takenRelationshipIds(): Set<number> {
|
||||||
|
return new Set(
|
||||||
|
this.familyMembers
|
||||||
|
.map((m) => (typeof m.relationshipId === 'object' ? m.relationshipId?.id : m.relationshipId))
|
||||||
|
.filter((id) => !!id)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get availableRelationships(): any[] {
|
||||||
|
const taken = this.takenRelationshipIds;
|
||||||
|
return (this.familyMemberOptions?.relationshipIdOptions || []).filter((r: any) => !taken.has(r.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
get takenRelationships(): any[] {
|
||||||
|
const taken = this.takenRelationshipIds;
|
||||||
|
return (this.familyMemberOptions?.relationshipIdOptions || []).filter((r: any) => taken.has(r.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleAddAsClientPanel(): void {
|
||||||
|
this.showAddAsClientPanel = !this.showAddAsClientPanel;
|
||||||
|
this.selectedNewRelationship = null;
|
||||||
|
if (this.showAddAsClientPanel && !this.familyMemberOptions) {
|
||||||
|
this.addAsClientLoading = true;
|
||||||
|
this.clientsService.getClientTemplate().subscribe({
|
||||||
|
next: (template: any) => {
|
||||||
|
this.familyMemberOptions = template.familyMemberOptions;
|
||||||
|
this.addAsClientLoading = false;
|
||||||
|
},
|
||||||
|
error: () => (this.addAsClientLoading = false)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selectNewRelationship(rel: any): void {
|
||||||
|
this.selectedNewRelationship = this.selectedNewRelationship?.id === rel.id ? null : rel;
|
||||||
|
}
|
||||||
|
|
||||||
|
createClientForRelationship(): void {
|
||||||
|
if (!this.selectedNewRelationship) return;
|
||||||
|
const prefill = {
|
||||||
|
relationshipId: this.selectedNewRelationship.id,
|
||||||
|
relationship: this.selectedNewRelationship.name,
|
||||||
|
parentClientId: this.selectedMemberId
|
||||||
|
};
|
||||||
|
this.router.navigate(
|
||||||
|
[
|
||||||
|
'/clients',
|
||||||
|
'create'
|
||||||
|
],
|
||||||
|
{ state: { prefill } }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
toggleFamilyCard(memberId: number): void {
|
toggleFamilyCard(memberId: number): void {
|
||||||
if (this.expandedFamilyCards.has(memberId)) {
|
if (this.expandedFamilyCards.has(memberId)) {
|
||||||
this.expandedFamilyCards.delete(memberId);
|
this.expandedFamilyCards.delete(memberId);
|
||||||
@@ -200,6 +289,55 @@ export class NominationApplyComponent implements OnInit {
|
|||||||
return risk === 1 ? 'low' : risk === 2 ? 'medium' : risk === 3 ? 'high' : '';
|
return risk === 1 ? 'low' : risk === 2 ? 'medium' : risk === 3 ? 'high' : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isLoanOverdue(loan: any): boolean {
|
||||||
|
const isClosed = loan.status?.id === 600 || (loan.status?.value ?? '').toString().toLowerCase().includes('closed');
|
||||||
|
if (isClosed) return false;
|
||||||
|
return (loan.summary?.totalOverdue ?? 0) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
loanStatusLabel(loan: any): string {
|
||||||
|
const statusVal = (loan.status?.value ?? '').toString().toLowerCase();
|
||||||
|
if (statusVal.includes('closed')) return 'Closed';
|
||||||
|
if ((loan.summary?.totalOverdue ?? 0) > 0) return 'Overdue';
|
||||||
|
if (statusVal.includes('active')) return 'Active';
|
||||||
|
return loan.status?.value ?? '—';
|
||||||
|
}
|
||||||
|
|
||||||
|
loanStatusClass(loan: any): string {
|
||||||
|
const statusVal = (loan.status?.value ?? '').toString().toLowerCase();
|
||||||
|
if (statusVal.includes('closed')) return 'closed';
|
||||||
|
if ((loan.summary?.totalOverdue ?? 0) > 0) return 'overdue';
|
||||||
|
return 'active';
|
||||||
|
}
|
||||||
|
|
||||||
|
familyMemberAccounts(linkedClientId: number): any {
|
||||||
|
return this.familyMemberAccountsMap['c' + linkedClientId] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get nomineeLoanAccounts(): any[] {
|
||||||
|
return this.nomineeAccounts?.loanAccounts ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
get nomineeSavingsAccounts(): any[] {
|
||||||
|
return this.nomineeAccounts?.savingsAccounts ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
get nomineeGuarantorAccounts(): any[] {
|
||||||
|
return this.nomineeAccounts?.guarantorAccounts ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
get isEligible(): boolean {
|
||||||
|
if (!this.nomineeAccounts) return true;
|
||||||
|
const nomineeOverdue = (this.nomineeAccounts?.loanAccounts ?? []).some((l: any) => this.isLoanOverdue(l));
|
||||||
|
if (nomineeOverdue) return false;
|
||||||
|
for (const key of Object.keys(this.familyMemberAccountsMap)) {
|
||||||
|
const fmAccounts = this.familyMemberAccountsMap[key];
|
||||||
|
const overdue = (fmAccounts?.loanAccounts ?? []).some((l: any) => this.isLoanOverdue(l));
|
||||||
|
if (overdue) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
kycExpiryInfo(expiryDate: any): { date: Date | null; isExpired: boolean; isExpiringSoon: boolean; daysLeft: number } {
|
kycExpiryInfo(expiryDate: any): { date: Date | null; isExpired: boolean; isExpiringSoon: boolean; daysLeft: number } {
|
||||||
const date = this.parseDob(expiryDate);
|
const date = this.parseDob(expiryDate);
|
||||||
if (!date) return { date: null, isExpired: false, isExpiringSoon: false, daysLeft: 0 };
|
if (!date) return { date: null, isExpired: false, isExpiringSoon: false, daysLeft: 0 };
|
||||||
@@ -208,6 +346,22 @@ export class NominationApplyComponent implements OnInit {
|
|||||||
return { date, isExpired: daysLeft < 0, isExpiringSoon: daysLeft >= 0 && daysLeft <= 180, daysLeft };
|
return { date, isExpired: daysLeft < 0, isExpiringSoon: daysLeft >= 0 && daysLeft <= 180, daysLeft };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearMember(): void {
|
||||||
|
this.selectedMemberId = null;
|
||||||
|
this.memberSearchCtrl.setValue('', { emitEvent: false });
|
||||||
|
this.filteredClients = [];
|
||||||
|
this.selectedShareAccountNo = null;
|
||||||
|
this.selectedShareCount = null;
|
||||||
|
this.familyMembers = [];
|
||||||
|
this.kycData = null;
|
||||||
|
this.familyMemberKycMap = {};
|
||||||
|
this.nomineeAccounts = null;
|
||||||
|
this.familyMemberAccountsMap = {};
|
||||||
|
this.familyAccountsLoading = false;
|
||||||
|
this.familyLoading = false;
|
||||||
|
this.profileForm.reset();
|
||||||
|
}
|
||||||
|
|
||||||
onMemberSelect(client: any): void {
|
onMemberSelect(client: any): void {
|
||||||
this.selectedMemberId = client.id;
|
this.selectedMemberId = client.id;
|
||||||
this.memberSearchCtrl.setValue(client.displayName, { emitEvent: false });
|
this.memberSearchCtrl.setValue(client.displayName, { emitEvent: false });
|
||||||
@@ -218,6 +372,14 @@ export class NominationApplyComponent implements OnInit {
|
|||||||
this.familyMemberKycMap = {};
|
this.familyMemberKycMap = {};
|
||||||
this.familyKycLoading = false;
|
this.familyKycLoading = false;
|
||||||
this.expandedFamilyCards = new Set<number>();
|
this.expandedFamilyCards = new Set<number>();
|
||||||
|
this.showAddAsClientPanel = false;
|
||||||
|
this.selectedNewRelationship = null;
|
||||||
|
this.familyExpanded = true;
|
||||||
|
this.kycExpanded = true;
|
||||||
|
this.eligExpanded = true;
|
||||||
|
this.nomineeAccounts = null;
|
||||||
|
this.familyMemberAccountsMap = {};
|
||||||
|
this.familyAccountsLoading = false;
|
||||||
|
|
||||||
forkJoin({
|
forkJoin({
|
||||||
detail: this.clientsService.getClientData(client.id.toString()),
|
detail: this.clientsService.getClientData(client.id.toString()),
|
||||||
@@ -281,6 +443,7 @@ export class NominationApplyComponent implements OnInit {
|
|||||||
this.familyLoading = false;
|
this.familyLoading = false;
|
||||||
this.familyMembers = Array.isArray(family) ? family : [];
|
this.familyMembers = Array.isArray(family) ? family : [];
|
||||||
this.kycData = kyc;
|
this.kycData = kyc;
|
||||||
|
this.nomineeAccounts = accounts;
|
||||||
|
|
||||||
const linkedMembers = this.familyMembers.filter((m) => !!m.linkedClientId);
|
const linkedMembers = this.familyMembers.filter((m) => !!m.linkedClientId);
|
||||||
if (linkedMembers.length > 0) {
|
if (linkedMembers.length > 0) {
|
||||||
@@ -296,6 +459,20 @@ export class NominationApplyComponent implements OnInit {
|
|||||||
this.familyMemberKycMap = kycMap;
|
this.familyMemberKycMap = kycMap;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (linkedMembers.length > 0) {
|
||||||
|
this.familyAccountsLoading = true;
|
||||||
|
const accountCalls: { [k: string]: any } = {};
|
||||||
|
linkedMembers.forEach((m) => {
|
||||||
|
accountCalls['c' + m.linkedClientId] = this.clientsService
|
||||||
|
.getClientAccountData(m.linkedClientId.toString())
|
||||||
|
.pipe(catchError(() => of(null)));
|
||||||
|
});
|
||||||
|
forkJoin(accountCalls).subscribe((accountsMap: any) => {
|
||||||
|
this.familyAccountsLoading = false;
|
||||||
|
this.familyMemberAccountsMap = accountsMap;
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,6 +548,10 @@ export class NominationApplyComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get manualEntryMembers(): any[] {
|
||||||
|
return this.familyMembers.filter((m) => !m.linkedClientId);
|
||||||
|
}
|
||||||
|
|
||||||
submit(): void {
|
submit(): void {
|
||||||
if (!this.selectedMemberId) {
|
if (!this.selectedMemberId) {
|
||||||
this.snackBar.open('Please search and select a member first', 'Close', { duration: 3000 });
|
this.snackBar.open('Please search and select a member first', 'Close', { duration: 3000 });
|
||||||
@@ -382,6 +563,33 @@ export class NominationApplyComponent implements OnInit {
|
|||||||
this.declarationForm.markAllAsTouched();
|
this.declarationForm.markAllAsTouched();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const manualMembers = this.manualEntryMembers;
|
||||||
|
if (manualMembers.length > 0) {
|
||||||
|
const names = manualMembers
|
||||||
|
.map((m) => {
|
||||||
|
const name = this.familyMemberDisplayName(m);
|
||||||
|
const rel = m.relationship || m.relationshipId?.name || '';
|
||||||
|
return rel ? `${name} (${rel})` : name;
|
||||||
|
})
|
||||||
|
.join(', ');
|
||||||
|
this.snackBar.open(
|
||||||
|
`Cannot submit: ${names} ${manualMembers.length === 1 ? 'is' : 'are'} not registered as CBS client${manualMembers.length === 1 ? '' : 's'}. Please use "Create as Client" to register them first.`,
|
||||||
|
'Close',
|
||||||
|
{ duration: 8000, panelClass: 'snack-error' }
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.isEligible) {
|
||||||
|
this.snackBar.open(
|
||||||
|
'Cannot submit: Nominee or a family member has overdue loans. No Due Certificate is not eligible.',
|
||||||
|
'Close',
|
||||||
|
{ duration: 8000, panelClass: 'snack-error' }
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.submitting = true;
|
this.submitting = true;
|
||||||
|
|
||||||
const bv = this.profileForm.value;
|
const bv = this.profileForm.value;
|
||||||
|
|||||||
@@ -8,8 +8,6 @@
|
|||||||
|
|
||||||
.nomination-detail {
|
.nomination-detail {
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
max-width: 1100px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
|
|||||||
@@ -8,8 +8,6 @@
|
|||||||
|
|
||||||
.nomination-list {
|
.nomination-list {
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
max-width: 1300px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
|
|||||||
@@ -8,8 +8,6 @@
|
|||||||
|
|
||||||
.voting-interface {
|
.voting-interface {
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
max-width: 900px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
|
|||||||
+11
-37
@@ -104,86 +104,63 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<table
|
<table mat-table [dataSource]="guarantorDetails | accountsFilter: 'guarantor' : showDeletedGuarantorsAccounts">
|
||||||
class="flex-fill"
|
|
||||||
mat-table
|
|
||||||
[dataSource]="guarantorDetails | accountsFilter: 'guarantor' : showDeletedGuarantorsAccounts"
|
|
||||||
>
|
|
||||||
<ng-container matColumnDef="fullname">
|
<ng-container matColumnDef="fullname">
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.name' | translate }}</th>
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.name' | translate }}</th>
|
||||||
<td mat-cell *matCellDef="let guarantor">
|
<td mat-cell *matCellDef="let guarantor">
|
||||||
@if (!guarantor.guarantorFundingDetails) {
|
|
||||||
<span> {{ guarantor.firstname }} {{ guarantor.lastname }} </span>
|
<span> {{ guarantor.firstname }} {{ guarantor.lastname }} </span>
|
||||||
}
|
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container matColumnDef="relationship">
|
<ng-container matColumnDef="relationship">
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Relationship' | translate }}</th>
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Relationship' | translate }}</th>
|
||||||
<td mat-cell *matCellDef="let guarantor">
|
<td mat-cell *matCellDef="let guarantor">
|
||||||
@if (!guarantor.guarantorFundingDetails) {
|
<span>{{ guarantor.clientRelationshipType?.name }}</span>
|
||||||
<span>
|
|
||||||
{{ guarantor.clientRelationshipType?.name }}
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container matColumnDef="guarantortype">
|
<ng-container matColumnDef="guarantortype">
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Guarantor Type' | translate }}</th>
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Guarantor Type' | translate }}</th>
|
||||||
<td mat-cell *matCellDef="let guarantor">
|
<td mat-cell *matCellDef="let guarantor">
|
||||||
@if (!guarantor.guarantorFundingDetails) {
|
<span>{{ guarantor.guarantorType.value }}</span>
|
||||||
<span>
|
|
||||||
{{ guarantor.guarantorType.value }}
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container matColumnDef="depositAccount">
|
<ng-container matColumnDef="depositAccount">
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Deposit Account' | translate }}</th>
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Deposit Account' | translate }}</th>
|
||||||
<td mat-cell *matCellDef="let guarantor">
|
<td mat-cell *matCellDef="let guarantor">
|
||||||
@if (!guarantor.guarantorFundingDetails) {
|
@if (guarantor.guarantorFundingDetails?.length) {
|
||||||
<span> </span>
|
<span>{{ guarantor.guarantorFundingDetails[0].savingsAccount?.accountNo }}</span>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container matColumnDef="amount">
|
<ng-container matColumnDef="amount">
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Amount' | translate }}</th>
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Amount' | translate }}</th>
|
||||||
<td mat-cell *matCellDef="let guarantor">
|
<td mat-cell *matCellDef="let guarantor">
|
||||||
@if (!guarantor.guarantorFundingDetails) {
|
@if (guarantor.guarantorFundingDetails?.length) {
|
||||||
<span> </span>
|
<span>{{ guarantor.guarantorFundingDetails[0].amount | formatNumber }}</span>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container matColumnDef="remainingAmount">
|
<ng-container matColumnDef="remainingAmount">
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Remaining Amount' | translate }}</th>
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Remaining Amount' | translate }}</th>
|
||||||
<td mat-cell *matCellDef="let guarantor">
|
<td mat-cell *matCellDef="let guarantor">
|
||||||
@if (!guarantor.guarantorFundingDetails) {
|
@if (guarantor.guarantorFundingDetails?.length) {
|
||||||
<span> </span>
|
<span>{{ guarantor.guarantorFundingDetails[0].amountRemaining | formatNumber }}</span>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container matColumnDef="status">
|
<ng-container matColumnDef="status">
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Status' | translate }}</th>
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Status' | translate }}</th>
|
||||||
<td mat-cell *matCellDef="let guarantor">
|
<td mat-cell *matCellDef="let guarantor">
|
||||||
@if (!guarantor.guarantorFundingDetails) {
|
|
||||||
<span>
|
|
||||||
@if (guarantor.status) {
|
@if (guarantor.status) {
|
||||||
<span>
|
<span>{{ 'labels.inputs.Active' | translate }}</span>
|
||||||
{{ 'labels.inputs.Active' | translate }}
|
|
||||||
</span>
|
|
||||||
}
|
}
|
||||||
@if (!guarantor.status) {
|
@if (!guarantor.status) {
|
||||||
<span>
|
<span>{{ 'labels.inputs.Deleted' | translate }}</span>
|
||||||
{{ 'labels.inputs.Deleted' | translate }}
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
</span>
|
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container matColumnDef="action">
|
<ng-container matColumnDef="action">
|
||||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Action' | translate }}</th>
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Action' | translate }}</th>
|
||||||
<td mat-cell *matCellDef="let guarantor; let id = index">
|
<td mat-cell *matCellDef="let guarantor; let id = index">
|
||||||
@if (!guarantor.guarantorFundingDetails) {
|
|
||||||
<span>
|
<span>
|
||||||
<button
|
<button
|
||||||
mat-raised-button
|
mat-raised-button
|
||||||
@@ -200,7 +177,6 @@
|
|||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
@if (guarantor.status) {
|
@if (guarantor.status) {
|
||||||
<span>
|
|
||||||
<button
|
<button
|
||||||
mat-raised-button
|
mat-raised-button
|
||||||
class="account-action-button"
|
class="account-action-button"
|
||||||
@@ -210,10 +186,8 @@
|
|||||||
>
|
>
|
||||||
<fa-icon icon="trash" class="m-r-10"></fa-icon>
|
<fa-icon icon="trash" class="m-r-10"></fa-icon>
|
||||||
</button>
|
</button>
|
||||||
</span>
|
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
}
|
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<tr mat-header-row *matHeaderRowDef="guarantorsDisplayedColumns"></tr>
|
<tr mat-header-row *matHeaderRowDef="guarantorsDisplayedColumns"></tr>
|
||||||
|
|||||||
@@ -800,4 +800,8 @@ export class LoansService {
|
|||||||
const httpParams = new HttpParams().set('offset', offset.toString()).set('limit', limit.toString());
|
const httpParams = new HttpParams().set('offset', offset.toString()).set('limit', limit.toString());
|
||||||
return this.http.get('/entityDatatableChecks', { params: httpParams });
|
return this.http.get('/entityDatatableChecks', { params: httpParams });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getLoanGuarantors(loanId: string): Observable<any> {
|
||||||
|
return this.http.get(`/loans/${loanId}/guarantors`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
export default {
|
export default {
|
||||||
'mifos_x': {
|
'mifos_x': {
|
||||||
'version': '260619',
|
'version': '260623',
|
||||||
'hash': '822fa4c6'
|
'hash': '3d9b0480'
|
||||||
},
|
},
|
||||||
'allow_switching_backend_instance': true
|
'allow_switching_backend_instance': true
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,6 +33,19 @@
|
|||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.snack-error .mdc-snackbar__surface {
|
||||||
|
background-color: #dc2626 !important;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.snack-error .mat-mdc-snack-bar-label {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.snack-error .mat-mdc-button {
|
||||||
|
color: #fecaca !important;
|
||||||
|
}
|
||||||
|
|
||||||
// ===== Sub-menu Mega Panels (rendered in CDK overlay) =====
|
// ===== Sub-menu Mega Panels (rendered in CDK overlay) =====
|
||||||
.deposits-mega-panel,
|
.deposits-mega-panel,
|
||||||
.hrm-mega-panel {
|
.hrm-mega-panel {
|
||||||
|
|||||||
Reference in New Issue
Block a user