diff --git a/src/app/clients/client-stepper/client-general-step/client-general-step.component.html b/src/app/clients/client-stepper/client-general-step/client-general-step.component.html index 6bbc7ba49..fdcb8c635 100644 --- a/src/app/clients/client-stepper/client-general-step/client-general-step.component.html +++ b/src/app/clients/client-stepper/client-general-step/client-general-step.component.html @@ -42,7 +42,9 @@ @if (createClientForm.get('legalFormId')?.value) { - {{ createClientForm.get('legalFormId')?.value === 1 ? 'person' : 'business' }} + {{ + createClientForm.get('legalFormId')?.value === 1 ? 'person' : 'business' + }} {{ getSelectedLegalFormLabel() }} } @@ -67,12 +69,12 @@ @if (getSelectedNationality().flagType === 'india') { - - - - - - + + + + + + } @else { @@ -88,12 +90,12 @@ @if (nat.flagType === 'india') { - - - - - - + + + + + + } @else { @@ -129,7 +131,10 @@ @if (createClientForm.get('fullname')) { - {{ 'labels.inputs.' + getDateLabel(createClientForm.get('legalFormId')?.value, ['Name', 'Entity Name']) | translate }} + {{ + 'labels.inputs.' + getDateLabel(createClientForm.get('legalFormId')?.value, ['Name', 'Entity Name']) + | translate + }} @if (createClientForm.controls.fullname.hasError('required')) { @@ -150,30 +155,39 @@
@if (!sanctionsVerified && !sanctionsBlocked) { - } @if (sanctionsVerified) { RBI KYC Master Direction (2016) — Section 51 & PMLA 2002 - - Verified - - Verified + info + matTooltipClass="sanctions-tooltip" + >info } @if (sanctionsBlocked) { RBI KYC Master Direction (2016) — Section 51 & PMLA 2002 Sanctions Match - info + matTooltipClass="sanctions-tooltip" + >info }
@@ -181,7 +195,9 @@ } - @if (createClientForm.get('firstname') || createClientForm.get('middlename') || createClientForm.get('lastname')) { + @if ( + createClientForm.get('firstname') || createClientForm.get('middlename') || createClientForm.get('lastname') + ) {
@if (createClientForm.get('salutationId') && salutationOptions?.length > 0) { @@ -213,7 +229,10 @@ {{ 'labels.inputs.Middle Name' | translate }} person_outline - @if (createClientForm.controls.middlename?.hasError('pattern') && createClientForm.controls.middlename?.touched) { + @if ( + createClientForm.controls.middlename?.hasError('pattern') && + createClientForm.controls.middlename?.touched + ) { Only letters, spaces, dots and hyphens allowed } @@ -243,30 +262,39 @@
@if (!sanctionsVerified && !sanctionsBlocked) { - } @if (sanctionsVerified) { RBI KYC Master Direction (2016) — Section 51 & PMLA 2002 - - Verified - - Verified + info + matTooltipClass="sanctions-tooltip" + >info } @if (sanctionsBlocked) { RBI KYC Master Direction (2016) — Section 51 & PMLA 2002 Sanctions Match - info + matTooltipClass="sanctions-tooltip" + >info }
@@ -278,10 +306,18 @@ Date of Birth calendar_today - + - @if (createClientForm.get('dateOfBirth')?.hasError('required') && createClientForm.get('dateOfBirth')?.touched) { + @if ( + createClientForm.get('dateOfBirth')?.hasError('required') && createClientForm.get('dateOfBirth')?.touched + ) { Date of Birth is required } @@ -324,7 +360,6 @@ } } - @@ -341,9 +376,15 @@
- {{ ageBreakdown.years }} yrs - {{ ageBreakdown.months }} mos - {{ ageBreakdown.days }} days + {{ ageBreakdown.years }} yrs + {{ ageBreakdown.months }} mos + {{ ageBreakdown.days }} days MINOR
@if (!selectedGuardian && !guardianLinking) { @@ -357,13 +398,14 @@
- @if (!selectedGuardian && !guardianLinking) {
person_off
- No guardian linked yet. Click "Link Guardian" to search and add. + No guardian linked yet. Click "Link Guardian" to search and add. Guardian is mandatory for minor accounts
@@ -377,22 +419,33 @@ man Father woman Mother - gavel Legal Guardian - elderly Grandparent + gavel Legal Guardian + elderly Grandparent group Other
- + Search Guardian by Name or Client ID person_search - + placeholder="Select relationship first, then search..." + autocomplete="off" + /> @if (guardianSearching) { @@ -413,11 +466,17 @@
{{ result.displayName }} - ID: {{ result.id }} · A/C: {{ result.accountNumber }} · {{ result.officeName }}@if (result.dateOfBirth) { · DOB: {{ result.dateOfBirth | dateFormat }} }@if (result.fatherSpouseName) { · {{ result.fatherSpouseName }} } + ID: {{ result.id }} · A/C: {{ result.accountNumber }} · + {{ result.officeName }} + @if (result.dateOfBirth) { + · DOB: {{ result.dateOfBirth | dateFormat }} + } + @if (result.fatherSpouseName) { + · {{ result.fatherSpouseName }} + }
- + {{ result.status?.value }}
@@ -429,7 +488,13 @@ }
-
@@ -453,8 +518,12 @@
@if (selectedGuardian.profileImageUrl) { - Photo + Photo } @else { } @@ -470,26 +539,40 @@
- {{ createClientForm.get('guardianDetails.guardianRelation')?.value || '-' }} + {{ + createClientForm.get('guardianDetails.guardianRelation')?.value || '-' + }} - {{ selectedGuardian.accountNo || selectedGuardian.accountNumber || '-' }} + {{ + selectedGuardian.accountNo || selectedGuardian.accountNumber || '-' + }} - + {{ selectedGuardian.status?.value || '-' }} - - @@ -506,16 +589,22 @@ @if (selectedGuardian.panNumber) {
PAN - {{ selectedGuardian.panNumber }} - @if (selectedGuardian.panVerified) { check_circle } + {{ selectedGuardian.panNumber }} + @if (selectedGuardian.panVerified) { + check_circle + }
} @if (selectedGuardian.aadhaarMasked) {
Aadhaar - {{ selectedGuardian.aadhaarMasked }} - @if (selectedGuardian.aadhaarVerified) { check_circle } + {{ selectedGuardian.aadhaarMasked }} + @if (selectedGuardian.aadhaarVerified) { + check_circle + }
} @@ -535,8 +624,12 @@ @if (selectedGuardian.signatureUrl) {
Specimen Signature - Signature + Signature
} @@ -549,7 +642,7 @@ @if (createClientForm.get('clientNonPersonDetails')) { -
+
Constitution Type @@ -607,7 +700,10 @@
- Select registered clients as authorized signatories — as per Board Resolution / Partnership Deed. Max 10. + Select registered clients as authorized signatories — as per Board Resolution / Partnership Deed. Max + 10.
@@ -638,27 +734,45 @@ {{ sig.displayName }} @if (sig._loading) { - + } {{ sig.cifNumber || sig.id }} {{ sig.panNumber || '-' }} - @if (sig.panVerified) { check_circle } + @if (sig.panVerified) { + check_circle + } @if (sig.dinNumber) { {{ sig.dinNumber }} - @if (sig.dinVerified) { check_circle } - @if (!sig.dinVerified) { cancel } + @if (sig.dinVerified) { + check_circle + } + @if (!sig.dinVerified) { + cancel + } } @else { - } - {{ sig.status?.value || '-' }} + {{ + sig.status?.value || '-' + }}
@@ -728,7 +857,6 @@
- } @@ -737,9 +865,12 @@

Contact Details - info + matTooltipPosition="above" + >info

@@ -747,21 +878,35 @@ {{ 'labels.inputs.Mobile No' | translate }} phone_android - + (paste)="$event.preventDefault()" + /> @if (createClientForm.get('mobileNo')?.hasError('required') && createClientForm.get('mobileNo')?.touched) { Mobile number is required - } @else if (createClientForm.get('mobileNo')?.hasError('pattern') && createClientForm.get('mobileNo')?.touched) { + } @else if ( + createClientForm.get('mobileNo')?.hasError('pattern') && createClientForm.get('mobileNo')?.touched + ) { Enter valid 10-digit number (starts with 6-9) }
@if (!mobileVerified) { - @@ -774,19 +919,33 @@ Alternate Mobile No phone_android - - @if (createClientForm.get('alternateMobileNo')?.hasError('pattern') && createClientForm.get('alternateMobileNo')?.touched) { + (paste)="$event.preventDefault()" + /> + @if ( + createClientForm.get('alternateMobileNo')?.hasError('pattern') && + createClientForm.get('alternateMobileNo')?.touched + ) { Enter valid 10-digit number (starts with 6-9) }
@if (!alternateMobileVerified) { - @@ -800,21 +959,27 @@ {{ 'labels.inputs.Email Address' | translate }} email - @if (createClientForm.get('emailAddress')?.hasError('required') && createClientForm.get('emailAddress')?.touched) { + @if ( + createClientForm.get('emailAddress')?.hasError('required') && createClientForm.get('emailAddress')?.touched + ) { Email is required } @else if (createClientForm.get('emailAddress')?.invalid && createClientForm.get('emailAddress')?.touched) { Enter a valid email (e.g. name@example.com) } - @if (createClientForm.get('legalFormId')?.value === LegalFormId.PERSON && maritalStatusOptions.length > 0 && !isMinor) { + @if ( + createClientForm.get('legalFormId')?.value === LegalFormId.PERSON && maritalStatusOptions.length > 0 && !isMinor + ) { Marital Status @if (getSelectedMaritalStatus()) { - {{ getMaritalStatusIcon(getSelectedMaritalStatus().name) }} + {{ + getMaritalStatusIcon(getSelectedMaritalStatus().name) + }} {{ getSelectedMaritalStatus().name }} } @@ -833,83 +998,249 @@
+ + @if (createClientForm.get('legalFormId')?.value === LegalFormId.PERSON) { +
+

+ + Education & Social Details +

+
+ + @if (!isMinor) { + + Education Level + + + @if (getSelectedEducationLevel()) { + + {{ + getEducationLevelIcon(getSelectedEducationLevel().name) + }} + {{ getSelectedEducationLevel().name }} + + } + + @for (el of educationLevelOptions; track el) { + + + {{ getEducationLevelIcon(el.name) }} + {{ el.name }} + + + } + + + + + + Degree / Qualification + + + @if (getSelectedDegreeQualification()) { + + {{ + getDegreeQualificationIcon(getSelectedDegreeQualification().name) + }} + {{ getSelectedDegreeQualification().name }} + + } + + @for (dq of filteredDegreeQualificationOptions; track dq) { + + + {{ getDegreeQualificationIcon(dq.name) }} + {{ dq.name }} + + + } + @if (!getSelectedEducationLevel()) { + + Please select Education Level first + + } @else if (filteredDegreeQualificationOptions.length === 0) { + + No options available + + } + + + } + + + + Religion + + + @if (getSelectedReligion()) { + + {{ getReligionIcon(getSelectedReligion().name) }} + {{ getSelectedReligion().name }} + + } + + @for (r of religionOptions; track r) { + + + {{ getReligionIcon(r.name) }} + {{ r.name }} + + + } + + + + + + Social Category + + + @if (getSelectedSocialCategory()) { + + {{ + getSocialCategoryIcon(getSelectedSocialCategory().name) + }} + {{ getSelectedSocialCategory().name }} + + } + + @for (sc of socialCategoryOptions; track sc) { + + + {{ getSocialCategoryIcon(sc.name) }} + {{ sc.name }} + + + } + + +
+
+ } + @if (createClientForm.get('legalFormId')?.value === LegalFormId.PERSON) { -
-

- - Financial & Classification -

-
- - - - {{ 'labels.inputs.Occupation Category' | translate }} - - - @if (getSelectedOccupationCategory()) { - - {{ getOccupationCategoryIcon(getSelectedOccupationCategory().name) }} - {{ getSelectedOccupationCategory().name }} - +
+

+ + Financial & Classification +

+
+ + {{ 'labels.inputs.Occupation Category' | translate }} + + + @if (getSelectedOccupationCategory()) { + + {{ + getOccupationCategoryIcon(getSelectedOccupationCategory().name) + }} + {{ getSelectedOccupationCategory().name }} + + } + + @for (cat of occupationCategoryOptions; track cat) { + + + {{ getOccupationCategoryIcon(cat.name) }} + {{ cat.name }} + + } - - @for (cat of occupationCategoryOptions; track cat) { - - - {{ getOccupationCategoryIcon(cat.name) }} - {{ cat.name }} - - - } - - + + - - {{ 'labels.inputs.Occupation' | translate }} - - - @if (getSelectedOccupation()) { - - {{ getOccupationIcon(getSelectedOccupation().name) }} - {{ getSelectedOccupation().name }} - + + {{ 'labels.inputs.Occupation' | translate }} + + + @if (getSelectedOccupation()) { + + {{ getOccupationIcon(getSelectedOccupation().name) }} + {{ getSelectedOccupation().name }} + + } + + @for (occ of occupationOptions; track occ) { + + + {{ getOccupationIcon(occ.name) }} + {{ occ.name }} + + } - - @for (occ of occupationOptions; track occ) { - - - {{ getOccupationIcon(occ.name) }} - {{ occ.name }} - - + + + + + Monthly Income + ₹ + + @if ( + createClientForm.get('monthlyIncome')?.hasError('pattern') && createClientForm.get('monthlyIncome')?.touched + ) { + Enter a valid amount + } @else if ( + createClientForm.get('monthlyIncome')?.hasError('min') && createClientForm.get('monthlyIncome')?.touched + ) { + Amount cannot be negative } - - + - - Monthly Income - ₹ - - @if (createClientForm.get('monthlyIncome')?.hasError('pattern') && createClientForm.get('monthlyIncome')?.touched) { - Enter a valid amount - } @else if (createClientForm.get('monthlyIncome')?.hasError('min') && createClientForm.get('monthlyIncome')?.touched) { - Amount cannot be negative - } - + + Annual Turnover (Last FY) + ₹ + + @if ( + createClientForm.get('annualTurnover')?.hasError('pattern') && + createClientForm.get('annualTurnover')?.touched + ) { + Enter a valid amount + } @else if ( + createClientForm.get('annualTurnover')?.hasError('min') && createClientForm.get('annualTurnover')?.touched + ) { + Amount cannot be negative + } + - - Annual Turnover (Last FY) - ₹ - - @if (createClientForm.get('annualTurnover')?.hasError('pattern') && createClientForm.get('annualTurnover')?.touched) { - Enter a valid amount - } @else if (createClientForm.get('annualTurnover')?.hasError('min') && createClientForm.get('annualTurnover')?.touched) { - Amount cannot be negative - } - + + Agriculture Income + ₹ + + @if ( + createClientForm.get('agricultureIncome')?.hasError('pattern') && + createClientForm.get('agricultureIncome')?.touched + ) { + Enter a valid amount + } @else if ( + createClientForm.get('agricultureIncome')?.hasError('min') && + createClientForm.get('agricultureIncome')?.touched + ) { + Amount cannot be negative + } + + + + Non-Agriculture Income + ₹ + + @if ( + createClientForm.get('nonAgricultureIncome')?.hasError('pattern') && + createClientForm.get('nonAgricultureIncome')?.touched + ) { + Enter a valid amount + } @else if ( + createClientForm.get('nonAgricultureIncome')?.hasError('min') && + createClientForm.get('nonAgricultureIncome')?.touched + ) { + Amount cannot be negative + } + +
-
} diff --git a/src/app/clients/client-stepper/client-general-step/client-general-step.component.scss b/src/app/clients/client-stepper/client-general-step/client-general-step.component.scss index c30cd211e..b762b87ed 100644 --- a/src/app/clients/client-stepper/client-general-step/client-general-step.component.scss +++ b/src/app/clients/client-stepper/client-general-step/client-general-step.component.scss @@ -95,8 +95,8 @@ form { background: #f0f9ff; border: 1px solid #bae6fd; border-radius: 6px; - margin-top: -8px; - margin-bottom: 4px; + margin-top: 12px; + margin-bottom: 16px; } .full-name-preview { @@ -408,7 +408,10 @@ form { .guardian-cancel-link { color: #94a3b8 !important; margin-top: 8px; - &:hover { color: #ef4444 !important; } + + &:hover { + color: #ef4444 !important; + } } /* ═══ Guardian Row Table ═══ */ @@ -437,7 +440,10 @@ form { align-items: center; padding: 10px 12px; transition: background 0.1s; - &:hover { background: #f8fafc; } + + &:hover { + background: #f8fafc; + } } .guardian-row__col { @@ -445,12 +451,33 @@ form { font-size: 13px; color: #1e293b; - &--photo { flex: 0 0 50px; } - &--name { 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; } + &--photo { + flex: 0 0 50px; + } + + &--name { + 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 { @@ -543,14 +570,22 @@ form { color: #64748b; } - &:hover mat-icon { color: #1074b9; } - &--delete:hover mat-icon { color: #ef4444; } + &:hover mat-icon { + color: #1074b9; + } + + &--delete:hover mat-icon { + color: #ef4444; + } } .guardian-row__zoomable { cursor: zoom-in; transition: transform 0.15s; - &:hover { transform: scale(1.05); } + + &:hover { + transform: scale(1.05); + } } /* Expandable detail panel */ @@ -558,12 +593,19 @@ form { border-top: 1px solid #e2e8f0; background: #f8fafc; padding: 16px 20px; - animation: guardianSlideDown 0.2s ease-out; + animation: guardian-slide-down 0.2s ease-out; } -@keyframes guardianSlideDown { - from { opacity: 0; max-height: 0; } - to { opacity: 1; max-height: 500px; } +@keyframes guardian-slide-down { + from { + opacity: 0; + max-height: 0; + } + + to { + opacity: 1; + max-height: 500px; + } } .guardian-expand__grid { @@ -666,7 +708,9 @@ form { width: 100%; } - .guardian-row__header { display: none; } + .guardian-row__header { + display: none; + } .guardian-row__data { flex-wrap: wrap; @@ -674,10 +718,23 @@ form { } .guardian-row__col { - &--photo { flex: 0 0 40px; } - &--name { flex: 1 1 200px; } - &--rel, &--cif, &--status { flex: 0 0 auto; } - &--actions { flex: 0 0 auto; } + &--photo { + flex: 0 0 40px; + } + + &--name { + flex: 1 1 200px; + } + + &--rel, + &--cif, + &--status { + flex: 0 0 auto; + } + + &--actions { + flex: 0 0 auto; + } } .guardian-expand__grid { @@ -777,8 +834,13 @@ form { transition: background 0.1s; border-bottom: 1px solid #f1f5f9; - &:last-child { border-bottom: none; } - &:hover { background: #f0f7ff; } + &:last-child { + border-bottom: none; + } + + &:hover { + background: #f0f7ff; + } } .guardian-result-item__avatar { @@ -882,7 +944,10 @@ form { .guardian-card__remove { color: #94a3b8 !important; - &:hover { color: #ef4444 !important; } + + &:hover { + color: #ef4444 !important; + } } .guardian-card__body { @@ -899,8 +964,13 @@ form { border-bottom: 1px solid #f1f5f9; border-right: 1px solid #f1f5f9; - &:nth-child(2n) { border-right: none; } - &:nth-last-child(-n+2) { border-bottom: none; } + &:nth-child(2n) { + border-right: none; + } + + &:nth-last-child(-n + 2) { + border-bottom: none; + } } .guardian-card__label { @@ -1069,10 +1139,22 @@ form { color: #0f172a; } - .row-micro td { background: #ecfdf5; } - .row-small td { background: #e0f2fe; } - .row-medium td { background: #fffbeb; } - .row-large td { background: #f8fafc; color: #94a3b8; } + .row-micro td { + background: #ecfdf5; + } + + .row-small td { + background: #e0f2fe; + } + + .row-medium td { + background: #fffbeb; + } + + .row-large td { + background: #f8fafc; + color: #94a3b8; + } } .msme-dot { @@ -1082,10 +1164,21 @@ form { border-radius: 50%; margin-right: 6px; - &.green { background: #10b981; } - &.blue { background: #0ea5e9; } - &.amber { background: #f59e0b; } - &.gray { background: #cbd5e1; } + &.green { + background: #10b981; + } + + &.blue { + background: #0ea5e9; + } + + &.amber { + background: #f59e0b; + } + + &.gray { + background: #cbd5e1; + } } .msme-class-table-footer { @@ -1499,19 +1592,68 @@ form { border-bottom: 1px solid #f1f5f9; transition: background 0.15s; - &:last-child { border-bottom: none; } - &:hover { background: #f8fafc; } + &:last-child { + border-bottom: none; + } + + &:hover { + background: #f8fafc; + } } .sig-table__col { - &--sno { flex: 0 0 32px; text-align: center; color: #94a3b8; font-size: 12px; } - &--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; } + &--sno { + flex: 0 0 32px; + text-align: center; + color: #94a3b8; + font-size: 12px; + } + + &--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 { @@ -1526,11 +1668,21 @@ form { font-size: 12px; overflow: hidden; - &.has-photo { padding: 0; } - .avatar-photo { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; } + &.has-photo { + 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 { font-size: 14px !important; @@ -1569,7 +1721,10 @@ form { font-size: 11px; font-weight: 600; color: #94a3b8; - &.active { color: #22c55e; } + + &.active { + color: #22c55e; + } } .sig-action-icon { @@ -1577,7 +1732,10 @@ form { width: 18px !important; height: 18px !important; color: #64748b; - &.remove { color: #ef4444; } + + &.remove { + color: #ef4444; + } } .sig-add-wrapper { diff --git a/src/app/clients/client-stepper/client-general-step/client-general-step.component.ts b/src/app/clients/client-stepper/client-general-step/client-general-step.component.ts index 77d89d2ae..ec3519fb9 100644 --- a/src/app/clients/client-stepper/client-general-step/client-general-step.component.ts +++ b/src/app/clients/client-stepper/client-general-step/client-general-step.component.ts @@ -7,7 +7,17 @@ */ /** Angular Imports */ -import { Component, OnInit, OnDestroy, OnChanges, SimpleChanges, Input, Output, EventEmitter, inject } from '@angular/core'; +import { + Component, + OnInit, + OnDestroy, + OnChanges, + SimpleChanges, + Input, + Output, + EventEmitter, + inject +} from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; import { MatProgressSpinner } from '@angular/material/progress-spinner'; import { @@ -35,7 +45,6 @@ import { MatIcon } from '@angular/material/icon'; import { MatSelectTrigger } from '@angular/material/select'; import { MatTooltip } from '@angular/material/tooltip'; import { MatDialog, MAT_DIALOG_DATA, MatDialogContent, MatDialogClose } from '@angular/material/dialog'; -import { Inject } from '@angular/core'; import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; import { DateFormatPipe } from '../../../pipes/date-format.pipe'; import { SanctionsVerifyDialogComponent } from './sanctions-verify-dialog.component'; @@ -121,6 +130,14 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges salutationOptions: any; /** Marital Status Options */ maritalStatusOptions: any[] = []; + /** Education Level Options */ + educationLevelOptions: any[] = []; + /** Degree / Qualification Options */ + degreeQualificationOptions: any[] = []; + /** Religion Options */ + religionOptions: any[] = []; + /** Social Category Options */ + socialCategoryOptions: any[] = []; /** Nationality Options */ nationalityOptions = [ { value: 'INDIAN', label: 'Indian', flagType: 'india', icon: '' }, @@ -166,17 +183,149 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges alternateDipTimestamp: string | null = null; /** Constitution Types Master with KYC field metadata */ constitutionTypesMaster = [ - { value: 'PRIVATE_LIMITED', label: 'Private Limited Company', icon: 'business', panLabel: 'PAN of Company', panHint: 'PAN mandatory. CIN from MCA.', regHint: 'Certificate of Incorporation No.', showCin: true, additionalField: 'Constitution as per Articles of Association', documents: 'Certificate of Incorporation, Memorandum & Articles of Association, PAN, Board Resolution', signatoryKyc: 'KYC of all Authorized Signatories (OVDs + PAN + Photo)', note: 'Board Resolution to specify authorized signatories' }, - { value: 'PUBLIC_LIMITED', label: 'Public Limited Company', icon: 'domain', panLabel: 'PAN of Company', panHint: 'PAN mandatory. CIN from MCA.', regHint: 'Certificate of Incorporation No.', showCin: true, additionalField: 'Constitution as per Articles of Association', documents: 'Certificate of Incorporation, Memorandum & Articles of Association, PAN, Board Resolution', signatoryKyc: 'KYC of all Authorized Signatories (OVDs + PAN + Photo)', note: 'Board Resolution to specify authorized signatories' }, - { value: 'LLP', label: 'Limited Liability Partnership (LLP)', icon: 'handshake', panLabel: 'PAN of LLP', panHint: 'PAN mandatory. LLPIN from MCA.', regHint: 'Certificate of Incorporation No.', showCin: true, additionalField: 'LLP Agreement', documents: 'Certificate of Incorporation, LLP Agreement, PAN, Designated Partners Resolution', signatoryKyc: 'KYC of all Designated Partners / Authorized Signatories', note: 'LLPIN from MCA required' }, - { value: 'PARTNERSHIP', label: 'Partnership Firm', icon: 'handshake', panLabel: 'PAN of Firm', panHint: 'PAN of Firm mandatory', regHint: 'Registration Certificate No. (if registered)', showCin: false, additionalField: 'Partners Names (linked client-wise)', documents: 'Partnership Deed, Registration Certificate (if registered), PAN of Firm', signatoryKyc: 'KYC of all Partners / Authorized Signatories', note: 'Registration optional but preferred' }, - { value: 'PROPRIETORSHIP', label: 'Proprietorship', icon: 'person', panLabel: 'PAN of Proprietor', panHint: 'PAN of Proprietor (no separate entity PAN needed)', regHint: 'GST / Shop License No.', showCin: false, additionalField: '', documents: 'GST Certificate / Shop License / Utility Bill, PAN of Proprietor', signatoryKyc: "Proprietor's individual KYC (same as individual requirements)", note: 'At least 2 proofs of business existence required. Proprietor name = Client name.' }, - { value: 'HUF', label: 'HUF (Hindu Undivided Family)', icon: 'family_restroom', panLabel: 'PAN of HUF', panHint: 'PAN of HUF mandatory', regHint: 'HUF Declaration / Deed No.', showCin: false, additionalField: 'Karta Name', documents: 'HUF Declaration/Deed, PAN of HUF', signatoryKyc: 'KYC of Karta', note: '' }, - { value: 'TRUST', label: 'Trust', icon: 'verified_user', panLabel: 'PAN of Trust', panHint: 'PAN of Trust mandatory', regHint: 'Trust Deed / Registration No.', showCin: false, additionalField: 'Purpose of Trust', documents: 'Trust Deed, Registration Certificate, PAN of Trust', signatoryKyc: 'KYC of Trustees / Authorized Signatories', note: 'Foreign contribution rules apply if applicable' }, - { value: 'SOCIETY', label: 'Society', icon: 'groups', panLabel: 'PAN of Society', panHint: 'PAN of Society mandatory', regHint: 'Registration Certificate No.', showCin: false, additionalField: 'Managing Committee Details', documents: 'Registration Certificate, PAN of Society, Resolution authorizing signatories', signatoryKyc: 'KYC of Authorized Signatories', note: 'Governed by Societies Registration Act' }, - { value: 'ASSOCIATION', label: 'Association / Club', icon: 'diversity_3', panLabel: 'PAN', panHint: 'PAN mandatory', regHint: 'Registration Certificate No.', showCin: false, additionalField: 'Club Constitution Document', documents: 'Registration Certificate, PAN, Resolution authorizing signatories', signatoryKyc: 'KYC of Authorized Signatories', note: 'Committee approval needed for account' }, - { value: 'GOVT_PSU', label: 'Government Dept / PSU', icon: 'account_balance', panLabel: 'PAN (if applicable)', panHint: 'PAN if applicable', regHint: 'Govt. Notification / Order No.', showCin: false, additionalField: 'Gazette Notification Reference', documents: 'Relevant Government Notification / Order, PAN (if applicable)', signatoryKyc: 'Authorized Signatory KYC', note: 'Special KYC norms as per RBI circular' }, - { value: 'COOPERATIVE', label: 'Cooperative Society / Cooperative Bank', icon: 'hub', panLabel: 'PAN', panHint: 'PAN mandatory. RBI license if bank.', regHint: 'Registration Certificate No.', showCin: false, additionalField: 'Bye-laws of the Cooperative', documents: 'Registration Certificate, PAN, Resolution authorizing signatories', signatoryKyc: 'KYC of Authorized Signatories', note: 'Separate RBI licensing check for cooperative banks' } + { + value: 'PRIVATE_LIMITED', + label: 'Private Limited Company', + icon: 'business', + panLabel: 'PAN of Company', + panHint: 'PAN mandatory. CIN from MCA.', + regHint: 'Certificate of Incorporation No.', + showCin: true, + additionalField: 'Constitution as per Articles of Association', + documents: 'Certificate of Incorporation, Memorandum & Articles of Association, PAN, Board Resolution', + signatoryKyc: 'KYC of all Authorized Signatories (OVDs + PAN + Photo)', + note: 'Board Resolution to specify authorized signatories' + }, + { + value: 'PUBLIC_LIMITED', + label: 'Public Limited Company', + icon: 'domain', + panLabel: 'PAN of Company', + panHint: 'PAN mandatory. CIN from MCA.', + regHint: 'Certificate of Incorporation No.', + showCin: true, + additionalField: 'Constitution as per Articles of Association', + documents: 'Certificate of Incorporation, Memorandum & Articles of Association, PAN, Board Resolution', + signatoryKyc: 'KYC of all Authorized Signatories (OVDs + PAN + Photo)', + note: 'Board Resolution to specify authorized signatories' + }, + { + value: 'LLP', + label: 'Limited Liability Partnership (LLP)', + icon: 'handshake', + panLabel: 'PAN of LLP', + panHint: 'PAN mandatory. LLPIN from MCA.', + regHint: 'Certificate of Incorporation No.', + showCin: true, + additionalField: 'LLP Agreement', + documents: 'Certificate of Incorporation, LLP Agreement, PAN, Designated Partners Resolution', + signatoryKyc: 'KYC of all Designated Partners / Authorized Signatories', + note: 'LLPIN from MCA required' + }, + { + value: 'PARTNERSHIP', + label: 'Partnership Firm', + icon: 'handshake', + panLabel: 'PAN of Firm', + panHint: 'PAN of Firm mandatory', + regHint: 'Registration Certificate No. (if registered)', + showCin: false, + additionalField: 'Partners Names (linked client-wise)', + documents: 'Partnership Deed, Registration Certificate (if registered), PAN of Firm', + signatoryKyc: 'KYC of all Partners / Authorized Signatories', + note: 'Registration optional but preferred' + }, + { + value: 'PROPRIETORSHIP', + label: 'Proprietorship', + icon: 'person', + panLabel: 'PAN of Proprietor', + panHint: 'PAN of Proprietor (no separate entity PAN needed)', + regHint: 'GST / Shop License No.', + showCin: false, + additionalField: '', + documents: 'GST Certificate / Shop License / Utility Bill, PAN of Proprietor', + signatoryKyc: "Proprietor's individual KYC (same as individual requirements)", + note: 'At least 2 proofs of business existence required. Proprietor name = Client name.' + }, + { + value: 'HUF', + label: 'HUF (Hindu Undivided Family)', + icon: 'family_restroom', + panLabel: 'PAN of HUF', + panHint: 'PAN of HUF mandatory', + regHint: 'HUF Declaration / Deed No.', + showCin: false, + additionalField: 'Karta Name', + documents: 'HUF Declaration/Deed, PAN of HUF', + signatoryKyc: 'KYC of Karta', + note: '' + }, + { + value: 'TRUST', + label: 'Trust', + icon: 'verified_user', + panLabel: 'PAN of Trust', + panHint: 'PAN of Trust mandatory', + regHint: 'Trust Deed / Registration No.', + showCin: false, + additionalField: 'Purpose of Trust', + documents: 'Trust Deed, Registration Certificate, PAN of Trust', + signatoryKyc: 'KYC of Trustees / Authorized Signatories', + note: 'Foreign contribution rules apply if applicable' + }, + { + value: 'SOCIETY', + label: 'Society', + icon: 'groups', + panLabel: 'PAN of Society', + panHint: 'PAN of Society mandatory', + regHint: 'Registration Certificate No.', + showCin: false, + additionalField: 'Managing Committee Details', + documents: 'Registration Certificate, PAN of Society, Resolution authorizing signatories', + signatoryKyc: 'KYC of Authorized Signatories', + note: 'Governed by Societies Registration Act' + }, + { + value: 'ASSOCIATION', + label: 'Association / Club', + icon: 'diversity_3', + panLabel: 'PAN', + panHint: 'PAN mandatory', + regHint: 'Registration Certificate No.', + showCin: false, + additionalField: 'Club Constitution Document', + documents: 'Registration Certificate, PAN, Resolution authorizing signatories', + signatoryKyc: 'KYC of Authorized Signatories', + note: 'Committee approval needed for account' + }, + { + value: 'GOVT_PSU', + label: 'Government Dept / PSU', + icon: 'account_balance', + panLabel: 'PAN (if applicable)', + panHint: 'PAN if applicable', + regHint: 'Govt. Notification / Order No.', + showCin: false, + additionalField: 'Gazette Notification Reference', + documents: 'Relevant Government Notification / Order, PAN (if applicable)', + signatoryKyc: 'Authorized Signatory KYC', + note: 'Special KYC norms as per RBI circular' + }, + { + value: 'COOPERATIVE', + label: 'Cooperative Society / Cooperative Bank', + icon: 'hub', + panLabel: 'PAN', + panHint: 'PAN mandatory. RBI license if bank.', + regHint: 'Registration Certificate No.', + showCin: false, + additionalField: 'Bye-laws of the Cooperative', + documents: 'Registration Certificate, PAN, Resolution authorizing signatories', + signatoryKyc: 'KYC of Authorized Signatories', + note: 'Separate RBI licensing check for cooperative banks' + } ]; /** Currently selected constitution metadata */ @@ -206,18 +355,44 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges this.maxDate = this.settingsService.businessDate; this.setOptions(); // Fetch marital status options from code values - this.clientService.getCodeValuesByName('MaritalStatus').subscribe(options => { + this.clientService.getCodeValuesByName('MaritalStatus').subscribe((options) => { this.maritalStatusOptions = options || []; }); + // Fetch education level options from code values + this.clientService.getCodeValuesByName('EducationLevel').subscribe((options) => { + this.educationLevelOptions = options || []; + }); + // Fetch degree / qualification options from code values + this.clientService.getCodeValuesByName('DegreeQualification').subscribe((options) => { + this.degreeQualificationOptions = options || []; + }); + // Fetch religion options from code values + this.clientService.getCodeValuesByName('Religion').subscribe((options) => { + this.religionOptions = options || []; + }); + // Fetch social category options from code values + this.clientService.getCodeValuesByName('SocialCategory').subscribe((options) => { + this.socialCategoryOptions = options || []; + }); + // Reset degree selection whenever education level changes (degree list is filtered by level) + this.createClientForm + .get('educationLevelId') + ?.valueChanges.pipe(takeUntil(this.destroy$)) + .subscribe(() => { + const current = this.createClientForm.get('degreeQualificationId')?.value; + if (current && !this.filteredDegreeQualificationOptions.some((d: any) => d.id === current)) { + this.createClientForm.get('degreeQualificationId')?.setValue(''); + } + }); // Fetch all constitution options from code ID 17 - this.clientService.getCodeValuesById(17).subscribe(options => { + this.clientService.getCodeValuesById(17).subscribe((options) => { if (options?.length) { this.constitutionOptions = options; // Sort master by value length descending so longer/more-specific keys match first const sortedMaster = [...this.constitutionTypesMaster].sort((a, b) => b.value.length - a.value.length); this.enrichedConstitutionOptions = options.map((opt: any) => { const normalized = opt.name?.toUpperCase().replace(/[\s_-]+/g, '') || ''; - const master = sortedMaster.find(m => normalized.includes(m.value.replace(/_/g, ''))); + const master = sortedMaster.find((m) => normalized.includes(m.value.replace(/_/g, ''))); return { id: opt.id, value: master?.value || opt.name, @@ -256,6 +431,10 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges accountNo: data.accountNo, genderId: data.gender?.id, maritalStatusId: data.maritalStatus?.id || '', + educationLevelId: data.educationLevel?.id || data.educationLevelId || '', + degreeQualificationId: data.degreeQualification?.id || data.degreeQualificationId || '', + religionId: data.religion?.id || data.religionId || '', + socialCategoryId: data.socialCategory?.id || data.socialCategoryId || '', mobileNo: data.mobileNo, alternateMobileNo: data.alternateMobileNo || '', emailAddress: data.emailAddress, @@ -264,7 +443,11 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges occupationId: data.occupation?.id || data.occupationId || '', monthlyIncome: data.monthlyIncome || '', annualTurnover: data.annualTurnover || '', - submittedOnDate: data.timeline?.submittedOnDate ? new Date(data.timeline.submittedOnDate) : this.settingsService.businessDate + agricultureIncome: data.agricultureIncome || '', + nonAgricultureIncome: data.nonAgricultureIncome || '', + submittedOnDate: data.timeline?.submittedOnDate + ? new Date(data.timeline.submittedOnDate) + : this.settingsService.businessDate }); // Patch name fields (already created by buildDependencies via legalFormId subscription) @@ -296,6 +479,34 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges } } + // Restore DIP / sanctions verification state from existing client data + if (data.dipVerified === true || data.dipVerified === 'true' || data.dipVerified === 1) { + this.mobileVerified = true; + } + if (data.dipLookupLogId) { + this.dipLookupLogId = data.dipLookupLogId; + } + if (data.dipTimestamp) { + this.dipTimestamp = data.dipTimestamp; + } + if (data.alternateDipVerified === true || data.alternateDipVerified === 'true' || data.alternateDipVerified === 1) { + this.alternateMobileVerified = true; + } + if (data.alternateDipLookupLogId) { + this.alternateDipLookupLogId = data.alternateDipLookupLogId; + } + if (data.alternateDipTimestamp) { + this.alternateDipTimestamp = data.alternateDipTimestamp; + } + if (data.nameVerified === true || data.nameVerified === 'true' || data.nameVerified === 1) { + this.sanctionsVerified = true; + } else if (data.nameVerified === false || data.nameVerified === 'false' || data.nameVerified === 0) { + this.sanctionsVerified = false; + } + if (data.screeningLogId) { + this.screeningLogId = data.screeningLogId; + } + // Set activation date if client is active (activationDate control is added by active subscription) if (data.active && data.timeline?.activatedOnDate) { setTimeout(() => { @@ -324,19 +535,73 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges active: [false], addSavings: [false], accountNo: [''], - genderId: ['', Validators.required], + genderId: [ + '', + Validators.required + ], maritalStatusId: [''], - mobileNo: ['', [Validators.required, Validators.pattern(/^[6-9]\d{9}$/), Validators.maxLength(10)]], - alternateMobileNo: ['', [Validators.pattern(/^[6-9]\d{9}$/), Validators.maxLength(10)]], + educationLevelId: [''], + degreeQualificationId: [''], + religionId: [''], + socialCategoryId: [''], + mobileNo: [ + '', + [ + Validators.required, + Validators.pattern(/^[6-9]\d{9}$/), + Validators.maxLength(10) + ] + ], + alternateMobileNo: [ + '', + [ + Validators.pattern(/^[6-9]\d{9}$/), + Validators.maxLength(10) + ] + ], emailAddress: [ '', - [Validators.required, Validators.email, Validators.pattern(/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/), Validators.maxLength(100)] + [ + Validators.required, + Validators.email, + Validators.pattern(/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/), + Validators.maxLength(100) + ] + ], + dateOfBirth: [ + '', + Validators.required ], - dateOfBirth: ['', Validators.required], occupationCategoryId: [''], occupationId: [''], - monthlyIncome: ['', [Validators.pattern(/^\d+(\.\d{1,2})?$/), Validators.min(0)]], - annualTurnover: ['', [Validators.pattern(/^\d+(\.\d{1,2})?$/), Validators.min(0)]], + monthlyIncome: [ + '', + [ + Validators.pattern(/^\d+(\.\d{1,2})?$/), + Validators.min(0) + ] + ], + annualTurnover: [ + '', + [ + Validators.pattern(/^\d+(\.\d{1,2})?$/), + Validators.min(0) + ] + ], + agricultureIncome: [ + '', + [ + Validators.pattern(/^\d+(\.\d{1,2})?$/), + Validators.min(0) + ] + ], + nonAgricultureIncome: [ + '', + [ + Validators.pattern(/^\d+(\.\d{1,2})?$/), + Validators.min(0) + ] + ], submittedOnDate: [ this.settingsService.businessDate, Validators.required @@ -388,7 +653,13 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges Validators.maxLength(50) ]) ); - this.createClientForm.addControl('middlename', new UntypedFormControl('', [Validators.pattern(/^[A-Za-z][A-Za-z\s.'-]*$/), Validators.maxLength(50)])); + this.createClientForm.addControl( + 'middlename', + new UntypedFormControl('', [ + Validators.pattern(/^[A-Za-z][A-Za-z\s.'-]*$/), + Validators.maxLength(50) + ]) + ); this.createClientForm.addControl( 'lastname', new UntypedFormControl('', [ @@ -418,7 +689,10 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges this.createClientForm.addControl( 'clientNonPersonDetails', this.formBuilder.group({ - constitutionId: ['', Validators.required], + constitutionId: [ + '', + Validators.required + ], mainBusinessLineId: [''], incorpNumber: [''], authorizedSignatory: [''], @@ -467,10 +741,19 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges this.isMinor = years < 10; // Add/remove guardian form group based on minor status if (this.isMinor && !wasMinor) { - this.createClientForm.addControl('guardianDetails', this.formBuilder.group({ - guardianClientId: ['', Validators.required], - guardianRelation: ['', Validators.required] - })); + this.createClientForm.addControl( + 'guardianDetails', + this.formBuilder.group({ + guardianClientId: [ + '', + Validators.required + ], + guardianRelation: [ + '', + Validators.required + ] + }) + ); this.selectedGuardian = null; this.guardianSearchText = ''; this.guardianSearchResults = []; @@ -523,13 +806,15 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges }); // Reset DIP verification when mobile number changes - this.createClientForm.get('mobileNo') + this.createClientForm + .get('mobileNo') .valueChanges.pipe(takeUntil(this.destroy$)) .subscribe(() => { this.mobileVerified = false; }); - this.createClientForm.get('alternateMobileNo') + this.createClientForm + .get('alternateMobileNo') .valueChanges.pipe(takeUntil(this.destroy$)) .subscribe(() => { this.alternateMobileVerified = false; @@ -700,7 +985,9 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges target.officeName = data.officeName || null; target._loading = false; }, - error: () => { target._loading = false; } + error: () => { + target._loading = false; + } }); this.clientService.getClientKyc(cid).subscribe({ @@ -710,24 +997,36 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges target.aadhaarMasked = kyc?.aadhaarNumberMasked || '-'; target.aadhaarVerified = kyc?.aadhaarVerified || false; }, - error: () => { target.panNumber = '-'; target.aadhaarMasked = '-'; } + error: () => { + target.panNumber = '-'; + target.aadhaarMasked = '-'; + } }); this.clientService.getClientAccountData(cid).subscribe({ next: (accts: any) => { const savings = accts?.savingsAccounts || []; const active = savings.find((a: any) => a.status?.active) || savings[0]; - target.accountType = active ? (active.productName || 'Savings') : '-'; + target.accountType = active ? active.productName || 'Savings' : '-'; target.accountNo = active?.accountNo || '-'; }, - error: () => { target.accountType = '-'; target.accountNo = '-'; } + error: () => { + target.accountType = '-'; + target.accountNo = '-'; + } }); this.clientService.getClientAddressData(cid).subscribe({ next: (addresses: any[]) => { if (addresses?.length) { const addr = addresses[0]; - const parts = [addr.addressLine1, addr.townVillage, addr.city, addr.countyDistrict, addr.addressLine3].filter(Boolean); + const parts = [ + addr.addressLine1, + addr.townVillage, + addr.city, + addr.countyDistrict, + addr.addressLine3 + ].filter(Boolean); if (parts.length) { target.fullAddress = parts.join(', ') + (addr.postalCode ? ' - ' + addr.postalCode : ''); } @@ -749,8 +1048,8 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges // Fetch signature image this.clientService.getClientDocuments(cid).subscribe({ next: (docs: any[]) => { - const sigDoc = docs?.find((d: any) => - d.name?.toLowerCase().includes('signature') || d.description?.toLowerCase().includes('signature') + const sigDoc = docs?.find( + (d: any) => d.name?.toLowerCase().includes('signature') || d.description?.toLowerCase().includes('signature') ); if (sigDoc) { this.clientService.getClientSignatureImage(cid, sigDoc.id.toString()).subscribe({ @@ -801,19 +1100,29 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges onSignatorySearch(text: string): void { this.signatorySearchText = text; clearTimeout(this.signatorySearchTimer); - if (!text || text.length < 2) { this.signatorySearchResults = []; return; } + if (!text || text.length < 2) { + this.signatorySearchResults = []; + return; + } this.signatorySearching = true; this.signatorySearchTimer = setTimeout(() => { this.clientService.searchByText(text, 0, 10).subscribe({ - next: (data: any) => { this.signatorySearchResults = data?.content || []; this.signatorySearching = false; this.clientService.enrichSearchResults(this.signatorySearchResults); }, - error: () => { this.signatorySearchResults = []; this.signatorySearching = false; } + next: (data: any) => { + this.signatorySearchResults = data?.content || []; + this.signatorySearching = false; + this.clientService.enrichSearchResults(this.signatorySearchResults); + }, + error: () => { + this.signatorySearchResults = []; + this.signatorySearching = false; + } }); }, 400); } addSignatory(client: any): void { // Prevent duplicates - if (this.signatories.find(s => s.id === client.id)) { + if (this.signatories.find((s) => s.id === client.id)) { this.signatorySearchResults = []; this.signatorySearchText = ''; return; @@ -886,26 +1195,26 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges } private syncSignatoryNames(): void { - const names = this.signatories.map(s => s.displayName).join(', '); + const names = this.signatories.map((s) => s.displayName).join(', '); this.createClientForm.get('clientNonPersonDetails')?.patchValue({ authorizedSignatory: names }); } /** Watch MSME turnover & investment for auto-classification */ /** Maps salutation name to gender. Returns gender option or null. */ private salutationGenderMap: Record = { - 'mr': 'male', + mr: 'male', 'mr.': 'male', - 'shri': 'male', - 'sri': 'male', - 'master': 'male', - 'mrs': 'female', + shri: 'male', + sri: 'male', + master: 'male', + mrs: 'female', 'mrs.': 'female', - 'ms': 'female', + ms: 'female', 'ms.': 'female', - 'miss': 'female', - 'smt': 'female', + miss: 'female', + smt: 'female', 'smt.': 'female', - 'kumari': 'female', + kumari: 'female' // Dr, Er, CA, Adv etc. are gender-neutral — no auto-fill }; @@ -919,39 +1228,41 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges const salCtrl = this.createClientForm.get('salutationId'); if (!salCtrl) return; - this.salutationSub = salCtrl.valueChanges - .pipe(takeUntil(this.destroy$)) - .subscribe((salutationId: any) => { - if (!salutationId || !this.salutationOptions?.length || !this.genderOptions?.length) return; + this.salutationSub = salCtrl.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((salutationId: any) => { + if (!salutationId || !this.salutationOptions?.length || !this.genderOptions?.length) return; - // Find the salutation name from the selected ID - const salutation = this.salutationOptions.find((s: any) => s.id === salutationId); - if (!salutation) return; + // Find the salutation name from the selected ID + const salutation = this.salutationOptions.find((s: any) => s.id === salutationId); + if (!salutation) return; - const salName = (salutation.name || '').toLowerCase().trim().replace(/\.$/, ''); - const mappedGender = this.salutationGenderMap[salName] || this.salutationGenderMap[salName + '.']; - if (!mappedGender) return; + const salName = (salutation.name || '').toLowerCase().trim().replace(/\.$/, ''); + const mappedGender = this.salutationGenderMap[salName] || this.salutationGenderMap[salName + '.']; + if (!mappedGender) return; - // Find matching gender option by name - const genderOption = this.genderOptions.find((g: any) => - (g.name || '').toLowerCase().trim() === mappedGender - ); + // Find matching gender option by name + const genderOption = this.genderOptions.find((g: any) => (g.name || '').toLowerCase().trim() === mappedGender); - if (genderOption) { - this.createClientForm.get('genderId')?.setValue(genderOption.id); - } - }); + if (genderOption) { + this.createClientForm.get('genderId')?.setValue(genderOption.id); + } + }); } /** Returns capitalized full name preview from first + middle + last */ get fullNamePreview(): string { - const capitalize = (s: string) => s ? s.charAt(0).toUpperCase() + s.slice(1).toLowerCase() : ''; + const capitalize = (s: string) => (s ? s.charAt(0).toUpperCase() + s.slice(1).toLowerCase() : ''); const salutationId = this.createClientForm?.get('salutationId')?.value; const salutation = this.salutationOptions?.find((s: any) => s.id === salutationId)?.name || ''; const first = capitalize(this.createClientForm?.get('firstname')?.value || ''); const middle = capitalize(this.createClientForm?.get('middlename')?.value || ''); const last = capitalize(this.createClientForm?.get('lastname')?.value || ''); - const full = [first, middle, last].filter(Boolean).join(' '); + const full = [ + first, + middle, + last + ] + .filter(Boolean) + .join(' '); return salutation ? `${salutation} ${full}` : full; } @@ -1029,7 +1340,7 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges getSelectedNationality(): any { const val = this.createClientForm.get('nationalityId')?.value; - return this.nationalityOptions.find(n => n.value === val) || null; + return this.nationalityOptions.find((n) => n.value === val) || null; } getSelectedBusiness(): any { @@ -1056,6 +1367,229 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges return 'help_outline'; } + getSelectedEducationLevel(): any { + const val = this.createClientForm.get('educationLevelId')?.value; + return this.educationLevelOptions.find((e: any) => e.id === val) || null; + } + + getEducationLevelIcon(name: string): string { + const n = (name || '').toLowerCase(); + if ( + n.includes('school') || + n.includes('10th') || + n.includes('12th') || + n.includes('ssc') || + n.includes('hsc') || + n.includes('matric') || + n.includes('intermediate') + ) + return 'school'; + if (n.includes('diploma') || n.includes('iti')) return 'card_membership'; + if (n.includes('under') || n.includes('bachelor') || (n.includes('graduate') && !n.includes('post'))) + return 'menu_book'; + if (n.includes('post') || n.includes('master')) return 'auto_stories'; + if (n.includes('doctor') || n.includes('phd') || n.includes('ph.d')) return 'workspace_premium'; + if (n.includes('profession')) return 'workspace_premium'; + if (n.includes('none') || n.includes('illiter') || n.includes('below')) return 'block'; + return 'school'; + } + + getSelectedDegreeQualification(): any { + const val = this.createClientForm.get('degreeQualificationId')?.value; + return this.degreeQualificationOptions.find((d: any) => d.id === val) || null; + } + + /** + * Classifies an education level name into one of: school | undergraduate | postgraduate | doctorate. + * Used to filter the Degree/Qualification dropdown to relevant options. + */ + getEducationLevelCategory(name: string): string { + const n = (name || '').toLowerCase().replace(/[\s_-]+/g, ''); + if (n.includes('doctor') || n.includes('phd') || n.includes('profession')) return 'doctorate'; + if (n.includes('postgrad') || n === 'pg' || n.includes('master')) return 'postgraduate'; + if (n.includes('undergrad') || n === 'ug' || n.includes('bachelor') || n.includes('graduate')) + return 'undergraduate'; + if ( + n.includes('school') || + n.includes('10th') || + n.includes('12th') || + n.includes('ssc') || + n.includes('hsc') || + n.includes('matric') || + n.includes('intermediate') || + n.includes('diploma') || + n.includes('iti') || + n.includes('below') + ) + return 'school'; + return ''; + } + + /** + * Classifies a degree / qualification name into the education level it belongs to. + */ + getDegreeQualificationCategory(name: string): string { + const n = (name || '').toLowerCase(); + // Doctorate / professional + if ( + n.includes('ph.d') || + n.includes('phd') || + n.includes('d.litt') || + n.includes('d.sc') || + n.startsWith('ca ') || + n === 'ca' || + n.includes('chartered accountant') || + n.startsWith('cs ') || + n === 'cs' || + n.includes('company secretary') || + n.includes('cma') || + n.includes('cfa') || + n.includes('doctorate') + ) + return 'doctorate'; + // Postgraduate — starts with M. or contains master / PG-specific + if ( + n.startsWith('m.') || + n.startsWith('m ') || + n.includes('master') || + n.includes('mba') || + n.includes('mca') || + n.includes('mds') || + n.includes('msw') || + n.includes('mfa') || + n.match(/\bmd\b/) || + n.match(/\bms\b/) + ) + return 'postgraduate'; + // Undergraduate — starts with B. or contains bachelor / UG-specific + if ( + n.startsWith('b.') || + n.startsWith('b ') || + n.includes('bachelor') || + n.includes('bba') || + n.includes('bca') || + n.includes('mbbs') || + n.includes('bds') || + n.includes('bams') || + n.includes('bhms') || + n.includes('bfa') || + n.includes('bhm') || + n.includes('llb') || + n.includes('nursing') + ) + return 'undergraduate'; + // School + if ( + n.includes('10th') || + n.includes('12th') || + n.includes('ssc') || + n.includes('hsc') || + n.includes('matric') || + n.includes('intermediate') || + n.includes('diploma') || + n.includes('iti') || + n.includes('below') + ) + return 'school'; + return ''; + } + + /** + * Degrees filtered by the currently selected Education Level. + * Returns all degrees if no level is selected, or category can't be determined. + */ + get filteredDegreeQualificationOptions(): any[] { + const level = this.getSelectedEducationLevel(); + if (!level) return []; + const category = this.getEducationLevelCategory(level.name); + if (!category) return this.degreeQualificationOptions; + const filtered = this.degreeQualificationOptions.filter((d: any) => { + const c = this.getDegreeQualificationCategory(d.name); + return c === category || c === ''; + }); + return filtered.length > 0 ? filtered : this.degreeQualificationOptions; + } + + getSelectedReligion(): any { + const val = this.createClientForm.get('religionId')?.value; + return this.religionOptions.find((r: any) => r.id === val) || null; + } + + getReligionIcon(name: string): string { + const n = (name || '').toLowerCase(); + if (n.includes('hindu')) return 'temple_hindu'; + if (n.includes('muslim') || n.includes('islam')) return 'mosque'; + if (n.includes('christ')) return 'church'; + if (n.includes('sikh')) return 'temple_buddhist'; + if (n.includes('buddh')) return 'temple_buddhist'; + if (n.includes('jain')) return 'temple_hindu'; + if (n.includes('jew') || n.includes('juda')) return 'synagogue'; + if (n.includes('parsi') || n.includes('zoroastrian')) return 'local_fire_department'; + if (n.includes('bahai') || n.includes("baha'i")) return 'auto_awesome'; + if (n.includes('none') || n.includes('atheist') || n.includes('not disclosed')) return 'visibility_off'; + if (n.includes('other')) return 'more_horiz'; + return 'public'; + } + + getSelectedSocialCategory(): any { + const val = this.createClientForm.get('socialCategoryId')?.value; + return this.socialCategoryOptions.find((s: any) => s.id === val) || null; + } + + getSocialCategoryIcon(name: string): string { + const n = (name || '').toLowerCase().replace(/[\s.-]+/g, ''); + if (n.includes('general') || n === 'gen' || n.includes('unreserved')) return 'groups'; + if (n.includes('obc') || n.includes('otherbackward')) return 'diversity_3'; + if (n === 'sc' || n.includes('scheduledcaste')) return 'diversity_1'; + if (n === 'st' || n.includes('scheduledtribe')) return 'forest'; + if (n.includes('ews') || n.includes('economically')) return 'savings'; + if (n.includes('minority')) return 'diversity_2'; + if (n.includes('other')) return 'more_horiz'; + if (n.includes('notdisclosed')) return 'visibility_off'; + return 'groups'; + } + + getDegreeQualificationIcon(name: string): string { + const n = (name || '').toLowerCase(); + if ( + n.includes('b.tech') || + n.includes('m.tech') || + n.includes('b.e') || + n.includes('m.e') || + n.includes('engineer') + ) + return 'engineering'; + if ( + n.includes('mbbs') || + n.includes('md') || + n.includes('ms ') || + n.includes('medic') || + n.includes('bhms') || + n.includes('bams') + ) + return 'local_hospital'; + if (n.includes('bds') || n.includes('mds') || n.includes('dental')) return 'medical_services'; + if (n.includes('pharm')) return 'medication'; + if (n.includes('b.com') || n.includes('m.com') || n.includes('commerce')) return 'trending_up'; + if (n.includes('mba') || n.includes('bba') || n.includes('business')) return 'business_center'; + if (n.includes('bca') || n.includes('mca') || n.includes('comput')) return 'computer'; + if (n.includes('llb') || n.includes('llm') || n.includes('law')) return 'gavel'; + if (n.includes('b.ed') || n.includes('m.ed') || n.includes('educat')) return 'cast_for_education'; + if (n.includes('b.sc') || n.includes('m.sc') || n.includes('science')) return 'science'; + if (n.includes('b.a') || n.includes('m.a') || n.includes('arts')) return 'palette'; + if (n.includes('arch')) return 'architecture'; + if (n.includes('nurs')) return 'medical_information'; + if (n.includes('ca ') || n.includes('chartered')) return 'calculate'; + if (n.includes('cs ') || n.includes('company secretary')) return 'description'; + if (n.includes('cfa') || n.includes('cma')) return 'pie_chart'; + if (n.includes('phd') || n.includes('ph.d') || n.includes('doctorate')) return 'workspace_premium'; + if (n.includes('10th') || n.includes('ssc') || n.includes('matric')) return 'menu_book'; + if (n.includes('12th') || n.includes('hsc') || n.includes('intermediate')) return 'auto_stories'; + if (n.includes('diploma') || n.includes('iti')) return 'card_membership'; + if (n.includes('other')) return 'more_horiz'; + return 'school'; + } + getBusinessIcon(name: string): string { const n = (name || '').toLowerCase(); if (n.includes('manufactur')) return 'precision_manufacturing'; @@ -1155,14 +1689,17 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges } const unsupportedFields = [ - 'guardianDetails', 'addressDetails', 'entityKycDetails' + 'guardianDetails', + 'addressDetails', + 'entityKycDetails' ]; - unsupportedFields.forEach(f => delete generalDetails[f]); + unsupportedFields.forEach((f) => delete generalDetails[f]); // Format dateOfRegistration if present if (generalDetails.clientNonPersonDetails?.dateOfRegistration instanceof Date) { generalDetails.clientNonPersonDetails.dateOfRegistration = this.dateUtils.formatDate( - generalDetails.clientNonPersonDetails.dateOfRegistration, dateFormat + generalDetails.clientNonPersonDetails.dateOfRegistration, + dateFormat ); } @@ -1242,14 +1779,18 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges credit_card PAN {{ data.panNumber || '-' }} - @if (data.panVerified) { check_circle } + @if (data.panVerified) { + check_circle + }
fingerprint Aadhaar {{ data.aadhaarMasked || '-' }} - @if (data.aadhaarVerified) { check_circle } + @if (data.aadhaarVerified) { + check_circle + }
@@ -1287,73 +1828,186 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges `, - styles: [` - :host { --bg: #F0F4F8; --surface: #FFFFFF; --border: #E2E8F0; --border-input: #CBD5E1; - --primary: #1E6BB8; --primary-light: #EBF3FF; --text: #1A2332; --text-muted: #64748B; - --radius-card: 12px; } + styles: [ + ` + :host { + --bg: #f0f4f8; + --surface: #ffffff; + --border: #e2e8f0; + --border-input: #cbd5e1; + --primary: #1e6bb8; + --primary-light: #ebf3ff; + --text: #1a2332; + --text-muted: #64748b; + --radius-card: 12px; + } - .sd { background: var(--bg); border-radius: var(--radius-card); overflow: hidden; } + .sd { + background: var(--bg); + border-radius: var(--radius-card); + overflow: hidden; + } - /* Header */ - .sd-header { - display: flex; align-items: center; gap: 14px; - padding: 20px 24px; background: var(--surface); border-bottom: 1px solid var(--border); - } - .sd-avatar { - width: 56px; height: 56px; border-radius: 50%; background: var(--primary-light); - display: flex; align-items: center; justify-content: center; - flex-shrink: 0; overflow: hidden; border: 2px solid var(--border); - } - .sd-avatar--photo { border-color: var(--primary); } - .sd-avatar img { width: 100%; height: 100%; object-fit: cover; } - .sd-avatar-icon { font-size: 22px; color: var(--primary); } - .sd-header-info { flex: 1; } - .sd-name { margin: 0; font-size: 18px; font-weight: 700; color: var(--text); } - .sd-sub { margin: 2px 0 0; font-size: 13px; color: var(--text-muted); } - .sd-close { color: var(--text-muted); } + /* Header */ + .sd-header { + display: flex; + align-items: center; + gap: 14px; + padding: 20px 24px; + background: var(--surface); + border-bottom: 1px solid var(--border); + } + .sd-avatar { + width: 56px; + height: 56px; + border-radius: 50%; + background: var(--primary-light); + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + overflow: hidden; + border: 2px solid var(--border); + } + .sd-avatar--photo { + border-color: var(--primary); + } + .sd-avatar img { + width: 100%; + height: 100%; + object-fit: cover; + } + .sd-avatar-icon { + font-size: 22px; + color: var(--primary); + } + .sd-header-info { + flex: 1; + } + .sd-name { + margin: 0; + font-size: 18px; + font-weight: 700; + color: var(--text); + } + .sd-sub { + margin: 2px 0 0; + font-size: 13px; + color: var(--text-muted); + } + .sd-close { + color: var(--text-muted); + } - /* Body */ - .sd-body { padding: 0 !important; background: var(--bg); max-height: none !important; overflow: visible !important; padding-bottom: 16px !important; } - .sd-loading { display: flex; align-items: center; gap: 10px; padding: 20px 24px; color: var(--text-muted); font-size: 13px; } + /* Body */ + .sd-body { + padding: 0 !important; + background: var(--bg); + max-height: none !important; + overflow: visible !important; + padding-bottom: 16px !important; + } + .sd-loading { + display: flex; + align-items: center; + gap: 10px; + padding: 20px 24px; + color: var(--text-muted); + font-size: 13px; + } - /* Sections */ - .sd-section { - background: var(--surface); margin: 12px 16px; padding: 16px 20px; - border-radius: var(--radius-card); border: 1px solid var(--border); - } - .sd-section--kyc { background: var(--primary-light); border-color: #c7ddf5; } - .sd-section--sig { padding-bottom: 20px; } + /* Sections */ + .sd-section { + background: var(--surface); + margin: 12px 16px; + padding: 16px 20px; + border-radius: var(--radius-card); + border: 1px solid var(--border); + } + .sd-section--kyc { + background: var(--primary-light); + border-color: #c7ddf5; + } + .sd-section--sig { + padding-bottom: 20px; + } - .sd-row { display: flex; gap: 24px; } - .sd-row + .sd-row { margin-top: 14px; } - .sd-field { flex: 1; display: flex; flex-direction: column; } - .sd-field--full { flex: 1 1 100%; } + .sd-row { + display: flex; + gap: 24px; + } + .sd-row + .sd-row { + margin-top: 14px; + } + .sd-field { + flex: 1; + display: flex; + flex-direction: column; + } + .sd-field--full { + flex: 1 1 100%; + } - .sd-label { - font-size: 11px; font-weight: 600; text-transform: uppercase; color: var(--text-muted); - letter-spacing: 0.6px; display: flex; align-items: center; gap: 4px; margin-bottom: 4px; - } - .sd-val { - font-size: 15px; font-weight: 600; color: var(--text); - display: flex; align-items: center; gap: 5px; - } - .sd-val--mono { font-family: 'JetBrains Mono', 'Fira Code', monospace; letter-spacing: 0.5px; } + .sd-label { + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + color: var(--text-muted); + letter-spacing: 0.6px; + display: flex; + align-items: center; + gap: 4px; + margin-bottom: 4px; + } + .sd-val { + font-size: 15px; + font-weight: 600; + color: var(--text); + display: flex; + align-items: center; + gap: 5px; + } + .sd-val--mono { + font-family: 'JetBrains Mono', 'Fira Code', monospace; + letter-spacing: 0.5px; + } - .sd-icon { font-size: 15px !important; width: 15px !important; height: 15px !important; color: var(--primary); } - .sd-tick { font-size: 18px !important; width: 18px !important; height: 18px !important; color: var(--primary); } + .sd-icon { + font-size: 15px !important; + width: 15px !important; + height: 15px !important; + color: var(--primary); + } + .sd-tick { + font-size: 18px !important; + width: 18px !important; + height: 18px !important; + color: var(--primary); + } - /* Signature frame */ - .sd-sig-frame { - margin-top: 8px; padding: 12px; background: var(--surface); - border: 1px dashed var(--border-input); border-radius: 8px; - display: inline-block; - } - .sd-sig-frame img { max-width: 300px; max-height: 100px; display: block; } + /* Signature frame */ + .sd-sig-frame { + margin-top: 8px; + padding: 12px; + background: var(--surface); + border: 1px dashed var(--border-input); + border-radius: 8px; + display: inline-block; + } + .sd-sig-frame img { + max-width: 300px; + max-height: 100px; + display: block; + } - @media (max-width: 480px) { - .sd-row { flex-direction: column; gap: 12px; } - } - `], + @media (max-width: 480px) { + .sd-row { + flex-direction: column; + gap: 12px; + } + } + ` + ], imports: [ STANDALONE_SHARED_IMPORTS, MatIcon, @@ -1369,7 +2023,8 @@ export class SignatoryDetailDialogComponent { data: any; loading = false; - constructor(@Inject(MAT_DIALOG_DATA) inputData: any) { + constructor() { + const inputData = inject(MAT_DIALOG_DATA); this.data = { ...inputData }; const cid = inputData.id?.toString(); if (!cid) return; @@ -1377,18 +2032,25 @@ export class SignatoryDetailDialogComponent { // Fetch profile image this.clientService.getClientProfileImage(cid).subscribe({ next: (img: any) => { - if (img) { this.data = { ...this.data, profileImageUrl: this.sanitizer.bypassSecurityTrustResourceUrl(img) }; } + if (img) { + this.data = { ...this.data, profileImageUrl: this.sanitizer.bypassSecurityTrustResourceUrl(img) }; + } } }); // Fetch signature this.clientService.getClientDocuments(cid).subscribe({ next: (docs: any[]) => { - const sigDoc = docs?.find((d: any) => d.name?.toLowerCase().includes('signature') || d.description?.toLowerCase().includes('signature')); + const sigDoc = docs?.find( + (d: any) => d.name?.toLowerCase().includes('signature') || d.description?.toLowerCase().includes('signature') + ); if (sigDoc) { this.clientService.getClientSignatureImage(cid, sigDoc.id.toString()).subscribe({ next: (blob: Blob) => { - this.data = { ...this.data, signatureUrl: this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(blob)) }; + this.data = { + ...this.data, + signatureUrl: this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(blob)) + }; } }); } @@ -1415,19 +2077,54 @@ export class SignatoryDetailDialogComponent { `, - styles: [` - .zoom-dialog { background: #fff; border-radius: 12px; overflow: hidden; } - .zoom-dialog__header { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; background: #0f172a; color: #fff; } - .zoom-dialog__title { font-size: 14px; font-weight: 600; } - .zoom-dialog__close { color: rgba(255,255,255,0.7) !important; } - .zoom-dialog__close:hover { color: #fff !important; } - .zoom-dialog__body { padding: 20px; display: flex; align-items: center; justify-content: center; background: #f8fafc; } - .zoom-dialog__img { max-width: 100%; max-height: 70vh; object-fit: contain; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.1); } - `], - imports: [MatIcon, MatDialogClose] + styles: [ + ` + .zoom-dialog { + background: #fff; + border-radius: 12px; + overflow: hidden; + } + .zoom-dialog__header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 12px 16px; + background: #0f172a; + color: #fff; + } + .zoom-dialog__title { + font-size: 14px; + font-weight: 600; + } + .zoom-dialog__close { + color: rgba(255, 255, 255, 0.7) !important; + } + .zoom-dialog__close:hover { + color: #fff !important; + } + .zoom-dialog__body { + padding: 20px; + display: flex; + align-items: center; + justify-content: center; + background: #f8fafc; + } + .zoom-dialog__img { + max-width: 100%; + max-height: 70vh; + object-fit: contain; + border-radius: 8px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); + } + ` + ], + imports: [ + MatIcon, + MatDialogClose + ] }) export class GuardianImageZoomDialogComponent { - constructor(@Inject(MAT_DIALOG_DATA) public data: { imageUrl: string; title: string }) {} + data = inject<{ imageUrl: string; title: string }>(MAT_DIALOG_DATA); } /** @@ -1453,17 +2150,59 @@ export class GuardianImageZoomDialogComponent { `, - styles: [` - .gdc { background: #fff; border-radius: 12px; overflow: hidden; } - .gdc-header { display: flex; align-items: center; gap: 10px; padding: 16px 20px; background: #fef2f2; border-bottom: 1px solid #fecaca; } - .gdc-header__icon { color: #dc2626; font-size: 24px; width: 24px; height: 24px; } - .gdc-header__title { margin: 0; font-size: 16px; font-weight: 600; color: #991b1b; } - .gdc-body { padding: 20px; } - .gdc-body p { margin: 0; font-size: 14px; color: #374151; line-height: 1.5; } - .gdc-actions { display: flex; justify-content: flex-end; gap: 10px; padding: 12px 20px; border-top: 1px solid #f1f5f9; } - .gdc-actions button { display: flex; align-items: center; gap: 6px; } - `], - imports: [MatIcon, MatDialogClose] + styles: [ + ` + .gdc { + background: #fff; + border-radius: 12px; + overflow: hidden; + } + .gdc-header { + display: flex; + align-items: center; + gap: 10px; + padding: 16px 20px; + background: #fef2f2; + border-bottom: 1px solid #fecaca; + } + .gdc-header__icon { + color: #dc2626; + font-size: 24px; + width: 24px; + height: 24px; + } + .gdc-header__title { + margin: 0; + font-size: 16px; + font-weight: 600; + color: #991b1b; + } + .gdc-body { + padding: 20px; + } + .gdc-body p { + margin: 0; + font-size: 14px; + color: #374151; + line-height: 1.5; + } + .gdc-actions { + display: flex; + justify-content: flex-end; + gap: 10px; + padding: 12px 20px; + border-top: 1px solid #f1f5f9; + } + .gdc-actions button { + display: flex; + align-items: center; + gap: 6px; + } + ` + ], + imports: [ + MatIcon, + MatDialogClose + ] }) export class GuardianDeleteConfirmDialogComponent {} - diff --git a/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.html b/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.html index 5299d941b..9b6140c0a 100644 --- a/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.html +++ b/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.html @@ -88,14 +88,18 @@ } @if (client?.dateOfBirth) {
- {{ client.legalFormId === LegalFormId.PERSON ? 'Date of Birth' : 'Incorporation Date' }} + {{ + client.legalFormId === LegalFormId.PERSON ? 'Date of Birth' : 'Incorporation Date' + }} {{ client.dateOfBirth | dateFormat }}
} @if (client?.legalFormId === LegalFormId.PERSON && client?.genderId) {
Gender - {{ client.genderId | find: clientTemplate.genderOptions : 'id' : 'name' }} + {{ + client.genderId | find: clientTemplate.genderOptions : 'id' : 'name' + }}
} @if (client?.legalFormId) { @@ -107,13 +111,17 @@ @if (client?.officeId) {
Office - {{ client.officeId | find: clientTemplate.officeOptions : 'id' : 'name' }} + {{ + client.officeId | find: clientTemplate.officeOptions : 'id' : 'name' + }}
} @if (client?.staffId) {
Staff - {{ client.staffId | find: clientTemplate.staffOptions : 'id' : 'displayName' }} + {{ + client.staffId | find: clientTemplate.staffOptions : 'id' : 'displayName' + }}
} @if (client?.legalFormId === LegalFormId.PERSON) { @@ -134,10 +142,42 @@ {{ client.maritalStatusId | find: maritalStatusOptions : 'id' : 'name' }} } + @if (client?.educationLevelId) { +
+ Education Level + {{ + client.educationLevelId | find: educationLevelOptions : 'id' : 'name' + }} +
+ } + @if (client?.degreeQualificationId) { +
+ Degree / Qualification + {{ + client.degreeQualificationId | find: degreeQualificationOptions : 'id' : 'name' + }} +
+ } + @if (client?.religionId) { +
+ Religion + {{ client.religionId | find: religionOptions : 'id' : 'name' }} +
+ } + @if (client?.socialCategoryId) { +
+ Social Category + {{ + client.socialCategoryId | find: socialCategoryOptions : 'id' : 'name' + }} +
+ } @if (client?.salutationId) {
Salutation - {{ client.salutationId | find: clientTemplate.salutationOptions : 'id' : 'name' }} + {{ + client.salutationId | find: clientTemplate.salutationOptions : 'id' : 'name' + }}
} @if (client?.ageYears != null) { @@ -150,7 +190,11 @@
Minor - + {{ client.isMinor ? 'Yes (Age < 10)' : 'No' }} @@ -172,13 +216,19 @@ @if (client.clientNonPersonDetails.constitutionId) {
Constitution - {{ client.clientNonPersonDetails.constitutionId | find: clientTemplate.clientNonPersonConstitutionOptions : 'id' : 'name' }} + {{ + client.clientNonPersonDetails.constitutionId + | find: clientTemplate.clientNonPersonConstitutionOptions : 'id' : 'name' + }}
} @if (client.clientNonPersonDetails.mainBusinessLineId) {
Main Business Line - {{ client.clientNonPersonDetails.mainBusinessLineId | find: clientTemplate.clientNonPersonMainBusinessLineOptions : 'id' : 'name' }} + {{ + client.clientNonPersonDetails.mainBusinessLineId + | find: clientTemplate.clientNonPersonMainBusinessLineOptions : 'id' : 'name' + }}
} @if (client.clientNonPersonDetails.incorpNumber) { @@ -215,17 +265,15 @@
DIP Verification - + {{ client?.dipVerified ? 'Verified' : 'Pending' }}
- @if (client?.dipLookupLogId) { -
- DIP Lookup Log ID - {{ client.dipLookupLogId }} -
- } @if (client?.alternateMobileNo) {
Alternate Mobile No @@ -236,18 +284,18 @@
Alternate DIP Verification - + {{ client?.alternateDipVerified ? 'Verified' : 'Pending' }}
} - @if (client?.alternateDipLookupLogId) { -
- Alternate DIP Lookup Log ID - {{ client.alternateDipLookupLogId }} -
- } @if (client?.emailAddress) {
Email Address @@ -257,78 +305,103 @@
Name Verification (Sanctions) - - {{ client?.nameVerified ? 'Verified — Clear' : (client?.nameVerified === false ? 'Sanctions Match' : 'Pending') }} + + {{ + client?.nameVerified + ? 'Verified — Clear' + : client?.nameVerified === false + ? 'Sanctions Match' + : 'Pending' + }}
- @if (client?.screeningLogId) { -
- Screening Log ID - {{ client.screeningLogId }} -
- }
@if (client?.legalFormId !== 2) { -
-
- - Financial & Classification -
-
-
- @if (client?.occupationCategoryId) { -
- Occupation Category - {{ client.occupationCategoryId | find: clientTemplate.occupationCategoryOptions : 'id' : 'name' }} -
- } - @if (client?.occupationId) { -
- Occupation - {{ client.occupationId | find: clientTemplate.occupationOptions : 'id' : 'name' }} -
- } - @if (client?.clientTypeId) { -
- Client Type - {{ client.clientTypeId | find: clientTemplate.clientTypeOptions : 'id' : 'name' }} -
- } - @if (client?.clientClassificationId) { -
- Client Classification - {{ client.clientClassificationId | find: clientTemplate.clientClassificationOptions : 'id' : 'name' }} -
- } - @if (client?.monthlyIncome) { -
- Monthly Income - ₹ {{ client.monthlyIncome | number }} -
- } - @if (client?.annualTurnover) { -
- Annual Turnover - ₹ {{ client.annualTurnover | number }} -
- } - @if (!client?.occupationCategoryId && !client?.occupationId && !client?.clientTypeId && !client?.clientClassificationId && !client?.monthlyIncome && !client?.annualTurnover) { -
- No classification data provided -
- } +
+
+ + Financial & Classification +
+
+
+ @if (client?.occupationCategoryId) { +
+ Occupation Category + {{ + client.occupationCategoryId | find: clientTemplate.occupationCategoryOptions : 'id' : 'name' + }} +
+ } + @if (client?.occupationId) { +
+ Occupation + {{ + client.occupationId | find: clientTemplate.occupationOptions : 'id' : 'name' + }} +
+ } + @if (client?.clientTypeId) { +
+ Client Type + {{ + client.clientTypeId | find: clientTemplate.clientTypeOptions : 'id' : 'name' + }} +
+ } + @if (client?.clientClassificationId) { +
+ Client Classification + {{ + client.clientClassificationId | find: clientTemplate.clientClassificationOptions : 'id' : 'name' + }} +
+ } + @if (client?.monthlyIncome) { +
+ Monthly Income + ₹ {{ client.monthlyIncome | number }} +
+ } + @if (client?.annualTurnover) { +
+ Annual Turnover + ₹ {{ client.annualTurnover | number }} +
+ } + @if ( + !client?.occupationCategoryId && + !client?.occupationId && + !client?.clientTypeId && + !client?.clientClassificationId && + !client?.monthlyIncome && + !client?.annualTurnover + ) { +
+ No classification data provided +
+ } +
-
} - @if (kycData?.panNumber || kycData?.aadhaarNumberMasked || kycData?.gstinNumber || kycData?.enterprisePan || kycData?.cinNumber) { + @if ( + kycData?.panNumber || + kycData?.aadhaarNumberMasked || + kycData?.gstinNumber || + kycData?.enterprisePan || + kycData?.cinNumber + ) {
@@ -391,9 +464,13 @@
Risk Category - @if (kycData.riskCategory === 1) { Low Risk } - @else if (kycData.riskCategory === 2) { Medium Risk } - @else if (kycData.riskCategory === 3) { High Risk } + @if (kycData.riskCategory === 1) { + Low Risk + } @else if (kycData.riskCategory === 2) { + Medium Risk + } @else if (kycData.riskCategory === 3) { + High Risk + }
} @@ -414,7 +491,7 @@
Guardian Details - Minor + Minor
@@ -450,7 +527,13 @@
Status - + {{ guardian.status.value }} @@ -524,7 +607,12 @@
} } - @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 + ) {
No address data provided
@@ -563,40 +651,76 @@
@if (entityClassificationData.msme.udyamRegistrationNumber) { -
Udyam Registration No.{{ entityClassificationData.msme.udyamRegistrationNumber }}
+
+ Udyam Registration No.{{ entityClassificationData.msme.udyamRegistrationNumber }} +
} @if (entityClassificationData.msme.msmeClassification) { -
MSME Classification{{ entityClassificationData.msme.msmeClassification }}
+
+ MSME Classification{{ entityClassificationData.msme.msmeClassification }} +
} @if (entityClassificationData.msme.enterpriseType) { -
Enterprise Type{{ entityClassificationData.msme.enterpriseType }}
+
+ Enterprise Type{{ entityClassificationData.msme.enterpriseType }} +
} @if (entityClassificationData.msme.nicActivityCode) { -
NIC Activity Code{{ entityClassificationData.msme.nicActivityCode }}
+
+ NIC Activity Code{{ entityClassificationData.msme.nicActivityCode }} +
} @if (entityClassificationData.msme.annualTurnover) { -
Annual Turnover₹ {{ entityClassificationData.msme.annualTurnover }} Cr
+
+ Annual Turnover₹ {{ entityClassificationData.msme.annualTurnover }} Cr +
} @if (entityClassificationData.msme.investmentPlantMachinery) { -
Investment (Plant & Machinery)₹ {{ entityClassificationData.msme.investmentPlantMachinery }} Cr
+
+ Investment (Plant & Machinery)₹ {{ entityClassificationData.msme.investmentPlantMachinery }} Cr +
} @if (entityClassificationData.msme.financialYearAssessment) { -
FY of Assessment{{ entityClassificationData.msme.financialYearAssessment }}
+
+ FY of Assessment{{ entityClassificationData.msme.financialYearAssessment }} +
} @if (entityClassificationData.msme.socialCategoryOwner) { -
Social Category{{ entityClassificationData.msme.socialCategoryOwner }}
+
+ Social Category{{ entityClassificationData.msme.socialCategoryOwner }} +
} @if (entityClassificationData.msme.genderPrimaryOwner) { -
Gender of Owner{{ entityClassificationData.msme.genderPrimaryOwner }}
+
+ Gender of Owner{{ entityClassificationData.msme.genderPrimaryOwner }} +
} @if (entityClassificationData.msme.noOfEmployees) { -
No. of Employees{{ entityClassificationData.msme.noOfEmployees }}
+
+ No. of Employees{{ entityClassificationData.msme.noOfEmployees }} +
} @if (entityClassificationData.msme.womenLedEnterprise) { -
Women-led EnterpriseYes
+
+ Women-led EnterpriseYes +
} @if (entityClassificationData.msme.divyangPwdOwned) { -
Divyang (PWD) OwnedYes
+
+ Divyang (PWD) OwnedYes +
}
@@ -613,19 +737,34 @@
@if (entityClassificationData.startup.dpiitRecognitionNumber) { -
DPIIT Recognition No.{{ entityClassificationData.startup.dpiitRecognitionNumber }}
+
+ DPIIT Recognition No.{{ entityClassificationData.startup.dpiitRecognitionNumber }} +
} @if (entityClassificationData.startup.entityStage) { -
Entity Stage{{ entityClassificationData.startup.entityStage }}
+
+ Entity Stage{{ entityClassificationData.startup.entityStage }} +
} @if (entityClassificationData.startup.sectorAsDpiit) { -
Sector (DPIIT){{ entityClassificationData.startup.sectorAsDpiit }}
+
+ Sector (DPIIT){{ entityClassificationData.startup.sectorAsDpiit }} +
} @if (entityClassificationData.startup.taxExemption80iac) { -
80IAC Tax ExemptionYes
+
+ 80IAC Tax ExemptionYes +
} @if (entityClassificationData.startup.pslLoanLimit) { -
PSL Loan Limit₹ {{ entityClassificationData.startup.pslLoanLimit | number }} Cr
+
+ PSL Loan Limit₹ {{ entityClassificationData.startup.pslLoanLimit | number }} Cr +
}
@@ -642,22 +781,40 @@
@if (entityClassificationData.mudra.mudraCategory) { -
Mudra Category{{ entityClassificationData.mudra.mudraCategory }}
+
+ Mudra Category{{ entityClassificationData.mudra.mudraCategory }} +
} @if (entityClassificationData.mudra.mudraLoanAccountNo) { -
Loan Account No.{{ entityClassificationData.mudra.mudraLoanAccountNo }}
+
+ Loan Account No.{{ entityClassificationData.mudra.mudraLoanAccountNo }} +
} @if (entityClassificationData.mudra.sanctionedAmount) { -
Sanctioned Amount₹ {{ entityClassificationData.mudra.sanctionedAmount | number }}
+
+ Sanctioned Amount₹ {{ entityClassificationData.mudra.sanctionedAmount | number }} +
} @if (entityClassificationData.mudra.lendingInstitutionName) { -
Lending Institution{{ entityClassificationData.mudra.lendingInstitutionName }}
+
+ Lending Institution{{ entityClassificationData.mudra.lendingInstitutionName }} +
} @if (entityClassificationData.mudra.repaymentStatus) { -
Repayment Status{{ entityClassificationData.mudra.repaymentStatus }}
+
+ Repayment Status{{ entityClassificationData.mudra.repaymentStatus }} +
} @if (entityClassificationData.mudra.npaFlag) { -
NPA FlagNPA
+
+ NPA FlagNPA +
}
@@ -674,19 +831,36 @@
@if (entityClassificationData.eou.eouRegistrationNumber) { -
EOU Registration No.{{ entityClassificationData.eou.eouRegistrationNumber }}
+
+ EOU Registration No.{{ entityClassificationData.eou.eouRegistrationNumber }} +
} @if (entityClassificationData.eou.registeredDcZone) { -
Registered DC Zone{{ entityClassificationData.eou.registeredDcZone }}
+
+ Registered DC Zone{{ entityClassificationData.eou.registeredDcZone }} +
} @if (entityClassificationData.eou.gstinEou) { -
GSTIN (EOU){{ entityClassificationData.eou.gstinEou }}
+
+ GSTIN (EOU){{ entityClassificationData.eou.gstinEou }} +
} @if (entityClassificationData.eou.annualExportObligation) { -
Export Obligation₹ {{ entityClassificationData.eou.annualExportObligation | number }} Cr
+
+ Export Obligation₹ {{ entityClassificationData.eou.annualExportObligation | number }} Cr +
} @if (entityClassificationData.eou.adCategoryBankCode) { -
AD Bank Code{{ entityClassificationData.eou.adCategoryBankCode }}
+
+ AD Bank Code{{ entityClassificationData.eou.adCategoryBankCode }} +
}
@@ -703,22 +877,40 @@
@if (entityClassificationData.importexport.iecCode) { -
IEC Code{{ entityClassificationData.importexport.iecCode }}
+
+ IEC Code{{ entityClassificationData.importexport.iecCode }} +
} @if (entityClassificationData.importexport.iecStatus) { -
IEC Status{{ entityClassificationData.importexport.iecStatus }}
+
+ IEC Status{{ entityClassificationData.importexport.iecStatus }} +
} @if (entityClassificationData.importexport.tradeType) { -
Trade Type{{ entityClassificationData.importexport.tradeType }}
+
+ Trade Type{{ entityClassificationData.importexport.tradeType }} +
} @if (entityClassificationData.importexport.primaryHsCode) { -
Primary HS Code{{ entityClassificationData.importexport.primaryHsCode }}
+
+ Primary HS Code{{ entityClassificationData.importexport.primaryHsCode }} +
} @if (entityClassificationData.importexport.eefcAccountRequired) { -
EEFC AccountRequired
+
+ EEFC AccountRequired +
} @if (entityClassificationData.importexport.femaDeclarationObtained) { -
FEMA DeclarationObtained
+
+ FEMA DeclarationObtained +
}
@@ -735,22 +927,40 @@
@if (entityClassificationData.psl.pslCategory) { -
PSL Category{{ entityClassificationData.psl.pslCategory }}
+
+ PSL Category{{ entityClassificationData.psl.pslCategory }} +
} @if (entityClassificationData.psl.pslSubCategory) { -
PSL Sub-category{{ entityClassificationData.psl.pslSubCategory }}
+
+ PSL Sub-category{{ entityClassificationData.psl.pslSubCategory }} +
} @if (entityClassificationData.psl.pslSanctionedLimit) { -
Sanctioned Limit₹ {{ entityClassificationData.psl.pslSanctionedLimit | number }}
+
+ Sanctioned Limit₹ {{ entityClassificationData.psl.pslSanctionedLimit | number }} +
} @if (entityClassificationData.psl.fiddReportingCode) { -
FIDD Reporting Code{{ entityClassificationData.psl.fiddReportingCode }}
+
+ FIDD Reporting Code{{ entityClassificationData.psl.fiddReportingCode }} +
} @if (entityClassificationData.psl.weakerSectionFlag) { -
Weaker SectionYes
+
+ Weaker SectionYes +
} @if (entityClassificationData.psl.womenEntrepreneurFlag) { -
Women EntrepreneurYes
+
+ Women EntrepreneurYes +
}
@@ -767,25 +977,46 @@
@if (entityClassificationData.nps.industrySector) { -
Industry Sector{{ entityClassificationData.nps.industrySector }}
+
+ Industry Sector{{ entityClassificationData.nps.industrySector }} +
} @if (entityClassificationData.nps.nicCode) { -
NIC Code{{ entityClassificationData.nps.nicCode }}
+
+ NIC Code{{ entityClassificationData.nps.nicCode }} +
} @if (entityClassificationData.nps.annualRevenueBand) { -
Annual Revenue Band{{ entityClassificationData.nps.annualRevenueBand }}
+
+ Annual Revenue Band{{ entityClassificationData.nps.annualRevenueBand }} +
} @if (entityClassificationData.nps.riskCategory) { -
Risk Category{{ entityClassificationData.nps.riskCategory }}
+
+ Risk Category{{ entityClassificationData.nps.riskCategory }} +
} @if (entityClassificationData.nps.amlRiskClassification) { -
AML Risk{{ entityClassificationData.nps.amlRiskClassification }}
+
+ AML Risk{{ entityClassificationData.nps.amlRiskClassification }} +
} @if (entityClassificationData.nps.pepFlag) { -
PEP FlagPEP
+
+ PEP FlagPEP +
} @if (entityClassificationData.nps.amlWatchlistMatch) { -
AML WatchlistMatch Found
+
+ AML WatchlistMatch Found +
}
@@ -793,58 +1024,64 @@ } } - @if (client?.legalFormId !== 2) { -
-
- - Family Members - @if (familyMembers.length > 0) { - {{ familyMembers.length }} - } -
-
- @if (familyMembers.length === 0) { -
- warning - No family members added. At least one is required. -
- } @else { -
- @for (member of familyMembers; track member; let i = $index) { -
-
{{ i + 1 }}
-
- person -
-
- {{ member.firstName || member.displayName }} - - @if (member.relationship) { {{ member.relationship }} } - @if (member.dateOfBirth) { · DOB: {{ member.dateOfBirth }} } - -
- @if (member.aadhaarNumber || member.panNumber) { -
- @if (member.panNumber) { PAN: {{ member.panNumber }} } - @if (member.aadhaarNumber) { Aadhaar: {{ member.aadhaarNumber }} } +
+
+ + Family Members + @if (familyMembers.length > 0) { + {{ familyMembers.length }} + } +
+
+ @if (familyMembers.length === 0) { +
+ warning + No family members added. At least one is required. +
+ } @else { +
+ @for (member of familyMembers; track member; let i = $index) { +
+
{{ i + 1 }}
+
+ person
- } -
- @if (member.isDependent) { - Dependent - } - @if (member.linkedClientId) { - Linked +
+ {{ member.firstName || member.displayName }} + + @if (member.relationship) { + {{ member.relationship }} + } + @if (member.dateOfBirth) { + · DOB: {{ member.dateOfBirth }} + } + +
+ @if (member.aadhaarNumber || member.panNumber) { +
+ @if (member.panNumber) { + PAN: {{ member.panNumber }} + } + @if (member.aadhaarNumber) { + Aadhaar: {{ member.aadhaarNumber }} + } +
} +
+ @if (member.isDependent) { + Dependent + } + @if (member.linkedClientId) { + Linked + } +
-
- } -
- } + } +
+ } +
-
} -
diff --git a/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.ts b/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.ts index 1ee6a88df..b4c491044 100644 --- a/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.ts +++ b/src/app/clients/client-stepper/client-preview-step/client-preview-step.component.ts @@ -73,9 +73,22 @@ export class ClientPreviewStepComponent { @Input() maritalStatusId: number | null = null; /** Marital Status Options */ @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 */ - addressTabs = ['permanent', 'current', 'office', 'correspondence']; + addressTabs = [ + 'permanent', + 'current', + 'office', + 'correspondence' + ]; addressTabLabels: Record = { permanent: 'Permanent Address', current: 'Current / Residential', diff --git a/src/app/clients/clients-view/personal-data-tab/personal-data-tab.component.html b/src/app/clients/clients-view/personal-data-tab/personal-data-tab.component.html index 6b22ded4d..228daa57b 100644 --- a/src/app/clients/clients-view/personal-data-tab/personal-data-tab.component.html +++ b/src/app/clients/clients-view/personal-data-tab/personal-data-tab.component.html @@ -7,7 +7,6 @@ -->
-
@@ -50,13 +49,15 @@ @if (clientViewData.dateOfBirth) {
{{ 'labels.inputs.Date of Birth' | translate }}
-
{{ clientViewData.dateOfBirth | dateFormat:'DD-MMMM-YYYY' }}
+
{{ clientViewData.dateOfBirth | dateFormat: 'DD-MMMM-YYYY' }}
} @if (clientViewData.ageYears != null) {
Age
-
{{ clientViewData.ageYears }}y {{ clientViewData.ageMonths || 0 }}m {{ clientViewData.ageDays || 0 }}d
+
+ {{ clientViewData.ageYears }}y {{ clientViewData.ageMonths || 0 }}m {{ clientViewData.ageDays || 0 }}d +
} @if (clientViewData.gender) { @@ -71,11 +72,39 @@
{{ clientViewData.maritalStatus.name }}
} + @if (clientViewData.educationLevel) { +
+
Education Level
+
{{ clientViewData.educationLevel.name }}
+
+ } + @if (clientViewData.degreeQualification) { +
+
Degree / Qualification
+
{{ clientViewData.degreeQualification.name }}
+
+ } + @if (clientViewData.religion) { +
+
Religion
+
{{ clientViewData.religion.name }}
+
+ } + @if (clientViewData.socialCategory) { +
+
Social Category
+
{{ clientViewData.socialCategory.name }}
+
+ } @if (clientViewData.isMinor != null) {
Minor
- + {{ clientViewData.isMinor ? 'Yes (Age < 10)' : 'No' }}
@@ -84,7 +113,11 @@
{{ 'labels.inputs.Is staff' | translate }}
- + {{ clientViewData.isStaff ? 'Yes' : 'No' }}
@@ -100,7 +133,7 @@ @if (clientViewData.dateOfBirth) {
{{ 'labels.inputs.Incorporation Date' | translate }}
-
{{ clientViewData.dateOfBirth | dateFormat:'DD-MM-YYYY' }}
+
{{ clientViewData.dateOfBirth | dateFormat: 'DD-MM-YYYY' }}
} } @@ -112,7 +145,9 @@ }
Nationality
-
{{ clientViewData.nationalityId || clientViewData.nationality || 'Indian' }}
+
+ {{ clientViewData.nationalityId || clientViewData.nationality || 'Indian' }} +
@@ -176,7 +211,9 @@ @if (clientViewData.clientNonPersonDetails.incorpValidityTillDate) {
{{ 'labels.inputs.Incorporation Validity Till Date' | translate }}
-
{{ clientViewData.clientNonPersonDetails.incorpValidityTillDate | dateFormat:'DD-MM-YYYY' }}
+
+ {{ clientViewData.clientNonPersonDetails.incorpValidityTillDate | dateFormat: 'DD-MM-YYYY' }} +
} @if (clientViewData.clientNonPersonDetails.incorpNumber) { @@ -221,7 +258,9 @@
{{ 'labels.inputs.Email' | translate }}
- {{ clientViewData.emailAddress || '-' }} + {{ clientViewData.emailAddress || '-' }}
@@ -240,15 +279,19 @@
Mobile DIP Status
- @if (clientViewData.mobileVerified === true || clientViewData.mobileVerified === 'true' || clientViewData.mobileVerified === 1 - || clientViewData.dipVerified === true || clientViewData.dipVerified === 'true' || clientViewData.dipVerified === 1) { + @if ( + clientViewData.mobileVerified === true || + clientViewData.mobileVerified === 'true' || + clientViewData.mobileVerified === 1 || + clientViewData.dipVerified === true || + clientViewData.dipVerified === 'true' || + clientViewData.dipVerified === 1 + ) { Verified } @else { - - Pending - + Pending }
@@ -258,26 +301,21 @@
{{ formatDateTime(clientViewData.dipTimestamp) }}
} - @if (clientViewData.dipLookupLogId) { -
-
DIP Lookup Log ID
-
{{ clientViewData.dipLookupLogId }}
-
- } - @if (clientViewData.alternateMobileNo) {
Alternate Mobile DIP Status
- @if (clientViewData.alternateDipVerified === true || clientViewData.alternateDipVerified === 'true' || clientViewData.alternateDipVerified === 1) { + @if ( + clientViewData.alternateDipVerified === true || + clientViewData.alternateDipVerified === 'true' || + clientViewData.alternateDipVerified === 1 + ) { Verified } @else { - - Pending - + Pending }
@@ -287,93 +325,91 @@
{{ formatDateTime(clientViewData.alternateDipTimestamp) }}
} - @if (clientViewData.alternateDipLookupLogId) { -
-
Alternate DIP Log ID
-
{{ clientViewData.alternateDipLookupLogId }}
-
- } }
Name Screening (Sanctions)
- @if (clientViewData.nameVerified === true || clientViewData.nameVerified === 'true' || clientViewData.nameVerified === 1) { + @if ( + clientViewData.nameVerified === true || + clientViewData.nameVerified === 'true' || + clientViewData.nameVerified === 1 + ) { Clear - } @else if (clientViewData.nameVerified === false || clientViewData.nameVerified === 'false' || clientViewData.nameVerified === 0) { - Match Found + } @else if ( + clientViewData.nameVerified === false || + clientViewData.nameVerified === 'false' || + clientViewData.nameVerified === 0 + ) { + Match Found } @else { Pending }
- @if (clientViewData.screeningLogId) { -
-
Screening Log ID
-
{{ clientViewData.screeningLogId }}
-
- }
@if (clientViewData?.legalForm?.id !== 2) { -
-
- -

Financial & Classification

-
-
-
-
-
Monthly Income
-
- @if (clientViewData.monthlyIncome != null && clientViewData.monthlyIncome !== '') { - ₹ {{ clientViewData.monthlyIncome | number }} - } @else { - - - } +
+
+ +

Financial & Classification

+
+
+
+
+
Monthly Income
+
+ @if (clientViewData.monthlyIncome != null && clientViewData.monthlyIncome !== '') { + ₹ {{ clientViewData.monthlyIncome | number }} + } @else { + - + } +
+
+
Annual Turnover (Last FY)
+
+ @if (clientViewData.annualTurnover != null && clientViewData.annualTurnover !== '') { + ₹ {{ clientViewData.annualTurnover | number }} + } @else { + - + } +
+
+ @if (clientViewData.clientType) { +
+
{{ 'labels.inputs.Client Type' | translate }}
+
{{ clientViewData.clientType.name }}
+
+ } + @if (clientViewData.clientClassification) { +
+
{{ 'labels.inputs.Client Classification' | translate }}
+
{{ clientViewData.clientClassification.name }}
+
+ } + @if (clientViewData.occupationCategory) { +
+
{{ 'labels.inputs.Occupation Category' | translate }}
+
{{ clientViewData.occupationCategory.name }}
+
+ } + @if (clientViewData.occupation) { +
+
{{ 'labels.inputs.Occupation' | translate }}
+
{{ clientViewData.occupation.name }}
+
+ }
-
-
Annual Turnover (Last FY)
-
- @if (clientViewData.annualTurnover != null && clientViewData.annualTurnover !== '') { - ₹ {{ clientViewData.annualTurnover | number }} - } @else { - - - } -
-
- @if (clientViewData.clientType) { -
-
{{ 'labels.inputs.Client Type' | translate }}
-
{{ clientViewData.clientType.name }}
-
- } - @if (clientViewData.clientClassification) { -
-
{{ 'labels.inputs.Client Classification' | translate }}
-
{{ clientViewData.clientClassification.name }}
-
- } - @if (clientViewData.occupationCategory) { -
-
{{ 'labels.inputs.Occupation Category' | translate }}
-
{{ clientViewData.occupationCategory.name }}
-
- } - @if (clientViewData.occupation) { -
-
{{ 'labels.inputs.Occupation' | translate }}
-
{{ clientViewData.occupation.name }}
-
- }
-
} @@ -436,19 +472,19 @@ @if (clientViewData.timeline?.submittedOnDate) {
{{ 'labels.inputs.Submitted On' | translate }}
-
{{ clientViewData.timeline.submittedOnDate | dateFormat:'DD-MM-YYYY' }}
+
{{ clientViewData.timeline.submittedOnDate | dateFormat: 'DD-MM-YYYY' }}
} @if (clientViewData.activationDate) {
{{ 'labels.inputs.Activation Date' | translate }}
-
{{ clientViewData.activationDate | dateFormat:'DD-MM-YYYY' }}
+
{{ clientViewData.activationDate | dateFormat: 'DD-MM-YYYY' }}
} @if (clientViewData.timeline?.closedOnDate) {
{{ 'labels.inputs.Closed Date' | translate }}
-
{{ clientViewData.timeline.closedOnDate | dateFormat:'DD-MM-YYYY' }}
+
{{ clientViewData.timeline.closedOnDate | dateFormat: 'DD-MM-YYYY' }}
}
@@ -467,10 +503,14 @@
{{ 'labels.inputs.Client Status' | translate }}
- + [class.status-pending]=" + clientViewData.status.value !== 'Active' && clientViewData.status.value !== 'Closed' + " + > {{ 'labels.status.' + clientViewData.status.value | translate }}
@@ -485,5 +525,4 @@
-
diff --git a/src/app/clients/clients.component.html b/src/app/clients/clients.component.html index c5227e293..73d4f6bcd 100644 --- a/src/app/clients/clients.component.html +++ b/src/app/clients/clients.component.html @@ -74,7 +74,6 @@ CKYC Pending -
@@ -89,7 +88,9 @@ {{ 'labels.inputs.name' | translate }} - {{ row.legalForm?.id === 2 ? 'business' : 'person' }} + {{ + row.legalForm?.id === 2 ? 'business' : 'person' + }} {{ hideClientData ? maskName(row.displayName) : row.displayName }} @if (row._ckycDone) { verified @@ -111,18 +112,6 @@ {{ row.cifNumber }} - - - CKYC Number - - @if (row._ckycNumber) { - {{ row._ckycNumber }} - } @else { - — - } - - - {{ 'labels.inputs.Legal Form' | translate }} @@ -149,7 +138,11 @@
@for (step of getProfileSteps(row); track step.label) { - + {{ step.label.charAt(0) }} } @@ -157,12 +150,6 @@ - - - {{ 'labels.inputs.Office Name' | translate }} - {{ row.officeName }} - - {{ 'labels.inputs.Actions' | translate }} diff --git a/src/app/clients/clients.component.ts b/src/app/clients/clients.component.ts index 65c0993a6..fd99d30f7 100644 --- a/src/app/clients/clients.component.ts +++ b/src/app/clients/clients.component.ts @@ -90,11 +90,9 @@ export class ClientsComponent implements OnInit { 'displayName', 'accountNumber', 'cifNumber', - 'ckycNumber', 'legalForm', 'status', 'profileCompletion', - 'officeName', 'actions' ]; dataSource: MatTableDataSource = new MatTableDataSource(); @@ -167,34 +165,37 @@ export class ClientsComponent implements OnInit { // Use individual subscriptions instead of forkJoin to avoid EMPTY observable issues clients.forEach((client, i) => { - this.clientService.getClientData(client.id?.toString()).pipe( - catchError(() => of(null)) - ).subscribe((data: any) => { - if (data?.legalForm) { - clients[i].legalForm = data.legalForm; - this.applyClientFilters(); - } - }); + this.clientService + .getClientData(client.id?.toString()) + .pipe(catchError(() => of(null))) + .subscribe((data: any) => { + if (data?.legalForm) { + clients[i].legalForm = data.legalForm; + this.applyClientFilters(); + } + }); - this.clientService.getClientKyc(client.id?.toString()).pipe( - catchError(() => of(null)) - ).subscribe((kyc: any) => { - if (kyc) { - clients[i]._kycDone = kyc.kycStatus === 300; - clients[i]._ckycDone = !!kyc.ckycNumber; - clients[i]._ckycNumber = kyc.ckycNumber || ''; - this.applyClientFilters(); - } - }); + this.clientService + .getClientKyc(client.id?.toString()) + .pipe(catchError(() => of(null))) + .subscribe((kyc: any) => { + if (kyc) { + clients[i]._kycDone = kyc.kycStatus === 300; + clients[i]._ckycDone = !!kyc.ckycNumber; + clients[i]._ckycNumber = kyc.ckycNumber || ''; + this.applyClientFilters(); + } + }); - this.clientService.getClientAddressData(client.id?.toString()).pipe( - catchError(() => of(null)) - ).subscribe((addr: any) => { - if (addr && Array.isArray(addr) && addr.length > 0) { - clients[i]._addressDone = true; - this.applyClientFilters(); - } - }); + this.clientService + .getClientAddressData(client.id?.toString()) + .pipe(catchError(() => of(null))) + .subscribe((addr: any) => { + if (addr && Array.isArray(addr) && addr.length > 0) { + clients[i]._addressDone = true; + this.applyClientFilters(); + } + }); }); } @@ -255,7 +256,7 @@ export class ClientsComponent implements OnInit { } getCompletedCount(row: any): number { - return this.getProfileSteps(row).filter(s => s.done).length; + return this.getProfileSteps(row).filter((s) => s.done).length; } private resetPaginator() { diff --git a/src/app/clients/create-client/create-client.component.html b/src/app/clients/create-client/create-client.component.html index cf32712fa..1fb1b2347 100644 --- a/src/app/clients/create-client/create-client.component.html +++ b/src/app/clients/create-client/create-client.component.html @@ -12,7 +12,12 @@

{{ 'labels.heading.Create Client' | translate }}

- + @@ -62,7 +67,8 @@
}
- edit Edit } @else { - }
} -
@@ -155,13 +166,18 @@ edit Edit } @else { - } } - @@ -220,9 +240,19 @@ } @if (selectedTabIndex < totalTabs - 1) {
-
`, - styles: [` - .scd { background: #fff; border-radius: 12px; overflow: hidden; } - .scd-header { - display: flex; align-items: center; gap: 14px; - padding: 20px 24px 16px; - } - .scd-header__icon-wrap { - width: 44px; height: 44px; border-radius: 12px; - background: #eff6ff; display: flex; align-items: center; justify-content: center; - } - .scd-header__icon { color: #1074b9; font-size: 24px; width: 24px; height: 24px; } - .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] + styles: [ + ` + .scd { + background: #fff; + border-radius: 12px; + overflow: hidden; + } + .scd-header { + display: flex; + align-items: center; + gap: 14px; + padding: 20px 24px 16px; + } + .scd-header__icon-wrap { + width: 44px; + height: 44px; + border-radius: 12px; + background: #eff6ff; + display: flex; + align-items: center; + justify-content: center; + } + .scd-header__icon { + color: #1074b9; + font-size: 24px; + width: 24px; + height: 24px; + } + .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 {} + +/** + * Verify Client Required Alert Dialog + */ +@Component({ + selector: 'mifosx-verify-client-alert-dialog', + template: ` +
+
+
+ shield +
+
+

Verification Required

+

Client name must be screened before saving

+
+
+
+

+ Please click Verify Client to screen the client name against the UN Security Council + Consolidated Sanctions List before proceeding. +

+
+ gavel + RBI KYC Master Direction (2016) — Section 51 & PMLA 2002. Sanctions screening is mandatory before + account opening. +
+
+
+ +
+
+ `, + 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 {} diff --git a/src/app/clients/edit-client/edit-client.component.html b/src/app/clients/edit-client/edit-client.component.html index 9c3ba70df..9dcdd46df 100644 --- a/src/app/clients/edit-client/edit-client.component.html +++ b/src/app/clients/edit-client/edit-client.component.html @@ -180,6 +180,12 @@ [addressData]="clientIndiaAddressStep?.addressData" [guardian]="clientGeneralStep?.selectedGuardian" [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()" > diff --git a/src/app/kyc-management/kyc-management.component.html b/src/app/kyc-management/kyc-management.component.html index 613116623..aaf28f3ed 100644 --- a/src/app/kyc-management/kyc-management.component.html +++ b/src/app/kyc-management/kyc-management.component.html @@ -77,15 +77,24 @@
@@ -100,8 +109,15 @@ - - + + + + + + + + + @@ -111,7 +127,9 @@
{{ row.name }}
{{ row.account }}
- + - - - - + + + + @@ -165,7 +191,10 @@
-
1
+
+
1
+
+
Notice 1 — Alert window opens
31 March 2027 · Auto-scheduled · ✓ Delivered
@@ -176,7 +205,10 @@
-
2
+
+
2
+
+
Notice 2 — Follow-up
10 April 2027 · +10 days · ⏳ Pending
@@ -189,7 +221,10 @@
-
3
+
+
3
+
+
Notice 3 — Final notice
20 April 2027 · +20 days · ✗ Not dispatched
@@ -198,16 +233,23 @@ Email IVR Reg. Post - Branch Visit + Home Visit
- - + +
-
!
+
+
!
+
+
Debit Freeze — if no action
30 April 2027 · Auto-triggered on expiry
@@ -219,7 +261,10 @@
-
!
+
+
!
+
+
Inoperative — +60 days
30 June 2027 · No compliance
@@ -270,7 +315,7 @@
🏦
-
Branch Visit
+
Home Visit
Manual
@@ -287,17 +332,28 @@
India Post Notice
-
Dispatch Date
10 April 2027
-
Reg. Post Docket No.
RP-BPL-20270410-443
-
Status
In Transit
- +
+
Dispatch Date
+
10 April 2027
+
+
+
Reg. Post Docket No.
+
RP-BPL-20270410-443
+
+
+
Status
+
In Transit
+
+
-
Branch Visit
+
Home Visit
Scheduled Date
@@ -334,15 +390,26 @@
@@ -357,15 +424,24 @@
CustomerRiskExpiry DateDays LeftNoticesStatePending ActionNext StageCustomerRiskExpiry DateDays LeftNoticesStatePending ActionNext Stage
{{ row.riskLabel.split(' ')[0] }} + {{ row.riskLabel.split(' ')[0] }} + {{ row.expiry }}
@@ -125,10 +143,18 @@
{{ row.noticesSent }}/3{{ stateLabel(row.state) }}{{ row.noticePending }}{{ row.nextStage }} + {{ row.noticesSent }}/3 + + {{ stateLabel(row.state) }} + + {{ row.noticePending }} + + {{ row.nextStage }} +
- - - + + + + + + + + + - + @@ -376,7 +452,10 @@ - + @@ -387,7 +466,10 @@ - + @@ -398,7 +480,10 @@ - + @@ -409,9 +494,12 @@ - + - + @@ -420,7 +508,10 @@ - + @@ -454,19 +545,29 @@
-
Active
1,847
KYC valid
+
Active
+
1,847
+
KYC valid
-
Alert Window
47
Expiring < 30d
+
Alert Window
+
47
+
Expiring < 30d
-
Debit Freeze
23
Debits blocked
+
Debit Freeze
+
23
+
Debits blocked
-
Inoperative
8
All txns blocked
+
Inoperative
+
8
+
All txns blocked
-
Dormant
3
RBI reportable
+
Dormant
+
3
+
RBI reportable
@@ -478,20 +579,28 @@
-
Active → Debit FreezeEnabled
-
KYC expires + 3 notices sent + no response → debits blocked, credits allowed
+
+ Active → Debit FreezeEnabled +
+
+ KYC expires + 3 notices sent + no response → debits blocked, credits allowed +
Window: 31 Mar – 30 Apr 3/3 notices
-
Debit Freeze → InoperativeEnabled
+
+ Debit Freeze → InoperativeEnabled +
+60 days from expiry, still unresolved → all transactions blocked
Apr 30 – Jun 30
-
Inoperative → DormantEnabled
+
+ Inoperative → DormantEnabled +
+150 days from expiry → dormant, RBI Dormant Account reporting triggered
Jun 30 – Sep 30 @@ -533,7 +642,9 @@
⚠ Maker-Checker Required
-
All manual changes require second officer approval and are logged in audit inventory.
+
+ All manual changes require second officer approval and are logged in audit inventory. +
@@ -547,35 +658,64 @@
#Account / CustomerNoticeChannelDispatchedReference / DocketDeliveryClient ActionOfficer#Account / CustomerNoticeChannelDispatchedReference / DocketDeliveryClient ActionOfficer
001
Rajesh K. Sharma
ACC-BPL-2024-00847
+
Rajesh K. Sharma
+
ACC-BPL-2024-00847
+
Notice 1 SMS + Email 31 Mar 2027
002
Rajesh K. Sharma
ACC-BPL-2024-00847
+
Rajesh K. Sharma
+
ACC-BPL-2024-00847
+
Notice 2 Reg. Post + IVR 10 Apr 2027
003
Priya Malhotra
ACC-BPL-2021-00231
+
Priya Malhotra
+
ACC-BPL-2021-00231
+
Notice 1 SMS + Email 10 Mar 2027
004
Priya Malhotra
ACC-BPL-2021-00231
+
Priya Malhotra
+
ACC-BPL-2021-00231
+
Notice 2 Reg. Post 20 Mar 2027
005
Priya Malhotra
ACC-BPL-2021-00231
+
Priya Malhotra
+
ACC-BPL-2021-00231
+
Notice 3Branch VisitHome Visit 30 Mar 2027 VIS-BPL-20270330-012 ✓ Done
006
Suresh Patel
ACC-BPL-2019-00112
+
Suresh Patel
+
ACC-BPL-2019-00112
+
Notice 3 Video KYC 5 Feb 2027
- + + + + + + + + + - + - + - + - + - + - +
AccountFromToTriggerDateByAction
AccountFromToTriggerDateByAction
Priya Malhotra
ACC-BPL-2021-00231
+
Priya Malhotra
+
ACC-BPL-2021-00231
+
Active Debit Freeze Auto — KYC Expired 10 Apr 2027 Scheduler + +
Suresh Patel
ACC-BPL-2019-00112
+
Suresh Patel
+
ACC-BPL-2019-00112
+
Debit Freeze Inoperative Auto — 60d elapsed 15 May 2027 Scheduler + +
Kavitha Nair
ACC-BPL-2018-00088
+
Kavitha Nair
+
ACC-BPL-2018-00088
+
Inoperative Dormant Auto — 150d elapsed 1 Jun 2027 Scheduler + +
@@ -600,11 +740,21 @@
- - - - - + + + + +
@@ -623,36 +773,86 @@
⚡
{{ stateLabel(current.state) }} — {{ current.daysLeft }} days to expiry
-
Next pending: {{ current.noticePending }}. Next stage: {{ current.nextStage }}.
+
+ Next pending: {{ current.noticePending }}. Next stage: {{ current.nextStage }}. +
-
{{ current.noticesSent }}
Notices Sent
-
{{ noticesPending(current) }}
Notices Pending
-
{{ current.actionTaken.length }}
Actions Taken
-
{{ current.pendingActions.length }}
Pending Actions
+
+
{{ current.noticesSent }}
+
Notices Sent
+
+
+
{{ noticesPending(current) }}
+
Notices Pending
+
+
+
{{ current.actionTaken.length }}
+
Actions Taken
+
+
+
{{ current.pendingActions.length }}
+
Pending Actions
+
KYC Information
-
PAN
{{ current.kyc.pan }}
-
PAN Status
{{ current.kyc.panStatus }}
-
Aadhaar
{{ current.kyc.aadhaar }}
-
Aadhaar Status
{{ current.kyc.aadhaarStatus }}
-
KYC Validity
{{ current.kyc.validity }}
-
Expiry Date
{{ current.expiry }}
+
+
PAN
+
{{ current.kyc.pan }}
+
+
+
PAN Status
+
+ {{ current.kyc.panStatus }} +
+
+
+
Aadhaar
+
{{ current.kyc.aadhaar }}
+
+
+
Aadhaar Status
+
+ {{ current.kyc.aadhaarStatus }} +
+
+
+
KYC Validity
+
{{ current.kyc.validity }}
+
+
+
Expiry Date
+
{{ current.expiry }}
+
Contact Details
-
Mobile
{{ current.contact.mobile }}
-
Email
{{ current.contact.email }}
-
Address
{{ current.contact.address }}
-
Branch
{{ current.contact.branch }}
+
+
Mobile
+
{{ current.contact.mobile }}
+
+
+
Email
+
{{ current.contact.email }}
+
+
+
Address
+
{{ current.contact.address }}
+
+
+
Branch
+
{{ current.contact.branch }}
+
@@ -688,22 +888,44 @@
All Notices Dispatched
- + + + + + + + + + @for (n of current.notices; track n.ref; let i = $index) { - + - - - + + + } @if (noticesPending(current) > 0) { - + + + }
#NoticeChannelDateReferenceDeliveryResponse
#NoticeChannelDateReferenceDeliveryResponse
{{ i + 1 }}{{ n.no }} + {{ + n.no + }} + {{ n.channel }} {{ n.date }}{{ n.ref }}{{ n.delivery }}{{ n.response }} + {{ n.ref }} + + {{ n.delivery }} + + {{ n.response }} +
⏳ {{ noticesPending(current) }} notice(s) still pending dispatch
+ ⏳ {{ noticesPending(current) }} notice(s) still pending dispatch +
@@ -718,28 +940,55 @@
Evidence is captured during branch visits or video KYC sessions.
} @else { -
{{ current.evidence.type === 'visit' ? 'Branch Visit Evidence' : 'Video KYC Session Log' }}
+
+ {{ current.evidence.type === 'visit' ? 'Home Visit Evidence' : 'Video KYC Session Log' }} +
-
Date
{{ current.evidence.date }}
-
Officer / Agent
{{ current.evidence.officer }}
+
+
Date
+
{{ current.evidence.date }}
+
+
+
Officer / Agent
+
{{ current.evidence.officer }}
+
@if (current.evidence.type === 'visit') { -
GPS Coordinates
{{ current.evidence.gps }}
-
Location
{{ current.evidence.location }}
+
+
GPS Coordinates
+
{{ current.evidence.gps }}
+
+
+
Location
+
{{ current.evidence.location }}
+
} -
Reference / Docket
{{ current.evidence.docket || current.evidence.ref }}
-
Outcome
No Compliance
+
+
Reference / Docket
+
{{ current.evidence.docket || current.evidence.ref }}
+
+
+
Outcome
+
No Compliance
+
@if (current.evidence.type === 'visit') {
Map Placeholder
-
{{ current.evidence.gps }}
{{ current.evidence.location }}
+
+ {{ current.evidence.gps }} +
{{ current.evidence.location }}
+
Photo Evidence
Visit photo uploaded · Tap to view
}
Officer Notes
{{ current.evidence.outcome }}
- - + +
} } @@ -754,7 +1003,7 @@
{{ action }}
Initiate via available channels
- + } @@ -764,6 +1013,158 @@ } + + @if (homeVisitOpen && current) { +
+
+
+
{{ current.av }}
+
+
Schedule Home Visit
+
{{ current.name }} · {{ current.account }}
+
+
✕
+
+ +
+
+
Visit Address
+
{{ current.name }}
+
{{ current.contact.address }}
+
Branch: {{ current.contact.branch }}
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ + +
+
+ } + + + @if (vkycOpen && current) { +
+
+
+
{{ current.av }}
+
+
Send Video KYC Link
+
{{ current.name }} · {{ current.account }}
+
+
✕
+
+ +
+
+
Customer Contact
+
{{ current.name }}
+
+ {{ current.contact.mobile }} +
+
+ {{ current.contact.email }} +
+
+ +
+ +
+ + + +
+
+ +
+ + +
+ +
+ + +
Default: 48 hours (RBI Video-based KYC norms)
+
+ +
+ + +
+ +
+ + +
+
+ + +
+
+ } +
{{ toastMsg }}
diff --git a/src/app/kyc-management/kyc-management.component.scss b/src/app/kyc-management/kyc-management.component.scss index fa1a5e711..54228d63a 100644 --- a/src/app/kyc-management/kyc-management.component.scss +++ b/src/app/kyc-management/kyc-management.component.scss @@ -34,7 +34,11 @@ --km-g900: #0f172a; 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); color: var(--km-g800); font-size: 14px; @@ -130,7 +134,7 @@ background: #fff; border: 1px solid var(--km-g200); 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 { @@ -172,11 +176,30 @@ flex-shrink: 0; } -.km-ci-t { background: var(--km-teal-l); color: var(--km-teal); } -.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); } +.km-ci-t { + background: var(--km-teal-l); + color: var(--km-teal); +} + +.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 */ .km-b { @@ -189,17 +212,53 @@ font-weight: 500; } -.km-b-gr { background: var(--km-green-l); color: var(--km-green); } -.km-b-am { 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-b-gr { + background: var(--km-green-l); + color: var(--km-green); +} -.km-ml-auto { margin-left: auto; } -.km-ml-12 { margin-left: 12px; } +.km-b-am { + 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 */ .km-btn { @@ -216,14 +275,57 @@ transition: all 0.15s; } -.km-btn-t { background: var(--km-teal); border-color: var(--km-teal); color: #fff; &: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-btn-t { + background: var(--km-teal); + border-color: var(--km-teal); + color: #fff; -.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 */ .km-stat-row { @@ -238,12 +340,12 @@ border: 1px solid var(--km-g200); border-radius: 12px; 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; transition: all 0.15s; &: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; } -.km-sp-ac { background: var(--km-green-l); color: var(--km-green); } -.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; } +.km-sp-ac { + background: var(--km-green-l); + color: var(--km-green); +} + +.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 */ .km-pbar { @@ -424,10 +545,25 @@ font-weight: 600; } -.km-d-ok { background: var(--km-green-l); color: var(--km-green); } -.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); } +.km-d-ok { + background: var(--km-green-l); + color: var(--km-green); +} + +.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 */ .km-pagination { @@ -489,12 +625,41 @@ flex-shrink: 0; } -.km-nd1 { background: var(--km-blue-l); border-color: #93c5fd; color: var(--km-blue); } -.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-nd1 { + background: var(--km-blue-l); + border-color: #93c5fd; + color: var(--km-blue); +} + +.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 { width: 1px; @@ -509,9 +674,13 @@ color: var(--km-g800); } -.km-warn { color: var(--km-amber); } -.km-warn-o { color: var(--km-orange); } -.km-warn-d { color: var(--km-g600); } +.km-warn { + color: var(--km-amber); +} + +.km-warn-o { + color: var(--km-orange); +} .km-ns { font-size: 11px; @@ -520,9 +689,20 @@ margin-bottom: 5px; } -.km-ok { color: var(--km-green); font-weight: 600; } -.km-pending { color: var(--km-amber); font-weight: 600; } -.km-fail { color: var(--km-red); font-weight: 600; } +.km-ok { + color: var(--km-green); + font-weight: 600; +} + +.km-pending { + color: var(--km-amber); + font-weight: 600; +} + +.km-fail { + color: var(--km-red); + font-weight: 600; +} .km-ntags { display: flex; @@ -541,12 +721,41 @@ border: 1px solid; } -.km-cp-sms { background: #eff6ff; color: #1d4ed8; border-color: #93c5fd; } -.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-cp-sms { + background: #eff6ff; + color: #1d4ed8; + border-color: #93c5fd; +} + +.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 { margin-top: 8px; @@ -574,9 +783,20 @@ background: var(--km-teal-l); } -.km-channel-i { font-size: 16px; margin-bottom: 3px; } -.km-channel-n { font-size: 11px; font-weight: 600; } -.km-channel-d { font-size: 10px; color: var(--km-g500); } +.km-channel-i { + font-size: 16px; + margin-bottom: 3px; +} + +.km-channel-n { + font-size: 11px; + font-weight: 600; +} + +.km-channel-d { + font-size: 10px; + color: var(--km-g500); +} /* Meta items */ .km-mi { @@ -618,15 +838,79 @@ transition: all 0.15s; &: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-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-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-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 { font-size: 10px; @@ -719,7 +1003,7 @@ .km-overlay { position: fixed; inset: 0; - background: rgba(0, 0, 0, 0.45); + background: rgb(0 0 0 / 45%); z-index: 1200; display: flex; align-items: center; @@ -735,7 +1019,7 @@ max-height: 90vh; display: flex; 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 { @@ -787,7 +1071,9 @@ margin-left: auto; flex-shrink: 0; - &:hover { background: var(--km-g100); } + &:hover { + background: var(--km-g100); + } } .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 { width: 28px; @@ -881,9 +1169,23 @@ position: relative; } -.km-ps-done .km-ps-dot { background: var(--km-teal); 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-done .km-ps-dot { + background: var(--km-teal); + 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 { font-size: 10px; @@ -893,9 +1195,19 @@ max-width: 70px; } -.km-ps-done .km-ps-label { color: var(--km-teal); } -.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; } +.km-ps-done .km-ps-label { + color: var(--km-teal); +} + +.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 */ .km-next-banner { @@ -908,15 +1220,45 @@ gap: 12px; } -.km-nb-alert { background: var(--km-amber-l); border-color: #fcd34d; } -.km-nb-urgent { background: var(--km-orange-l); 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-alert { + background: var(--km-amber-l); + border-color: #fcd34d; +} -.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; } +.km-nb-urgent { + background: var(--km-orange-l); + 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 */ .km-mstat-row { @@ -969,7 +1311,9 @@ padding: 12px 0; border-bottom: 1px solid var(--km-g100); - &:last-child { border-bottom: none; } + &:last-child { + border-bottom: none; + } } .km-mti-dot { @@ -985,14 +1329,36 @@ flex-shrink: 0; } -.km-mti-done { background: var(--km-green-l); border-color: var(--km-green); color: var(--km-green); } -.km-mti-pend { background: var(--km-amber-l); border-color: var(--km-amber); color: var(--km-amber); } +.km-mti-done { + background: var(--km-green-l); + border-color: var(--km-green); + color: var(--km-green); +} -.km-mti-text { font-size: 13px; font-weight: 500; } -.km-done { color: var(--km-g700); } -.km-pend { color: var(--km-amber); } +.km-mti-pend { + background: var(--km-amber-l); + 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 { text-align: center; @@ -1009,8 +1375,15 @@ color: var(--km-g400); } -.km-empty-i { font-size: 28px; margin-bottom: 8px; } -.km-empty-s { font-size: 12px; margin-top: 4px; } +.km-empty-i { + font-size: 28px; + margin-bottom: 8px; +} + +.km-empty-s { + font-size: 12px; + margin-top: 4px; +} /* Evidence */ .km-map { @@ -1069,8 +1442,15 @@ gap: 12px; } -.km-action-t { font-size: 13px; font-weight: 600; } -.km-action-d { font-size: 12px; color: var(--km-g500); } +.km-action-t { + font-size: 13px; + font-weight: 600; +} + +.km-action-d { + font-size: 12px; + color: var(--km-g500); +} /* Toast */ .km-toast { @@ -1094,12 +1474,178 @@ } } -@media (max-width: 900px) { +@media (width <= 900px) { .km-stat-row, - .km-sc-row { grid-template-columns: repeat(2, 1fr); } + .km-sc-row { + grid-template-columns: repeat(2, 1fr); + } + .km-r2, - .km-notif-grid { grid-template-columns: 1fr; } - .km-mstat-row { grid-template-columns: repeat(2, 1fr); } - .km-prog-steps { flex-wrap: wrap; gap: 10px; } - .km-ps::after { display: none; } + .km-notif-grid { + grid-template-columns: 1fr; + } + + .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; } diff --git a/src/app/kyc-management/kyc-management.component.ts b/src/app/kyc-management/kyc-management.component.ts index ab3b25b12..eebb3120d 100644 --- a/src/app/kyc-management/kyc-management.component.ts +++ b/src/app/kyc-management/kyc-management.component.ts @@ -89,6 +89,38 @@ export class KycManagementComponent { toastVisible = false; 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 = { rajesh: { id: 'rajesh', @@ -119,14 +151,35 @@ export class KycManagementComponent { branch: 'Bhopal Main' }, 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, noticePending: 'Notice 3', nextStage: 'Debit Freeze', - pendingActions: ['Send Notice 3 (due 20 Apr 2027)', 'Schedule Branch Visit', 'Send Video KYC link'], - actionTaken: ['Notice 1 delivered', 'Notice 2 dispatched via Reg. Post'], + pendingActions: [ + '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 }, priya: { @@ -158,15 +211,45 @@ export class KycManagementComponent { branch: 'Bhopal Main' }, 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: 'N3', channel: 'Branch Visit', date: '30 Mar 2027', ref: 'VIS-BPL-20270330-012', delivery: 'Visit Done', response: 'Refused' } + { + 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: 'N3', + channel: 'Home Visit', + date: '30 Mar 2027', + ref: 'VIS-BPL-20270330-012', + delivery: 'Visit Done', + response: 'Refused' + } ], noticesSent: 3, noticePending: 'None — all 3 sent', nextStage: 'Inoperative by 30 Jun 2027', - pendingActions: ['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'], + pendingActions: [ + '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: { type: 'visit', date: '30 Mar 2027', @@ -206,15 +289,46 @@ export class KycManagementComponent { branch: 'Bhopal South' }, 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: 'N3', channel: 'Video KYC', date: '5 Mar 2027', ref: 'VKYC-2027-0305-007', delivery: 'No Response', response: 'No Action' } + { + 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: 'N3', + channel: 'Video KYC', + date: '5 Mar 2027', + ref: 'VKYC-2027-0305-007', + delivery: 'No Response', + response: 'No Action' + } ], noticesSent: 3, noticePending: 'None', nextStage: 'Dormant by 30 Sep 2027', - pendingActions: ['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'], + pendingActions: [ + '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: { type: 'vkyc', date: '5 Mar 2027', @@ -252,22 +366,54 @@ export class KycManagementComponent { branch: 'Bhopal East' }, 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: 'N3', channel: 'Branch Visit', date: '22 Dec 2026', ref: 'VIS-BPL-20261222-019', delivery: 'Not Found', response: 'Not Found at Address' } + { + 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: 'N3', + channel: 'Home Visit', + date: '22 Dec 2026', + ref: 'VIS-BPL-20261222-019', + delivery: 'Not Found', + response: 'Not Found at Address' + } ], noticesSent: 3, noticePending: 'None', nextStage: 'Dormant (Final) — RBI Reporting', - pendingActions: ['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'], + pendingActions: [ + '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: { type: 'visit', date: '22 Dec 2026', officer: 'Rekha Sharma', gps: '23.2491° N, 77.4384° E', 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' } }, @@ -300,12 +446,23 @@ export class KycManagementComponent { branch: 'Bhopal Main' }, 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, noticePending: 'Notice 2 & 3', 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'], evidence: null }, @@ -338,14 +495,35 @@ export class KycManagementComponent { branch: 'Bhopal North' }, 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, noticePending: 'URGENT: Notice 3', nextStage: 'Debit Freeze in 3 days', - pendingActions: ['URGENT: Send Notice 3 NOW', 'Schedule Branch Visit immediately', 'Consider Video KYC as last resort'], - actionTaken: ['Notice 1 delivered', 'Notice 2 delivered via Reg. Post + IVR'], + pendingActions: [ + '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 } }; @@ -384,6 +562,108 @@ export class KycManagementComponent { 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 */ getStepperState(c: KmClient): { label: string; state: 'done' | 'pending' | 'current' | 'danger' }[] { return [ @@ -393,19 +673,20 @@ export class KycManagementComponent { { label: 'Notice 3', state: - c.noticesSent >= 3 - ? 'done' - : c.noticesSent >= 2 - ? c.state === 'urgent' - ? 'danger' - : 'current' - : 'pending' + c.noticesSent >= 3 ? 'done' : c.noticesSent >= 2 ? (c.state === 'urgent' ? 'danger' : 'current') : 'pending' }, { 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' } ]; } @@ -418,26 +699,30 @@ export class KycManagementComponent { stateLabel(state: string): string { return ( - { - alert: 'Alert Window', - freeze: 'Debit Freeze', - inop: 'Inoperative', - dormant: 'Dormant', - urgent: 'Urgent Alert' - } as Record - )[state] || state; + ( + { + alert: 'Alert Window', + freeze: 'Debit Freeze', + inop: 'Inoperative', + dormant: 'Dormant', + urgent: 'Urgent Alert' + } as Record + )[state] || state + ); } stateClass(state: string): string { return ( - { - alert: 'sp-al', - freeze: 'sp-fr', - inop: 'sp-in', - dormant: 'sp-do', - urgent: 'sp-fr' - } as Record - )[state] || 'sp-al'; + ( + { + alert: 'sp-al', + freeze: 'sp-fr', + inop: 'sp-in', + dormant: 'sp-do', + urgent: 'sp-fr' + } as Record + )[state] || 'sp-al' + ); } riskClass(risk: string): string { diff --git a/src/app/shared/styles/list-layout.scss b/src/app/shared/styles/list-layout.scss index b16a422cf..086ec3c0c 100644 --- a/src/app/shared/styles/list-layout.scss +++ b/src/app/shared/styles/list-layout.scss @@ -390,6 +390,7 @@ $mono: 'Roboto Mono', 'DM Mono', monospace; color: $text-muted; border-bottom: 1px solid $border; padding: 0 12px; + white-space: nowrap; } // Rows @@ -421,6 +422,7 @@ $mono: 'Roboto Mono', 'DM Mono', monospace; font-weight: 400; color: $text; padding: 0 12px; + white-space: nowrap; font-family: Inter, Roboto,