feat: Client & CKYC UI improvements — filters, KYC management, image crop+merge

- Clients list: CKYC icon, CKYC Number column, status/legal form/CKYC filters
- CKYC: SFTP upload logs, date formatting, modal z-index fix, non-dismissible dialogs
- KYC Management: new standalone module with alert dashboard, notification scheduler, audit inventory, account status
- Image cropper: ngx-image-cropper integration for front+back document merge into single PNG
- Sidebar: KYC Management menu entry after CKYC

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Aditya
2026-04-16 13:51:11 +05:30
co-authored by Claude Opus 4.6
parent 79e455c4ed
commit d72e969dc3
210 changed files with 14273 additions and 1139 deletions
@@ -305,23 +305,85 @@
<mat-datepicker #incorpDatePicker></mat-datepicker>
</mat-form-field>
<!-- GSTIN (all non-individual) -->
<mat-form-field appearance="outline" class="form-field">
<mat-label>GSTIN Number</mat-label>
<input
matInput
formControlName="gstinNumber"
placeholder="15-character GSTIN"
maxlength="15"
style="text-transform: uppercase"
(input)="
kycForm.get('gstinNumber')?.setValue($any($event.target).value.toUpperCase(), { emitEvent: false })
"
/>
@if (kycForm.get('gstinNumber')?.hasError('pattern') && kycForm.get('gstinNumber')?.touched) {
<mat-error>Invalid GSTIN format (e.g. 22AAAAA0000A1Z5)</mat-error>
}
</mat-form-field>
<!-- GSTIN (all non-individual) with Verify -->
<div class="form-field verify-field">
<mat-form-field appearance="outline" class="verify-input">
<mat-label>GSTIN Number</mat-label>
<span matPrefix class="field-prefix-icon"><mat-icon>receipt_long</mat-icon></span>
<input
matInput
formControlName="gstinNumber"
placeholder="15-character GSTIN"
maxlength="15"
style="text-transform: uppercase"
(input)="
kycForm.get('gstinNumber')?.setValue($any($event.target).value.toUpperCase(), { emitEvent: false })
"
/>
@if (kycForm.get('gstinNumber')?.hasError('pattern') && kycForm.get('gstinNumber')?.touched) {
<mat-error>Invalid GSTIN format (e.g. 22AAAAA0000A1Z5)</mat-error>
}
@if (gstinVerifyStatus === 'VERIFIED') {
<mat-icon matSuffix class="verify-icon-ok">check_circle</mat-icon>
}
@if (gstinVerifyStatus === 'FAILED') {
<mat-icon matSuffix class="verify-icon-fail">cancel</mat-icon>
}
</mat-form-field>
<div class="verify-action">
@if (gstinVerifyStatus === 'NONE' || gstinVerifyStatus === 'FAILED') {
<button
mat-raised-button
color="primary"
type="button"
class="verify-btn-inline"
[disabled]="
readOnly ||
!kycForm.get('gstinNumber')?.value ||
kycForm.get('gstinNumber')?.value.length < 15 ||
gstinVerifying ||
!clientId
"
(click)="verifyGstin()"
>
@if (gstinVerifying) {
<mat-progress-spinner diameter="16" mode="indeterminate"></mat-progress-spinner>
} @else {
<fa-icon icon="shield-alt"></fa-icon> Verify GSTIN
}
</button>
}
@if (gstinVerifyStatus === 'VERIFIED') {
<span class="verify-chip verified"><fa-icon icon="check-circle"></fa-icon> Verified</span>
}
</div>
</div>
@if (gstinVerifyStatus === 'VERIFIED') {
<div class="pan-result-card full-width">
<div class="pan-result-header">
<fa-icon icon="check-circle" class="pan-result-ok-icon"></fa-icon>
<span>GSTIN Verified Successfully</span>
</div>
<div class="pan-result-body">
@if (gstinVerifyName) {
<div class="pan-result-row">
<span class="pan-result-label">Trade Name / Legal Name</span>
<span class="pan-result-value name">{{ gstinVerifyName }}</span>
</div>
}
@if (gstinVerifyState) {
<div class="pan-result-row">
<span class="pan-result-label">State</span>
<span class="pan-result-value">{{ gstinVerifyState }}</span>
</div>
}
<div class="pan-result-row">
<span class="pan-result-label">Verified On</span>
<span class="pan-result-value date">{{ gstinVerifiedOn }}</span>
</div>
</div>
</div>
}
<!-- PAN — Proprietorship uses Individual PAN, others use Enterprise PAN -->
@if (constitutionMeta?.value === 'PROPRIETORSHIP') {