feat: add KYC document renewal alerts, Aadhaar/PAN exceptions, and AML recurring alerts UI; remove old journal AML marking
This commit is contained in:
@@ -56,6 +56,42 @@
|
||||
}
|
||||
}
|
||||
|
||||
<!-- What PAN / Aadhaar are matched against. Shown before the officer clicks Verify, because
|
||||
the provider matches on exactly these two values and returns nothing identifiable when
|
||||
they disagree — a blind check that fails is much harder to act on than a visible one. -->
|
||||
@if (!isNonIndividual) {
|
||||
<div
|
||||
class="identity-basis full-width"
|
||||
[class.identity-basis--incomplete]="!verificationName || !verificationDateOfBirth"
|
||||
>
|
||||
<mat-icon class="identity-basis__icon">badge</mat-icon>
|
||||
<div class="identity-basis__body">
|
||||
<span class="identity-basis__legend">Documents are verified against the General tab</span>
|
||||
<div class="identity-basis__pairs">
|
||||
<span class="identity-basis__pair">
|
||||
<span class="identity-basis__k">Name</span>
|
||||
<span class="identity-basis__v">{{ verificationName || 'Not entered' }}</span>
|
||||
</span>
|
||||
<span class="identity-basis__pair">
|
||||
<span class="identity-basis__k">Date of Birth</span>
|
||||
<span class="identity-basis__v">
|
||||
{{ verificationDateOfBirth ? (verificationDateOfBirth | date: 'dd MMM yyyy') : 'Not entered' }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@if (!verificationName || !verificationDateOfBirth) {
|
||||
<span class="identity-basis__warn">
|
||||
Both are required for PAN and Aadhaar verification — complete them on the General tab first.
|
||||
</span>
|
||||
} @else {
|
||||
<span class="identity-basis__hint">
|
||||
Must match the card exactly. Salutation is excluded; a mismatch in either fails verification.
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Individual PAN with inline verify -->
|
||||
@if (!isMinor && kycForm.get('hasPan')?.value) {
|
||||
<div class="form-field verify-field">
|
||||
@@ -105,8 +141,109 @@
|
||||
@if (panVerifyStatus === 'VERIFIED') {
|
||||
<span class="verify-chip verified"><fa-icon icon="check-circle"></fa-icon> Verified</span>
|
||||
}
|
||||
@if (panVerifyStatus !== 'VERIFIED' && panDeclarationTaken) {
|
||||
<span class="verify-chip declared"><fa-icon icon="file-signature"></fa-icon> Declaration</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (panVerifyStatus === 'VERIFIED') {
|
||||
<div
|
||||
class="pal-card full-width"
|
||||
[class.pal-card--ok]="panAadhaarSeedingStatus === 'Y'"
|
||||
[class.pal-card--fail]="panAadhaarSeedingStatus === 'N'"
|
||||
[class.pal-card--info]="panAadhaarSeedingStatus === 'E'"
|
||||
[class.pal-card--pending]="
|
||||
panAadhaarSeedingStatus !== 'Y' && panAadhaarSeedingStatus !== 'N' && panAadhaarSeedingStatus !== 'E'
|
||||
"
|
||||
style="margin-bottom: 16px"
|
||||
>
|
||||
<div class="pal-card__left">
|
||||
<div class="pal-card__icon">
|
||||
<mat-icon>credit_card</mat-icon>
|
||||
</div>
|
||||
<div class="pal-card__txt">
|
||||
<div class="pal-card__small">Verification Status</div>
|
||||
<div class="pal-card__title">PAN ↔ Aadhaar</div>
|
||||
<div class="pal-card__status">
|
||||
@if (panAadhaarSeedingStatus === 'Y') {
|
||||
<mat-icon>check_circle</mat-icon> Linked (Seeded)
|
||||
} @else if (panAadhaarSeedingStatus === 'N') {
|
||||
<mat-icon>cancel</mat-icon> Not Linked
|
||||
} @else if (panAadhaarSeedingStatus === 'E') {
|
||||
<mat-icon>info</mat-icon> Exempt
|
||||
} @else {
|
||||
<mat-icon>radio_button_unchecked</mat-icon> Pending
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pal-card__badge">
|
||||
@if (panAadhaarSeedingStatus === 'Y') {
|
||||
<mat-icon>check</mat-icon>
|
||||
} @else if (panAadhaarSeedingStatus === 'N') {
|
||||
<mat-icon>close</mat-icon>
|
||||
} @else if (panAadhaarSeedingStatus === 'E') {
|
||||
<mat-icon>info</mat-icon>
|
||||
} @else {
|
||||
<mat-icon>schedule</mat-icon>
|
||||
}
|
||||
</div>
|
||||
<div class="pal-card__hint">As per Income Tax Portal — Section 139AA</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!--
|
||||
PAN could not be verified but a declaration is on file. Shown in full because it is the
|
||||
only thing standing between this client and a PAN KYC exception — an officer reviewing
|
||||
the record has to be able to see what was declared, not just that something was.
|
||||
-->
|
||||
@if (panVerifyStatus !== 'VERIFIED' && panDeclarationTaken) {
|
||||
<div class="pan-decl-card">
|
||||
<div class="pan-decl-card__head">
|
||||
<mat-icon class="pan-decl-card__icon">assignment_turned_in</mat-icon>
|
||||
<span class="pan-decl-card__title">PAN Declaration Taken</span>
|
||||
@if (panDeclarationTakenOn) {
|
||||
<span class="pan-decl-card__date">{{ panDeclarationTakenOn }}</span>
|
||||
}
|
||||
</div>
|
||||
<div class="pan-decl-card__rows">
|
||||
<div class="pan-decl-card__row">
|
||||
<span class="k">Does not match</span>
|
||||
<span class="v strong">{{ panDeclarationMismatchLabel }}</span>
|
||||
</div>
|
||||
@if (panDeclaration?.nameOnPan) {
|
||||
<div class="pan-decl-card__row">
|
||||
<span class="k">Name on PAN</span>
|
||||
<span class="v">{{ panDeclaration?.nameOnPan }}</span>
|
||||
</div>
|
||||
}
|
||||
@if (panDeclaration?.dobOnPan) {
|
||||
<div class="pan-decl-card__row">
|
||||
<span class="k">DOB on PAN</span>
|
||||
<span class="v">{{ panDeclaration?.dobOnPan | date: 'dd MMMM yyyy' }}</span>
|
||||
</div>
|
||||
}
|
||||
@if (panDeclaration?.fileName) {
|
||||
<div class="pan-decl-card__row">
|
||||
<span class="k">Declaration file</span>
|
||||
<span class="v">{{ panDeclaration?.fileName }}</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="pan-decl-card__note">
|
||||
PAN remains unverified. No KYC exception is raised for PAN while this declaration is on file.
|
||||
</div>
|
||||
@if (!readOnly) {
|
||||
<div class="pan-decl-card__actions">
|
||||
<button mat-stroked-button type="button" (click)="openPanDeclaration()">Edit Declaration</button>
|
||||
<button mat-stroked-button type="button" class="danger" (click)="removePanDeclaration()">
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
<!-- Aadhaar with independent verify (Individual only) -->
|
||||
@@ -175,9 +312,68 @@
|
||||
@if (aadhaarVerifyStatus === 'VERIFIED') {
|
||||
<span class="verify-chip verified"><fa-icon icon="check-circle"></fa-icon> Verified</span>
|
||||
}
|
||||
@if (aadhaarVerifyStatus !== 'VERIFIED' && aadhaarReceiptTaken) {
|
||||
<span class="verify-chip declared"><fa-icon icon="receipt"></fa-icon> Receipt</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
A customer who has applied for Aadhaar but not received it has no number to verify, so no
|
||||
amount of retrying the OTP will clear the check. Offer the enrolment slip instead rather
|
||||
than leaving the officer unable to finish the client.
|
||||
-->
|
||||
@if (aadhaarVerifyStatus !== 'VERIFIED' && !aadhaarReceiptTaken && !aadhaarOtpSent && !readOnly) {
|
||||
<div class="aad-rcpt-offer full-width">
|
||||
<mat-icon>receipt_long</mat-icon>
|
||||
<span>Customer has applied for Aadhaar but not received it yet?</span>
|
||||
<button mat-stroked-button type="button" (click)="openAadhaarReceipt()">Record Enrolment Receipt</button>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!--
|
||||
Aadhaar is unverified but an enrolment receipt is on file. Shown in full because it is the
|
||||
only thing standing between this client and an Aadhaar KYC exception.
|
||||
-->
|
||||
@if (aadhaarVerifyStatus !== 'VERIFIED' && aadhaarReceiptTaken) {
|
||||
<div class="aad-rcpt-card full-width">
|
||||
<div class="aad-rcpt-card__head">
|
||||
<mat-icon class="aad-rcpt-card__icon">receipt_long</mat-icon>
|
||||
<span class="aad-rcpt-card__title">Aadhaar Enrolment Receipt Recorded</span>
|
||||
@if (aadhaarReceiptTakenOn) {
|
||||
<span class="aad-rcpt-card__date">{{ aadhaarReceiptTakenOn }}</span>
|
||||
}
|
||||
</div>
|
||||
<div class="aad-rcpt-card__rows">
|
||||
<div class="aad-rcpt-card__row">
|
||||
<span class="k">Enrolment ID</span>
|
||||
<span class="v mono">{{ aadhaarReceipt?.enrolmentId }}</span>
|
||||
</div>
|
||||
@if (aadhaarReceipt?.enrolmentDate) {
|
||||
<div class="aad-rcpt-card__row">
|
||||
<span class="k">Enrolment date</span>
|
||||
<span class="v">{{ aadhaarReceipt?.enrolmentDate | date: 'dd MMMM yyyy' }}</span>
|
||||
</div>
|
||||
}
|
||||
@if (aadhaarReceipt?.remarks) {
|
||||
<div class="aad-rcpt-card__row">
|
||||
<span class="k">Remarks</span>
|
||||
<span class="v">{{ aadhaarReceipt?.remarks }}</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="aad-rcpt-card__note">
|
||||
Aadhaar remains unverified. No KYC exception is raised for Aadhaar while this receipt is on file.
|
||||
</div>
|
||||
@if (!readOnly) {
|
||||
<div class="aad-rcpt-card__actions">
|
||||
<button mat-stroked-button type="button" (click)="openAadhaarReceipt()">Edit Receipt</button>
|
||||
<button mat-stroked-button type="button" class="danger" (click)="removeAadhaarReceipt()">Remove</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Aadhaar OTP Section -->
|
||||
@if (aadhaarOtpSent && aadhaarVerifyStatus !== 'VERIFIED') {
|
||||
<div class="otp-card full-width">
|
||||
@@ -725,51 +921,53 @@
|
||||
}
|
||||
|
||||
<!-- KYC Exception Flag -->
|
||||
<div
|
||||
class="exception-flag-box"
|
||||
style="
|
||||
margin-top: 20px;
|
||||
padding: 16px;
|
||||
border: 1px solid #f59e0b;
|
||||
background: #fffbeb;
|
||||
border-radius: 8px;
|
||||
clear: both;
|
||||
"
|
||||
>
|
||||
<h5
|
||||
@if (!kycAllDone) {
|
||||
<div
|
||||
class="exception-flag-box"
|
||||
style="
|
||||
margin: 0 0 12px 0;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #b45309;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-top: 20px;
|
||||
padding: 16px;
|
||||
border: 1px solid #f59e0b;
|
||||
background: #fffbeb;
|
||||
border-radius: 8px;
|
||||
clear: both;
|
||||
"
|
||||
>
|
||||
<fa-icon icon="exclamation-triangle"></fa-icon>
|
||||
KYC Exception Flag
|
||||
</h5>
|
||||
<div style="display: flex; align-items: center; gap: 20px; flex-wrap: wrap">
|
||||
<mat-checkbox formControlName="isException">Mark as KYC Exception</mat-checkbox>
|
||||
<h5
|
||||
style="
|
||||
margin: 0 0 12px 0;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #b45309;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
"
|
||||
>
|
||||
<fa-icon icon="exclamation-triangle"></fa-icon>
|
||||
KYC Exception Flag
|
||||
</h5>
|
||||
<div style="display: flex; align-items: center; gap: 20px; flex-wrap: wrap">
|
||||
<mat-checkbox formControlName="isException">Mark as KYC Exception</mat-checkbox>
|
||||
|
||||
@if (kycForm.get('isException')?.value) {
|
||||
<mat-form-field appearance="outline" style="flex: 1; min-width: 280px; margin-bottom: -1.25em">
|
||||
<mat-label>Exception Reasons</mat-label>
|
||||
<mat-select formControlName="exceptionReason" multiple>
|
||||
@if (!isNonIndividual) {
|
||||
<mat-option value="PAN Card Verification Failed">PAN Card Verification Failed</mat-option>
|
||||
<mat-option value="Aadhaar Verification Failed">Aadhaar Verification Failed</mat-option>
|
||||
} @else {
|
||||
<mat-option value="Enterprise PAN Verification Failed">Enterprise PAN Verification Failed</mat-option>
|
||||
<mat-option value="GSTIN Verification Failed">GSTIN Verification Failed</mat-option>
|
||||
<mat-option value="CIN Verification Failed">CIN Verification Failed</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
}
|
||||
@if (kycForm.get('isException')?.value) {
|
||||
<mat-form-field appearance="outline" style="flex: 1; min-width: 280px; margin-bottom: -1.25em">
|
||||
<mat-label>Exception Reasons</mat-label>
|
||||
<mat-select formControlName="exceptionReason" multiple>
|
||||
@if (!isNonIndividual) {
|
||||
<mat-option value="PAN Card Verification Failed">PAN Card Verification Failed</mat-option>
|
||||
<mat-option value="Aadhaar Verification Failed">Aadhaar Verification Failed</mat-option>
|
||||
} @else {
|
||||
<mat-option value="Enterprise PAN Verification Failed">Enterprise PAN Verification Failed</mat-option>
|
||||
<mat-option value="GSTIN Verification Failed">GSTIN Verification Failed</mat-option>
|
||||
<mat-option value="CIN Verification Failed">CIN Verification Failed</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- ═══ SECTION: KYC Document Management ═══ -->
|
||||
@@ -847,19 +1045,23 @@
|
||||
<!-- Existing saved ID-proof doc — locked by default. Replace flow
|
||||
unlocks automatically when the section-level Edit button toggles
|
||||
`readOnly` off. -->
|
||||
@if (existingKycDocuments.length > 0 && !kycDocFiles.front && readOnly) {
|
||||
@if (existingKycDocSides.front && !kycDocFiles.front && readOnly) {
|
||||
<div class="kyc-doc-dropzone kyc-doc-dropzone--has kyc-doc-dropzone--locked">
|
||||
<div class="kyc-doc-dropzone__existing">
|
||||
@if (kycDocMergedPreview) {
|
||||
<img [src]="kycDocMergedPreview" alt="Saved KYC document" class="kyc-doc-dropzone__thumb" />
|
||||
@if (existingKycDocSides.front?.previewUrl) {
|
||||
<img
|
||||
[src]="existingKycDocSides.front?.previewUrl"
|
||||
alt="Saved front side"
|
||||
class="kyc-doc-dropzone__thumb"
|
||||
/>
|
||||
} @else {
|
||||
<mat-icon class="kyc-doc-dropzone__doc-ic">description</mat-icon>
|
||||
}
|
||||
<div class="kyc-doc-dropzone__existing-meta">
|
||||
<span class="kyc-doc-dropzone__existing-name">
|
||||
{{ existingKycDocuments[0].fileName || existingKycDocuments[0].name }}
|
||||
{{ existingKycDocSides.front?.fileName }}
|
||||
</span>
|
||||
<button type="button" class="kyc-doc-dropzone__view" (click)="viewExistingKycDoc()">
|
||||
<button type="button" class="kyc-doc-dropzone__view" (click)="viewExistingKycDoc('front')">
|
||||
<mat-icon>visibility</mat-icon>
|
||||
Click here to view
|
||||
</button>
|
||||
@@ -869,8 +1071,8 @@
|
||||
} @else {
|
||||
<label
|
||||
class="kyc-doc-dropzone"
|
||||
[class.kyc-doc-dropzone--has]="kycDocFiles.front || existingKycDocuments.length > 0"
|
||||
[class.kyc-doc-dropzone--req]="!kycDocFiles.front && existingKycDocuments.length === 0"
|
||||
[class.kyc-doc-dropzone--has]="kycDocFiles.front || existingKycDocSides.front"
|
||||
[class.kyc-doc-dropzone--req]="!kycDocFiles.front && !existingKycDocSides.front"
|
||||
>
|
||||
<input type="file" accept=".jpg,.jpeg,.png,.pdf" hidden (change)="onKycDocFile($event, 'front')" />
|
||||
@if (kycDocFiles.front) {
|
||||
@@ -886,24 +1088,28 @@
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
} @else if (existingKycDocuments.length > 0) {
|
||||
} @else if (existingKycDocSides.front) {
|
||||
<!-- Edit mode WITH existing doc — let the user view it OR
|
||||
click anywhere to replace. -->
|
||||
<div class="kyc-doc-dropzone__existing">
|
||||
@if (kycDocMergedPreview) {
|
||||
<img [src]="kycDocMergedPreview" alt="Saved KYC document" class="kyc-doc-dropzone__thumb" />
|
||||
@if (existingKycDocSides.front?.previewUrl) {
|
||||
<img
|
||||
[src]="existingKycDocSides.front?.previewUrl"
|
||||
alt="Saved front side"
|
||||
class="kyc-doc-dropzone__thumb"
|
||||
/>
|
||||
} @else {
|
||||
<mat-icon class="kyc-doc-dropzone__doc-ic">description</mat-icon>
|
||||
}
|
||||
<div class="kyc-doc-dropzone__existing-meta">
|
||||
<span class="kyc-doc-dropzone__existing-name">
|
||||
{{ existingKycDocuments[0].fileName || existingKycDocuments[0].name }}
|
||||
{{ existingKycDocSides.front?.fileName }}
|
||||
</span>
|
||||
<div class="kyc-doc-dropzone__existing-actions">
|
||||
<button
|
||||
type="button"
|
||||
class="kyc-doc-dropzone__view"
|
||||
(click)="$event.preventDefault(); $event.stopPropagation(); viewExistingKycDoc()"
|
||||
(click)="$event.preventDefault(); $event.stopPropagation(); viewExistingKycDoc('front')"
|
||||
>
|
||||
<mat-icon>visibility</mat-icon>
|
||||
View
|
||||
@@ -930,25 +1136,45 @@
|
||||
<div class="kyc-doc-upload-slot">
|
||||
<div class="kyc-doc-upload-head">
|
||||
<span class="kyc-doc-upload-title"> <mat-icon>image</mat-icon> Back side </span>
|
||||
<!-- Hide Required pill when saved combined doc covers both sides -->
|
||||
@if (!(existingKycDocuments.length > 0 && !kycDocFiles.back)) {
|
||||
<!-- Hide Required pill when a back scan (or a stacked scan holding it) is on file -->
|
||||
@if (!((existingKycDocSides.back || kycDocIsLegacyCombined) && !kycDocFiles.back)) {
|
||||
<span class="kyc-doc-upload-req">Required</span>
|
||||
}
|
||||
</div>
|
||||
<!-- Locked view: saved combined doc covers the back side too. -->
|
||||
@if (existingKycDocuments.length > 0 && !kycDocFiles.back && readOnly) {
|
||||
<!--
|
||||
A stacked scan that could not be cut in two has no separate back image. Say so rather
|
||||
than repeating the front slot's picture here.
|
||||
-->
|
||||
@if (kycDocIsLegacyCombined && !kycDocFiles.back) {
|
||||
<div class="kyc-doc-dropzone kyc-doc-dropzone--has kyc-doc-dropzone--locked">
|
||||
<div class="kyc-doc-dropzone__existing">
|
||||
@if (kycDocMergedPreview) {
|
||||
<img [src]="kycDocMergedPreview" alt="Saved KYC document" class="kyc-doc-dropzone__thumb" />
|
||||
<mat-icon class="kyc-doc-dropzone__doc-ic">filter_none</mat-icon>
|
||||
<div class="kyc-doc-dropzone__existing-meta">
|
||||
<span class="kyc-doc-dropzone__existing-name">Included in the combined scan</span>
|
||||
<button type="button" class="kyc-doc-dropzone__view" (click)="viewExistingKycDoc('front')">
|
||||
<mat-icon>visibility</mat-icon>
|
||||
View both sides
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
} @else if (existingKycDocSides.back && !kycDocFiles.back && readOnly) {
|
||||
<div class="kyc-doc-dropzone kyc-doc-dropzone--has kyc-doc-dropzone--locked">
|
||||
<div class="kyc-doc-dropzone__existing">
|
||||
@if (existingKycDocSides.back?.previewUrl) {
|
||||
<img
|
||||
[src]="existingKycDocSides.back?.previewUrl"
|
||||
alt="Saved back side"
|
||||
class="kyc-doc-dropzone__thumb"
|
||||
/>
|
||||
} @else {
|
||||
<mat-icon class="kyc-doc-dropzone__doc-ic">description</mat-icon>
|
||||
}
|
||||
<div class="kyc-doc-dropzone__existing-meta">
|
||||
<span class="kyc-doc-dropzone__existing-name">
|
||||
{{ existingKycDocuments[0].fileName || existingKycDocuments[0].name }}
|
||||
{{ existingKycDocSides.back?.fileName }}
|
||||
</span>
|
||||
<button type="button" class="kyc-doc-dropzone__view" (click)="viewExistingKycDoc()">
|
||||
<button type="button" class="kyc-doc-dropzone__view" (click)="viewExistingKycDoc('back')">
|
||||
<mat-icon>visibility</mat-icon>
|
||||
Click here to view
|
||||
</button>
|
||||
@@ -958,8 +1184,8 @@
|
||||
} @else {
|
||||
<label
|
||||
class="kyc-doc-dropzone"
|
||||
[class.kyc-doc-dropzone--has]="kycDocFiles.back || existingKycDocuments.length > 0"
|
||||
[class.kyc-doc-dropzone--req]="!kycDocFiles.back && existingKycDocuments.length === 0"
|
||||
[class.kyc-doc-dropzone--has]="kycDocFiles.back || existingKycDocSides.back"
|
||||
[class.kyc-doc-dropzone--req]="!kycDocFiles.back && !existingKycDocSides.back"
|
||||
>
|
||||
<input type="file" accept=".jpg,.jpeg,.png,.pdf" hidden (change)="onKycDocFile($event, 'back')" />
|
||||
@if (kycDocFiles.back) {
|
||||
@@ -975,23 +1201,27 @@
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
} @else if (existingKycDocuments.length > 0) {
|
||||
<!-- Edit mode WITH existing combined doc — view + replace. -->
|
||||
} @else if (existingKycDocSides.back) {
|
||||
<!-- Edit mode WITH an existing back scan — view + replace. -->
|
||||
<div class="kyc-doc-dropzone__existing">
|
||||
@if (kycDocMergedPreview) {
|
||||
<img [src]="kycDocMergedPreview" alt="Saved KYC document" class="kyc-doc-dropzone__thumb" />
|
||||
@if (existingKycDocSides.back?.previewUrl) {
|
||||
<img
|
||||
[src]="existingKycDocSides.back?.previewUrl"
|
||||
alt="Saved back side"
|
||||
class="kyc-doc-dropzone__thumb"
|
||||
/>
|
||||
} @else {
|
||||
<mat-icon class="kyc-doc-dropzone__doc-ic">description</mat-icon>
|
||||
}
|
||||
<div class="kyc-doc-dropzone__existing-meta">
|
||||
<span class="kyc-doc-dropzone__existing-name">
|
||||
{{ existingKycDocuments[0].fileName || existingKycDocuments[0].name }}
|
||||
{{ existingKycDocSides.back?.fileName }}
|
||||
</span>
|
||||
<div class="kyc-doc-dropzone__existing-actions">
|
||||
<button
|
||||
type="button"
|
||||
class="kyc-doc-dropzone__view"
|
||||
(click)="$event.preventDefault(); $event.stopPropagation(); viewExistingKycDoc()"
|
||||
(click)="$event.preventDefault(); $event.stopPropagation(); viewExistingKycDoc('back')"
|
||||
>
|
||||
<mat-icon>visibility</mat-icon>
|
||||
View
|
||||
@@ -1027,7 +1257,9 @@
|
||||
<mat-icon>filter_none</mat-icon>
|
||||
Combined preview (front + back)
|
||||
</span>
|
||||
<span class="kyc-merged__hint">Saved as a single PNG. Re-crop a side to update the merged file.</span>
|
||||
<span class="kyc-merged__hint">
|
||||
Preview only — each side is saved as its own file. Re-crop a side to update it.
|
||||
</span>
|
||||
</div>
|
||||
<div class="kyc-merged__preview">
|
||||
<img [src]="kycDocMergedPreview" alt="Merged KYC document" />
|
||||
@@ -1197,27 +1429,36 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
<mat-form-field appearance="outline" class="form-field full-width">
|
||||
<mat-label>{{ isKycVerified ? 'Reason for Re-edit *' : 'Remarks' }}</mat-label>
|
||||
<textarea
|
||||
matInput
|
||||
formControlName="remarks"
|
||||
rows="2"
|
||||
[placeholder]="isKycVerified ? 'Mandatory — explain why KYC is being re-edited' : ''"
|
||||
></textarea>
|
||||
@if (isKycVerified && !kycForm.get('remarks')?.value?.trim()) {
|
||||
<mat-error>Remarks are mandatory when updating a verified KYC record</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
|
||||
@if (isKycVerified) {
|
||||
<div class="kyc-reedit-alert">
|
||||
<mat-icon class="kyc-reedit-alert__icon">info</mat-icon>
|
||||
<span
|
||||
>KYC is <strong>VERIFIED</strong>. Any update will reset the status to <strong>IN PROGRESS</strong> and
|
||||
require PAN re-verification.</span
|
||||
>
|
||||
</div>
|
||||
@if (kycForm.get('allowRiskOverride')?.value || isKycVerified) {
|
||||
<mat-form-field appearance="outline" class="form-field full-width">
|
||||
<mat-label>
|
||||
{{
|
||||
isKycVerified
|
||||
? 'Reason for Re-edit'
|
||||
: kycForm.get('allowRiskOverride')?.value
|
||||
? 'Remarks (Required for Risk Override)'
|
||||
: 'Remarks'
|
||||
}}
|
||||
</mat-label>
|
||||
<textarea
|
||||
matInput
|
||||
formControlName="remarks"
|
||||
rows="2"
|
||||
[placeholder]="
|
||||
isKycVerified
|
||||
? 'Mandatory — explain why KYC is being re-edited'
|
||||
: kycForm.get('allowRiskOverride')?.value
|
||||
? 'Mandatory — explain why you are overriding the auto-calculated risk category'
|
||||
: ''
|
||||
"
|
||||
></textarea>
|
||||
@if ((isKycVerified || kycForm.get('allowRiskOverride')?.value) && !kycForm.get('remarks')?.value?.trim()) {
|
||||
<mat-error>
|
||||
Remarks are mandatory when
|
||||
{{ isKycVerified ? 'updating a verified KYC record' : 'overriding the risk category' }}
|
||||
</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -1393,6 +1634,39 @@
|
||||
<mat-option value="REGULATORY">Regulatory Authority</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline" class="form-field" (click)="freezeDatePicker.open()">
|
||||
<mat-label>Freeze Date</mat-label>
|
||||
<span matPrefix class="field-prefix-icon"><mat-icon>calendar_today</mat-icon></span>
|
||||
<input matInput required [max]="today" [matDatepicker]="freezeDatePicker" formControlName="freezeDate" />
|
||||
<mat-datepicker-toggle matSuffix [for]="freezeDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #freezeDatePicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline" class="form-field">
|
||||
<mat-label>Freeze Amount</mat-label>
|
||||
<span matPrefix class="field-prefix-icon"><mat-icon>currency_rupee</mat-icon></span>
|
||||
<input matInput type="number" min="0" formControlName="freezeAmount" />
|
||||
<mat-hint>Leave blank for a full freeze</mat-hint>
|
||||
</mat-form-field>
|
||||
}
|
||||
|
||||
@if (kycForm.get('accountStatus')?.value === 'UNFREEZE') {
|
||||
<mat-form-field appearance="outline" class="form-field" (click)="unfreezeDatePicker.open()">
|
||||
<mat-label>Unfreeze Date</mat-label>
|
||||
<span matPrefix class="field-prefix-icon"><mat-icon>lock_open</mat-icon></span>
|
||||
<input matInput required [max]="today" [matDatepicker]="unfreezeDatePicker" formControlName="unfreezeDate" />
|
||||
<mat-datepicker-toggle matSuffix [for]="unfreezeDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #unfreezeDatePicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
}
|
||||
|
||||
@if (kycForm.get('accountStatus')?.value === 'NO_DEBIT') {
|
||||
<mat-form-field appearance="outline" class="form-field" (click)="noDebitDatePicker.open()">
|
||||
<mat-label>No Debit From Date</mat-label>
|
||||
<span matPrefix class="field-prefix-icon"><mat-icon>money_off</mat-icon></span>
|
||||
<input matInput required [matDatepicker]="noDebitDatePicker" formControlName="noDebitDate" />
|
||||
<mat-datepicker-toggle matSuffix [for]="noDebitDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #noDebitDatePicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1545,4 +1819,34 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Form Validation Summary -->
|
||||
@if (kycForm && kycForm.invalid) {
|
||||
<div
|
||||
class="kyc-validation-summary"
|
||||
style="
|
||||
margin-top: 24px;
|
||||
padding: 16px;
|
||||
background: #fff5f5;
|
||||
border: 1px solid #fee2e2;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
clear: both;
|
||||
"
|
||||
>
|
||||
<mat-icon style="color: #ef4444; flex-shrink: 0">error_outline</mat-icon>
|
||||
<div style="display: flex; flex-direction: column; gap: 4px">
|
||||
<span style="font-size: 14px; font-weight: 600; color: #991b1b"
|
||||
>Please complete/correct the following fields to proceed:</span
|
||||
>
|
||||
<ul style="margin: 4px 0 0 16px; padding: 0; font-size: 13px; color: #b91c1c; line-height: 1.5">
|
||||
@for (label of invalidFieldLabels; track label) {
|
||||
<li>{{ label }}</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user