Files
cbsnexora/src/app/clients/clients-view/ckyc-tab/ckyc-upload-dialog/ckyc-upload-dialog.component.html
T
niteshandClaude Sonnet 5 31e99b1363 fix(clients): rebuild CKYC document upload as a popup with full proof details
The CKYC tab's document cards now open a dedicated dialog (with client-side
preview) instead of expanding inline, collecting document type, number, and
issue/expiry dates the same way client creation does. Fixes several bugs
found along the way:

- Uploading a document type that's valid for both identity and address
  (Aadhaar, Passport, etc.) no longer 404s or 500s — the "no such endpoint"
  and case-sensitive ClientIdentifierStatus.valueOf("Active") crashes are
  both resolved.
- Edit Client's Identity-proof and Address-proof steps no longer show each
  other's document when both are dual-purpose OVD types, and no longer show
  stale/mismatched front-back scans after a document type change.
- Address-proof's own document preview never showed a document at all when
  no m_client_kyc record existed yet.

Document number and issue/expiry dates are persisted on the identifier
(documentKey + a shared "Issue date: ..., Expiry date: ..." description
convention) and read back by both the CKYC tab and Edit Client so the same
details appear everywhere.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-14 11:39:14 +05:30

168 lines
6.7 KiB
HTML

<!--
Copyright since 2025 Mifos Initiative
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<div class="cud-header">
<div class="cud-header__badge">
<fa-icon icon="folder-open"></fa-icon>
</div>
<div class="cud-header__text">
<h2 mat-dialog-title class="cud-title">{{ data.title }}</h2>
<p class="cud-subtitle">Fill in the document details, then attach the scan.</p>
</div>
<button mat-icon-button mat-dialog-close class="cud-close" aria-label="Close">
<fa-icon icon="times"></fa-icon>
</button>
</div>
<mat-dialog-content class="cud-body">
@if (data.isProofKind) {
<div class="cud-section">
<span class="cud-section__label"><fa-icon icon="id-card"></fa-icon> Document type</span>
<mat-form-field appearance="outline" class="cud-field" subscriptSizing="dynamic">
<mat-select
[value]="selectedProofType"
[compareWith]="compareProofType"
placeholder="Select document type"
(selectionChange)="onProofTypeChange($event.value)"
>
@for (p of data.proofOptions; track p.id) {
<mat-option [value]="p">{{ p.name }}</mat-option>
}
</mat-select>
</mat-form-field>
<p class="cud-hint">Upload requirements will update automatically based on the type you pick.</p>
</div>
@if (selectedProofType) {
@if (selectedProofType.info) {
<div class="cud-banner">
<fa-icon icon="check-circle"></fa-icon>
<span>{{ selectedProofType.info }}</span>
</div>
}
<div class="cud-section">
<span class="cud-section__label">
<fa-icon icon="hashtag"></fa-icon>
{{ selectedProofType.documentKeyLabel || 'Document number' }}
</span>
<mat-form-field appearance="outline" class="cud-field" subscriptSizing="dynamic">
<input
matInput
[(ngModel)]="documentKey"
[ngModelOptions]="{ standalone: true }"
[placeholder]="selectedProofType.documentKeyPlaceholder || 'Enter document number'"
/>
</mat-form-field>
</div>
<div class="cud-section cud-section--dates">
<div class="cud-date-field">
<span class="cud-section__label"><fa-icon icon="calendar-check"></fa-icon> Issue date</span>
<mat-form-field appearance="outline" class="cud-field" subscriptSizing="dynamic">
<input
matInput
readonly
[matDatepicker]="issuePicker"
[(ngModel)]="issueDate"
[ngModelOptions]="{ standalone: true }"
placeholder="Optional"
(click)="issuePicker.open()"
/>
<mat-datepicker-toggle matSuffix [for]="issuePicker"></mat-datepicker-toggle>
<mat-datepicker #issuePicker></mat-datepicker>
</mat-form-field>
</div>
<div class="cud-date-field">
<span class="cud-section__label"><fa-icon icon="calendar-check"></fa-icon> Expiry date</span>
<mat-form-field appearance="outline" class="cud-field" subscriptSizing="dynamic">
<input
matInput
readonly
[matDatepicker]="expiryPicker"
[(ngModel)]="expiryDate"
[ngModelOptions]="{ standalone: true }"
placeholder="Optional"
(click)="expiryPicker.open()"
/>
<mat-datepicker-toggle matSuffix [for]="expiryPicker"></mat-datepicker-toggle>
<mat-datepicker #expiryPicker></mat-datepicker>
</mat-form-field>
</div>
</div>
<div class="cud-divider"><span>Scan</span></div>
<div class="cud-grid" [class.cud-grid--two]="sides === 2">
<div class="cud-slot">
<span class="cud-slot__label">{{ sides === 2 ? 'Front side' : 'Document' }}</span>
<label class="cud-dropzone" [class.cud-dropzone--has]="front">
<input type="file" [attr.accept]="data.accept" hidden (change)="onFilePick('front', $event)" />
@if (frontPreview) {
<img [src]="frontPreview" alt="Front side preview" class="cud-dropzone__img" />
} @else if (front) {
<fa-icon icon="file-alt" class="cud-dropzone__icon"></fa-icon>
<p class="cud-dropzone__name">{{ front.name }}</p>
} @else {
<fa-icon icon="cloud-upload-alt" class="cud-dropzone__icon"></fa-icon>
<p class="cud-dropzone__text">Click to choose file</p>
}
</label>
</div>
@if (sides === 2) {
<div class="cud-slot">
<span class="cud-slot__label">Back side</span>
<label class="cud-dropzone" [class.cud-dropzone--has]="back">
<input type="file" [attr.accept]="data.accept" hidden (change)="onFilePick('back', $event)" />
@if (backPreview) {
<img [src]="backPreview" alt="Back side preview" class="cud-dropzone__img" />
} @else if (back) {
<fa-icon icon="file-alt" class="cud-dropzone__icon"></fa-icon>
<p class="cud-dropzone__name">{{ back.name }}</p>
} @else {
<fa-icon icon="cloud-upload-alt" class="cud-dropzone__icon"></fa-icon>
<p class="cud-dropzone__text">Click to choose file</p>
}
</label>
</div>
}
</div>
}
} @else {
<div class="cud-grid">
<div class="cud-slot">
<span class="cud-slot__label">Document</span>
<label class="cud-dropzone" [class.cud-dropzone--has]="front">
<input type="file" [attr.accept]="data.accept" hidden (change)="onFilePick('front', $event)" />
@if (frontPreview) {
<img [src]="frontPreview" alt="Preview" class="cud-dropzone__img" />
} @else if (front) {
<fa-icon icon="file-alt" class="cud-dropzone__icon"></fa-icon>
<p class="cud-dropzone__name">{{ front.name }}</p>
} @else {
<fa-icon icon="cloud-upload-alt" class="cud-dropzone__icon"></fa-icon>
<p class="cud-dropzone__text">Click to choose file</p>
}
</label>
</div>
</div>
}
</mat-dialog-content>
<mat-dialog-actions class="cud-actions" align="end">
<button mat-stroked-button type="button" (click)="cancel()">
<fa-icon icon="times"></fa-icon>
&nbsp;Cancel
</button>
<button mat-flat-button color="primary" type="button" [disabled]="!canSubmit" (click)="submit()">
<fa-icon icon="upload"></fa-icon>
&nbsp;Upload
</button>
</mat-dialog-actions>